Fixed file category loading and small workflow changes
This commit is contained in:
@@ -1212,7 +1212,7 @@ class AppBase:
|
||||
return results
|
||||
|
||||
# Downloads all files from a namespace
|
||||
# Currently only working on local version of Shuffle
|
||||
# Currently only working on local version of Shuffle (2023)
|
||||
def get_file_category_ids(self, category):
|
||||
org_id = self.full_execution["workflow"]["execution_org"]["id"]
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.5.77
|
||||
github.com/shuffle/shuffle-shared v0.5.78
|
||||
golang.org/x/crypto v0.16.0
|
||||
google.golang.org/api v0.125.0
|
||||
google.golang.org/grpc v1.55.0
|
||||
@@ -52,7 +52,7 @@ require (
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/frikky/schemaless v0.0.5 // indirect
|
||||
github.com/frikky/schemaless v0.0.6 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
|
||||
@@ -176,8 +176,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
|
||||
github.com/frikky/kin-openapi v0.41.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/schemaless v0.0.5 h1:ptQ0FpQpm/+e7HCYt7Wmc+vcMSelTYCRpKqtLVN2SOY=
|
||||
github.com/frikky/schemaless v0.0.5/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||
github.com/frikky/schemaless v0.0.6 h1:mPWbqCxiOz0HUmdN+IiVOHqquCzA0aachzOdMTCaKtg=
|
||||
github.com/frikky/schemaless v0.0.6/go.mod h1:mooDxY+D6weHjhKvjy3+IE9S7P4g4cpNnidkdRv/cHQ=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsouza/go-dockerclient v1.9.7 h1:FlIrT71E62zwKgRvCvWGdxRD+a/pIy+miY/n3MXgfuw=
|
||||
@@ -455,6 +455,8 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shuffle/shuffle-shared v0.5.78 h1:emHTEu+WboTZQUUcPDrxMx70RtVuZ1LtkYjG2KzncBE=
|
||||
github.com/shuffle/shuffle-shared v0.5.78/go.mod h1:Lg6/+qjQlWzNKwj4/4ATpvScyP2JQGLkTPlNlRM6RJk=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
|
||||
@@ -4953,6 +4953,7 @@ func initHandlers() {
|
||||
// PS: For cloud, this has to use cloud storage.
|
||||
// https://developer.box.com/reference/get-files-id-content/
|
||||
// 1. Creating the "get file" option. Make it possible to run this in the frontend.
|
||||
r.HandleFunc("/api/v1/files/download_remote", shuffle.HandleDownloadRemoteFiles).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/files/namespaces/{namespace}", shuffle.HandleGetFileNamespace).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/files/{fileId}/content", shuffle.HandleGetFileContent).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/files/create", shuffle.HandleCreateFile).Methods("POST", "OPTIONS")
|
||||
|
||||
@@ -77,7 +77,6 @@ const ConfigureWorkflow = (props) => {
|
||||
const [checkStarted, setCheckStarted] = React.useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
//console.log("Configure Workflow: Required actions: ", requiredActions)
|
||||
if (requiredActions.length === 0) {
|
||||
if (setConfigurationFinished !== undefined) {
|
||||
setConfigurationFinished(true)
|
||||
@@ -703,9 +702,9 @@ const ConfigureWorkflow = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
toast("Failed to set app auth: " + responseJson.reason);
|
||||
toast("Failed to set app authentication: " + responseJson.reason);
|
||||
} else {
|
||||
toast("App auth set for app " + app.name.replace("_", " "));
|
||||
toast("App authentication set for app " + app.name.replace("_", " "));
|
||||
setFinalized(true)
|
||||
setOpened(false)
|
||||
}
|
||||
@@ -743,6 +742,25 @@ const ConfigureWorkflow = (props) => {
|
||||
<div style={{display: "flex", }}
|
||||
onClick={() => {
|
||||
setOpened(!opened);
|
||||
|
||||
// Scroll to it
|
||||
const element = document.getElementById("app-config");
|
||||
if (element) {
|
||||
// Scroll down 100px
|
||||
setTimeout(() => {
|
||||
element.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
top: 100,
|
||||
})
|
||||
|
||||
//element.scrollIntoView({
|
||||
// behavior: "smooth",
|
||||
// block: "center",
|
||||
// inline: "center"
|
||||
//});
|
||||
}, 250)
|
||||
}
|
||||
|
||||
}}
|
||||
>
|
||||
<div style={{display: "flex", flex: 7, }}>
|
||||
@@ -1317,7 +1335,7 @@ const ConfigureWorkflow = (props) => {
|
||||
</Typography>
|
||||
: null}
|
||||
|
||||
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 250, }}>
|
||||
<List style={{paddingBottom: window.location.pathname.includes("/workflows/") ? 0 : 350, }}>
|
||||
{requiredActions.map((data, index) => {
|
||||
|
||||
// AppWrapper = Default in a workflow, only shows with steps
|
||||
|
||||
@@ -16,6 +16,11 @@ import {
|
||||
Divider,
|
||||
Select,
|
||||
MenuItem,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
|
||||
import {
|
||||
@@ -30,9 +35,8 @@ import {
|
||||
Add as AddIcon,
|
||||
} from "@mui/icons-material";
|
||||
|
||||
//import { useAlert
|
||||
import Dropzone from "../components/Dropzone.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
||||
import theme from "../theme.jsx";
|
||||
|
||||
const Files = (props) => {
|
||||
@@ -45,6 +49,13 @@ const Files = (props) => {
|
||||
const [fileContent, setFileContent] = React.useState("");
|
||||
const [openEditor, setOpenEditor] = React.useState(false);
|
||||
const [renderTextBox, setRenderTextBox] = React.useState(false);
|
||||
const [loadFileModalOpen, setLoadFileModalOpen] = React.useState(false);
|
||||
|
||||
const [field1, setField1] = React.useState("");
|
||||
const [field2, setField2] = React.useState("");
|
||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/standards")
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("main");
|
||||
const [downloadFolder, setDownloadFolder] = React.useState("translation_standards");
|
||||
|
||||
//const alert = useAlert();
|
||||
const allowedFileTypes = ["txt", "py", "yaml", "yml","json", "html", "js", "csv", "log"]
|
||||
@@ -67,7 +78,7 @@ const Files = (props) => {
|
||||
|
||||
}
|
||||
|
||||
const runUpdateText = (text) =>{
|
||||
const runUpdateText = (text) =>{
|
||||
fetch(`${globalUrl}/api/v1/files/${openFileId}/edit`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
@@ -76,17 +87,34 @@ const Files = (props) => {
|
||||
},
|
||||
body:text,
|
||||
credentials: "include",
|
||||
}).then((response) => {
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
console.log("Can't update file");
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
//console.log(text);
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success === true) {
|
||||
toast("Successfully updated file");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast("Error updating file: " + error.toString());
|
||||
})
|
||||
}
|
||||
|
||||
const getFiles = () => {
|
||||
fetch(globalUrl + "/api/v1/files", {
|
||||
const getFiles = (namespace) => {
|
||||
var parsedurl = `${globalUrl}/api/v1/files`
|
||||
if (namespace === undefined || namespace === "default") {
|
||||
|
||||
} else if (namespace !== undefined && namespace !== null && namespace !== "") {
|
||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${namespace}?ids=true`
|
||||
} else if (selectedNamespace !== undefined && selectedNamespace !== null && selectedNamespace !== "default" && selectedNamespace !== "") {
|
||||
parsedurl = `${globalUrl}/api/v1/files/namespaces/${selectedNamespace}?ids=true`
|
||||
}
|
||||
|
||||
fetch(parsedurl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -104,12 +132,23 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.files !== undefined && responseJson.files !== null) {
|
||||
setFiles(responseJson.files);
|
||||
} else {
|
||||
setFiles(responseJson.files);
|
||||
} else if (responseJson.list !== undefined && responseJson.list !== null) {
|
||||
// Set the "namespace" field in all items
|
||||
if (namespace !== undefined && namespace !== null) {
|
||||
responseJson.list.forEach((item) => {
|
||||
item.namespace = namespace
|
||||
item.filename = item.name
|
||||
item.workflow_id = "global"
|
||||
})
|
||||
}
|
||||
|
||||
setFiles(responseJson.list);
|
||||
} else {
|
||||
setFiles([]);
|
||||
}
|
||||
|
||||
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null) {
|
||||
if (responseJson.namespaces !== undefined && responseJson.namespaces !== null && (fileNamespaces.length === 0 || responseJson.namespaces.length > fileNamespaces.length)) {
|
||||
setFileNamespaces(responseJson.namespaces);
|
||||
}
|
||||
})
|
||||
@@ -119,9 +158,214 @@ const Files = (props) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getFiles();
|
||||
getFiles(selectedNamespace)
|
||||
}, []);
|
||||
|
||||
const importStandardsFromUrl = (url, folder) => {
|
||||
if (url === undefined || url === null || url.length < 5) {
|
||||
toast("Please enter a valid URL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (folder === undefined || folder === null || folder.length < 2) {
|
||||
toast("Please enter a valid folder name")
|
||||
return
|
||||
}
|
||||
|
||||
const parsedData = {
|
||||
url: url,
|
||||
path: folder,
|
||||
field_3: downloadBranch || "master",
|
||||
};
|
||||
|
||||
if (field1.length > 0) {
|
||||
parsedData["field_1"] = field1;
|
||||
}
|
||||
|
||||
if (field2.length > 0) {
|
||||
parsedData["field_2"] = field2;
|
||||
}
|
||||
|
||||
toast(`Getting files from url ${url}. This may take a while if the repository is large. Please wait...`);
|
||||
fetch(globalUrl + "/api/v1/files/download_remote", {
|
||||
method: "POST",
|
||||
mode: "cors",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(parsedData),
|
||||
credentials: "include",
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
toast("Successfully loaded files from " + downloadUrl);
|
||||
setLoadFileModalOpen(false);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (!responseJson.success) {
|
||||
if (responseJson.reason !== undefined) {
|
||||
toast("Failed loading: " + responseJson.reason);
|
||||
} else {
|
||||
toast("Failed loading");
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast(error.toString());
|
||||
});
|
||||
}
|
||||
|
||||
const handleGithubValidation = () => {
|
||||
importStandardsFromUrl(downloadUrl, downloadFolder);
|
||||
}
|
||||
|
||||
const fileDownloadModal = loadFileModalOpen ?
|
||||
<Dialog
|
||||
open={loadFileModalOpen}
|
||||
onClose={() => {}}
|
||||
PaperProps={{
|
||||
style: {
|
||||
backgroundColor: theme.palette.surfaceColor,
|
||||
color: "white",
|
||||
minWidth: "800px",
|
||||
minHeight: "320px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DialogTitle>
|
||||
<div style={{ color: "rgba(255,255,255,0.9)" }}>
|
||||
Load Files from Github
|
||||
</div>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Files will be loaded from the repository and branch you specify, with the focus on files in one folder at a time. This is NOT recursive.
|
||||
</Typography>
|
||||
</DialogTitle>
|
||||
<DialogContent style={{ color: "rgba(255,255,255,0.65)" }}>
|
||||
Repository URL (supported: github, gitlab, bitbucket)
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadUrl}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadUrl(e.target.value)}
|
||||
placeholder="https://github.com/shuffle/standards"
|
||||
fullWidth
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Branch (default value is "main"):
|
||||
</span>
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadBranch}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadBranch(e.target.value)}
|
||||
placeholder="master"
|
||||
fullWidth
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Folder (can use / for subfolders):
|
||||
</span>
|
||||
<TextField
|
||||
style={{ backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
defaultValue={downloadFolder}
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadFolder(e.target.value)}
|
||||
placeholder="translation_standards"
|
||||
fullWidth
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Authentication (optional - private repos etc):
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setField1(e.target.value)}
|
||||
type="username"
|
||||
placeholder="Username / APIkey (optional)"
|
||||
fullWidth
|
||||
/>
|
||||
<TextField
|
||||
style={{ flex: 1, backgroundColor: theme.palette.inputColor }}
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
InputProps={{
|
||||
style: {
|
||||
color: "white",
|
||||
height: "50px",
|
||||
fontSize: "1em",
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setField2(e.target.value)}
|
||||
type="password"
|
||||
placeholder="Password (optional)"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
style={{ borderRadius: "0px" }}
|
||||
onClick={() => setLoadFileModalOpen(false)}
|
||||
color="primary"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
style={{ borderRadius: "0px" }}
|
||||
disabled={downloadUrl.length === 0 || !downloadUrl.includes("http")}
|
||||
onClick={() => {
|
||||
handleGithubValidation();
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
: null
|
||||
|
||||
const deleteFile = (file) => {
|
||||
fetch(globalUrl + "/api/v1/files/" + file.id, {
|
||||
method: "DELETE",
|
||||
@@ -140,7 +384,7 @@ const Files = (props) => {
|
||||
})
|
||||
.then((responseJson) => {
|
||||
if (responseJson.success) {
|
||||
toast("Successfully deleted file " + file.name);
|
||||
toast("Successfully deleted file")
|
||||
} else if (
|
||||
responseJson.reason !== undefined &&
|
||||
responseJson.reason !== null
|
||||
@@ -254,7 +498,7 @@ const Files = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateFile = (filename, file) => {
|
||||
const handleCreateFile = (filename, file) => {
|
||||
var data = {
|
||||
filename: filename,
|
||||
org_id: selectedOrganization.id,
|
||||
@@ -355,7 +599,7 @@ const Files = (props) => {
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
getFiles();
|
||||
getFiles()
|
||||
}, 2500);
|
||||
};
|
||||
|
||||
@@ -378,7 +622,21 @@ const Files = (props) => {
|
||||
}}
|
||||
onDrop={uploadFile}
|
||||
>
|
||||
<div>
|
||||
<div style={{position: "relative"}}>
|
||||
|
||||
<Tooltip color="primary" title={"Import files to Shuffle from Git"} placement="top">
|
||||
<IconButton
|
||||
color="secondary"
|
||||
style={{position: "absolute", right: 0, top: 0, }}
|
||||
variant="text"
|
||||
onClick={() => setLoadFileModalOpen(true)}
|
||||
>
|
||||
<CloudDownloadIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
{fileDownloadModal}
|
||||
|
||||
<div style={{ marginTop: 20, marginBottom: 20 }}>
|
||||
<h2 style={{ display: "inline" }}>Files</h2>
|
||||
<span style={{ marginLeft: 25 }}>
|
||||
@@ -393,6 +651,9 @@ const Files = (props) => {
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
@@ -426,6 +687,8 @@ const Files = (props) => {
|
||||
<CachedIcon />
|
||||
</Button>
|
||||
|
||||
|
||||
|
||||
{fileNamespaces !== undefined &&
|
||||
fileNamespaces !== null &&
|
||||
fileNamespaces.length > 1 ? (
|
||||
@@ -442,8 +705,13 @@ const Files = (props) => {
|
||||
}}
|
||||
value={selectedNamespace}
|
||||
onChange={(event) => {
|
||||
console.log("CHANGE NAMESPACE: ", event.target);
|
||||
setSelectedNamespace(event.target.value);
|
||||
|
||||
if (event.target.value === "all" || event.target.value === "default") {
|
||||
getFiles()
|
||||
} else {
|
||||
getFiles(event.target.value)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{fileNamespaces.map((data, index) => {
|
||||
@@ -453,7 +721,7 @@ const Files = (props) => {
|
||||
value={data}
|
||||
style={{ color: "white" }}
|
||||
>
|
||||
{data}
|
||||
{data.replaceAll("_", " ")}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
@@ -487,6 +755,8 @@ const Files = (props) => {
|
||||
<AddIcon/>
|
||||
</Button>
|
||||
</Tooltip> }
|
||||
|
||||
|
||||
{renderTextBox && <TextField
|
||||
onKeyPress={(event)=>{
|
||||
handleKeyDown(event);
|
||||
@@ -504,7 +774,7 @@ const Files = (props) => {
|
||||
autoFocus
|
||||
/>}</div>
|
||||
|
||||
<CodeEditor
|
||||
<ShuffleCodeEditor
|
||||
isCloud={isCloud}
|
||||
expansionModalOpen={openEditor}
|
||||
setExpansionModalOpen={setOpenEditor}
|
||||
@@ -602,7 +872,7 @@ const Files = (props) => {
|
||||
/>
|
||||
<ListItemText
|
||||
primary={
|
||||
file.workflow_id === "global" ? (
|
||||
file.workflow_id === "global" || file.workflow_id === "" || file.workflow_id === null || file.workflow_id === undefined ?
|
||||
<IconButton
|
||||
disabled={file.workflow_id === "global"}
|
||||
>
|
||||
@@ -615,7 +885,7 @@ const Files = (props) => {
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
) : (
|
||||
: (
|
||||
<Tooltip
|
||||
title={"Go to workflow"}
|
||||
style={{}}
|
||||
|
||||
@@ -88,12 +88,6 @@ import {
|
||||
SquareFoot as SquareFootIcon,
|
||||
} from '@mui/icons-material';
|
||||
|
||||
|
||||
//import CodeMirror from "@uiw/react-codemirror";
|
||||
//import "codemirror/keymap/sublime";
|
||||
//import "codemirror/theme/gruvbox-dark.css";
|
||||
//import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
notchedOutline: {
|
||||
borderColor: "#f85a3e !important",
|
||||
|
||||
@@ -5,13 +5,13 @@ import cystyle from "../defaultCytoscapeStyle.jsx";
|
||||
|
||||
const surfaceColor = "#27292D";
|
||||
const CytoscapeWrapper = (props) => {
|
||||
const { globalUrl, inworkflow } = props;
|
||||
const { globalUrl, inworkflow, height, width } = props;
|
||||
|
||||
const [elements, setElements] = useState([]);
|
||||
const [workflow, setWorkflow] = useState(inworkflow);
|
||||
const [cy, setCy] = React.useState();
|
||||
const bodyWidth = 200;
|
||||
const bodyHeight = 150;
|
||||
const bodyWidth = height === undefined ? 1000 : width
|
||||
const bodyHeight = width === undefined ? 1000 : height
|
||||
|
||||
const setupGraph = () => {
|
||||
const actions = workflow.actions.map((action) => {
|
||||
@@ -76,29 +76,6 @@ const CytoscapeWrapper = (props) => {
|
||||
};
|
||||
|
||||
// This is an attempt at prettier edges. The numbers are weird to work with.
|
||||
/*
|
||||
//http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html
|
||||
const sourcenode = actions.find(node => node.data._id === branch.source_id)
|
||||
const destinationnode = actions.find(node => node.data._id === branch.destination_id)
|
||||
if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) {
|
||||
//node.data._id = action["id"]
|
||||
console.log("SOURCE: ", sourcenode.position)
|
||||
console.log("DESTINATIONNODE: ", destinationnode.position)
|
||||
|
||||
var opposite = true
|
||||
if (sourcenode.position.x > destinationnode.position.x) {
|
||||
opposite = false
|
||||
} else {
|
||||
opposite = true
|
||||
}
|
||||
|
||||
edge.style = {
|
||||
'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"],
|
||||
'control-point-weight': ['0.3', '0.7'],
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return edge;
|
||||
});
|
||||
|
||||
@@ -135,9 +112,10 @@ const CytoscapeWrapper = (props) => {
|
||||
elements={elements}
|
||||
minZoom={0.35}
|
||||
maxZoom={2.0}
|
||||
zoom={1.0}
|
||||
style={{
|
||||
width: bodyWidth - 15,
|
||||
height: bodyHeight - 5,
|
||||
width: bodyWidth,
|
||||
height: bodyHeight,
|
||||
backgroundColor: surfaceColor,
|
||||
}}
|
||||
stylesheet={cystyle}
|
||||
|
||||
@@ -57,7 +57,6 @@ import { padding, textAlign } from '@mui/system';
|
||||
import data from '../frameworkStyle.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from '@uiw/codemirror-themes';
|
||||
|
||||
|
||||
|
||||
@@ -84,37 +83,6 @@ const pythonFilters = [
|
||||
{"name": "Handle JSON", "value": `{% python %}\nimport json\njsondata = json.loads(r"""$nodename""")\n{% endpython %}`, "example": ``},
|
||||
]
|
||||
|
||||
/*
|
||||
const shuffleTheme = createTheme({
|
||||
theme: 'dark',
|
||||
settings: {
|
||||
background: "rgba(40,40,40, 1)",
|
||||
foreground: '#75baff',
|
||||
caret: '#5d00ff',
|
||||
selection: '#036dd626',
|
||||
selectionMatch: '#036dd626',
|
||||
lineHighlight: '#8a91991a',
|
||||
gutterForeground: '#8a919966',
|
||||
},
|
||||
styles: [
|
||||
{ tag: t.comment, color: '#787b8099' },
|
||||
{ tag: t.variableName, color: '#0080ff' },
|
||||
{ tag: [t.string, t.special(t.brace)], color: '#5c6166' },
|
||||
{ tag: t.number, color: '#5c6166' },
|
||||
{ tag: t.bool, color: '#5c6166' },
|
||||
{ tag: t.null, color: '#5c6166' },
|
||||
{ tag: t.keyword, color: '#5c6166' },
|
||||
{ tag: t.operator, color: '#5c6166' },
|
||||
{ tag: t.className, color: '#5c6166' },
|
||||
{ tag: t.definition(t.typeName), color: '#5c6166' },
|
||||
{ tag: t.typeName, color: '#5c6166' },
|
||||
{ tag: t.angleBracket, color: '#5c6166' },
|
||||
{ tag: t.tagName, color: '#5c6166' },
|
||||
{ tag: t.attributeName, color: '#5c6166' },
|
||||
],
|
||||
});
|
||||
*/
|
||||
|
||||
const CodeEditor = (props) => {
|
||||
const {
|
||||
globalUrl,
|
||||
|
||||
@@ -5,6 +5,7 @@ import theme from '../theme.jsx';
|
||||
import { useNavigate, Link, useParams } from "react-router-dom";
|
||||
import AppSearchButtons from "../components/AppSearchButtons.jsx";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import RenderCytoscape from "../components/RenderCytoscape.jsx";
|
||||
import {
|
||||
Button,
|
||||
Typography,
|
||||
@@ -43,6 +44,8 @@ const WorkflowTemplatePopup = (props) => {
|
||||
const [missingDestination, setMissingDestination] = React.useState(undefined);
|
||||
const [configurationFinished, setConfigurationFinished] = React.useState(false);
|
||||
const [appSetupDone, setAppSetupDone] = React.useState(false)
|
||||
|
||||
const [requestSent, setRequestSent] = React.useState(false)
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
let navigate = useNavigate();
|
||||
@@ -294,6 +297,9 @@ const WorkflowTemplatePopup = (props) => {
|
||||
// middle:[]
|
||||
// name: "Email analysis"
|
||||
// source:{app_id: "accdaaf2eeba6a6ed43b2efc0112032d", app_name
|
||||
if (requestSent === true) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (srcapp.includes(":default") || dstapp.includes(":default")) {
|
||||
@@ -331,6 +337,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
},
|
||||
}
|
||||
|
||||
setRequestSent(true)
|
||||
const url = isCloud ? `${globalUrl}/api/v1/workflows/merge` : `https://shuffler.io/api/v1/workflows/merge`
|
||||
fetch(url, {
|
||||
method: "POST",
|
||||
@@ -344,6 +351,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
//console.log("Status not 200 for framework!");
|
||||
setRequestSent(false)
|
||||
}
|
||||
|
||||
setWorkflowLoading(false)
|
||||
@@ -361,6 +369,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
|
||||
if (responseJson.success === false) {
|
||||
//console.log("Error in workflow template: ", responseJson.error);
|
||||
setRequestSent(false)
|
||||
|
||||
const defaultMessage = "Failed to generate workflow the workflow - the Shuffle team has been notified. Contact support@shuffler.io for further assistance."
|
||||
if (responseJson.reason !== undefined && responseJson.reason !== null && responseJson.reason !== "") {
|
||||
@@ -386,6 +395,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("err in framework: ", error.toString());
|
||||
setRequestSent(false)
|
||||
setWorkflowLoading(false)
|
||||
})
|
||||
}
|
||||
@@ -422,6 +432,9 @@ const WorkflowTemplatePopup = (props) => {
|
||||
return null
|
||||
}
|
||||
|
||||
const divHeight = 500
|
||||
const divWidth = 500
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
anchor={"left"}
|
||||
@@ -559,12 +572,29 @@ const WorkflowTemplatePopup = (props) => {
|
||||
setConfigurationFinished={setConfigurationFinished}
|
||||
/>
|
||||
|
||||
|
||||
{/*workflow !== undefined && workflow !== null && workflow.id !== undefined && workflow.id !== null && workflow.id !== "" ?
|
||||
<div style={{position: "fixed", right: "5%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", height: divHeight, width: divWidth, borderRadius: theme.palette.borderRadius, }}>
|
||||
<RenderCytoscape
|
||||
inworkflow={workflow}
|
||||
height={divHeight}
|
||||
width={divWidth}
|
||||
/>
|
||||
</div>
|
||||
: null*/}
|
||||
|
||||
{errorMessage === "" && configurationFinished === true && workflow.id !== undefined && workflowLoading === false ?
|
||||
<Tooltip title="Workflow generated!" placement="top">
|
||||
<span style={{display: "flex", }}>
|
||||
<Tooltip title="Click to explore the workflow" placement="top">
|
||||
<span
|
||||
style={{position: "fixed", display: "flex", right: "10%", top: "20%", border: "1px solid rgba(255,255,255,0.3)", borderRadius: theme.palette.borderRadius, padding: "15px 30px 15px 30px", backgroundColor: theme.palette.platformColor, cursor: "pointer", }}
|
||||
onClick={() => {
|
||||
// Open in new tab
|
||||
window.open("/workflows/" + workflow.id, "_blank")
|
||||
}}
|
||||
>
|
||||
{/*<CheckIcon color="primary" sx={{ borderRadius: 4 }} /> */}
|
||||
<Typography variant="h6" style={{ marginLeft: 20, }}>
|
||||
Workflow generated!
|
||||
<Typography variant="h5" style={{ }}>
|
||||
Workflow Successfully Generated!
|
||||
</Typography>
|
||||
</span>
|
||||
</Tooltip>
|
||||
@@ -686,6 +716,7 @@ const WorkflowTemplatePopup = (props) => {
|
||||
<CheckIcon color="primary" sx={{ borderRadius: 4 }} style={{ position: "absolute", color: theme.palette.green, top: 10, right: 10, }} />
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ import { makeStyles } from "@mui/styles";
|
||||
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import countries from "../components/Countries.jsx";
|
||||
import CodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import getLocalCodeData from "../components/ShuffleCodeEditor.jsx";
|
||||
import CacheView from "../components/CacheView.jsx";
|
||||
|
||||
import {
|
||||
|
||||
@@ -121,7 +121,7 @@ import * as edgehandles from "cytoscape-edgehandles";
|
||||
import CytoscapeComponent from "react-cytoscapejs";
|
||||
import Draggable from "react-draggable";
|
||||
import cytoscapestyle from "../defaultCytoscapeStyle.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor.jsx";
|
||||
import ShuffleCodeEditor from "../components/ShuffleCodeEditor1.jsx";
|
||||
|
||||
import { validateJson, GetIconInfo } from "../views/Workflows.jsx";
|
||||
import { GetParsedPaths, internalIds, } from "../views/Apps.jsx";
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useInterval } from "react-powerhooks";
|
||||
import AppFramework from "../components/AppFramework.jsx";
|
||||
import { makeStyles, } from "@mui/styles";
|
||||
// nodejs library that concatenates classes
|
||||
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";
|
||||
//import { useAlert
|
||||
import React, { useState, useEffect } from "react"
|
||||
import { useInterval } from "react-powerhooks"
|
||||
import AppFramework from "../components/AppFramework.jsx"
|
||||
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"
|
||||
import { ToastContainer, toast } from "react-toastify"
|
||||
import { parsedDatatypeImages } from "../components/AppFramework.jsx"
|
||||
import { findSpecificApp } from "../components/AppFramework.jsx"
|
||||
|
||||
@@ -551,9 +551,7 @@ const Workflows = (props) => {
|
||||
|
||||
const [field1, setField1] = React.useState("");
|
||||
const [field2, setField2] = React.useState("");
|
||||
const [downloadUrl, setDownloadUrl] = React.useState(
|
||||
"https://github.com/frikky/shuffle-workflows"
|
||||
);
|
||||
const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/shuffle/workflows")
|
||||
const [downloadBranch, setDownloadBranch] = React.useState("master");
|
||||
const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] =
|
||||
React.useState(false);
|
||||
@@ -3630,7 +3628,7 @@ const Workflows = (props) => {
|
||||
const handleGithubValidation = () => {
|
||||
importWorkflowsFromUrl(downloadUrl);
|
||||
setLoadWorkflowsModalOpen(false);
|
||||
};
|
||||
}
|
||||
|
||||
const workflowDownloadModalOpen = loadWorkflowsModalOpen ? (
|
||||
<Dialog
|
||||
@@ -3677,11 +3675,11 @@ const Workflows = (props) => {
|
||||
},
|
||||
}}
|
||||
onChange={(e) => setDownloadUrl(e.target.value)}
|
||||
placeholder="https://github.com/frikky/shuffle-apps"
|
||||
placeholder="https://github.com/shuffle/workflows"
|
||||
fullWidth
|
||||
/>
|
||||
<span style={{ marginTop: 10 }}>
|
||||
Branch (default value is "master"):
|
||||
Branch (default value is "main"):
|
||||
</span>
|
||||
<div style={{ display: "flex" }}>
|
||||
<TextField
|
||||
|
||||
Reference in New Issue
Block a user