Added statistics to both backend and frontend + cloud sync
This commit is contained in:
@@ -71,8 +71,8 @@ SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500 # 1500 by default
|
||||
# the container to the docker0
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0
|
||||
|
||||
# Used for auto-cleanup of containers. REALLY important at scale.
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=false
|
||||
# Used for auto-cleanup of containers. REALLY important at scale. Set to false to see all container info.
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=true
|
||||
SHUFFLE_ELASTIC=true
|
||||
SHUFFLE_LOGS_DISABLED=false
|
||||
SHUFFLE_CHAT_DISABLED=false # Controls support chat
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module shuffle-shared
|
||||
|
||||
//replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
|
||||
replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared
|
||||
|
||||
go 1.19
|
||||
|
||||
|
||||
@@ -700,64 +700,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Removed as UserInput is now handled as an app
|
||||
if actionResult.Status == "WAITING" && actionResult.Action.AppName == "User Input" {
|
||||
log.Printf("[INFO] SHOULD WAIT A BIT AND RUN USER INPUT! WAITING!")
|
||||
|
||||
var trigger shuffle.Trigger
|
||||
err = json.Unmarshal([]byte(actionResult.Result), &trigger)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed unmarshaling actionresult for user input: %s", err)
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(`{"success": false}`))
|
||||
return
|
||||
}
|
||||
|
||||
orgId := workflowExecution.ExecutionOrg
|
||||
if len(workflowExecution.OrgId) == 0 && len(workflowExecution.Workflow.OrgId) > 0 {
|
||||
orgId = workflowExecution.Workflow.OrgId
|
||||
}
|
||||
|
||||
err := handleUserInput(trigger, orgId, workflowExecution.Workflow.ID, workflowExecution.ExecutionId)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed userinput handler: %s", err)
|
||||
|
||||
actionResult.Result = fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = "ABORTED"
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed to set execution during wait: %s", err)
|
||||
} else {
|
||||
log.Printf("[INFO] Successfully set the execution %s to waiting.", workflowExecution.ExecutionId)
|
||||
}
|
||||
|
||||
resp.WriteHeader(401)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Error: %s"}`, err)))
|
||||
return
|
||||
} else {
|
||||
log.Printf("[INFO] Successful userinput handler")
|
||||
resp.WriteHeader(200)
|
||||
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "CLOUD IS DONE"}`)))
|
||||
|
||||
actionResult.Result = `{"success": True, "reason": "Waiting for user feedback based on configuration"}`
|
||||
|
||||
workflowExecution.Results = append(workflowExecution.Results, actionResult)
|
||||
workflowExecution.Status = actionResult.Status
|
||||
err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true)
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Failed setting userinput: %s", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Successfully set the execution to waiting.")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionResult, resp)
|
||||
}
|
||||
|
||||
|
||||
@@ -1856,8 +1856,6 @@ const AppFramework = (props) => {
|
||||
//autounselectify={true}
|
||||
var usecasediff = -100
|
||||
const bgColor = color === undefined || color === null || color.length === 0 ? theme.palette.surfaceColor : color
|
||||
console.log("Background: ", bgColor)
|
||||
|
||||
return (
|
||||
<div style={{margin: "auto", backgroundColor: bgColor, position: "relative", }}>
|
||||
<div style={{position: "absolute"}}>
|
||||
|
||||
@@ -200,7 +200,7 @@ const Appsearch = props => {
|
||||
const CustomHits = connectHits(InputHits)
|
||||
|
||||
return (
|
||||
<div style={{width: "100%", textAlign: "center", position: "relative", height: "100%",}}>
|
||||
<div style={{width: 287, height: 295, padding: "16px 16px 267px 16px", alignItems: "center", gap: 138,}}>
|
||||
<InstantSearch searchClient={searchClient} indexName="appsearch">
|
||||
{/* showSearch === false ? null :
|
||||
<div style={{maxWidth: 450, margin: "auto", }}>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
} from "@mui/material";
|
||||
|
||||
|
||||
@@ -294,9 +294,6 @@ const AuthenticationData = (props) => {
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
disableUnderline: true,
|
||||
@@ -386,7 +383,6 @@ const AuthenticationData = (props) => {
|
||||
PaperProps={{
|
||||
style: {
|
||||
pointerEvents: "auto",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 600,
|
||||
minHeight: 600,
|
||||
@@ -441,9 +437,6 @@ const AuthenticationData = (props) => {
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
marginLeft: "5px",
|
||||
maxWidth: "95%",
|
||||
height: 50,
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
|
||||
+1010
-316
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,280 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TextField,
|
||||
IconButton,
|
||||
Button,
|
||||
Typography,
|
||||
Grid,
|
||||
Paper,
|
||||
Chip,
|
||||
Checkbox,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
RadialBarChart,
|
||||
RadialAreaChart,
|
||||
RadialAxis,
|
||||
StackedBarSeries,
|
||||
TooltipArea,
|
||||
ChartTooltip,
|
||||
TooltipTemplate,
|
||||
RadialAreaSeries,
|
||||
RadialPointSeries,
|
||||
RadialArea,
|
||||
RadialLine,
|
||||
TreeMap,
|
||||
TreeMapSeries,
|
||||
TreeMapLabel,
|
||||
TreeMapRect,
|
||||
Line,
|
||||
LineChart,
|
||||
LineSeries,
|
||||
LinearYAxis,
|
||||
LinearXAxis,
|
||||
LinearYAxisTickSeries,
|
||||
LinearXAxisTickSeries,
|
||||
Area,
|
||||
AreaChart,
|
||||
AreaSeries,
|
||||
AreaSparklineChart,
|
||||
PointSeries,
|
||||
GridlineSeries,
|
||||
Gridline,
|
||||
Stripes,
|
||||
Gradient,
|
||||
GradientStop,
|
||||
LinearXAxisTickLabel,
|
||||
} from 'reaviz';
|
||||
|
||||
const LineChartWrapper = ({keys, inputname, height, width}) => {
|
||||
const [hovered, setHovered] = useState("");
|
||||
const inputdata = keys.data === undefined ? keys : keys.data
|
||||
|
||||
return (
|
||||
<div style={{color: "white", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: 30, marginTop: 15, }}>
|
||||
<Typography variant="h6" style={{marginBotton: 15, }}>
|
||||
{inputname}
|
||||
</Typography>
|
||||
<BarChart
|
||||
width={"100%"}
|
||||
height={height}
|
||||
data={inputdata}
|
||||
gridlines={
|
||||
<GridlineSeries line={<Gridline direction="all" />} />
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const AppStats = (defaultprops) => {
|
||||
const { globalUrl, selectedOrganization, userdata, } = defaultprops;
|
||||
const [keys, setKeys] = useState([])
|
||||
const [searches, setSearches] = useState([]);
|
||||
const [clickData, setClickData] = useState(undefined);
|
||||
const [conversionData, setConversionData] = useState(undefined);
|
||||
const [statistics, setStatistics] = useState(undefined);
|
||||
const [appRuns, setAppruns] = useState(undefined);
|
||||
const [workflowRuns, setWorkflowRuns] = useState(undefined);
|
||||
const [subflowRuns, setSubflowRuns] = useState(undefined);
|
||||
|
||||
const handleDataSetting = (inputdata, grouping) => {
|
||||
if (inputdata === undefined || inputdata === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const dailyStats = inputdata.daily_statistics
|
||||
if (dailyStats === undefined || dailyStats === null) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log("Looking at daily data: ", inputdata)
|
||||
|
||||
var appRuns = {
|
||||
"key": "App Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var workflowRuns = {
|
||||
"key": "Workflow Runs (includes subflows)",
|
||||
"data": []
|
||||
}
|
||||
|
||||
var subflowRuns = {
|
||||
"key": "Subflow Runs",
|
||||
"data": []
|
||||
}
|
||||
|
||||
for (let key in dailyStats) {
|
||||
// Always skips first one as it has accumulated data in it
|
||||
if (key === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
const item = dailyStats[key]
|
||||
|
||||
if (item["date"] === undefined) {
|
||||
console.log("No date: ", item)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if app_executions key in item
|
||||
if (item["app_executions"] !== undefined && item["app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
// Check if workflow_executions key in item
|
||||
if (item["workflow_executions"] !== undefined && item["workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (item["subflow_executions"] !== undefined && item["subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(item["date"]),
|
||||
data: item["subflow_executions"]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Adds data for today
|
||||
console.log("Inputdata: ", inputdata)
|
||||
if (inputdata["daily_app_executions"] !== undefined && inputdata["daily_app_executions"] !== null) {
|
||||
appRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_app_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_workflow_executions"] !== undefined && inputdata["daily_workflow_executions"] !== null) {
|
||||
workflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_workflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
if (inputdata["daily_subflow_executions"] !== undefined && inputdata["daily_subflow_executions"] !== null) {
|
||||
subflowRuns["data"].push({
|
||||
key: new Date(),
|
||||
data: inputdata["daily_subflow_executions"]
|
||||
})
|
||||
}
|
||||
|
||||
setSubflowRuns(subflowRuns)
|
||||
setWorkflowRuns(workflowRuns)
|
||||
setAppruns(appRuns)
|
||||
}
|
||||
|
||||
const getStats = () => {
|
||||
fetch(`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for workflows :O!: ", response.status);
|
||||
return;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson["success"] === false) {
|
||||
return
|
||||
}
|
||||
|
||||
setStatistics(responseJson)
|
||||
handleDataSetting(responseJson, "day")
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error: ", error)
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getStats()
|
||||
}, [])
|
||||
|
||||
const paperStyle = {
|
||||
textAlign: "center",
|
||||
padding: 40,
|
||||
margin: 5,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
maxWidth: 300,
|
||||
}
|
||||
|
||||
const data = (
|
||||
<div className="content" style={{width: "100%", margin: "auto", }}>
|
||||
<Typography variant="body1" style={{margin: "auto", marginLeft: 10, marginBottom: 20, }}>
|
||||
All Stat widgets are monthly and gathered from <a
|
||||
href={`${globalUrl}/api/v1/orgs/${selectedOrganization.id}/stats`}
|
||||
target="_blank"
|
||||
style={{ textDecoration: "none", color: "#f85a3e",}}
|
||||
>Your Organization Statistics. </a>
|
||||
This is a feature to help give you more insight into Shuffle, and will be populating over time.
|
||||
</Typography>
|
||||
{statistics !== undefined ?
|
||||
<div style={{display: "flex", textAlign: "center",}}>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_workflow_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
Workflow Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
<Paper style={paperStyle}>
|
||||
<Typography variant="h4">
|
||||
{statistics.monthly_app_executions}
|
||||
</Typography>
|
||||
<Typography variant="h6">
|
||||
App Runs
|
||||
</Typography>
|
||||
</Paper>
|
||||
</div>
|
||||
: null}
|
||||
|
||||
{appRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={appRuns} height={300} width={"100%"} inputname={"Daily App Runs"}/>
|
||||
}
|
||||
|
||||
{workflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={workflowRuns} height={300} width={"100%"} inputname={"Daily Workflow Runs (including subflows)"}/>
|
||||
}
|
||||
|
||||
{subflowRuns === undefined ?
|
||||
null
|
||||
:
|
||||
<LineChartWrapper keys={subflowRuns} height={300} width={"100%"} inputname={"Subflow Runs"}/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
const dataWrapper = (
|
||||
<div style={{ maxWidth: 1366, margin: "auto" }}>{data}</div>
|
||||
);
|
||||
|
||||
return dataWrapper;
|
||||
}
|
||||
|
||||
export default AppStats;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import { MuiChipsInput } from "mui-chips-input";
|
||||
import UsecaseSearch from "../components/UsecaseSearch.jsx"
|
||||
import WorkflowGrid from "../components/WorkflowGrid.jsx"
|
||||
import dayjs from 'dayjs';
|
||||
import WorkflowTemplatePopup from "./WorkflowTemplatePopup.jsx";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
@@ -57,7 +58,7 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
|
||||
const EditWorkflow = (props) => {
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, } = props
|
||||
const { globalUrl, workflow, setWorkflow, modalOpen, setModalOpen, showUpload, usecases, setNewWorkflow, appFramework, isEditing, userdata, apps, } = props
|
||||
|
||||
const [_, setUpdate] = React.useState(""); // Used for rendering, don't remove
|
||||
|
||||
@@ -143,7 +144,9 @@ const EditWorkflow = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const newWorkflow = isEditing === true ? false : true
|
||||
const priority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
console.log("PRIO: ", priority)
|
||||
|
||||
var upload = "";
|
||||
var total_count = 0
|
||||
@@ -161,6 +164,7 @@ const EditWorkflow = (props) => {
|
||||
maxWidth: isMobile ? "90%" : 650,
|
||||
minHeight: 400,
|
||||
paddingTop: 25,
|
||||
paddingLeft: 50,
|
||||
//minWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
//maxWidth: isMobile ? "90%" : newWorkflow === true ? 1000 : 550,
|
||||
},
|
||||
@@ -170,7 +174,7 @@ const EditWorkflow = (props) => {
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, color: "rgba(255,255,255,0.9)" }}>
|
||||
<div style={{display: "flex"}}>
|
||||
<Typography variant="h6" style={{flex: 9, }}>
|
||||
<Typography variant="h4" style={{flex: 9, }}>
|
||||
{newWorkflow ? "New" : "Editing"} workflow
|
||||
</Typography>
|
||||
{newWorkflow === true ? null :
|
||||
@@ -193,7 +197,7 @@ const EditWorkflow = (props) => {
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary" style={{maxWidth: 440,}}>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 20, maxWidth: 440,}}>
|
||||
Workflows can be built from scratch, or from templates. <a href="/usecases" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Usecases</a> can help you discover next steps, and you can <a href="/search?tab=workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>search</a> for them directly. <a href="/docs/workflows" rel="noopener noreferrer" target="_blank" style={{ textDecoration: "none", color: "#f86a3e" }}>Learn more</a>
|
||||
</Typography>
|
||||
{showUpload === true ?
|
||||
@@ -480,7 +484,7 @@ const EditWorkflow = (props) => {
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions style={{paddingRight: 20, }}>
|
||||
<DialogActions style={{paddingRight: 100, }}>
|
||||
<Button
|
||||
style={{}}
|
||||
onClick={() => {
|
||||
@@ -542,11 +546,38 @@ const EditWorkflow = (props) => {
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Submit"}
|
||||
{submitLoading ? <CircularProgress color="secondary" /> : "Done"}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
{newWorkflow === true ?
|
||||
<span style={{marginTop: 30, }}>
|
||||
<Typography variant="h6" style={{marginLeft: 30, paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
|
||||
{newWorkflow === true && name.length > 5 ?
|
||||
{priority === null || priority === undefined ? null :
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
|
||||
srcapp={priority.description.split("&").length > 2 ? priority.description.split("&")[0] : ""}
|
||||
img1={priority.description.split("&").length > 2 ? priority.description.split("&")[1] : ""}
|
||||
|
||||
dstapp={priority.description.split("&").length > 3 ? priority.description.split("&")[2] : ""}
|
||||
img2={priority.description.split("&").length > 3 ? priority.description.split("&")[3] : ""}
|
||||
title={priority.name}
|
||||
description={priority.description.split("&").length > 4 ? priority.description.split("&")[4] : ""}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
</span>
|
||||
: null}
|
||||
|
||||
{newWorkflow === true && name.length > 2 ?
|
||||
<div style={{marginLeft: 30, }}>
|
||||
<WorkflowGrid
|
||||
maxRows={1}
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ReactGA from 'react-ga4';
|
||||
|
||||
import 'react-alice-carousel/lib/alice-carousel.css';
|
||||
import TrendingFlatIcon from '@mui/icons-material/TrendingFlat';
|
||||
import theme from '../theme.jsx';
|
||||
import CheckBoxSharpIcon from '@mui/icons-material/CheckBoxSharp';
|
||||
import {
|
||||
Checkbox,
|
||||
Button,
|
||||
Collapse,
|
||||
IconButton,
|
||||
FormGroup,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
FormLabel,
|
||||
FormControlLabel,
|
||||
Select,
|
||||
MenuItem,
|
||||
Grid,
|
||||
Paper,
|
||||
Typography,
|
||||
TextField,
|
||||
Zoom,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemText,
|
||||
Divider,
|
||||
Tooltip,
|
||||
Chip,
|
||||
ButtonGroup,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
const ExploreWorkflow = (props) => {
|
||||
const { userdata, globalUrl } = props
|
||||
const [activeUsecases, setActiveUsecases] = useState(0);
|
||||
const [modalOpen, setModalOpen] = React.useState(false);
|
||||
const [suggestedUsecases, setSuggestedUsecases] = useState([])
|
||||
const [usecasesSet, setUsecasesSet] = useState(false)
|
||||
const [apps, setApps] = useState([])
|
||||
const sizing = 475
|
||||
|
||||
let navigate = useNavigate();
|
||||
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
borderRadius: 40,
|
||||
//border: "2px solid rgba(255,255,255,0.3)",
|
||||
}
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
// Find priorities in userdata.priorities and check if the item.type === "usecase"
|
||||
// If so, set the item.isActive to true
|
||||
if (usecasesSet === false && userdata.priorities !== undefined && userdata.priorities !== null && userdata.priorities.length > 0 && suggestedUsecases.length === 0) {
|
||||
|
||||
var tmpUsecases = []
|
||||
for (let i = 0; i < userdata.priorities.length; i++) {
|
||||
if (userdata.priorities[i].type !== "usecase" || userdata.priorities[i].active === false) {
|
||||
continue
|
||||
}
|
||||
|
||||
tmpUsecases.push(userdata.priorities[i])
|
||||
}
|
||||
|
||||
setSuggestedUsecases(tmpUsecases)
|
||||
setUsecasesSet(true)
|
||||
loadApps()
|
||||
}
|
||||
|
||||
const modalView = (
|
||||
// console.log("key:", dataValue.key),
|
||||
//console.log("value:",dataValue.value),
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle style={{}}>
|
||||
<div style={{ color: "white", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Sign Up</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginLeft: 8, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Setup</span>
|
||||
<div style={{ borderTop: "1px solid rgba(255, 132, 68, 1)", width: 85, marginRight: 8 }} />
|
||||
<CheckBoxSharpIcon sx={{ borderRadius: 4, color: "rgba(255, 132, 68, 1)" }} style={{ width: 24 }} />
|
||||
<span style={{ marginLeft: 8, color: "rgba(255, 132, 68, 1)", fontSize: 16, width: 60 }}>Explore</span>
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<Typography style={{ fontSize: 16, width: 252, marginLeft: 167 }}>
|
||||
Here’s a recommended workflow:
|
||||
</Typography>
|
||||
{/* <div style={{ marginTop: 0, maxWidth: 700, minWidth: 700, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<Tooltip title={"Previous usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
// backgroundColor: thumbIndex === 0 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slidePrev()
|
||||
}}
|
||||
>
|
||||
<ArrowBackIosNewIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<AliceCarousel
|
||||
style={{ backgroundColor: theme.palette.surfaceColor, minHeight: 750, maxHeight: 750, }}
|
||||
items={formattedCarousel}
|
||||
activeIndex={thumbIndex}
|
||||
infiniteLoop
|
||||
mouseTracking={false}
|
||||
responsive={responsive}
|
||||
// activeIndex={activeIndex}
|
||||
controlsStrategy="responsive"
|
||||
autoPlay={false}
|
||||
infinite={true}
|
||||
animationType="fadeout"
|
||||
animationDuration={800}
|
||||
disableButtonsControls
|
||||
|
||||
/>
|
||||
</div>
|
||||
<Tooltip title={"Next usecase"}>
|
||||
<IconButton
|
||||
style={{
|
||||
backgroundColor: thumbIndex === usecaseButtons.length - 1 ? "inherit" : "white",
|
||||
zIndex: 5000,
|
||||
minHeight: 50,
|
||||
maxHeight: 50,
|
||||
color: "grey",
|
||||
marginTop: 150,
|
||||
borderRadius: 50,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
slideNext()
|
||||
}}
|
||||
>
|
||||
<ArrowForwardIosIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<DialogActions style={{ paddingLeft: "30px", paddingRight: '30px' }}>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => setModalOpen(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => {
|
||||
console.log("hello")
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div style={{ marginTop: 0, margin: "auto", minHeight: sizing, maxHeight: sizing, }}>
|
||||
{modalView}
|
||||
<Typography variant="h4" style={{ marginLeft: 8, marginTop: 40, marginRight: 30, marginBottom: 0, }} color="rgba(241, 241, 241, 1)">
|
||||
Start using workflows
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ marginLeft: 8, marginTop: 10, marginRight: 30, marginBottom: 40, }} color="rgba(158, 158, 158, 1)">
|
||||
Based on what you selected here’s our recommendations!
|
||||
</Typography>
|
||||
|
||||
<div style={{ marginTop: 0, }}>
|
||||
<div className="thumbs" style={{ display: "flex" }}>
|
||||
<div style={{ minWidth: 554, maxWidth: 554, borderRadius: theme.palette.borderRadius, }}>
|
||||
<Grid item xs={11} style={{}}>
|
||||
{suggestedUsecases.length === 0 && usecasesSet ?
|
||||
<Typography variant="h6" style={{ marginTop: 30, marginBottom: 50, }} color="rgba(158, 158, 158, 1)">
|
||||
All Workflows are already added for your current apps!
|
||||
</Typography>
|
||||
:
|
||||
suggestedUsecases.map((priority, index) => {
|
||||
|
||||
const srcapp = priority.description.split("&")[0]
|
||||
var image1 = priority.description.split("&")[1]
|
||||
var image2 = ""
|
||||
var dstapp = ""
|
||||
if (priority.description.split("&").length > 3) {
|
||||
dstapp = priority.description.split("&")[2]
|
||||
image2 = priority.description.split("&")[3]
|
||||
}
|
||||
|
||||
const name = priority.name.replace("Suggested Usecase: ", "")
|
||||
|
||||
var description = ""
|
||||
if (priority.description.split("&").length > 4) {
|
||||
description = priority.description[4]
|
||||
}
|
||||
|
||||
// FIXME: Should have a proper description
|
||||
description = ""
|
||||
|
||||
return (
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
globalUrl={globalUrl}
|
||||
img1={image1}
|
||||
srcapp={srcapp}
|
||||
img2={image2}
|
||||
dstapp={dstapp}
|
||||
title={name}
|
||||
description={description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
<div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Button variant="contained" type="submit"
|
||||
fullWidth style={{
|
||||
borderRadius: 200,
|
||||
height: 51,
|
||||
width: 464,
|
||||
fontSize: 16,
|
||||
padding: "16px 24px",
|
||||
margin: "auto",
|
||||
itemAlign: "center",
|
||||
background: activeUsecases === 0 ? "rgba(47, 47, 47, 1)" : "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
color: activeUsecases === 0? "rgba(158, 158, 158, 1)" : "rgba(241, 241, 241, 1)",
|
||||
border: activeUsecases === 0 ? "1px solid rgba(158, 158, 158, 1)" : "none",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate("/workflows?message="+activeUsecases+" workflows added")
|
||||
}}>
|
||||
Continue to workflows
|
||||
</Button>
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant="body2" style={{ fontSize: 16, marginTop: 24 }} color="rgba(158, 158, 158, 1)">
|
||||
<Link style={{ color: "#f86a3e", marginLeft: 145 }} to="/usecases" className="btn btn-primary">
|
||||
Explore usecases
|
||||
</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default ExploreWorkflow
|
||||
@@ -212,7 +212,8 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
|
||||
const notificationWidth = 300
|
||||
const imagesize = 22;
|
||||
const boxColor = "#86c142";
|
||||
const boxColor = "#86c142";
|
||||
|
||||
const NotificationItem = (props) => {
|
||||
const {data} = props
|
||||
|
||||
@@ -263,7 +264,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
{data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ?
|
||||
<Link to={data.reference_url} style={{color: "#f86a3e", textDecoration: "none",}}>
|
||||
<Typography variant="body1">
|
||||
{data.title}
|
||||
{data.title} ({data.amount})
|
||||
</Typography >
|
||||
</Link>
|
||||
:
|
||||
@@ -277,7 +278,7 @@ const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, ho
|
||||
:
|
||||
null
|
||||
}
|
||||
<Typography variant="body2">
|
||||
<Typography variant="body2" style={{maxHeight: 200, overflowX: "hidden", overflowY: "auto", }}>
|
||||
{data.description}
|
||||
</Typography >
|
||||
{/*data.tags !== undefined && data.tags !== null && data.tags.length > 0 ?
|
||||
|
||||
@@ -2278,30 +2278,30 @@ const ParsedAction = (props) => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", backgroundColor: theme.palette.inputColor, overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
|
||||
<MenuItem
|
||||
key={innerdata.name}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginLeft: 15,
|
||||
color: "white",
|
||||
minWidth: 250,
|
||||
maxWidth: 250,
|
||||
padding: 0,
|
||||
position: "relative",
|
||||
}}
|
||||
value={innerdata}
|
||||
onMouseOver={() => {
|
||||
//console.log("HOVER: ", pathdata);
|
||||
}}
|
||||
onClick={() => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" style={{paddingBottom: 5}}>
|
||||
{innerdata.name}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<Paper style={{minHeight: 500, maxHeight: 500, minWidth: 275, maxWidth: 275, position: "fixed", top: menuPosition1.top-200, left: menuPosition1.left-455, padding: "10px 0px 10px 10px", overflow: "hidden", overflowY: "auto", border: "1px solid rgba(255,255,255,0.3)",}}>
|
||||
<MenuItem
|
||||
key={innerdata.name}
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
marginLeft: 15,
|
||||
color: "white",
|
||||
minWidth: 250,
|
||||
maxWidth: 250,
|
||||
padding: 0,
|
||||
position: "relative",
|
||||
}}
|
||||
value={innerdata}
|
||||
onMouseOver={() => {
|
||||
//console.log("HOVER: ", pathdata);
|
||||
}}
|
||||
onClick={() => {
|
||||
handleItemClick([innerdata]);
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" style={{paddingBottom: 5}}>
|
||||
{innerdata.name}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
{parsedPaths.map((pathdata, index) => {
|
||||
// FIXME: Should be recursive in here
|
||||
//<VpnKeyIcon style={iconStyle} />
|
||||
|
||||
@@ -69,7 +69,7 @@ const Priority = (props) => {
|
||||
|
||||
|
||||
return (
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: 100, maxHeight: 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{border: priority.active === false ? "1px solid #000000" : priority.severity === 1 ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, marginTop: 10, marginBottom: 10, padding: 15, textAlign: "center", minHeight: isCloud ? 70 : 100, maxHeight: isCloud ? 70 : 100, textAlign: "left", backgroundColor: theme.palette.surfaceColor, display: "flex", }}>
|
||||
<div style={{flex: 2, overflow: "hidden",}}>
|
||||
<span style={{display: "flex", }}>
|
||||
{priority.type === "usecase" || priority.type == "apps" ? <AutoFixHighIcon style={{height: 19, width: 19, marginLeft: 3, marginRight: 10, }}/> : null}
|
||||
|
||||
@@ -26,22 +26,21 @@ import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx";
|
||||
import {
|
||||
FullscreenExit as FullscreenExitIcon,
|
||||
Extension as ExtensionIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
Apps as AppsIcon,
|
||||
FavoriteBorder as FavoriteBorderIcon,
|
||||
Schedule as ScheduleIcon,
|
||||
FormatListNumbered as FormatListNumberedIcon,
|
||||
SquareFoot as SquareFootIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
Circle as CircleIcon,
|
||||
Add as AddIcon,
|
||||
PlayArrow as PlayArrowIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import {
|
||||
AutoFixHigh as AutoFixHighIcon,
|
||||
Close as CloseIcon,
|
||||
CompressOutlined,
|
||||
QrCodeScannerOutlined,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
import { validateJson } from "../views/Workflows.jsx";
|
||||
import ReactJson from "react-json-view";
|
||||
import PaperComponent from "../components/PaperComponent.jsx";
|
||||
@@ -823,10 +822,6 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
const executeSingleAction = (inputdata) => {
|
||||
//if (serverside === true) {
|
||||
// return
|
||||
//}
|
||||
|
||||
if (validation === true) {
|
||||
inputdata = JSON.stringify(inputdata)
|
||||
}
|
||||
@@ -834,7 +829,10 @@ const CodeEditor = (props) => {
|
||||
// Shuffle Tools 1.2.0 (in most cases?)
|
||||
const appid = toolsAppId !== undefined && toolsAppId !== null && toolsAppId.length > 0 ? toolsAppId : "3e2bdf9d5069fe3f4746c29d68785a6a"
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":"repeat_back_to_me","label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters":[{"description":"The message to repeat","id":"","name":"call","example":"REPEATING: Hello world","value":inputdata,"multiline":true,"options":null,"action_field":"","variant":"STATIC_VALUE","required":true,"configuration":false,"tags":null,"schema":{"type":"string"},"skip_multicheck":false,"value_replace":null,"unique_toggled":false,"autocompleted":false}],"execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
const actionname = selectedAction.name === "execute_python" && !inputdata.replaceAll(" ", "").includes("{%python%}") ? "execute_python" : "repeat_back_to_me"
|
||||
const params = actionname === "execute_python" ? [{"name": "code", "value":inputdata}] : [{"name":"call", "value": inputdata}]
|
||||
|
||||
const actiondata = {"description":"Repeats the call parameter","id":"","name":actionname,"label":"","node_type":"","environment":"","sharing":false,"private_id":"","public_id":"","app_id": appid,"tags":null,"authentication":[],"tested":false,"parameters": params, "execution_variable":{"description":"","id":"","name":"","value":""},"returns":{"description":"","example":"","id":"","schema":{"type":"string"}},"authentication_id":"","example":"","auth_not_required":false,"source_workflow":"","run_magic_output":false,"run_magic_input":false,"execution_delay":0,"app_name":"Shuffle Tools","app_version":"1.2.0","selectedAuthentication":{}}
|
||||
|
||||
setExecutionResult({
|
||||
"valid": false,
|
||||
@@ -924,6 +922,20 @@ const CodeEditor = (props) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 18,
|
||||
color: "grey",
|
||||
}}
|
||||
onClick={() => {
|
||||
setExpansionModalOpen(false)
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<div style={{display: "flex"}}>
|
||||
<div style={{flex: 1, }}>
|
||||
{ isFileEditor ?
|
||||
@@ -1555,7 +1567,7 @@ const CodeEditor = (props) => {
|
||||
<IconButton disabled={executing} color="primary" style={{border: `1px solid ${theme.palette.primary.main}`, marginLeft: 100, padding: 8}} variant="contained" onClick={() => {
|
||||
executeSingleAction(expOutput)
|
||||
}}>
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
<Tooltip title="Try it! This runs the Shuffle Tools 'repeat back to me' or 'execute python' action with what you see in the expected output window. Commonly used to test your Python scripts or Liquid filters, not requiring the full workflow to run again." placement="top">
|
||||
{executing ? <CircularProgress style={{height: 18, width: 18, }} /> : <PlayArrowIcon style={{height: 18, width: 18, }} /> }
|
||||
|
||||
</Tooltip>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,7 +154,6 @@ const AppGrid = props => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success !== false) {
|
||||
console.log("Usecases: ", responseJson)
|
||||
//handleKeysetting(responseJson, workflows)
|
||||
}
|
||||
})
|
||||
@@ -187,10 +186,10 @@ const AppGrid = props => {
|
||||
}, [])
|
||||
|
||||
if (localMessage !== inputsearch && inputsearch !== undefined && inputsearch !== null && inputsearch.length > 0) {
|
||||
console.log("In refinement: ", inputsearch)
|
||||
//setLocalMessage(inputsearch)
|
||||
refine(inputsearch)
|
||||
defaultSearch = inputsearch
|
||||
return null
|
||||
} else if (onlyResults === true) {
|
||||
// Don't return anything unless refinement works
|
||||
return null
|
||||
@@ -244,9 +243,7 @@ const AppGrid = props => {
|
||||
return (
|
||||
<div>
|
||||
{onlyResults === true && hits.length > 0 ?
|
||||
<Typography variant="h6" style={{paddingBottom: 0, }}>
|
||||
Relevant Workflows
|
||||
</Typography>
|
||||
null
|
||||
: null}
|
||||
<Grid container spacing={4} style={paperAppContainer}>
|
||||
{hits.map((data, index) => {
|
||||
|
||||
@@ -277,7 +277,7 @@ const WorkflowPaper = (props) => {
|
||||
>
|
||||
{data.tags !== undefined && data.tags !== null
|
||||
? data.tags.map((tag, index) => {
|
||||
if (index >= 3) {
|
||||
if (index >= 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import { toast } from "react-toastify"
|
||||
import theme from '../theme.jsx';
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Drawer,
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
Check as CheckIcon,
|
||||
TrendingFlat as TrendingFlatIcon,
|
||||
Close as CloseIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
import WorkflowTemplatePopup2 from "./WorkflowTemplatePopup.jsx";
|
||||
import ConfigureWorkflow from "../components/ConfigureWorkflow.jsx";
|
||||
|
||||
const WorkflowTemplatePopup = (props) => {
|
||||
const { userdata, globalUrl, img1, srcapp, img2, dstapp, title, description, visualOnly, apps } = props;
|
||||
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const [workflowLoading, setWorkflowLoading] = useState(false);
|
||||
const [workflow, setWorkflow] = useState({});
|
||||
|
||||
const [appAuthentication, setAppAuthentication] = React.useState(undefined);
|
||||
const imagestyle = {
|
||||
height: 40,
|
||||
width: 40,
|
||||
borderRadius: 40,
|
||||
}
|
||||
|
||||
if (title === undefined || title === null || title === "") {
|
||||
console.log("No title for workflow template popup!");
|
||||
return null
|
||||
}
|
||||
|
||||
const getWorkflow = (workflowId) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/${workflowId}`, {
|
||||
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) {
|
||||
console.log("Error in workflow loading for ID ", workflowId)
|
||||
} else {
|
||||
setWorkflow(responseJson)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const loadAppAuth = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps/authentication`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for setting app auth :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
toast("Failed to get app auth: " + responseJson.reason);
|
||||
return
|
||||
}
|
||||
|
||||
var newauth = [];
|
||||
for (let authkey in responseJson.data) {
|
||||
if (responseJson.data[authkey].defined === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newauth.push(responseJson.data[authkey]);
|
||||
}
|
||||
|
||||
setAppAuthentication(newauth);
|
||||
})
|
||||
.catch((error) => {
|
||||
//toast(error.toString());
|
||||
console.log("New auth error: ", error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const getGeneratedWorkflow = () => {
|
||||
// POST
|
||||
// https://shuffler.io/api/v1/workflows/merge
|
||||
// destination: {app_id: "b9c2feaf99b6309dabaeaa8518c61d3d", app_name: "Servicenow_API", app_version: "",…}
|
||||
// id: ""
|
||||
// middle:[]
|
||||
// name: "Email analysis"
|
||||
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
|
||||
|
||||
setWorkflowLoading(true)
|
||||
|
||||
|
||||
// FIXME: Remove hardcoding here after testing, and user srcapp/dstapp
|
||||
const newsrcapp = srcapp
|
||||
const newdstapp = dstapp
|
||||
|
||||
const mergedata = {
|
||||
name: title,
|
||||
id: "",
|
||||
source: {
|
||||
app_name: newsrcapp,
|
||||
},
|
||||
middle: [],
|
||||
destination: {
|
||||
app_name: newdstapp,
|
||||
},
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/workflows/merge", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify(mergedata),
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for framework!");
|
||||
}
|
||||
|
||||
setWorkflowLoading(false)
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === false) {
|
||||
console.log("Error in workflow template: ", responseJson.error);
|
||||
|
||||
setErrorMessage("Failed to generate workflow for these tools - the Shuffle team has been notified. Click out of this window to continue. Contact support@shuffler.io for further assistance.")
|
||||
|
||||
setIsActive(true)
|
||||
//setTimeout(() => {
|
||||
// setModalOpen(false)
|
||||
//}, 5000)
|
||||
} else {
|
||||
console.log("Success in workflow template: ", responseJson);
|
||||
setIsActive(true)
|
||||
if (responseJson.workflow_id === "") {
|
||||
console.log("Failed to build workflow for these tools. Closing in 3 seconds.")
|
||||
return
|
||||
}
|
||||
|
||||
getWorkflow(responseJson.workflow_id)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
const isFinished = () => {
|
||||
// Look for configuration fields being done in the current modal
|
||||
// 1. Start by finding the modal
|
||||
const template = document.getElementById("workflow-template")
|
||||
if (template === null || template == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Find item in template with id app-config
|
||||
const appconfig = template.getElementsByClassName("app-config")
|
||||
if (appconfig === null || appconfig == undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
console.log("APPCONFIG: ", appconfig)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const ModalView = () => {
|
||||
return (
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
open={modalOpen}
|
||||
onClose={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: "black",
|
||||
color: "white",
|
||||
minWidth: 700,
|
||||
maxWidth: 700,
|
||||
paddingTop: 75,
|
||||
itemAlign: "center",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
style={{
|
||||
zIndex: 5000,
|
||||
position: "absolute",
|
||||
top: 14,
|
||||
right: 14,
|
||||
color: "white",
|
||||
}}
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent style={{marginTop: 0, marginLeft: 75, maxWidth: 470, }}>
|
||||
<Typography variant="h4">
|
||||
<b>Configure Workflow</b>
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{marginTop: 25, }}>
|
||||
Selected Workflow:
|
||||
</Typography>
|
||||
<div style={{marginBottom: 0, }} id="workflow-template">
|
||||
<WorkflowTemplatePopup2
|
||||
globalUrl={globalUrl}
|
||||
img1={img1}
|
||||
srcapp={srcapp}
|
||||
img2={img2}
|
||||
dstapp={dstapp}
|
||||
title={title}
|
||||
description={description}
|
||||
visualOnly={true}
|
||||
/>
|
||||
</div>
|
||||
{workflowLoading ?
|
||||
<div style={{marginTop: 75, textAlign: "center", }}>
|
||||
<Typography variant="h4"> Generating the Workflow...
|
||||
</Typography>
|
||||
<CircularProgress style={{marginLeft: 125, marginTop: 10, }}/>
|
||||
</div>
|
||||
:
|
||||
<div>
|
||||
<Typography variant="h6" style={{marginTop: 75, }}>
|
||||
{errorMessage !== "" ? errorMessage : ""}
|
||||
</Typography>
|
||||
</div>
|
||||
}
|
||||
<ConfigureWorkflow
|
||||
userdata={userdata}
|
||||
theme={theme}
|
||||
globalUrl={globalUrl}
|
||||
workflow={workflow}
|
||||
appAuthentication={appAuthentication}
|
||||
setAppAuthentication={setAppAuthentication}
|
||||
apps={apps}
|
||||
/>
|
||||
{errorMessage === "" ?
|
||||
<Button
|
||||
style={{marginTop: 50, }}
|
||||
variant={isFinished() ? "contained" : "outlined"}
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
: null}
|
||||
</DialogContent>
|
||||
</Drawer>
|
||||
)
|
||||
}
|
||||
|
||||
var parsedTitle = title
|
||||
const maxlength = 30
|
||||
if (title.length > maxlength) {
|
||||
parsedTitle = title.substring(0, maxlength) + "..."
|
||||
}
|
||||
|
||||
parsedTitle.replaceAll("_", " ")
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", maxWidth: 470, minWidth: 470, height: 78, borderRadius: 8 }}>
|
||||
<ModalView />
|
||||
<div
|
||||
// variant={isActive === 1 ? "contained" : "outlined"}
|
||||
color="secondary"
|
||||
disabled={visualOnly === true}
|
||||
style={{
|
||||
margin: 4,
|
||||
width: "100%",
|
||||
borderRadius: 8,
|
||||
textTransform: "none",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
border: isActive ? errorMessage !== "" ? "1px solid red" : `2px solid ${theme.palette.green}` : isHovered ? "1px solid #f85a3e" : "1px solid rgba(33, 33, 33, 1)",
|
||||
cursor: isActive ? errorMessage !== "" ? "not-allowed" : "pointer" : "pointer",
|
||||
padding: "10px 20px 10px 20px",
|
||||
position: "relative",
|
||||
|
||||
}}
|
||||
onMouseEnter={() => {
|
||||
setIsHovered(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setIsHovered(false)
|
||||
}}
|
||||
onClick={() => {
|
||||
if (visualOnly === true) {
|
||||
console.log("Not showing more than visuals.")
|
||||
return
|
||||
}
|
||||
|
||||
//setIsActive(!isActive)
|
||||
if (errorMessage !== "") {
|
||||
toast("Already failed to generate workflow for these apps. Please try again later or contact support@shuffler.io.")
|
||||
} else if (isActive) {
|
||||
toast("Workflow already generated. Please try another template, or continue to the next page.")
|
||||
|
||||
// FIXME: Remove these?
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
//getGeneratedWorkflow()
|
||||
} else {
|
||||
|
||||
loadAppAuth()
|
||||
setModalOpen(true)
|
||||
getGeneratedWorkflow()
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", itemAlign: "left", textAlign: "left", }}>
|
||||
<div style={{display: "flex", flex: 1, marginTop: 3, }}>
|
||||
<img src={img1} style={imagestyle} />
|
||||
{img2 !== "" ?
|
||||
<span style={{display: "flex", }}>
|
||||
<TrendingFlatIcon style={{ marginTop: 7 }} />
|
||||
<img src={img2} style={imagestyle} />
|
||||
</span>
|
||||
:
|
||||
<span style={{width: 50, }} />
|
||||
}
|
||||
</div>
|
||||
<div style={{ flex: 3, marginLeft: 20, }}>
|
||||
<Typography variant="body1" style={{ marginTop: description.length === 0 ? 10 : 0, }} color="rgba(241, 241, 241, 1)">
|
||||
{parsedTitle}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="textSecondary" style={{ marginTop: 0, marginRight: 0, maxHeight: 16, overflow: "hidden",}} color="rgba(158, 158, 158, 1)">
|
||||
{description}
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{isActive === true && errorMessage === "" ?
|
||||
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowTemplatePopup
|
||||
@@ -18,10 +18,12 @@ const theme = createTheme(adaptV4Theme({
|
||||
secondary: "rgba(255,255,255,0.7)",
|
||||
},
|
||||
type: "dark",
|
||||
inputColor: "#383B40",
|
||||
inputColor: "rgba(39,41,45,1)",
|
||||
//inputColor: "#383B40",
|
||||
surfaceColor: "#27292d",
|
||||
platformColor: "#1c1c1d",
|
||||
backgroundColor: "#1a1a1a",
|
||||
green: "#5cc879",
|
||||
borderRadius: 5,
|
||||
defaultBorder: "1px solid rgba(255,255,255,0.3)",
|
||||
jsonTheme: "brewer",
|
||||
@@ -70,11 +72,6 @@ const theme = createTheme(adaptV4Theme({
|
||||
},
|
||||
},
|
||||
overrides: {
|
||||
MuiPaper: {
|
||||
root: {
|
||||
backgroundColor: "#1c1c1d",
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
list: {
|
||||
backgroundColor: "#27292d",
|
||||
|
||||
@@ -1692,15 +1692,24 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
const userId = user.id;
|
||||
const data = { user_id: userId };
|
||||
|
||||
fetch(globalUrl + "/api/v1/generateapikey", {
|
||||
console.log(user, userdata)
|
||||
|
||||
var fetchdata = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
credentials: "include",
|
||||
})
|
||||
}
|
||||
|
||||
if (userId === userdata.id) {
|
||||
fetchdata.method = "GET"
|
||||
} else {
|
||||
fetchdata.body = JSON.stringify(data)
|
||||
}
|
||||
|
||||
fetch(globalUrl + "/api/v1/generateapikey", fetchdata)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for WORKFLOW EXECUTION :O!");
|
||||
@@ -1889,8 +1898,11 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
open={selectedUserModalOpen}
|
||||
onClose={() => {
|
||||
setSelectedUserModalOpen(false);
|
||||
setImage2FA("");
|
||||
setSecret2FA("");
|
||||
|
||||
setImage2FA("");
|
||||
setValue2FA("");
|
||||
setSecret2FA("");
|
||||
setShow2faSetup(false);
|
||||
}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
@@ -2156,7 +2168,7 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
<Card
|
||||
style={{
|
||||
margin: 4,
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minHeight: expanded ? 250 : "inherit",
|
||||
maxHeight: expanded ? 250 : "inherit",
|
||||
@@ -2515,9 +2527,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
</span>
|
||||
/>
|
||||
<Tab
|
||||
disabled={!isCloud}
|
||||
label=<span>
|
||||
Billing
|
||||
Billing (Beta)
|
||||
</span>
|
||||
/>
|
||||
<Tab
|
||||
@@ -3246,10 +3257,8 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
selectedOrganization.child_orgs !== null &&
|
||||
selectedOrganization.child_orgs.length > 0
|
||||
) {
|
||||
console.log("In here?");
|
||||
var active = [];
|
||||
for (var key in userdata.orgs) {
|
||||
console.log("ORG: ", userdata.orgs[key]);
|
||||
const found =
|
||||
selectedOrganization.child_orgs.find(
|
||||
(item) => item.id === userdata.orgs[key].id
|
||||
@@ -3299,7 +3308,6 @@ Let me know if you're interested, or set up a call here: https://drift.me/${user
|
||||
) : null;
|
||||
|
||||
const run2FASetup = (data) => {
|
||||
console.log("2fa: ", data, show2faSetup);
|
||||
if (!show2faSetup) {
|
||||
get2faCode(data.id);
|
||||
} else {
|
||||
|
||||
@@ -580,15 +580,39 @@ const AngularWorkflow = (defaultprops) => {
|
||||
const cytoscapeWidth = isMobile ? bodyWidth - leftBarSize : bodyWidth - leftBarSize - 25
|
||||
|
||||
const [elements, setElements] = useState([]);
|
||||
const [loopRunning, setLoopRunning] = useState(false)
|
||||
|
||||
const stop = () => {
|
||||
setLoopRunning(false)
|
||||
}
|
||||
|
||||
const start = () => {
|
||||
setLoopRunning(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("In useeffect for loopRunning: ", loopRunning)
|
||||
if (loopRunning) {
|
||||
const intervalId = setInterval(() => {
|
||||
if (!loopRunning) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
|
||||
fetchUpdates()
|
||||
}, 3000)
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [loopRunning])
|
||||
|
||||
// No point going as fast, as the nodes aren't realtime anymore, but bulk updated.
|
||||
// Set it from 2500 to 6000 to reduce overall load
|
||||
const { start, stop } = useInterval({
|
||||
duration: 3000,
|
||||
startImmediate: false,
|
||||
callback: () => {
|
||||
fetchUpdates();
|
||||
},
|
||||
});
|
||||
//const { start, stop } = useInterval({
|
||||
// duration: 3000,
|
||||
// startImmediate: false,
|
||||
// callback: () => {
|
||||
// fetchUpdates();
|
||||
// },
|
||||
//});
|
||||
|
||||
const getAppDocs = (appname, location, version) => {
|
||||
fetch(`${globalUrl}/api/v1/docs/${appname}?location=${location}&version=${version}`, {
|
||||
@@ -924,7 +948,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const getWorkflowExecution = (id, execution_id) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/${id}/executions`, {
|
||||
fetch(`${globalUrl}/api/v2/workflows/${id}/executions`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -940,11 +964,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson !== undefined && responseJson !== null && responseJson.length > 0) {
|
||||
// FIXME: Sort this by time
|
||||
if (responseJson !== undefined && responseJson !== null && responseJson.executions !== undefined && responseJson.executions !== null && responseJson.executions.length > 0) {
|
||||
|
||||
// - means it's opposite
|
||||
const newkeys = sortByKey(responseJson, "-started_at");
|
||||
const newkeys = sortByKey(responseJson.executions, "-started_at");
|
||||
setWorkflowExecutions(newkeys);
|
||||
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
@@ -1021,6 +1044,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
},
|
||||
body: JSON.stringify(executionRequest),
|
||||
credentials: "include",
|
||||
cors: "no-cors",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
@@ -1279,7 +1303,7 @@ const AngularWorkflow = (defaultprops) => {
|
||||
};
|
||||
|
||||
const sendStreamRequest = (body) => {
|
||||
console.log("Stream not activated yet.")
|
||||
//console.log("Stream not activated yet.")
|
||||
return
|
||||
|
||||
// Session may be important here huh
|
||||
@@ -15302,6 +15326,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
overflow: "hidden",
|
||||
}}
|
||||
onMouseOver={() => {
|
||||
if (cy == undefined || cy == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var currentnode = cy.getElementById(data.action.id);
|
||||
if (currentnode !== undefined && currentnode !== null && currentnode.length !== 0) {
|
||||
currentnode.addClass("shuffle-hover-highlight");
|
||||
@@ -15314,6 +15342,10 @@ const AngularWorkflow = (defaultprops) => {
|
||||
//)
|
||||
}}
|
||||
onMouseOut={() => {
|
||||
if (cy == undefined || cy == null) {
|
||||
return
|
||||
}
|
||||
|
||||
var currentnode = cy.getElementById(data.action.id);
|
||||
if (currentnode.length !== 0) {
|
||||
currentnode.removeClass("shuffle-hover-highlight");
|
||||
|
||||
@@ -1354,7 +1354,8 @@ const Apps = (props) => {
|
||||
border: hover ? "1px solid #f85a3e" : "1px solid rgba(255,255,255,0.3)",
|
||||
cursor: hover ? "pointer" : "default",
|
||||
textAlign: "center",
|
||||
height: 125,
|
||||
minHeight: 150,
|
||||
maxHeight: 150,
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
|
||||
+181
-121
@@ -6,6 +6,7 @@ import { makeStyles, } from "@mui/styles";
|
||||
import classNames from "classnames";
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import WorkflowTemplatePopup from "../components/WorkflowTemplatePopup.jsx";
|
||||
|
||||
// react plugin used to create charts
|
||||
//import { Line, Bar } from "react-chartjs-2";
|
||||
@@ -90,7 +91,7 @@ const useStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
const UsecaseListComponent = ({userdata, keys, isCloud, globalUrl, frameworkData, isLoggedIn, workflows, setWorkflows}) => {
|
||||
const [expandedIndex, setExpandedIndex] = useState(-1);
|
||||
const [expandedItem, setExpandedItem] = useState(-1);
|
||||
const [inputUsecase, setInputUsecase] = useState({});
|
||||
@@ -103,17 +104,69 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
|
||||
const [selectedWorkflows, setSelectedWorkflows] = useState([])
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [apps, setApps] = useState([])
|
||||
|
||||
const classes = useStyles();
|
||||
const classes = useStyles();
|
||||
let navigate = useNavigate();
|
||||
|
||||
const [mitreTags, setMitreTags] = useState([]);
|
||||
|
||||
const loadApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson === null) {
|
||||
console.log("null-response from server")
|
||||
const pretend_apps = [{
|
||||
"name": "TBD",
|
||||
"app_name": "TBD",
|
||||
"app_version": "TBD",
|
||||
"description": "TBD",
|
||||
"version": "TBD",
|
||||
"large_image": "",
|
||||
}]
|
||||
|
||||
setApps(pretend_apps)
|
||||
return
|
||||
}
|
||||
|
||||
if (responseJson.success === false) {
|
||||
console.log("error loading apps: ", responseJson)
|
||||
return
|
||||
}
|
||||
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadApps()
|
||||
}, [])
|
||||
|
||||
if (keys === undefined || keys === null || keys.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const getUsecase = (name, index, subindex) => {
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(name.replaceAll(" ", "_"))}`, {
|
||||
const parseUsecase = (parsedUsecase, subcase) => {
|
||||
console.log("parseUsecase: ", parsedUsecase, subcase)
|
||||
}
|
||||
|
||||
const getUsecase = (subcase, index, subindex) => {
|
||||
console.log(subcase)
|
||||
|
||||
fetch(`${globalUrl}/api/v1/workflows/usecases/${escape(subcase.name.replaceAll(" ", "_"))}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -129,14 +182,26 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
var parsedUsecase = responseJson
|
||||
if (responseJson.success === false) {
|
||||
setInputUsecase({
|
||||
"name": name,
|
||||
})
|
||||
parsedUsecase = subcase
|
||||
|
||||
//img1={inputUsecase.srcimg}
|
||||
//srcapp={inputUsecase.srcapp}
|
||||
//img2={inputUsecase.dstimg}
|
||||
//dstapp={inputUsecase.dstapp}
|
||||
//title={inputUsecase.name}
|
||||
parsedUsecase = parseUsecase(parsedUsecase, subcase)
|
||||
} else {
|
||||
setInputUsecase(responseJson)
|
||||
parsedUsecase = responseJson
|
||||
|
||||
parsedUsecase = parseUsecase(parsedUsecase, responseJson)
|
||||
}
|
||||
|
||||
// Look for the type of app and fill in img1, srcapp...
|
||||
|
||||
|
||||
setInputUsecase(parsedUsecase)
|
||||
setExpandedIndex(index)
|
||||
setExpandedItem(subindex)
|
||||
|
||||
@@ -289,6 +354,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
if (selectedItem && subcase.matches.length > 0 && selectedWorkflows.length === 0 && firstLoad === true) {
|
||||
setFirstLoad(false)
|
||||
setSelectedWorkflows(subcase.matches)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,10 +382,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
}
|
||||
}
|
||||
|
||||
//const backgroundColor = selectedItem ? "inherit" : finished ? "inherit" : usecase.color
|
||||
const finished = subcase.matches.length > 0
|
||||
const backgroundColor = theme.palette.surfaceColor
|
||||
//"inherit"
|
||||
const itemBorder = `${selectedItem ? "3px" : expandedItem >= 0 ? "0px" : "1px"} solid ${usecase.color}`
|
||||
|
||||
const fixedName = subcase.name.toLowerCase().replace("_", " ")
|
||||
@@ -330,14 +394,14 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
//setSelectedWorkflows([])
|
||||
if (selectedItem) {
|
||||
} else {
|
||||
getUsecase(subcase.name, index, subindex)
|
||||
getUsecase(subcase, index, subindex)
|
||||
navigate(`/usecases?selected_object=${fixedName}`)
|
||||
|
||||
//const newitem = removeParam("selected_object", cursearch);
|
||||
//navigate(curpath + newitem)
|
||||
}
|
||||
}}>
|
||||
<Paper style={{padding: 25, minHeight: 75, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
<Paper style={{padding: 25, minHeight: isCloud ? 75 : 122, cursor: !selectedItem ? "pointer" : "default", border: itemBorder, backgroundColor: backgroundColor,}} onClick={() => {
|
||||
}}>
|
||||
{!selectedItem ?
|
||||
<div style={{textAlign: "left", position: "relative",}}>
|
||||
@@ -649,12 +713,12 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
: null}
|
||||
|
||||
{workflows !== undefined && workflows !== null && workflows.length > 0 ?
|
||||
<Autocomplete
|
||||
multiple
|
||||
<Autocomplete
|
||||
multiple
|
||||
id="workflow_matching"
|
||||
options={workflows}
|
||||
autoHighlight
|
||||
value={selectedWorkflows}
|
||||
value={selectedWorkflows}
|
||||
classes={{ inputRoot: classes.inputRoot }}
|
||||
ListboxProps={{
|
||||
style: {
|
||||
@@ -662,7 +726,7 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
color: "white",
|
||||
},
|
||||
}}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionSelected={(option, value) => option.id === value.id}
|
||||
getOptionLabel={(option) => {
|
||||
|
||||
if (
|
||||
@@ -685,120 +749,120 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
onChange={(event, newValue) => {
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
console.log("CLICK: ", newValue)
|
||||
//handleWorkflowSelectionUpdate({ target: { value: newValue} })
|
||||
//setSelectedWorkflows=
|
||||
//var newvalue = []
|
||||
//for (var key in newValue) {
|
||||
// if (newValue[key].id !== undefined) {
|
||||
// newvalue.push(newValue[key].id)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
// Doing this way as you may want to remove some too
|
||||
for (var key in workflows) {
|
||||
if (!newValue.find(data => data.id === workflows[key].id)) {
|
||||
// Check if it has the one in it
|
||||
if (workflows[key]["usecase_ids"] !== undefined && workflows[key]["usecase_ids"] !== null && workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
const filtered = workflows[key]["usecase_ids"].filter(data => data !== subcase.name)
|
||||
if (filtered !== undefined && filtered !== null) {
|
||||
//console.log("Removing: ", workflows[key].name, workflows[key])
|
||||
workflows[key]["usecase_ids"] = filtered
|
||||
|
||||
setWorkflow(workflows[key])
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
if (workflows[key]["usecase_ids"] === undefined || workflows[key]["usecase_ids"] === null) {
|
||||
workflows[key]["usecase_ids"] = [subcase.name]
|
||||
console.log("Setting: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
} else if (!workflows[key]["usecase_ids"].includes(subcase.name)) {
|
||||
workflows[key]["usecase_ids"].push(subcase.name)
|
||||
console.log("Adding: ", workflows[key].name)
|
||||
setWorkflow(workflows[key])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
setWorkflows(workflows)
|
||||
console.log("New: ", newValue)
|
||||
setSelectedWorkflows(newValue)
|
||||
//setUpdate(Math.random())
|
||||
}}
|
||||
renderOption={(props, option) => {
|
||||
//console.log("In options?: ", props, option)
|
||||
renderOption={(props, data, state) => {
|
||||
var newname = data.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
var newname = props.name
|
||||
if (newname === undefined || newname === null) {
|
||||
newname = "placeholder"
|
||||
}
|
||||
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{props.image !== undefined && props.image !== null && props.image.length > 0 ?
|
||||
<img src={props.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={option.selected}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
if (newname.length > 2) {
|
||||
newname = newname.charAt(0).toUpperCase() + newname.substring(1)
|
||||
}
|
||||
return (
|
||||
<li {...props}>
|
||||
<Tooltip arrow placement="left" title={
|
||||
<span style={{}}>
|
||||
{data.image !== undefined && data.image !== null && data.image.length > 0 ?
|
||||
<img src={data.image} alt={newname} style={{backgroundColor: theme.palette.surfaceColor, maxHeight: 200, minHeigth: 200, borderRadius: theme.palette.borderRadius, }} />
|
||||
: null}
|
||||
<Typography>
|
||||
Choose {newname}
|
||||
</Typography>
|
||||
</span>
|
||||
} placement="bottom">
|
||||
<span>
|
||||
<Checkbox
|
||||
icon={<CheckBoxOutlineBlankIcon fontSize="small" />}
|
||||
checkedIcon={<CheckBoxIcon fontSize="small" />}
|
||||
style={{ marginRight: 8 }}
|
||||
checked={selectedWorkflows.find(wf => wf.id === data.id) !== undefined}
|
||||
/>
|
||||
{newname}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</li>
|
||||
)
|
||||
}}
|
||||
renderInput={(params) => {
|
||||
return (
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
<TextField
|
||||
style={{
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}}
|
||||
{...params}
|
||||
label="Find your workflows"
|
||||
variant="outlined"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
: null}
|
||||
: null}
|
||||
<span style={{top: 30, position: "relative",}}>
|
||||
<Typography variant="body1" style={{marginTop: 0,}} onClick={() => {}}>
|
||||
Try it out
|
||||
</Typography>
|
||||
<WorkflowTemplatePopup
|
||||
userdata={userdata}
|
||||
|
||||
globalUrl={globalUrl}
|
||||
img1={inputUsecase.srcimg}
|
||||
srcapp={inputUsecase.srcapp}
|
||||
img2={inputUsecase.dstimg}
|
||||
dstapp={inputUsecase.dstapp}
|
||||
title={inputUsecase.name}
|
||||
description={inputUsecase.description}
|
||||
|
||||
apps={apps}
|
||||
/>
|
||||
</span>
|
||||
{/*
|
||||
|
||||
{/*subcase.matches.length > 0 ?
|
||||
<Grid container 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>
|
||||
*/}
|
||||
|
||||
{subcase.extra_buttons !== undefined && subcase.extra_buttons !== null && subcase.extra_buttons.length > 0 ?
|
||||
<div style={{marginTop: 25, }}>
|
||||
@@ -852,15 +916,9 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
<Typography variant="body1" style={{marginTop: 15, cursor: "pointer",}} onClick={() => {}}>
|
||||
See other Public Workflows for {} <OpenInNewIcon style={{marginTop: 5, marginLeft: 15, }}/>
|
||||
</Typography>
|
||||
{/*
|
||||
<div>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
No workflows yet.
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
</a>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
}
|
||||
<div style={{
|
||||
@@ -868,6 +926,8 @@ const UsecaseListComponent = ({keys, isCloud, globalUrl, frameworkData, isLogged
|
||||
width: 350,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
border: "1px solid rgba(255,255,255,0.3)",
|
||||
padding: 5,
|
||||
backgroundColor: theme.palette.backgroundColor,
|
||||
}}>
|
||||
<AppFramework
|
||||
inputUsecase={inputUsecase}
|
||||
|
||||
@@ -305,7 +305,6 @@ const LoginDialog = (props) => {
|
||||
paddingBottom: "30px",
|
||||
paddingTop: "30px",
|
||||
position: "relative",
|
||||
backgroundColor: theme.palette.inputColor,
|
||||
textAlign: "left",
|
||||
marginTop: 15,
|
||||
}}
|
||||
@@ -331,17 +330,25 @@ const LoginDialog = (props) => {
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>1.</b> Make sure shuffle-database folder has correct access:{" "}
|
||||
<b>1.</b> Make sure shuffle-database folder has correct access, and that you have a minimum of <b>2Gb of RAM available</b>:{" "}
|
||||
<br />
|
||||
<br />
|
||||
sudo chown 1000:1000 -R shuffle-database
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>2</b>. Restart docker-compose:
|
||||
<b>2.</b> Disable memory swap on the host
|
||||
<br />
|
||||
<br />
|
||||
sudo swapoff -a
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
style={{ marginBottom: 20, color: "white" }}
|
||||
>
|
||||
<b>3</b>. Restart docker-compose:
|
||||
<br />
|
||||
<br />
|
||||
sudo docker-compose restart
|
||||
@@ -513,7 +520,7 @@ const LoginDialog = (props) => {
|
||||
//id="sso_button"
|
||||
const ssoBtn = document.getElementById("sso_button");
|
||||
if (ssoBtn !== undefined && ssoBtn !== null) {
|
||||
console.log("SSO BTN: ", ssoBtn)
|
||||
//console.log("SSO BTN: ", ssoBtn)
|
||||
const cursearch = typeof window === "undefined" || window.location === undefined ? "" : window.location.search;
|
||||
var tmpView = new URLSearchParams(cursearch).get("autologin");
|
||||
if (tmpView !== undefined && tmpView !== null) {
|
||||
@@ -527,7 +534,7 @@ const LoginDialog = (props) => {
|
||||
}, 200);
|
||||
}, [ssoUrl])
|
||||
|
||||
return <div>{loadedCheck}</div>;
|
||||
return <div style={{paddingBottom: 150, }}>{loadedCheck}</div>;
|
||||
};
|
||||
|
||||
export default LoginDialog;
|
||||
|
||||
@@ -26,7 +26,7 @@ const SetAuthentication = (props) => {
|
||||
//const alert = useAlert();
|
||||
|
||||
const parseIncomingOpenapiData = (data) => {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
if (data.app === undefined || data.app === null) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ const SetAuthentication = (props) => {
|
||||
parsedapp.name = parsedapp.name.replaceAll("_", " ");
|
||||
setApp(parsedapp);
|
||||
|
||||
document.title = parsedapp.name + " App Auth";
|
||||
document.title = parsedapp.name + " App Auth";
|
||||
|
||||
}
|
||||
|
||||
console.log("App: ", app)
|
||||
console.log("App: ", app)
|
||||
|
||||
const getApp = (appid) => {
|
||||
if (serverside === true) {
|
||||
@@ -64,21 +64,21 @@ const SetAuthentication = (props) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_not_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_not_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
category: "appauth",
|
||||
action: `app_found`,
|
||||
label: appid,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return response.json();
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import theme from '../theme.jsx';
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
|
||||
import Drift from "react-driftjs";
|
||||
|
||||
const Welcome = (props) => {
|
||||
@@ -301,44 +302,47 @@ const Welcome = (props) => {
|
||||
flex: 1,
|
||||
padding: 0,
|
||||
textAlign: "center",
|
||||
maxWidth: 300,
|
||||
minWidth: 300,
|
||||
maxWidth: 275,
|
||||
minWidth: 275,
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}
|
||||
|
||||
const actionObject = {
|
||||
padding: "35px",
|
||||
maxHeight: 300,
|
||||
minHeight: 300,
|
||||
padding: "25px",
|
||||
maxHeight: 280,
|
||||
minHeight: 280,
|
||||
borderRadius: theme.palette.borderRadius,
|
||||
}
|
||||
|
||||
const imageStyle = {
|
||||
width: 150,
|
||||
width: 70,
|
||||
// height: 150,
|
||||
// margin: "auto",
|
||||
// marginTop: 10,
|
||||
borderRadius: 75,
|
||||
marginBottom: 18,
|
||||
// borderRadius: 75,
|
||||
objectFit: "scale-down",
|
||||
}
|
||||
const buttonStyle = {
|
||||
borderRadius: 8,
|
||||
borderRadius: 200,
|
||||
height: 51,
|
||||
width: 464,
|
||||
fontSize: 16,
|
||||
background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
// background: "linear-gradient(89.83deg, #FF8444 0.13%, #F2643B 99.84%)",
|
||||
background: "linear-gradient(90deg, #F86744 0%, #F34475 100%)",
|
||||
padding: "16px 24px",
|
||||
top: 75,
|
||||
top: 105,
|
||||
margin: "auto",
|
||||
itemAlign: "center",
|
||||
marginLeft: "65px",
|
||||
}
|
||||
|
||||
const defaultImage = "/images/experienced.png"
|
||||
const experienced_image = userdata !== undefined && userdata !== null && userdata.active_org !== undefined && userdata.active_org.image !== undefined && userdata.active_org.image !== null && userdata.active_org.image !== "" ? userdata.active_org.image : defaultImage
|
||||
return (
|
||||
<div style={{width: 1000, margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
|
||||
<div style={{ margin: "auto", paddingBottom: 150, minHeight: 1500, marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{position: "fixed", bottom: 110, right: 110, display: "flex", }}>
|
||||
<img src="/images/Arrow.png" style={{width: 250, height: "100%",}} />
|
||||
@@ -346,7 +350,7 @@ const Welcome = (props) => {
|
||||
*/}
|
||||
{showWelcome === true ?
|
||||
<div>
|
||||
<div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}>
|
||||
{/* <div style={{minWidth: 500, maxWidth: 500, margin: "auto", marginTop: isCloud ? "auto" : 20, }}>
|
||||
<Stepper
|
||||
activeStep={activeStep}
|
||||
color="primary"
|
||||
@@ -379,8 +383,8 @@ const Welcome = (props) => {
|
||||
)
|
||||
})}
|
||||
</Stepper>
|
||||
</div>
|
||||
<Grid container spacing={2} style={{ padding: 0, maxWidth: 1000, minWidth: 1000, margin: "auto", }}>
|
||||
</div> */}
|
||||
<Grid container spacing={1} style={{ padding: 0, maxWidth: 500, minWidth: 500, marginRight : "auto" , marginLeft: "auto" }}>
|
||||
<Grid item xs={window.location.href.includes("tab=2") ? 6 : 12}>
|
||||
<div>
|
||||
{/*
|
||||
@@ -414,7 +418,7 @@ const Welcome = (props) => {
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
{frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
|
||||
{/* {frameworkData === undefined || window.location.href.includes("tab=1") || window.location.href.includes("tab=3") ? null :
|
||||
<div style={{marginTop: 25, }}>
|
||||
<Typography variant="h6" style={{textAlign: "center", marginBottom: 25, }}>
|
||||
App Framework
|
||||
@@ -439,32 +443,35 @@ const Welcome = (props) => {
|
||||
/>
|
||||
</Fade>
|
||||
</div>
|
||||
}
|
||||
} */}
|
||||
</Grid>
|
||||
</div>
|
||||
:
|
||||
<Fade in={true}>
|
||||
<div style={{maxWidth: 700, margin: "auto", marginTop: 50, }}>
|
||||
{/*
|
||||
<div style={{display:"flex"}}>
|
||||
<ArrowBackIosIcon style={{color: "#9E9E9E",}} onClick={() => {
|
||||
navigate("/login")
|
||||
<div style={{maxWidth: 590, margin: "auto", marginTop: 50, }}>
|
||||
|
||||
{/* <div style={{display:"flex"}}>
|
||||
<ArrowBackIosIcon style={{color: "#9E9E9E", width: "16px",}} onClick={() => {
|
||||
navigate("/workflows")
|
||||
}}/>
|
||||
<Typography variant="body1" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 50, paddingRight: "366px"}} onClick={() => {
|
||||
navigate("/login")
|
||||
<Typography variant="h8" style={{color: "#9E9E9E",textAlign: "center", marginBottom: 20, paddingRight: "366px"}} onClick={() => {
|
||||
navigate("/workflows")
|
||||
}}>
|
||||
Back
|
||||
</Typography>
|
||||
</div>
|
||||
*/}
|
||||
<Typography variant="h4" style={{color: "#F1F1F1", textAlign: "center", marginTop: 50, }}>
|
||||
</div> */}
|
||||
|
||||
<Typography variant="h4" style={{color: "#F1F1F1", marginTop: 50, fontSize: 32}}>
|
||||
Help us get to know you
|
||||
</Typography>
|
||||
<Typography variant="body1" style={{color: "#9E9E9E", textAlign: "center", marginBottom: 50,}}>
|
||||
We will use this information to personalize your automation
|
||||
<Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
|
||||
Let us help you create a smoother journey.
|
||||
</Typography>
|
||||
{/* <Typography variant="body1" style={{color: "#9E9E9E", marginTop: 12 ,marginBottom: 50, fontSize: 16}}>
|
||||
We will use this information to personalize your automation
|
||||
</Typography> */}
|
||||
<div style={{display: "flex", marginTop: 70, width: 700, margin: "auto",}}>
|
||||
<div style={{border: "1px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
|
||||
<div style={{border: "2px solid #806BFF", borderRadius: theme.palette.borderRadius, }}>
|
||||
<Card style={paperObject} onClick={() => {
|
||||
if (isCloud) {
|
||||
ReactGA.event({
|
||||
@@ -506,7 +513,7 @@ const Welcome = (props) => {
|
||||
navigate("/workflows?message=Skipped intro")
|
||||
}}>
|
||||
<CardActionArea style={actionObject}>
|
||||
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 38 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, }} />
|
||||
<img src={experienced_image} style={{padding: experienced_image === defaultImage ? 2 : 10, objectFit: "scale-down", minHeight: experienced_image === defaultImage ? 40 : 70, maxHeight: experienced_image === defaultImage ? 40 : 70, bordeRadius: theme.palette.borderRadius, marginBottom: experienced_image === defaultImage ? 24 : 2 }} />
|
||||
<Typography variant="h4" style={{color: "#F1F1F1"}}>
|
||||
Experienced
|
||||
</Typography>
|
||||
@@ -517,17 +524,22 @@ const Welcome = (props) => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/*
|
||||
<div style={{display: "flex", flexDirection: "row", }}>
|
||||
|
||||
<div style={{ flexDirection: "row", }}>
|
||||
<Button variant="contained" type="submit" fullWidth style={buttonStyle} onClick={() => {
|
||||
navigate("/workflows?message=Skipped intro continue")
|
||||
ReactGA.event({
|
||||
category: "welcome",
|
||||
action: "click_welcome_continue",
|
||||
label: "",
|
||||
})
|
||||
}}>
|
||||
Continue
|
||||
</Button>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
|
||||
<div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
|
||||
|
||||
{/* <div style={{margin: "auto", borderRadius: theme.palette.borderRadius, marginTop: 50, width: 200, overflow: "wrap", padding: 25, cursor: "pointer", }} onClick={() => {
|
||||
if (window.drift !== undefined) {
|
||||
window.drift.api.startInteraction({ interactionId: 340045 })
|
||||
} else {
|
||||
@@ -537,7 +549,7 @@ const Welcome = (props) => {
|
||||
<Typography variant="body1" style={{margin: "auto", textAlign: "center"}}>
|
||||
Want a demo instead?
|
||||
</Typography>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</Fade>
|
||||
}
|
||||
|
||||
+127
-137
@@ -11,13 +11,13 @@ import { isMobile } from "react-device-detect"
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Divider,
|
||||
Divider,
|
||||
Avatar,
|
||||
Drawer,
|
||||
Grid,
|
||||
InputLabel,
|
||||
Select,
|
||||
ListSubheader,
|
||||
InputLabel,
|
||||
Select,
|
||||
ListSubheader,
|
||||
Paper,
|
||||
Tooltip,
|
||||
Button,
|
||||
@@ -533,7 +533,6 @@ const Workflows = (props) => {
|
||||
document.title = "Shuffle - Workflows";
|
||||
let navigate = useNavigate();
|
||||
|
||||
//const alert = useAlert();
|
||||
const classes = useStyles(theme);
|
||||
const imgSize = 60;
|
||||
|
||||
@@ -590,6 +589,9 @@ const Workflows = (props) => {
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false)
|
||||
const [videoViewOpen, setVideoViewOpen] = React.useState(false)
|
||||
const [gettingStartedItems, setGettingStartedItems] = React.useState([])
|
||||
const [selectedWorkflowIndexes, setSelectedWorkflowIndexes] = React.useState([])
|
||||
|
||||
const [apps, setApps] = React.useState([]);
|
||||
|
||||
const drawerWidth = drawerOpen ? 325 : 0
|
||||
|
||||
@@ -725,6 +727,30 @@ const Workflows = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getApps = () => {
|
||||
fetch(`${globalUrl}/api/v1/apps`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Status not 200 for apps :O!");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
setApps(responseJson);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("App loading error: "+error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const addFilter = (data) => {
|
||||
if (data === null || data === undefined) {
|
||||
console.log("No filter data")
|
||||
@@ -891,13 +917,15 @@ const Workflows = (props) => {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: 500,
|
||||
padding: 50,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ textAlign: "center", color: "rgba(255,255,255,0.9)" }}>
|
||||
Are you sure you want to delete this workflow? <div />
|
||||
Other workflows relying on this one may stop working
|
||||
Are you sure you want to delete {selectedWorkflowId.length > 0 ? filteredWorkflows.find((w) => w.id === selectedWorkflowId).name : `${selectedWorkflowIndexes.length} workflow${selectedWorkflowIndexes.length === 1 ? '' : 's'}`}? <div />
|
||||
|
||||
Other workflows relying on {selectedWorkflowIndexes.length > 0 ? "them" : "it"} one will stop working
|
||||
</div>
|
||||
</DialogTitle>
|
||||
<DialogContent
|
||||
@@ -912,7 +940,21 @@ const Workflows = (props) => {
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
}
|
||||
} else if (selectedWorkflowIndexes.length > 0) {
|
||||
// Do backwards so it doesn't change
|
||||
for (var i = selectedWorkflowIndexes.length - 1; i >= 0; i--) {
|
||||
const workflow = filteredWorkflows[selectedWorkflowIndexes[i]-1]
|
||||
if (workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null) {
|
||||
deleteWorkflow(workflow.id);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
getAvailableWorkflows();
|
||||
}, 1000);
|
||||
|
||||
setSelectedWorkflowIndexes([]);
|
||||
}
|
||||
setDeleteModalOpen(false);
|
||||
}}
|
||||
color="primary"
|
||||
@@ -1115,9 +1157,9 @@ const Workflows = (props) => {
|
||||
|
||||
// Ensures the zooming happens only once per load
|
||||
setTimeout(() => {
|
||||
fetchUsecases(newarray)
|
||||
setFirstLoad(false)
|
||||
}, 100)
|
||||
fetchUsecases(newarray)
|
||||
setFirstLoad(false)
|
||||
}, 100)
|
||||
|
||||
} else {
|
||||
if (isLoggedIn) {
|
||||
@@ -1223,8 +1265,9 @@ const Workflows = (props) => {
|
||||
setView(tmpView);
|
||||
}
|
||||
|
||||
getApps()
|
||||
getAvailableWorkflows();
|
||||
getFramework()
|
||||
getFramework()
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -1280,6 +1323,7 @@ const Workflows = (props) => {
|
||||
color: "white",
|
||||
margin: "10px",
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
position: "relative",
|
||||
};
|
||||
|
||||
const workflowActionStyle = {
|
||||
@@ -1708,21 +1752,22 @@ const Workflows = (props) => {
|
||||
<MenuItem
|
||||
style={{ backgroundColor: theme.palette.inputColor, color: "white" }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
setModalOpen(true);
|
||||
setEditingWorkflow(JSON.parse(JSON.stringify(data)));
|
||||
setNewWorkflowName(data.name);
|
||||
setNewWorkflowDescription(data.description);
|
||||
setDefaultReturnValue(data.default_return_value);
|
||||
if (data.tags !== undefined && data.tags !== null) {
|
||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
|
||||
}
|
||||
event.stopPropagation()
|
||||
ReactDOM.unstable_batchedUpdates(() => {
|
||||
setIsEditing(true)
|
||||
setModalOpen(true);
|
||||
setEditingWorkflow(JSON.parse(JSON.stringify(data)));
|
||||
setNewWorkflowName(data.name);
|
||||
setNewWorkflowDescription(data.description);
|
||||
setDefaultReturnValue(data.default_return_value);
|
||||
if (data.tags !== undefined && data.tags !== null) {
|
||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)));
|
||||
}
|
||||
|
||||
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
|
||||
setSelectedUsecases(data.usecase_ids)
|
||||
}
|
||||
})
|
||||
if (data.usecase_ids !== undefined && data.usecase_ids !== null && data.usecase_ids.length > 0) {
|
||||
setSelectedUsecases(data.usecase_ids)
|
||||
}
|
||||
})
|
||||
}}
|
||||
key={"change"}
|
||||
>
|
||||
@@ -2654,6 +2699,7 @@ const Workflows = (props) => {
|
||||
renderCell: (params) => {},
|
||||
},
|
||||
];
|
||||
|
||||
let rows = [];
|
||||
rows = filteredWorkflows.map((data, index) => {
|
||||
let obj = {
|
||||
@@ -2671,17 +2717,55 @@ const Workflows = (props) => {
|
||||
className={classes.datagrid}
|
||||
rows={rows}
|
||||
columns={columns}
|
||||
pageSize={20}
|
||||
pageSize={25}
|
||||
checkboxSelection
|
||||
autoHeight
|
||||
density="standard"
|
||||
onSelectionModelChange={(newSelection) => {
|
||||
//setSelectedWorkflows(newSelection.selectionModel);
|
||||
console.log(newSelection)
|
||||
|
||||
setSelectedWorkflowIndexes(newSelection)
|
||||
}}
|
||||
selectionModel={selectedWorkflowIndexes}
|
||||
components={{
|
||||
Toolbar: GridToolbar,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <div style={gridContainer}>{workflowData}</div>;
|
||||
return (
|
||||
<div style={gridContainer}>
|
||||
<Tooltip title={`New Workflow`} placement="bottom">
|
||||
<IconButton
|
||||
style={{position: "absolute", top: 10, right: 50, zIndex: 1000}}
|
||||
onClick={() => {
|
||||
setModalOpen(true)
|
||||
setIsEditing(false)
|
||||
}}
|
||||
>
|
||||
<AddCircleIcon style={{}} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{filteredWorkflows.length === 0 ? null :
|
||||
<IconButton
|
||||
style={{position: "absolute", top: 10, right: 10, zIndex: 1000}}
|
||||
disabled={selectedWorkflowIndexes.length === 0}
|
||||
onClick={() => {
|
||||
setDeleteModalOpen(true)
|
||||
}}
|
||||
>
|
||||
<Tooltip
|
||||
title={`Delete ${selectedWorkflowIndexes.length} workflows`}
|
||||
placement="top"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
}
|
||||
{workflowData}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
var total_count = 0
|
||||
@@ -3152,70 +3236,20 @@ const Workflows = (props) => {
|
||||
|
||||
const WorkflowView = () => {
|
||||
if (workflows.length === 0) {
|
||||
// Not going there yet
|
||||
//if ((userdata.tutorials !== undefined && userdata.tutorials !== null && !userdata.tutorials.includes("getting-started")) || userdata.tutorials === null) {
|
||||
// return <Navigate to="/getting-started" replace />;
|
||||
//}
|
||||
//return (
|
||||
// <div style={emptyWorkflowStyle}>
|
||||
// <Paper style={boxStyle}>
|
||||
// <div>
|
||||
// <h2>Welcome to Shuffle</h2>
|
||||
// </div>
|
||||
// <div>
|
||||
// <p>
|
||||
// <b>Shuffle</b> is a flexible, easy to use, automation platform
|
||||
// allowing users to integrate their services and devices freely.
|
||||
// It's made to significantly reduce the amount of manual labor,
|
||||
// and is focused on security applications.{" "}
|
||||
// <a
|
||||
// href="/docs/about"
|
||||
// style={{ textDecoration: "none", color: "#f85a3e" }}
|
||||
// >
|
||||
// Click here to learn more.
|
||||
// </a>
|
||||
// </p>
|
||||
// </div>
|
||||
// <div>
|
||||
// If you want to jump straight into it, click here to create your
|
||||
// first workflow:
|
||||
// </div>
|
||||
// <div style={{ display: "flex" }}>
|
||||
// <Button
|
||||
// id="second-step"
|
||||
// color="primary"
|
||||
// style={{ marginTop: "20px" }}
|
||||
// variant="outlined"
|
||||
// onClick={() => setModalOpen(true)}
|
||||
// >
|
||||
// New workflow
|
||||
// </Button>
|
||||
// <span style={{ paddingTop: 20, display: "flex" }}>
|
||||
// <Typography
|
||||
// style={{ marginTop: 5, marginLeft: 30, marginRight: 15 }}
|
||||
// >
|
||||
// ..OR
|
||||
// </Typography>
|
||||
// {workflowButtons}
|
||||
// </span>
|
||||
// </div>
|
||||
// </Paper>
|
||||
// </div>
|
||||
//)
|
||||
}
|
||||
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
var workflowDelay = -150
|
||||
var appDelay = -75
|
||||
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
const foundPriority = userdata === undefined || userdata === null ? null : userdata.priorities.find(prio => prio.type === "usecase" && prio.active === true)
|
||||
return (
|
||||
<div style={viewStyle}>
|
||||
<div style={workflowViewStyle}>
|
||||
<div style={{ display: "flex", marginTop: 25, }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
<Typography variant="h1" style={{fontSize: 30}}>
|
||||
Workflows
|
||||
</Typography>
|
||||
</div>
|
||||
{/*
|
||||
<div style={{ flex: 1 }}>
|
||||
@@ -3243,6 +3277,7 @@ const Workflows = (props) => {
|
||||
minWidth: 275,
|
||||
},
|
||||
}}
|
||||
rows={1}
|
||||
placeholder="Filter Workflows"
|
||||
color="primary"
|
||||
fullWidth
|
||||
@@ -3268,53 +3303,6 @@ const Workflows = (props) => {
|
||||
{workflowButtons}
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div style={flexContainerStyle}>
|
||||
<div style={{...flexBoxStyle, ...activeWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={mobileImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>ACTIVE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...availableWorkflowStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bookImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>AVAILABE WORKFLOWS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{...flexBoxStyle, ...notificationStyle}}>
|
||||
<div style={flexContentStyle}>
|
||||
<div><img src={bagImage} style={iconStyle} /></div>
|
||||
<div style={ blockRightStyle }>
|
||||
<div style={counterStyle}>{workflows.length}</div>
|
||||
<div style={fontSize_16}>NOTIFICATIONS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
|
||||
{/*
|
||||
chipRenderer={({ value, isFocused, isDisabled, handleClick, handleRequestDelete }, key) => {
|
||||
console.log("VALUE: ", value)
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={key}
|
||||
style={chipStyle}
|
||||
|
||||
>
|
||||
{value}
|
||||
</Chip>
|
||||
)
|
||||
}}
|
||||
*/}
|
||||
|
||||
<div style={{width: "100%", minHeight: isMobile ? 0 : 51, maxHeight: isMobile ? 0 : 51, marginTop: 10, }}>
|
||||
{!isMobile && usecases !== null && usecases !== undefined && usecases.length > 0 ?
|
||||
@@ -3772,12 +3760,12 @@ const Workflows = (props) => {
|
||||
}
|
||||
|
||||
const gettingStartedDrawer =
|
||||
<Drawer
|
||||
anchor={"right"}
|
||||
open={drawerOpen}
|
||||
variant="persistent"
|
||||
keepMounted={true}
|
||||
PaperProps={{
|
||||
<Drawer
|
||||
anchor={"right"}
|
||||
open={drawerOpen}
|
||||
variant="persistent"
|
||||
keepMounted={true}
|
||||
PaperProps={{
|
||||
style: {
|
||||
resize: "both",
|
||||
overflow: "auto",
|
||||
@@ -3928,6 +3916,7 @@ const Workflows = (props) => {
|
||||
{modalOpen === true ?
|
||||
<EditWorkflow
|
||||
globalUrl={globalUrl}
|
||||
userdata={userdata}
|
||||
workflow={editingWorkflow}
|
||||
setWorkflow={setEditingWorkflow}
|
||||
modalOpen={modalOpen}
|
||||
@@ -3938,6 +3927,7 @@ const Workflows = (props) => {
|
||||
isEditing={isEditing}
|
||||
|
||||
workflows={workflows}
|
||||
apps={apps}
|
||||
setWorkflows={setWorkflows}
|
||||
/>
|
||||
: null}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
NAME=shuffle-worker
|
||||
VERSION=1.2.1
|
||||
VERSION=1.2.2
|
||||
|
||||
echo "Running docker build with $NAME:$VERSION"
|
||||
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
||||
@@ -11,10 +11,10 @@ docker build . -t frikky/shuffle:$NAME -t frikky/shuffle:$NAME_$VERSION -t docke
|
||||
#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION
|
||||
#docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5
|
||||
#docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52
|
||||
docker push frikky/shuffle:$NAME
|
||||
docker push ghcr.io/frikky/$NAME:$VERSION
|
||||
docker push ghcr.io/frikky/$NAME:nightly
|
||||
|
||||
docker push shuffle/shuffle:$NAME
|
||||
docker push ghcr.io/shuffle/$NAME:$VERSION
|
||||
docker push ghcr.io/shuffle/$NAME:nightly
|
||||
#docker push frikky/shuffle:$NAME
|
||||
#docker push ghcr.io/frikky/$NAME:$VERSION
|
||||
#docker push ghcr.io/frikky/$NAME:nightly
|
||||
#
|
||||
#docker push shuffle/shuffle:$NAME
|
||||
#docker push ghcr.io/shuffle/$NAME:$VERSION
|
||||
#docker push ghcr.io/shuffle/$NAME:nightly
|
||||
|
||||
@@ -11,7 +11,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.4.17
|
||||
github.com/shuffle/shuffle-shared v0.4.50
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -47,16 +47,18 @@ require (
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opensearch-project/opensearch-go v1.1.0 // indirect
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go4.org v0.0.0-20201209231011-d4a079459e60 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
golang.org/x/tools v0.1.12 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.106.0 // indirect
|
||||
|
||||
@@ -60,17 +60,34 @@ github.com/adrg/strutil v0.2.3 h1:WZVn3ItPBovFmP4wMHHVXUr8luRaHrbyIuLlHt32GZQ=
|
||||
github.com/adrg/strutil v0.2.3/go.mod h1:+SNxbiH6t+O+5SZqIj5n/9i5yUjR+S3XXVrjEcN2mxg=
|
||||
github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ=
|
||||
github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk=
|
||||
github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
|
||||
github.com/aws/aws-sdk-go v1.44.263/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822 h1:hjXJeBcAMS1WGENGqDpzvmgS43oECTx8UXq31UBu0Jw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20221031212613-62deef7fc822/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y=
|
||||
github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -86,6 +103,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM=
|
||||
github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ=
|
||||
@@ -144,6 +162,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
@@ -155,6 +174,7 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
@@ -181,6 +201,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -201,6 +223,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opensearch-project/opensearch-go v1.1.0 h1:eG5sh3843bbU1itPRjA9QXbxcg8LaZ+DjEzQH9aLN3M=
|
||||
github.com/opensearch-project/opensearch-go v1.1.0/go.mod h1:+6/XHCuTH+fwsMJikZEWsucZ4eZMma3zNSeLrTtVGbo=
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0 h1:nQIEMr+A92CkhHrZgUhcfsrZjibvB3APXf2a1VwCmMQ=
|
||||
github.com/opensearch-project/opensearch-go/v2 v2.3.0/go.mod h1:8LDr9FCgUTVoT+5ESjc2+iaZuldqE+23Iq0r1XeNue8=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -222,6 +248,8 @@ github.com/shuffle/shuffle-shared v0.4.9 h1:mGCaLcSbrsQCy26pJXPlZAtitEzEEwqotGNj
|
||||
github.com/shuffle/shuffle-shared v0.4.9/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
|
||||
github.com/shuffle/shuffle-shared v0.4.17 h1:56ll366bdmIJu/7GFqNC2XTjjl0SGBf430PSq+EB6Ro=
|
||||
github.com/shuffle/shuffle-shared v0.4.17/go.mod h1:jQrYySmvp/0De5ftrAaY6xwwr7TMfqBmBxQ2AX9yrjQ=
|
||||
github.com/shuffle/shuffle-shared v0.4.50 h1:fJLfhWIJ5mYap4JwHnD/B5aaLyIULwylFSl3FoWlajM=
|
||||
github.com/shuffle/shuffle-shared v0.4.50/go.mod h1:X613gbo0dT3fnYvXDRwjQZyLC+T49T2nSQOrCV5QMlI=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -235,10 +263,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@@ -256,6 +286,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -322,8 +354,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
|
||||
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -344,6 +382,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -375,17 +414,31 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
|
||||
Reference in New Issue
Block a user