diff --git a/frontend/src/views/Workflows2.jsx b/frontend/src/views/Workflows2.jsx index 443181b5..f2b5e2f2 100644 --- a/frontend/src/views/Workflows2.jsx +++ b/frontend/src/views/Workflows2.jsx @@ -36,6 +36,8 @@ import { Button, TextField, FormControl, + FormControlLabel, + Switch, IconButton, Menu, MenuItem, @@ -53,6 +55,12 @@ import { Zoom, Collapse, Skeleton, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, } from "@mui/material"; // Material UI Icons @@ -1075,6 +1083,10 @@ const Workflows2 = (props) => { const [downloadBranch, setDownloadBranch] = React.useState("main"); const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = React.useState(false); + const [syncMode, setSyncMode] = React.useState(false); + const [remoteWorkflows, setRemoteWorkflows] = React.useState([]); + const [remoteWorkflowsLoading, setRemoteWorkflowsLoading] = React.useState(false); + const [syncListModalOpen, setSyncListModalOpen] = React.useState(false); const [exportModalOpen, setExportModalOpen] = React.useState(false); const [exportData, setExportData] = React.useState(""); const [dialogModalOpen, setDialogModalOpen] = React.useState(false); @@ -5967,6 +5979,78 @@ const Workflows2 = (props) => { ); }); + const listRemoteWorkflows = () => { + const parsedData = { + url: downloadUrl, + branch: downloadBranch || "master", + list_only: true, + }; + if (field1.length > 0) parsedData["username"] = field1; + if (field2.length > 0) parsedData["password"] = field2; + + setRemoteWorkflowsLoading(true); + fetch(globalUrl + "/api/v1/workflows/download_remote", { + method: "POST", + mode: "cors", + headers: { Accept: "application/json" }, + body: JSON.stringify(parsedData), + credentials: "include", + }) + .then((r) => r.json()) + .then((responseJson) => { + setRemoteWorkflowsLoading(false); + if (responseJson.success && Array.isArray(responseJson.workflows)) { + setRemoteWorkflows(responseJson.workflows); + setLoadWorkflowsModalOpen(false); + setSyncListModalOpen(true); + } else { + toast("Failed to list remote workflows: " + (responseJson.reason || "Unknown error")); + } + }) + .catch((err) => { + setRemoteWorkflowsLoading(false); + toast(err.toString()); + }); + }; + + const handleRemoteWorkflowAction = (remoteWf, action) => { + const parsedData = { + url: downloadUrl, + branch: downloadBranch || "master", + original_workflow_id: remoteWf.id, + }; + if (action === "sync") { + parsedData["sync_to_id"] = remoteWf.org_workflow_id || remoteWf.id; + } + if (field1.length > 0) parsedData["username"] = field1; + if (field2.length > 0) parsedData["password"] = field2; + + toast(action === "sync" ? `Syncing "${remoteWf.name}"...` : `Importing "${remoteWf.name}"...`); + fetch(globalUrl + "/api/v1/workflows/download_remote", { + method: "POST", + mode: "cors", + headers: { Accept: "application/json" }, + body: JSON.stringify(parsedData), + credentials: "include", + }) + .then((r) => r.json()) + .then((responseJson) => { + if (responseJson.success) { + toast.success(action === "sync" ? `"${remoteWf.name}" synced successfully` : `"${remoteWf.name}" imported successfully`); + // Refresh org workflow list and update local exists_in_org state + getAvailableWorkflows(); + setRemoteWorkflows((prev) => + prev.map((w) => + w.id === remoteWf.id ? { ...w, exists_in_org: true, org_workflow_id: w.id } : w + ) + ); + } else { + toast("Failed: " + (responseJson.reason || "Unknown error")); + } + }) + .catch((err) => toast(err.toString())); + }; + const importWorkflowsFromUrl = (url) => { console.log("IMPORT WORKFLOWS FROM ", downloadUrl); @@ -6018,8 +6102,12 @@ const Workflows2 = (props) => { }; const handleGithubValidation = () => { - importWorkflowsFromUrl(downloadUrl); - setLoadWorkflowsModalOpen(false); + if (syncMode) { + listRemoteWorkflows(); + } else { + importWorkflowsFromUrl(downloadUrl); + setLoadWorkflowsModalOpen(false); + } } const workflowDownloadModalOpen = loadWorkflowsModalOpen ? ( @@ -6141,6 +6229,22 @@ const Workflows2 = (props) => { fullWidth /> +