added upload and download flows on zustand and utils
This commit is contained in:
parent
d44648d6c1
commit
616a4f0031
15 changed files with 114 additions and 28 deletions
|
|
@ -5,7 +5,6 @@ import { useSSE } from "../../../contexts/SSEContext";
|
|||
import { postBuildInit } from "../../../controllers/API";
|
||||
import { FlowType } from "../../../types/flow";
|
||||
|
||||
import { FlowsContext } from "../../../contexts/flowsContext";
|
||||
import useAlertStore from "../../../stores/alertStore";
|
||||
import useFlowStore from "../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import AlertDropdown from "../../alerts/alertDropDown";
|
|||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import { useStoreStore } from "../../stores/storeStore";
|
||||
|
|
@ -22,7 +21,6 @@ import {
|
|||
} from "../ui/dropdown-menu";
|
||||
import { Separator } from "../ui/separator";
|
||||
import MenuBar from "./components/menuBar";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
||||
export default function Header(): JSX.Element {
|
||||
const notificationCenter = useAlertStore((state) => state.notificationCenter);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useContext, useEffect, useState } from "react";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import { uploadFile } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { FileComponentType } from "../../types/components";
|
||||
|
|
|
|||
|
|
@ -4,16 +4,14 @@ import IconComponent from "../../components/genericIconComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { removeApiKeys } from "../../utils/reactflowUtils";
|
||||
import { downloadFlow, removeApiKeys } from "../../utils/reactflowUtils";
|
||||
import BaseModal from "../baseModal";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
||||
const ExportModal = forwardRef(
|
||||
(props: { children: ReactNode }, ref): JSX.Element => {
|
||||
const { downloadFlow } = useContext(FlowsContext);
|
||||
const version = useDarkStore((state) => state.version);
|
||||
const setNoticeData = useAlertStore((state) => state.setNoticeData);
|
||||
const [checked, setChecked] = useState(true);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
|||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import { FlowSettingsPropsType } from "../../types/components";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import BaseModal from "../baseModal";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import IconComponent from "../../components/genericIconComponent";
|
|||
import { TagsSelector } from "../../components/tagsSelectorComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import {
|
||||
getStoreComponents,
|
||||
getStoreTags,
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ import ReactFlow, {
|
|||
SelectionDragHandler,
|
||||
addEdge,
|
||||
updateEdge,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
} from "reactflow";
|
||||
import GenericNode from "../../../../CustomNodes/GenericNode";
|
||||
import Chat from "../../../../components/chatComponent";
|
||||
|
|
@ -28,6 +26,8 @@ import { locationContext } from "../../../../contexts/locationContext";
|
|||
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import { APIClassType } from "../../../../types/api";
|
||||
import { FlowType, NodeType } from "../../../../types/flow";
|
||||
import {
|
||||
|
|
@ -41,8 +41,6 @@ import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
|||
import ConnectionLineComponent from "../ConnectionLineComponent";
|
||||
import SelectionMenu from "../SelectionMenuComponent";
|
||||
import ExtraSidebar from "../extraSidebarComponent";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
||||
const nodeTypes = {
|
||||
genericNode: GenericNode,
|
||||
|
|
@ -56,7 +54,9 @@ export default function Page({
|
|||
view?: boolean;
|
||||
}): JSX.Element {
|
||||
let { uploadFlow } = useContext(FlowsContext);
|
||||
const autoSaveCurrentFlow = useFlowsManagerStore((state) => state.autoSaveCurrentFlow);
|
||||
const autoSaveCurrentFlow = useFlowsManagerStore(
|
||||
(state) => state.autoSaveCurrentFlow
|
||||
);
|
||||
const types = useTypesStore((state) => state.types);
|
||||
const templates = useTypesStore((state) => state.templates);
|
||||
const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
|
||||
|
|
@ -160,13 +160,17 @@ export default function Page({
|
|||
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||
|
||||
const timeoutRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
if (reactFlowInstance) {
|
||||
reactFlowInstance.setNodes(flow?.data?.nodes ?? []);
|
||||
reactFlowInstance.setEdges(flow?.data?.edges ?? []);
|
||||
useFlowStore.setState({
|
||||
nodes: flow?.data?.nodes ?? [],
|
||||
edges: flow?.data?.edges ?? [],
|
||||
});
|
||||
reactFlowInstance.setViewport(
|
||||
flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 }
|
||||
);
|
||||
|
|
@ -186,7 +190,7 @@ export default function Page({
|
|||
return () => {
|
||||
clearTimeout(timeoutRef.current);
|
||||
};
|
||||
}, [flow, reactFlowInstance]);
|
||||
}, [currentFlowId, reactFlowInstance]);
|
||||
|
||||
const onConnectMod = useCallback(
|
||||
(params: Connection) => {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
const { uploadFlow } = useContext(FlowsContext);
|
||||
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
|
||||
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const hasApiKey = useStoreStore((state) => state.hasApiKey);
|
||||
|
|
@ -304,7 +306,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
(isPending ? "" : "button-disable")
|
||||
}
|
||||
onClick={(event) => {
|
||||
saveFlow({...currentFlow, data: {...currentFlow.data!, viewport: reactFlowInstance?.getViewport()!} }, true);
|
||||
saveFlow({...currentFlow, data: {nodes, edges, viewport: reactFlowInstance?.getViewport()!} }, true);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ export default function ComponentsComponent({
|
|||
}: {
|
||||
is_component?: boolean;
|
||||
}) {
|
||||
const { removeFlow, uploadFlow, addFlow, isLoading } =
|
||||
const { removeFlow, uploadFlow, addFlow } =
|
||||
useContext(FlowsContext);
|
||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
|
@ -51,7 +52,7 @@ export default function ComponentsComponent({
|
|||
const start = (pageIndex - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
setData(all.slice(start, end));
|
||||
}, [flows, pageIndex, pageSize]);
|
||||
}, [flows, isLoading, pageIndex, pageSize]);
|
||||
|
||||
const [data, setData] = useState<FlowType[]>([]);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,12 +10,16 @@ import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
|||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
import { downloadFlows } from "../../utils/reactflowUtils";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const { downloadFlows, uploadFlows, addFlow, uploadFlow } =
|
||||
const { addFlow, uploadFlow } =
|
||||
useContext(FlowsContext);
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
const uploadFlows = useFlowsManagerStore(
|
||||
(state) => state.uploadFlows
|
||||
);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const location = useLocation();
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import InputComponent from "../../components/inputComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { CONTROL_PATCH_USER_STATE } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import { resetPassword, updateUser } from "../../controllers/API";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import {
|
|||
SelectValue,
|
||||
} from "../../components/ui/select";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import {
|
||||
checkHasApiKey,
|
||||
getStoreComponents,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { create } from "zustand";
|
|||
import {
|
||||
readFlowsFromDatabase,
|
||||
updateFlowInDatabase,
|
||||
uploadFlowsToDatabase,
|
||||
} from "../controllers/API";
|
||||
import { FlowType } from "../types/flow";
|
||||
import { FlowState } from "../types/tabs";
|
||||
|
|
@ -25,6 +26,12 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
}));
|
||||
},
|
||||
flows: [],
|
||||
setFlows: (flows: FlowType[]) => {
|
||||
set({
|
||||
flows,
|
||||
currentFlow: flows.find((flow) => flow.id === get().currentFlowId),
|
||||
});
|
||||
},
|
||||
currentFlow: undefined,
|
||||
isLoading: true,
|
||||
setIsLoading: (isLoading: boolean) => set({ isLoading }),
|
||||
|
|
@ -52,7 +59,8 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
.then((dbData) => {
|
||||
if (dbData) {
|
||||
const { data, flows } = processFlows(dbData, false);
|
||||
set({ flows, isLoading: false });
|
||||
get().setFlows(flows);
|
||||
set({ isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
}));
|
||||
|
|
@ -94,14 +102,14 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
.getState()
|
||||
.setSuccessData({ title: "Changes saved successfully" });
|
||||
}
|
||||
set((oldState) => ({
|
||||
flows: oldState.flows.map((flow) => {
|
||||
get().setFlows(
|
||||
get().flows.map((flow) => {
|
||||
if (flow.id === updatedFlow.id) {
|
||||
return updatedFlow;
|
||||
}
|
||||
return flow;
|
||||
}),
|
||||
}));
|
||||
})
|
||||
);
|
||||
//update tabs state
|
||||
|
||||
useFlowStore.setState({ isPending: false });
|
||||
|
|
@ -117,6 +125,35 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
});
|
||||
});
|
||||
},
|
||||
uploadFlows: () => {
|
||||
return new Promise<void>((resolve) => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
// add a change event listener to the file input
|
||||
input.onchange = (event: Event) => {
|
||||
// check if the file type is application/json
|
||||
if (
|
||||
(event.target as HTMLInputElement).files![0].type ===
|
||||
"application/json"
|
||||
) {
|
||||
// get the file from the file input
|
||||
const file = (event.target as HTMLInputElement).files![0];
|
||||
// read the file as text
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
uploadFlowsToDatabase(formData).then(() => {
|
||||
get()
|
||||
.refreshFlows()
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
// trigger the file input click event to open the file dialog
|
||||
input.click();
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
export default useFlowsManagerStore;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { FlowState, FlowsState } from "../../tabs";
|
|||
|
||||
export type FlowsManagerStoreType = {
|
||||
flows: Array<FlowType>;
|
||||
setFlows: (flows: FlowType[]) => void;
|
||||
currentFlow: FlowType | undefined;
|
||||
currentFlowId: string;
|
||||
setCurrentFlowId: (currentFlowId: string) => void;
|
||||
|
|
@ -15,4 +16,5 @@ export type FlowsManagerStoreType = {
|
|||
refreshFlows: () => Promise<void>;
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>;
|
||||
autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => void;
|
||||
uploadFlows: () => Promise<void>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
updateEdgesHandleIdsType,
|
||||
} from "../types/utils/reactflowUtils";
|
||||
import { createRandomKey, getFieldTitle, toTitleCase } from "./utils";
|
||||
import { downloadFlowsFromDatabase } from "../controllers/API";
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
export function cleanEdges(nodes: Node[], edges: Edge[]) {
|
||||
|
|
@ -1205,3 +1206,48 @@ export function templatesGenerator(data: APIObjectType) {
|
|||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export function downloadFlow(
|
||||
flow: FlowType,
|
||||
flowName: string,
|
||||
flowDescription?: string
|
||||
) {
|
||||
let clonedFlow = cloneDeep(flow);
|
||||
removeFileNameFromComponents(clonedFlow);
|
||||
// create a data URI with the current flow data
|
||||
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify({
|
||||
...clonedFlow,
|
||||
name: flowName,
|
||||
description: flowDescription,
|
||||
})
|
||||
)}`;
|
||||
|
||||
// create a link element and set its properties
|
||||
const link = document.createElement("a");
|
||||
link.href = jsonString;
|
||||
link.download = `${
|
||||
flowName && flowName != ""
|
||||
? flowName
|
||||
: flow.name
|
||||
}.json`;
|
||||
|
||||
// simulate a click on the link element to trigger the download
|
||||
link.click();
|
||||
}
|
||||
|
||||
export 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();
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue