diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx
index 0bc7754cd..749ca348c 100644
--- a/src/frontend/src/contexts/tabsContext.tsx
+++ b/src/frontend/src/contexts/tabsContext.tsx
@@ -19,11 +19,13 @@ import { typesContext } from "./typesContext";
import { APITemplateType, TemplateVariableType } from "../types/api";
import { v4 as uuidv4 } from "uuid";
import { addEdge } from "reactflow";
-import _ from "lodash";
+import _, { flow } from "lodash";
import {
readFlowsFromDatabase,
deleteFlowFromDatabase,
saveFlowToDatabase,
+ downloadFlowsFromDatabase,
+ uploadFlowsToDatabase,
} from "../controllers/API";
const TabsContextInitialValue: TabsContextType = {
@@ -35,6 +37,8 @@ const TabsContextInitialValue: TabsContextType = {
updateFlow: (newFlow: FlowType) => {},
incrementNodeId: () => uuidv4(),
downloadFlow: (flow: FlowType) => {},
+ downloadFlows: () => {},
+ uploadFlows: () => {},
uploadFlow: () => {},
hardReset: () => {},
disableCopyPaste: false,
@@ -56,7 +60,7 @@ export const TabsContext = createContext
(
export function TabsProvider({ children }: { children: ReactNode }) {
const { setErrorData, setNoticeData } = useContext(alertContext);
const [tabId, setTabId] = useState("");
- const [flows, setFlows] = useState>([]);
+ const [flows, setFlows] = useState([]);
const [id, setId] = useState(uuidv4());
const { templates, reactFlowInstance } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
@@ -114,10 +118,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// }
// }
- useEffect(() => {
- // get data from db
- //get tabs locally saved
- // let tabsData = getLocalStorageTabsData();
+ function refreshFlows() {
getTabsDataFromDB().then((DbData) => {
if (DbData && Object.keys(templates).length > 0) {
try {
@@ -128,6 +129,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
}
}
});
+ }
+
+ useEffect(() => {
+ // get data from db
+ //get tabs locally saved
+ // let tabsData = getLocalStorageTabsData();
+ refreshFlows();
}, [templates]);
function getTabsDataFromDB() {
@@ -148,20 +156,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
}
- function processTabsData(tabsData) {
- tabsData.flows.forEach((flow) => {
- try {
- if (!flow.data) {
- return;
- }
- processFlowEdges(flow);
- processFlowNodes(flow);
- } catch (e) {
- console.error(e);
- }
- });
- }
-
function processFlowEdges(flow) {
flow.data.edges.forEach((edge) => {
edge.className = "";
@@ -244,6 +238,22 @@ export function TabsProvider({ children }: { children: ReactNode }) {
});
}
+ function downloadFlows() {
+ downloadFlowsFromDatabase().then((flows) => {
+ const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
+ JSON.stringify(flows)
+ )}`;
+
+ // create a link element and set its properties
+ const link = document.createElement("a");
+ link.href = jsonString;
+ link.download = `flows.json`;
+
+ // simulate a click on the link element to trigger the download
+ link.click();
+ });
+ }
+
function getNodeId() {
return `dndnode_` + incrementNodeId();
}
@@ -275,30 +285,41 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// trigger the file input click event to open the file dialog
input.click();
}
+
+ function uploadFlows() {
+ // create a file input
+ const input = document.createElement("input");
+ input.type = "file";
+ // add a change event listener to the file input
+ input.onchange = (e: Event) => {
+ // check if the file type is application/json
+ if ((e.target as HTMLInputElement).files[0].type === "application/json") {
+ // get the file from the file input
+ const file = (e.target as HTMLInputElement).files[0];
+ // read the file as text
+ const formData = new FormData();
+ formData.append("file", file);
+ uploadFlowsToDatabase(formData).then(() => {
+ refreshFlows();
+ });
+ }
+ };
+ // trigger the file input click event to open the file dialog
+ input.click();
+ }
/**
* Removes a flow from an array of flows based on its id.
* Updates the state of flows and tabIndex using setFlows and setTabIndex hooks.
* @param {string} id - The id of the flow to remove.
*/
function removeFlow(id: string) {
- setFlows((prevState) => {
- const newFlows = [...prevState];
- const index = newFlows.findIndex((flow) => flow.id === id);
+ const index = flows.findIndex((flow) => flow.id === id);
+ console.log(index);
if (index >= 0) {
deleteFlowFromDatabase(id).then(() => {
- let tabIndex = flows.findIndex((flow) => flow.id === tabId);
- if (index === tabIndex) {
- setTabId(flows[flows.length - 2].id);
- newFlows.splice(index, 1);
- } else {
- let flowId = flows[tabIndex].id;
- newFlows.splice(index, 1);
- setTabId(flowId);
- }
+ setFlows(flows.filter((flow) => flow.id !== id));
});
}
- return newFlows;
- });
}
/**
* Add a new flow to the list of flows.
@@ -510,6 +531,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
addFlow,
updateFlow,
downloadFlow,
+ downloadFlows,
+ uploadFlows,
uploadFlow,
getNodeId,
paste,
diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts
index 910aeacf6..007c7f9bb 100644
--- a/src/frontend/src/controllers/API/index.ts
+++ b/src/frontend/src/controllers/API/index.ts
@@ -148,6 +148,35 @@ export async function readFlowsFromDatabase() {
}
}
+export async function downloadFlowsFromDatabase() {
+ try {
+ const response = await fetch("/flows/download/");
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
+}
+
+export async function uploadFlowsToDatabase(flows) {
+ try {
+ const response = await fetch(`/flows/upload/`, {
+ method: "POST", // Or "PATCH" depending on your backend API
+ body: flows,
+ });
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ } catch (error) {
+ console.error(error);
+ throw error;
+ }
+}
+
/**
* Deletes a flow from the database.
*
@@ -198,7 +227,7 @@ export async function getFlowFromDatabase(flowId: number) {
*/
export async function getFlowStylesFromDatabase() {
try {
- const response = await fetch("/flows_styles/");
+ const response = await fetch("/flow_styles/");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
@@ -218,7 +247,7 @@ export async function getFlowStylesFromDatabase() {
*/
export async function saveFlowStyleToDatabase(flowStyle: FlowStyleType) {
try {
- const response = await fetch("/flows_styles/", {
+ const response = await fetch("/flow_styles/", {
method: "POST",
headers: {
accept: "application/json",
diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx
index 4532afe33..a7abb7528 100644
--- a/src/frontend/src/modals/exportModal/index.tsx
+++ b/src/frontend/src/modals/exportModal/index.tsx
@@ -30,7 +30,7 @@ export default function ExportModal() {
const { closePopUp } = useContext(PopUpContext);
const ref = useRef();
const { setErrorData } = useContext(alertContext);
- const { flows, tabIndex, updateFlow, downloadFlow } = useContext(TabsContext);
+ const { flows, tabId, updateFlow, downloadFlow } = useContext(TabsContext);
function setModalOpen(x: boolean) {
setOpen(x);
if (x === false) {
@@ -40,7 +40,7 @@ export default function ExportModal() {
}
}
const [checked, setChecked] = useState(true);
- const [name, setName] = useState(flows[tabIndex].name);
+ const [name, setName] = useState(flows.find((f) => f.id === tabId).name);
return (