Added usecase editing & app framework handling in any place
This commit is contained in:
@@ -87,6 +87,7 @@ import {
|
||||
ExpandMore as ExpandMoreIcon,
|
||||
VpnKey as VpnKeyIcon,
|
||||
AddComment as AddCommentIcon,
|
||||
Edit as EditIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
import {
|
||||
@@ -251,6 +252,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const [triggerFolders, setTriggerFolders] = React.useState([]);
|
||||
const [workflows, setWorkflows] = React.useState([]);
|
||||
const [showEnvironment, setShowEnvironment] = React.useState(false);
|
||||
const [editWorkflowDetails, setEditWorkflowDetails] = React.useState(false);
|
||||
|
||||
const [workflow, setWorkflow] = React.useState({});
|
||||
const [userSettings, setUserSettings] = React.useState({});
|
||||
@@ -1257,98 +1259,100 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const executeWorkflow = (executionArgument, startNode, hasSaved) => {
|
||||
if (hasSaved === false) {
|
||||
setExecutionRequestStarted(true);
|
||||
saveWorkflow(workflow, executionArgument, startNode);
|
||||
console.log("FIXME: Might have forgotten to save before executing.");
|
||||
return;
|
||||
}
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
if (hasSaved === false) {
|
||||
setExecutionRequestStarted(true);
|
||||
saveWorkflow(workflow, executionArgument, startNode);
|
||||
console.log("FIXME: Might have forgotten to save before executing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (workflow.public) {
|
||||
alert.info("Save it to get a new version");
|
||||
}
|
||||
if (workflow.public) {
|
||||
alert.info("Save it to get a new version");
|
||||
}
|
||||
|
||||
var returncheck = monitorUpdates();
|
||||
if (!returncheck) {
|
||||
alert.error("No startnode set.");
|
||||
return;
|
||||
}
|
||||
var returncheck = monitorUpdates();
|
||||
if (!returncheck) {
|
||||
alert.error("No startnode set.");
|
||||
return;
|
||||
}
|
||||
|
||||
setVisited([]);
|
||||
setExecutionRequest({});
|
||||
stop();
|
||||
setVisited([]);
|
||||
setExecutionRequest({});
|
||||
stop();
|
||||
|
||||
var curelements = cy.elements();
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].addClass("not-executing-highlight");
|
||||
}
|
||||
var curelements = cy.elements();
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].addClass("not-executing-highlight");
|
||||
}
|
||||
|
||||
const data = { execution_argument: executionArgument, start: startNode };
|
||||
fetch(
|
||||
globalUrl + "/api/v1/workflows/" + props.match.params.key + "/execute",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
}
|
||||
const data = { execution_argument: executionArgument, start: startNode };
|
||||
fetch(
|
||||
globalUrl + "/api/v1/workflows/" + props.match.params.key + "/execute",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to start: " + responseJson.reason);
|
||||
setExecutionRunning(false);
|
||||
setExecutionRequestStarted(false);
|
||||
stop();
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
alert.error("Failed to start: " + responseJson.reason);
|
||||
setExecutionRunning(false);
|
||||
setExecutionRequestStarted(false);
|
||||
stop();
|
||||
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].removeClass("not-executing-highlight");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
setExecutionRunning(true);
|
||||
setExecutionRequestStarted(false);
|
||||
}
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].removeClass("not-executing-highlight");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
setExecutionRunning(true);
|
||||
setExecutionRequestStarted(false);
|
||||
}
|
||||
|
||||
if (
|
||||
responseJson.execution_id === "" ||
|
||||
responseJson.execution_id === undefined ||
|
||||
responseJson.authorization === "" ||
|
||||
responseJson.authorization === undefined
|
||||
) {
|
||||
alert.error("Something went wrong during execution startup");
|
||||
console.log("BAD RESPONSE FOR EXECUTION: ", responseJson);
|
||||
setExecutionRunning(false);
|
||||
setExecutionRequestStarted(false);
|
||||
stop();
|
||||
if (
|
||||
responseJson.execution_id === "" ||
|
||||
responseJson.execution_id === undefined ||
|
||||
responseJson.authorization === "" ||
|
||||
responseJson.authorization === undefined
|
||||
) {
|
||||
alert.error("Something went wrong during execution startup");
|
||||
console.log("BAD RESPONSE FOR EXECUTION: ", responseJson);
|
||||
setExecutionRunning(false);
|
||||
setExecutionRequestStarted(false);
|
||||
stop();
|
||||
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].removeClass("not-executing-highlight");
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < curelements.length; i++) {
|
||||
curelements[i].removeClass("not-executing-highlight");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setExecutionRequest({
|
||||
execution_id: responseJson.execution_id,
|
||||
authorization: responseJson.authorization,
|
||||
});
|
||||
setExecutionData({});
|
||||
setExecutionModalOpen(true);
|
||||
setExecutionModalView(1);
|
||||
start();
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
});
|
||||
setExecutionRequest({
|
||||
execution_id: responseJson.execution_id,
|
||||
authorization: responseJson.authorization,
|
||||
});
|
||||
setExecutionData({});
|
||||
setExecutionModalOpen(true);
|
||||
setExecutionModalView(1);
|
||||
start();
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
// This can be used to only show prioritzed ones later
|
||||
@@ -1542,6 +1546,35 @@ const AngularWorkflow = (defaultprops) => {
|
||||
});
|
||||
};
|
||||
|
||||
// Searhc by username, userId, workflow, appId should all work
|
||||
const getUserProfile = (username) => {
|
||||
fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
}
|
||||
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Found creator: ", responseJson)
|
||||
setCreatorProfile(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
const getWorkflow = (workflow_id, sourcenode) => {
|
||||
console.log(
|
||||
//`Getting workflow ${workflow_id} with append value ${sourcenode}`
|
||||
@@ -1582,9 +1615,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
|
||||
if (responseJson.public) {
|
||||
alert.info(
|
||||
"This workflow is public. Save the workflow to "
|
||||
);
|
||||
alert.info("This workflow is public. Save the workflow to start using it.");
|
||||
|
||||
console.log("RESP: ", responseJson)
|
||||
if (Object.getOwnPropertyNames(creatorProfile).length === 0) {
|
||||
@@ -8686,13 +8717,13 @@ const AngularWorkflow = (defaultprops) => {
|
||||
workflow.triggers[selectedTriggerIndex].parameters[2] = {"name": "auth_headers", "value": ""}
|
||||
setWorkflow(workflow)
|
||||
} else {
|
||||
// Always update
|
||||
const newUrl = referenceUrl+"webhook_"+selectedTrigger.id
|
||||
//console.log("Validating webhook url: ", newUrl)
|
||||
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
||||
console.log("Url is wrong - updating")
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl
|
||||
setWorkflow(workflow)
|
||||
if (selectedTrigger.environment !== "cloud") {
|
||||
const newUrl = referenceUrl+"webhook_"+selectedTrigger.id
|
||||
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
||||
console.log("Url is wrong - should update. This functionality is temporarily disabled.")
|
||||
//workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl
|
||||
//setWorkflow(workflow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8898,13 +8929,13 @@ const AngularWorkflow = (defaultprops) => {
|
||||
// Always update
|
||||
const newUrl = referenceUrl + "webhook_" + selectedTrigger.id;
|
||||
//console.log("Validating webhook url: ", newUrl);
|
||||
if (
|
||||
newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value
|
||||
) {
|
||||
console.log("Url is wrong - updating");
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl;
|
||||
setWorkflow(workflow);
|
||||
}
|
||||
if (selectedTrigger.environment !== "cloud") {
|
||||
if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) {
|
||||
console.log("Url is wrong. NOT updating because of hybrid.");
|
||||
//workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl;
|
||||
//setWorkflow(workflow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const trigger_header_auth =
|
||||
@@ -8976,28 +9007,21 @@ const AngularWorkflow = (defaultprops) => {
|
||||
fullWidth
|
||||
onChange={(e) => {
|
||||
selectedTrigger.environment = e.target.value;
|
||||
setSelectedTrigger(selectedTrigger);
|
||||
if (e.target.value === "cloud") {
|
||||
const tmpvalue =
|
||||
workflow.triggers[
|
||||
selectedTriggerIndex
|
||||
].parameters[0].value.split("/");
|
||||
const tmpvalue = workflow.triggers[selectedTriggerIndex].parameters[0].value.split("/");
|
||||
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
||||
const newurl = "https://shuffler.io/" + urlpath.join("/");
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value =
|
||||
newurl;
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newurl;
|
||||
} else {
|
||||
const tmpvalue =
|
||||
workflow.triggers[
|
||||
selectedTriggerIndex
|
||||
].parameters[0].value.split("/");
|
||||
const tmpvalue = workflow.triggers[selectedTriggerIndex].parameters[0].value.split("/");
|
||||
const urlpath = tmpvalue.slice(3, tmpvalue.length);
|
||||
const newurl =
|
||||
window.location.origin + "/" + urlpath.join("/");
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value =
|
||||
newurl;
|
||||
const newurl = window.location.origin + "/" + urlpath.join("/");
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value = newurl;
|
||||
}
|
||||
|
||||
console.log("New value: ", workflow.triggers[selectedTriggerIndex].parameters[0])
|
||||
selectedTrigger.parameters[0] = workflow.triggers[selectedTriggerIndex].parameters[0]
|
||||
setSelectedTrigger(selectedTrigger);
|
||||
setWorkflow(workflow);
|
||||
setUpdate(Math.random());
|
||||
}}
|
||||
@@ -9086,10 +9110,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}
|
||||
}}
|
||||
helperText={
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0]
|
||||
.value !== undefined &&
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0]
|
||||
.value !== null &&
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined &&
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value !== null &&
|
||||
(workflow.triggers[
|
||||
selectedTriggerIndex
|
||||
].parameters[0].value.includes("localhost") ||
|
||||
@@ -9115,7 +9137,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
}}
|
||||
fullWidth
|
||||
disabled
|
||||
defaultValue={
|
||||
value={
|
||||
workflow.triggers[selectedTriggerIndex].parameters[0].value
|
||||
}
|
||||
color="primary"
|
||||
@@ -10515,31 +10537,33 @@ const AngularWorkflow = (defaultprops) => {
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip color="primary" title="Save (ctrl+s)" placement="top">
|
||||
<span>
|
||||
<Button
|
||||
disabled={savingState !== 0}
|
||||
color="primary"
|
||||
style={{
|
||||
height: workflow.public ? 100 : 50,
|
||||
width: workflow.public ? 100 : 64,
|
||||
marginLeft: 10,
|
||||
}}
|
||||
variant={
|
||||
lastSaved && !workflow.public ? "outlined" : "contained"
|
||||
}
|
||||
onClick={() => saveWorkflow()}
|
||||
>
|
||||
{savingState === 2 ? (
|
||||
<CircularProgress style={{ height: 35, width: 35 }} />
|
||||
) : savingState === 1 ? (
|
||||
<DoneIcon style={{ color: green }} />
|
||||
) : (
|
||||
<SaveIcon />
|
||||
)}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{userdata.avatar === creatorProfile.github_avatar ? null :
|
||||
<Tooltip color="primary" title="Save (ctrl+s)" placement="top">
|
||||
<span>
|
||||
<Button
|
||||
disabled={savingState !== 0}
|
||||
color="primary"
|
||||
style={{
|
||||
height: workflow.public ? 100 : 50,
|
||||
width: workflow.public ? 100 : 64,
|
||||
marginLeft: 10,
|
||||
}}
|
||||
variant={
|
||||
lastSaved && !workflow.public ? "outlined" : "contained"
|
||||
}
|
||||
onClick={() => saveWorkflow()}
|
||||
>
|
||||
{savingState === 2 ? (
|
||||
<CircularProgress style={{ height: 35, width: 35 }} />
|
||||
) : savingState === 1 ? (
|
||||
<DoneIcon style={{ color: green }} />
|
||||
) : (
|
||||
<SaveIcon />
|
||||
)}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{workflow.public ? (
|
||||
<Tooltip
|
||||
color="secondary"
|
||||
@@ -10774,6 +10798,8 @@ const AngularWorkflow = (defaultprops) => {
|
||||
globalUrl={globalUrl}
|
||||
setSelectedActionEnvironment={setSelectedActionEnvironment}
|
||||
requiresAuthentication={requiresAuthentication}
|
||||
setLastSaved={setLastSaved}
|
||||
lastSaved={lastSaved}
|
||||
/>
|
||||
|
||||
} else if (Object.getOwnPropertyNames(selectedComment).length > 0) {
|
||||
@@ -10849,34 +10875,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
//}}>Execute websocket</Button>
|
||||
//
|
||||
|
||||
// Searhc by username, userId, workflow, appId should all work
|
||||
const getUserProfile = (username) => {
|
||||
fetch(`${globalUrl}/api/v1/users/creators/${username}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
}
|
||||
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
console.log("Found creator: ", responseJson)
|
||||
if (responseJson.success !== false) {
|
||||
setCreatorProfile(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
})
|
||||
}
|
||||
|
||||
const leftView = workflow.public === true ?
|
||||
<div style={{minHeight: "80vh", height: "100%", minWidth: leftBarSize-70, maxWidth: leftBarSize-70, zIndex: 0, padding: 35, borderRight: "1px solid rgba(91,96,100,1)",}}>
|
||||
@@ -10929,14 +10927,6 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</div>
|
||||
</div>
|
||||
: null }
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<Typography variant="body1">
|
||||
Mitre Att&ck:
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
TBD
|
||||
</Typography>
|
||||
</div>
|
||||
{appGroup.length > 0 ?
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<Typography variant="body1">
|
||||
@@ -10967,6 +10957,15 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</AvatarGroup>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<Typography variant="body1">
|
||||
Mitre Att&ck:
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
TBD
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{display: "flex", marginTop: 10, }}>
|
||||
<Typography variant="body1">
|
||||
Related Workflows:
|
||||
@@ -10985,6 +10984,19 @@ const AngularWorkflow = (defaultprops) => {
|
||||
</Typography>
|
||||
</div>
|
||||
: null}
|
||||
{userdata.avatar === creatorProfile.github_avatar ?
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
style={{marginTop: 15, }}
|
||||
onClick={() => {
|
||||
setEditWorkflowDetails(true)
|
||||
}}
|
||||
>
|
||||
Edit Details
|
||||
</Button>
|
||||
: null}
|
||||
</div>
|
||||
: isMobile && leftViewOpen ?
|
||||
<div
|
||||
@@ -12311,23 +12323,35 @@ const parsedExecutionArgument = () => {
|
||||
|
||||
return (
|
||||
<div style={{maxWidth: 600, overflowX: "hidden", }}>
|
||||
<IconButton
|
||||
style={{
|
||||
marginBottom: 0, marginTop: 5, cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!showVariable) {
|
||||
setOpen(!open)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{data.value.length > 60 ?
|
||||
<IconButton
|
||||
style={{
|
||||
marginBottom: 0, marginTop: 5, cursor: "pointer",
|
||||
padding: 3,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!showVariable) {
|
||||
setOpen(!open)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{}}
|
||||
>
|
||||
<b>{data.name}</b> {showVariable ? data.value : null}
|
||||
</Typography>
|
||||
</IconButton>
|
||||
:
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{}}
|
||||
>
|
||||
<b>{data.name}</b>: {showVariable ? data.value : null}
|
||||
</Typography>
|
||||
</IconButton>
|
||||
}
|
||||
{open ?
|
||||
<Typography
|
||||
variant="body2"
|
||||
@@ -12697,6 +12721,32 @@ const parsedExecutionArgument = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const editWorkflowModal =
|
||||
<Dialog
|
||||
open={editWorkflowDetails}
|
||||
PaperComponent={PaperComponent}
|
||||
hideBackdrop={true}
|
||||
disableEnforceFocus={true}
|
||||
disableBackdropClick={true}
|
||||
style={{ pointerEvents: "none" }}
|
||||
aria-labelledby="draggable-dialog-title"
|
||||
onClose={() => {
|
||||
setEditWorkflowDetails(false)
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
pointerEvents: "auto",
|
||||
backgroundColor: surfaceColor,
|
||||
color: "white",
|
||||
border: theme.palette.defaultBorder,
|
||||
maxWidth: "100%",
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Edit workflow!
|
||||
</Dialog>
|
||||
|
||||
const ExecutionVariableModal = (props) => {
|
||||
const { variableInfo } = props
|
||||
|
||||
@@ -13604,6 +13654,7 @@ const parsedExecutionArgument = () => {
|
||||
{authenticationModal}
|
||||
{codePopoutModal}
|
||||
{configureWorkflowModal}
|
||||
{editWorkflowModal}
|
||||
<TextField
|
||||
id="copy_element_shuffle"
|
||||
value={to_be_copied}
|
||||
|
||||
@@ -4,6 +4,7 @@ import DetectionFramework from "../components/DetectionFramework.jsx";
|
||||
// nodejs library that concatenates classes
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
@@ -11,7 +12,9 @@ import { useAlert } from "react-alert";
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Grid,
|
||||
Paper,
|
||||
@@ -23,6 +26,7 @@ import {
|
||||
DoneAll as DoneAllIcon,
|
||||
Description as DescriptionIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
Edit as EditIcon,
|
||||
} from "@material-ui/icons";
|
||||
|
||||
import WorkflowPaper from "../components/WorkflowPaper.jsx"
|
||||
@@ -53,13 +57,114 @@ import {
|
||||
TreeMapRect,
|
||||
} from 'reaviz';
|
||||
|
||||
const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn}) => {
|
||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||
const [expandedItem, setExpandedItem] = useState(-1);
|
||||
const [inputUsecase, setInputUsecase] = useState({});
|
||||
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [description, setDescription] = useState("");
|
||||
const [video, setVideo] = useState("");
|
||||
const [blogpost, setBlogpost] = useState("");
|
||||
|
||||
const [mitreTags, setMitreTags] = useState([]);
|
||||
let navigate = useNavigate();
|
||||
if (keys === undefined || keys === null || keys.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const getUsecase = (name, index, subindex) => {
|
||||
fetch(globalUrl + "/api/v1/workflows/usecases/"+name.replaceAll(" ", "_"), {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
setInputUsecase({
|
||||
"name": name,
|
||||
})
|
||||
} else {
|
||||
setInputUsecase(responseJson)
|
||||
}
|
||||
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
setInputUsecase({})
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
})
|
||||
}
|
||||
|
||||
const setUsecaseItem = (inputUsecase) => {
|
||||
var parsedUsecase = inputUsecase
|
||||
|
||||
if (blogpost !== inputUsecase.blogpost) {
|
||||
inputUsecase.blogpost = blogpost
|
||||
parsedUsecase.blogpost = blogpost
|
||||
}
|
||||
|
||||
if (video !== inputUsecase.video) {
|
||||
inputUsecase.video = video
|
||||
parsedUsecase.video = video
|
||||
}
|
||||
|
||||
if (description !== inputUsecase.description) {
|
||||
inputUsecase.description = description
|
||||
parsedUsecase.description = description
|
||||
}
|
||||
|
||||
if (mitreTags !== inputUsecase.mitre) {
|
||||
inputUsecase.mitre = mitreTags
|
||||
parsedUsecase.mitre = mitreTags
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/workflows/usecases", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(parsedUsecase),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed updating: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to update framework for your org.")
|
||||
}
|
||||
} else {
|
||||
//alert.info("Updated usecase.")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
//alert.error(error.toString());
|
||||
//setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{marginTop: 25, minHeight: 1000,}}>
|
||||
<Typography variant="h1">
|
||||
@@ -81,6 +186,23 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
}
|
||||
|
||||
const selectedItem = subindex === expandedItem && index === expandedIndex
|
||||
if (selectedItem && subcase.name !== undefined && inputUsecase.name !== undefined) {
|
||||
if (subcase.name.toLowerCase().replaceAll(" ", "_") === inputUsecase.name.toLowerCase().replaceAll(" ", "_")) {
|
||||
console.log("Input: ", inputUsecase)
|
||||
if (inputUsecase.description !== undefined && inputUsecase.description !== null) {
|
||||
subcase.description = inputUsecase.description
|
||||
}
|
||||
|
||||
if (inputUsecase.blogpost !== undefined && inputUsecase.blogpost !== null) {
|
||||
subcase.blogpost = inputUsecase.blogpost
|
||||
}
|
||||
|
||||
if (inputUsecase.video !== undefined && inputUsecase.video !== null) {
|
||||
subcase.video = inputUsecase.video
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const finished = subcase.matches.length > 0
|
||||
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
||||
const backgroundColor = "inherit"
|
||||
@@ -90,14 +212,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
<Grid item xs={selectedItem ? 12 : 4} key={subindex} style={{minHeight: 110,}} onClick={() => {
|
||||
if (selectedItem) {
|
||||
} else {
|
||||
if (subcase.description !== undefined && subcase.description !== null && subcase.description.length > 0) {
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
}
|
||||
//if (subcase.description !== undefined && subcase.description !== null && subcase.description.length > 0) {
|
||||
getUsecase(subcase.name, index, subindex)
|
||||
//}
|
||||
}
|
||||
}}>
|
||||
<Paper style={{padding: "30px 30px 30px 30px", minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
console.log("Clicked: ", subcase)
|
||||
}}>
|
||||
{!selectedItem ?
|
||||
<div style={{textAlign: "left", position: "relative",}}>
|
||||
@@ -131,7 +251,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
placement="top"
|
||||
>
|
||||
<IconButton
|
||||
style={{paddingTop: 5, }}
|
||||
style={{marginTop: finished ? 5 : 40, }}
|
||||
onClick={(e) => {
|
||||
}}
|
||||
>
|
||||
@@ -168,10 +288,37 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
<Typography variant="h6">
|
||||
<b>{subcase.name}</b>
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
Description: {subcase.description}
|
||||
</Typography>
|
||||
<div style={{ position: "absolute", top: 0, right: 0 }}>
|
||||
{isLoggedIn === true ?
|
||||
<Tooltip
|
||||
title="Click to edit"
|
||||
placement="top"
|
||||
>
|
||||
<IconButton
|
||||
style={{paddingTop: 5, }}
|
||||
onClick={(e) => {
|
||||
setEditing(true)
|
||||
if (subcase.description !== undefined && subcase.description !== null) {
|
||||
setDescription(subcase.description)
|
||||
}
|
||||
|
||||
if (subcase.blogpost !== undefined && subcase.blogpost !== null) {
|
||||
setBlogpost(subcase.blogpost)
|
||||
}
|
||||
|
||||
if (subcase.video !== undefined && subcase.video !== null) {
|
||||
setVideo(subcase.video)
|
||||
}
|
||||
|
||||
if (subcase.mitre !== undefined && subcase.mitre !== null) {
|
||||
setMitreTags(subcase.mitre)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EditIcon style={{ color: usecase.color }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
: null}
|
||||
{subcase.blogpost !== null && subcase.blogpost !== undefined && subcase.blogpost.length > 0 ?
|
||||
<a
|
||||
href={subcase.blogpost}
|
||||
@@ -224,6 +371,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
onClick={(e) => {
|
||||
setExpandedItem(-1)
|
||||
setExpandedIndex(-1)
|
||||
setEditing(false)
|
||||
setInputUsecase({})
|
||||
}}
|
||||
>
|
||||
<CloseIcon style={{ color: "white" }} />
|
||||
@@ -231,65 +380,168 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl}) => {
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{marginTop: 25, display: "flex", minHeight: 400, maxHeight: 400, }}>
|
||||
{/*
|
||||
<img
|
||||
alt={subcase.name}
|
||||
src={"/images/detectionframework.png"}
|
||||
style={{
|
||||
flex: 1,
|
||||
height: 400,
|
||||
{editing ?
|
||||
<div style={{flex: 1, marginRight: 50, }}>
|
||||
<Typography variant="h6">
|
||||
Editing!
|
||||
</Typography>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginRight: 10,
|
||||
}}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
color="primary"
|
||||
label="Description"
|
||||
placeholder={"Description"}
|
||||
value={description}
|
||||
onChange={(event) => {
|
||||
setDescription(event.target.value)
|
||||
}}
|
||||
id="descriptionEditng"
|
||||
/>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginRight: 10,
|
||||
}}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
color="primary"
|
||||
label="Blogpost"
|
||||
placeholder={"Blogpost"}
|
||||
value={blogpost}
|
||||
onChange={(event) => {
|
||||
setBlogpost(event.target.value)
|
||||
}}
|
||||
id="blogpostEditing"
|
||||
/>
|
||||
<TextField
|
||||
style={{
|
||||
marginTop: 10,
|
||||
marginRight: 10,
|
||||
}}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
color="primary"
|
||||
label="Video"
|
||||
placeholder={"Video"}
|
||||
value={video}
|
||||
onChange={(event) => {
|
||||
setVideo(event.target.value)
|
||||
}}
|
||||
id="videoEditing"
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#383b49",
|
||||
border: "none",
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 5,
|
||||
marginTop: 20,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => {
|
||||
setDescription("")
|
||||
setVideo("")
|
||||
setBlogpost("")
|
||||
setEditing(false)
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
style={{
|
||||
color: "white",
|
||||
background: "#f85a3e",
|
||||
border: "none",
|
||||
height: 35,
|
||||
flex: 1,
|
||||
marginLeft: 10,
|
||||
marginTop: 20,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={(event) => {
|
||||
setEditing(false)
|
||||
setUsecaseItem(inputUsecase)
|
||||
setDescription("")
|
||||
setVideo("")
|
||||
setBlogpost("")
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div style={{flex: 1, textAlign: "left",}}>
|
||||
<Typography variant="body1">
|
||||
{subcase.description}
|
||||
</Typography>
|
||||
<Typography variant="h6" style={{marginTop: 15, }}>
|
||||
Your workflow{subcase.matches.length === 1 ? "" : "s"} ({subcase.matches.length})
|
||||
</Typography>
|
||||
{subcase.matches.length > 0 ?
|
||||
<Grid container xs={3} style={{maxWidth: 325, marginTop: 10, }}>
|
||||
{subcase.matches.map((workflow, workflowindex) => {
|
||||
return (
|
||||
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
||||
<WorkflowPaper key={workflowindex} data={workflow} />
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflow selected yet.
|
||||
</Typography>
|
||||
</div>
|
||||
}
|
||||
{isCloud !== false ?
|
||||
<div>
|
||||
<Typography variant="h6" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {
|
||||
navigate("/search?tab=workflows&q="+subcase.name)
|
||||
}}>
|
||||
Public workflows
|
||||
</Typography>
|
||||
{/*
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflows yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
}
|
||||
<div style={{
|
||||
height: 400,
|
||||
width: 400,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
/>
|
||||
*/}
|
||||
|
||||
<div style={{
|
||||
height: 400,
|
||||
width: 400,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}>
|
||||
<DetectionFramework
|
||||
frameworkData={undefined}
|
||||
selectedOption={"Draw"}
|
||||
showOptions={false}
|
||||
isLoaded={true}
|
||||
isLoggedIn={true}
|
||||
globalUrl={globalUrl}
|
||||
size={0.7}
|
||||
/>
|
||||
}}>
|
||||
<DetectionFramework
|
||||
inputUsecase={inputUsecase}
|
||||
frameworkData={frameworkData}
|
||||
selectedOption={"Draw"}
|
||||
showOptions={false}
|
||||
isLoaded={true}
|
||||
isLoggedIn={true}
|
||||
globalUrl={globalUrl}
|
||||
size={0.7}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{flex: 1, marginLeft: 10, textAlign: "center",}}>
|
||||
<Typography variant="h6">
|
||||
Your workflow{subcase.matches.length === 1 ? "" : "s"} ({subcase.matches.length})
|
||||
</Typography>
|
||||
{subcase.matches.length > 0 ?
|
||||
<Grid container xs={3} style={{maxWidth: 325, margin: "auto", marginTop: 10, itemAlign: "center", }}>
|
||||
{subcase.matches.map((workflow, workflowindex) => {
|
||||
return (
|
||||
<Grid key={workflowindex} item index={workflowindex} xs={12}>
|
||||
<WorkflowPaper key={workflowindex} data={workflow} />
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflow selected yet.
|
||||
</Typography>
|
||||
</div>
|
||||
}
|
||||
{isCloud !== false ?
|
||||
<Typography variant="h6">
|
||||
Public workflows
|
||||
</Typography>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Paper>
|
||||
@@ -449,11 +701,44 @@ const Dashboard = (props) => {
|
||||
const [selectedUsecases, setSelectedUsecases] = useState([]);
|
||||
const [usecases, setUsecases] = useState([]);
|
||||
const [workflows, setWorkflows] = useState([]);
|
||||
const [frameworkData, setFrameworkData] = useState(undefined);
|
||||
|
||||
const isCloud =
|
||||
window.location.host === "localhost:3002" ||
|
||||
window.location.host === "shuffler.io";
|
||||
|
||||
const getFramework = () => {
|
||||
fetch(globalUrl + "/api/v1/apps/frameworkConfiguration", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
//alert.error("Failed loading: " + responseJson.reason)
|
||||
} else {
|
||||
//alert.error("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
setFrameworkData(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
const getAvailableWorkflows = () => {
|
||||
fetch(globalUrl + "/api/v1/workflows", {
|
||||
method: "GET",
|
||||
@@ -605,6 +890,7 @@ const Dashboard = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
getAvailableWorkflows()
|
||||
getFramework()
|
||||
//fetchUsecases()
|
||||
}, []);
|
||||
|
||||
@@ -865,7 +1151,7 @@ const Dashboard = (props) => {
|
||||
</div>
|
||||
|
||||
{usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
<div style={{ display: "flex", marginLeft: 120,}}>
|
||||
<div style={{ display: "flex", marginLeft: 180, }}>
|
||||
{usecases.map((usecase, index) => {
|
||||
return (
|
||||
<Chip
|
||||
@@ -898,7 +1184,13 @@ const Dashboard = (props) => {
|
||||
</div>
|
||||
: null}
|
||||
|
||||
<UsecaseListComponent keys={selectedUsecases} isCloud={isCloud} globalUrl={globalUrl} />
|
||||
<UsecaseListComponent
|
||||
isLoggedIn={isLoggedIn}
|
||||
frameworkData={frameworkData}
|
||||
keys={selectedUsecases}
|
||||
isCloud={isCloud}
|
||||
globalUrl={globalUrl}
|
||||
/>
|
||||
|
||||
{treeKeys.length > 0 ?
|
||||
<TreeChart keys={treeKeys} />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactDOM from "react-dom"
|
||||
|
||||
import DetectionFramework from "../components/DetectionFramework.jsx";
|
||||
import { useAlert } from "react-alert";
|
||||
@@ -39,14 +40,18 @@ const Framework = (props) => {
|
||||
} else {
|
||||
alert.error("Failed to load framework for your org.")
|
||||
}
|
||||
} else {
|
||||
|
||||
setFrameworkLoaded(true)
|
||||
setFrameworkData(responseJson)
|
||||
} else {
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
setFrameworkData(responseJson)
|
||||
setFrameworkLoaded(true)
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
alert.error(error.toString());
|
||||
setFrameworkLoaded(true)
|
||||
alert.error(error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,7 +68,7 @@ const Framework = (props) => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
{frameworkLoaded === true ?
|
||||
{frameworkLoaded === true && isLoaded ?
|
||||
<DetectionFramework
|
||||
frameworkData={frameworkData}
|
||||
selectedOption={"Draw"}
|
||||
|
||||
@@ -509,6 +509,8 @@ const Workflows = (props) => {
|
||||
const [newWorkflowTags, setNewWorkflowTags] = React.useState([]);
|
||||
|
||||
const [defaultReturnValue, setDefaultReturnValue] = React.useState("");
|
||||
const [blogpost, setBlogpost] = React.useState("");
|
||||
const [status, setStatus] = React.useState("test");
|
||||
|
||||
const [deleteModalOpen, setDeleteModalOpen] = React.useState(false);
|
||||
const [publishModalOpen, setPublishModalOpen] = React.useState(false);
|
||||
@@ -816,7 +818,10 @@ const Workflows = (props) => {
|
||||
data.tags,
|
||||
data.default_return_value,
|
||||
{},
|
||||
false
|
||||
false,
|
||||
[],
|
||||
"",
|
||||
data.status,
|
||||
)
|
||||
.then((response) => {
|
||||
if (response !== undefined) {
|
||||
@@ -830,7 +835,10 @@ const Workflows = (props) => {
|
||||
data.tags,
|
||||
data.default_return_value,
|
||||
data,
|
||||
false
|
||||
false,
|
||||
[],
|
||||
"",
|
||||
data.status
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success(`Successfully imported ${data.name}`);
|
||||
@@ -883,12 +891,18 @@ const Workflows = (props) => {
|
||||
if (responseJson !== undefined) {
|
||||
setWorkflows(responseJson);
|
||||
fetchUsecases(responseJson)
|
||||
|
||||
var setProdFilter = false
|
||||
|
||||
if (responseJson !== undefined) {
|
||||
|
||||
var actionnamelist = [];
|
||||
var parsedactionlist = [];
|
||||
for (var key in responseJson) {
|
||||
const workflow = responseJson[key]
|
||||
if (workflow.status === "production") {
|
||||
setProdFilter = true
|
||||
}
|
||||
|
||||
for (var actionkey in responseJson[key].actions) {
|
||||
const action = responseJson[key].actions[actionkey];
|
||||
//console.log("Action: ", action)
|
||||
@@ -905,10 +919,22 @@ const Workflows = (props) => {
|
||||
setActionImageList(parsedactionlist);
|
||||
}
|
||||
|
||||
setFilteredWorkflows(responseJson);
|
||||
setWorkflowDone(true);
|
||||
|
||||
|
||||
if (setProdFilter === true) {
|
||||
setFilters(["status:production"]);
|
||||
const newWorkflows = responseJson.filter(workflow => workflow.status === "production")
|
||||
console.log(newWorkflows)
|
||||
if (newWorkflows !== undefined && newWorkflows !== null) {
|
||||
setFilteredWorkflows(newWorkflows);
|
||||
} else {
|
||||
setFilteredWorkflows(responseJson);
|
||||
}
|
||||
} else {
|
||||
setFilteredWorkflows(responseJson);
|
||||
}
|
||||
|
||||
// Ensures the zooming happens only once per load
|
||||
setWorkflowDone(true);
|
||||
setTimeout(() => {
|
||||
setFirstLoad(false)
|
||||
}, 100)
|
||||
@@ -946,7 +972,7 @@ const Workflows = (props) => {
|
||||
if (workflow.usecase_ids !== undefined && workflow.usecase_ids !== null) {
|
||||
for (var usecasekey in workflow.usecase_ids) {
|
||||
if (workflow.usecase_ids[usecasekey].toLowerCase() === subcategory.name.toLowerCase()) {
|
||||
console.log("Got match: ", workflow.usecase_ids[usecasekey])
|
||||
//console.log("Got match: ", workflow.usecase_ids[usecasekey])
|
||||
|
||||
category.matches.push({
|
||||
"workflow": workflow.id,
|
||||
@@ -1247,7 +1273,8 @@ const Workflows = (props) => {
|
||||
|
||||
// Add correct ID's for triggers
|
||||
// Add mag
|
||||
|
||||
|
||||
data.status = "test"
|
||||
let dataStr = JSON.stringify(data);
|
||||
let dataUri =
|
||||
"data:application/json;charset=utf-8," + encodeURIComponent(dataStr);
|
||||
@@ -1638,6 +1665,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
}
|
||||
|
||||
console.log("data: ", data)
|
||||
return (
|
||||
<div style={{width: "100%", position: "relative",}}>
|
||||
<Paper square style={paperAppStyle}>
|
||||
@@ -1667,7 +1695,16 @@ const Workflows = (props) => {
|
||||
{image}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Edit ${data.name}`} placement="bottom">
|
||||
<Tooltip arrow title={
|
||||
<span style={{}}>
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
<img src={data.image} alt={data.name} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 250, minHeigth: 250, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Edit {data.name}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<Typography
|
||||
variant="body1"
|
||||
style={{
|
||||
@@ -1732,7 +1769,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Trigger amount"
|
||||
title="Amount of triggers"
|
||||
placement="bottom"
|
||||
>
|
||||
<span
|
||||
@@ -1884,6 +1921,8 @@ const Workflows = (props) => {
|
||||
editingWorkflow,
|
||||
redirect,
|
||||
currentUsecases,
|
||||
inputblogpost,
|
||||
inputstatus,
|
||||
) => {
|
||||
var method = "POST";
|
||||
var extraData = "";
|
||||
@@ -1905,6 +1944,8 @@ const Workflows = (props) => {
|
||||
if (tags !== undefined) {
|
||||
workflowdata["tags"] = tags;
|
||||
}
|
||||
workflowdata["blogpost"] = inputblogpost
|
||||
workflowdata["status"] = inputstatus
|
||||
|
||||
if (defaultReturnValue !== undefined) {
|
||||
workflowdata["default_return_value"] = defaultReturnValue;
|
||||
@@ -2005,7 +2046,10 @@ const Workflows = (props) => {
|
||||
data.tags,
|
||||
data.default_return_value,
|
||||
{},
|
||||
false
|
||||
false,
|
||||
[],
|
||||
"",
|
||||
data.status,
|
||||
)
|
||||
.then((response) => {
|
||||
if (response !== undefined) {
|
||||
@@ -2022,7 +2066,10 @@ const Workflows = (props) => {
|
||||
data.tags,
|
||||
data.default_return_value,
|
||||
data,
|
||||
false
|
||||
false,
|
||||
[],
|
||||
"",
|
||||
data.status,
|
||||
).then((response) => {
|
||||
if (response !== undefined) {
|
||||
alert.success("Successfully imported " + data.name);
|
||||
@@ -2212,7 +2259,7 @@ const Workflows = (props) => {
|
||||
}
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title="Trigger amount"
|
||||
title="Amount of triggers"
|
||||
placement="bottom"
|
||||
>
|
||||
<span
|
||||
@@ -2535,7 +2582,41 @@ const Workflows = (props) => {
|
||||
</div>
|
||||
|
||||
{showMoreClicked ?
|
||||
<span>
|
||||
<span style={{marginTop: 25, }}>
|
||||
<TextField
|
||||
onBlur={(event) => {
|
||||
if (event.target.value.toLowerCase() === "test") {
|
||||
setStatus("test")
|
||||
} else if (event.target.value.toLowerCase() === "production") {
|
||||
setStatus("production")
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
defaultValue={status}
|
||||
placeholder="The status of the workflow. Can be test or production."
|
||||
rows="1"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
onBlur={(event) => setBlogpost(event.target.value)}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
color="primary"
|
||||
defaultValue={blogpost}
|
||||
placeholder="A blogpost or other reference for how this work workflow was built, and what it's for."
|
||||
rows="1"
|
||||
margin="dense"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
onBlur={(event) => setDefaultReturnValue(event.target.value)}
|
||||
InputProps={{
|
||||
@@ -2596,6 +2677,8 @@ const Workflows = (props) => {
|
||||
editingWorkflow,
|
||||
false,
|
||||
selectedUsecases,
|
||||
blogpost,
|
||||
status,
|
||||
);
|
||||
|
||||
setNewWorkflowName("");
|
||||
@@ -2612,6 +2695,8 @@ const Workflows = (props) => {
|
||||
{},
|
||||
true,
|
||||
selectedUsecases,
|
||||
blogpost,
|
||||
status,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2999,7 +3084,6 @@ const Workflows = (props) => {
|
||||
backgroundColor: filters.includes(usecase.name.toLowerCase()) ? usecase.color : theme.palette.surfaceColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
marginRight: index === usecases.length-1 ? 0 : 10,
|
||||
height: 60,
|
||||
cursor: "pointer",
|
||||
border: `2px solid ${usecase.color}`,
|
||||
overflow: "hidden",
|
||||
|
||||
Reference in New Issue
Block a user