update template working for local flow on browser
This commit is contained in:
parent
a8f2d69f9a
commit
d91beb9cda
7 changed files with 109 additions and 81 deletions
|
|
@ -59,5 +59,5 @@
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"proxy": "http://backend:7860"
|
"proxy": "http://127.0.0.1:5003"
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
import { createContext, useEffect, useState, useRef, ReactNode, useContext } from "react";
|
import {
|
||||||
|
createContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
ReactNode,
|
||||||
|
useContext,
|
||||||
|
} from "react";
|
||||||
import { FlowType } from "../types/flow";
|
import { FlowType } from "../types/flow";
|
||||||
import { LangFlowState, TabsContextType } from "../types/tabs";
|
import { LangFlowState, TabsContextType } from "../types/tabs";
|
||||||
import { normalCaseToSnakeCase } from "../utils";
|
import { normalCaseToSnakeCase, updateObject } from "../utils";
|
||||||
import { alertContext } from "./alertContext";
|
import { alertContext } from "./alertContext";
|
||||||
import { typesContext } from "./typesContext";
|
import { typesContext } from "./typesContext";
|
||||||
|
import { TemplateVariableType } from "../types/api";
|
||||||
|
|
||||||
const TabsContextInitialValue: TabsContextType = {
|
const TabsContextInitialValue: TabsContextType = {
|
||||||
save:()=>{},
|
save: () => {},
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
setTabIndex: (index: number) => {},
|
setTabIndex: (index: number) => {},
|
||||||
flows: [],
|
flows: [],
|
||||||
|
|
@ -14,11 +22,11 @@ const TabsContextInitialValue: TabsContextType = {
|
||||||
addFlow: (flowData?: any) => {},
|
addFlow: (flowData?: any) => {},
|
||||||
updateFlow: (newFlow: FlowType) => {},
|
updateFlow: (newFlow: FlowType) => {},
|
||||||
incrementNodeId: () => 0,
|
incrementNodeId: () => 0,
|
||||||
downloadFlow: (flow:FlowType) => {},
|
downloadFlow: (flow: FlowType) => {},
|
||||||
uploadFlow: () => {},
|
uploadFlow: () => {},
|
||||||
lockChat: false,
|
lockChat: false,
|
||||||
setLockChat:(prevState:boolean)=>{},
|
setLockChat: (prevState: boolean) => {},
|
||||||
hardReset:()=>{},
|
hardReset: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TabsContext = createContext<TabsContextType>(
|
export const TabsContext = createContext<TabsContextType>(
|
||||||
|
|
@ -26,61 +34,64 @@ export const TabsContext = createContext<TabsContextType>(
|
||||||
);
|
);
|
||||||
|
|
||||||
export function TabsProvider({ children }: { children: ReactNode }) {
|
export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const {setNoticeData} = useContext(alertContext)
|
const { setNoticeData } = useContext(alertContext);
|
||||||
const [tabIndex, setTabIndex] = useState(0);
|
const [tabIndex, setTabIndex] = useState(0);
|
||||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||||
const [id, setId] = useState(0);
|
const [id, setId] = useState(0);
|
||||||
const [lockChat, setLockChat] = useState(false);
|
const [lockChat, setLockChat] = useState(false);
|
||||||
const {templates} = useContext(typesContext )
|
const { templates } = useContext(typesContext);
|
||||||
|
|
||||||
const newNodeId = useRef(0);
|
const newNodeId = useRef(0);
|
||||||
function incrementNodeId() {
|
function incrementNodeId() {
|
||||||
newNodeId.current = newNodeId.current + 1;
|
newNodeId.current = newNodeId.current + 1;
|
||||||
return newNodeId.current;
|
return newNodeId.current;
|
||||||
}
|
}
|
||||||
function save(){
|
function save() {
|
||||||
console.log("save")
|
console.log("save");
|
||||||
if (flows.length !== 0)
|
if (flows.length !== 0)
|
||||||
window.localStorage.setItem(
|
window.localStorage.setItem(
|
||||||
"tabsData",
|
"tabsData",
|
||||||
JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//save tabs locally
|
//save tabs locally
|
||||||
save()
|
save();
|
||||||
}, [flows, id, tabIndex, newNodeId]);
|
}, [flows, id, tabIndex, newNodeId]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//get tabs locally saved
|
//get tabs locally saved
|
||||||
let cookie = window.localStorage.getItem("tabsData");
|
let cookie = window.localStorage.getItem("tabsData");
|
||||||
if (cookie) {
|
if (cookie && Object.keys(templates).length > 0) {
|
||||||
console.log(templates)
|
let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||||
console.log(Object.keys(templates).length)
|
cookieObject.flows.forEach((flow) => {
|
||||||
let cookieObject:LangFlowState = JSON.parse(cookie);
|
flow.data.nodes.forEach((node) => {
|
||||||
cookieObject.flows.forEach(flow=>{
|
node.data.node.template = updateObject(
|
||||||
console.log(flow)
|
node.data.node.template as TemplateVariableType,
|
||||||
// flow.data.nodes.forEach(node=>{
|
templates[node.data.type][
|
||||||
// console.log(node.data)
|
"template"
|
||||||
// })
|
] as unknown as TemplateVariableType
|
||||||
})
|
);
|
||||||
|
console.log(node)
|
||||||
|
});
|
||||||
|
});
|
||||||
setTabIndex(cookieObject.tabIndex);
|
setTabIndex(cookieObject.tabIndex);
|
||||||
setFlows(cookieObject.flows);
|
setFlows(cookieObject.flows);
|
||||||
setId(cookieObject.id);
|
setId(cookieObject.id);
|
||||||
newNodeId.current = cookieObject.nodeId;
|
newNodeId.current = cookieObject.nodeId;
|
||||||
}
|
}
|
||||||
}, [templates]);
|
}, [templates]);
|
||||||
function hardReset(){
|
function hardReset() {
|
||||||
newNodeId.current=0;
|
newNodeId.current = 0;
|
||||||
setTabIndex(0);setFlows([]);setId(0);
|
setTabIndex(0);
|
||||||
|
setFlows([]);
|
||||||
|
setId(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads the current flow as a JSON file
|
* Downloads the current flow as a JSON file
|
||||||
*/
|
*/
|
||||||
function downloadFlow(flow:FlowType) {
|
function downloadFlow(flow: FlowType) {
|
||||||
// create a data URI with the current flow data
|
// create a data URI with the current flow data
|
||||||
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
|
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
|
||||||
JSON.stringify(flow)
|
JSON.stringify(flow)
|
||||||
|
|
@ -93,7 +104,9 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
|
|
||||||
// simulate a click on the link element to trigger the download
|
// simulate a click on the link element to trigger the download
|
||||||
link.click();
|
link.click();
|
||||||
setNoticeData({title:"Warning: Critical data,JSON file may including API keys."})
|
setNoticeData({
|
||||||
|
title: "Warning: Critical data,JSON file may including API keys.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -150,7 +163,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
function addFlow(flow?: FlowType) {
|
function addFlow(flow?: FlowType) {
|
||||||
// Get data from the flow or set it to null if there's no flow provided.
|
// Get data from the flow or set it to null if there's no flow provided.
|
||||||
const data = flow?.data ? flow.data : null;
|
const data = flow?.data ? flow.data : null;
|
||||||
const description = flow?.description?flow.description:""
|
const description = flow?.description ? flow.description : "";
|
||||||
|
|
||||||
// Create a new flow with a default name if no flow is provided.
|
// Create a new flow with a default name if no flow is provided.
|
||||||
let newFlow: FlowType = {
|
let newFlow: FlowType = {
|
||||||
|
|
@ -182,7 +195,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const newFlows = [...prevState];
|
const newFlows = [...prevState];
|
||||||
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
|
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
newFlows[index].description = newFlow.description??""
|
newFlows[index].description = newFlow.description ?? "";
|
||||||
newFlows[index].data = newFlow.data;
|
newFlows[index].data = newFlow.data;
|
||||||
newFlows[index].name = newFlow.name;
|
newFlows[index].name = newFlow.name;
|
||||||
newFlows[index].chat = newFlow.chat;
|
newFlows[index].chat = newFlow.chat;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { createContext, ReactNode, useState } from "react";
|
import { createContext, ReactNode, useEffect, useState } from "react";
|
||||||
import { Node} from "reactflow";
|
import { Node} from "reactflow";
|
||||||
import { typesContextType } from "../types/typesContext";
|
import { typesContextType } from "../types/typesContext";
|
||||||
|
import { getAll } from "../controllers/API";
|
||||||
|
import { APIKindType } from "../types/api";
|
||||||
|
|
||||||
//context to share types adn functions from nodes to flow
|
//context to share types adn functions from nodes to flow
|
||||||
|
|
||||||
|
|
@ -23,6 +25,40 @@ export function TypesProvider({ children }:{children:ReactNode}) {
|
||||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||||
const [templates, setTemplates] = useState({});
|
const [templates, setTemplates] = useState({});
|
||||||
const [data, setData] = useState({});
|
const [data, setData] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function getTypes(): Promise<void> {
|
||||||
|
// Make an asynchronous API call to retrieve all data.
|
||||||
|
let result = await getAll();
|
||||||
|
|
||||||
|
// Update the state of the component with the retrieved data.
|
||||||
|
setData(result.data);
|
||||||
|
setTemplates(
|
||||||
|
Object.keys(result.data).reduce((acc, curr) => {
|
||||||
|
Object.keys(result.data[curr]).forEach((c: keyof APIKindType)=>{
|
||||||
|
acc[c] = result.data[curr][c]
|
||||||
|
})
|
||||||
|
return acc;
|
||||||
|
},{})
|
||||||
|
);
|
||||||
|
// Set the types by reducing over the keys of the result data and updating the accumulator.
|
||||||
|
setTypes(
|
||||||
|
Object.keys(result.data).reduce((acc, curr) => {
|
||||||
|
Object.keys(result.data[curr]).forEach((c: keyof APIKindType) => {
|
||||||
|
acc[c] = curr;
|
||||||
|
// Add the base classes to the accumulator as well.
|
||||||
|
result.data[curr][c].base_classes?.forEach((b) => {
|
||||||
|
acc[b] = curr;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Call the getTypes function.
|
||||||
|
getTypes();
|
||||||
|
}, [setTypes]);
|
||||||
|
|
||||||
function deleteNode(idx:string) {
|
function deleteNode(idx:string) {
|
||||||
reactFlowInstance.setNodes(
|
reactFlowInstance.setNodes(
|
||||||
reactFlowInstance.getNodes().filter((n:Node) => n.id !== idx)
|
reactFlowInstance.getNodes().filter((n:Node) => n.id !== idx)
|
||||||
|
|
|
||||||
|
|
@ -2,51 +2,14 @@ import { Bars2Icon } from "@heroicons/react/24/outline";
|
||||||
import DisclosureComponent from "../DisclosureComponent";
|
import DisclosureComponent from "../DisclosureComponent";
|
||||||
import { nodeColors, nodeIcons, nodeNames } from "../../../../utils";
|
import { nodeColors, nodeIcons, nodeNames } from "../../../../utils";
|
||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
import { getAll } from "../../../../controllers/API";
|
|
||||||
import { typesContext } from "../../../../contexts/typesContext";
|
import { typesContext } from "../../../../contexts/typesContext";
|
||||||
import {
|
import {
|
||||||
APIClassType,
|
APIClassType,
|
||||||
APIKindType,
|
|
||||||
APIObjectType,
|
APIObjectType,
|
||||||
} from "../../../../types/api";
|
} from "../../../../types/api";
|
||||||
|
|
||||||
export default function ExtraSidebar() {
|
export default function ExtraSidebar() {
|
||||||
const [data, setData] = useState({});
|
const {data} = useContext(typesContext)
|
||||||
const { setTypes } = useContext(typesContext);
|
|
||||||
const { setTemplates } = useContext(typesContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
async function getTypes(): Promise<void> {
|
|
||||||
// Make an asynchronous API call to retrieve all data.
|
|
||||||
let result = await getAll();
|
|
||||||
|
|
||||||
// Update the state of the component with the retrieved data.
|
|
||||||
setData(result.data);
|
|
||||||
setTemplates(
|
|
||||||
Object.keys(result.data).reduce((acc, curr) => {
|
|
||||||
Object.keys(result.data[curr]).forEach((c: keyof APIKindType)=>{
|
|
||||||
acc[c] = result.data[curr][c]
|
|
||||||
})
|
|
||||||
return acc;
|
|
||||||
},{})
|
|
||||||
);
|
|
||||||
// Set the types by reducing over the keys of the result data and updating the accumulator.
|
|
||||||
setTypes(
|
|
||||||
Object.keys(result.data).reduce((acc, curr) => {
|
|
||||||
Object.keys(result.data[curr]).forEach((c: keyof APIKindType) => {
|
|
||||||
acc[c] = curr;
|
|
||||||
// Add the base classes to the accumulator as well.
|
|
||||||
result.data[curr][c].base_classes?.forEach((b) => {
|
|
||||||
acc[b] = curr;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
}, {})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Call the getTypes function.
|
|
||||||
getTypes();
|
|
||||||
}, [setTypes]);
|
|
||||||
|
|
||||||
function onDragStart(
|
function onDragStart(
|
||||||
event: React.DragEvent<any>,
|
event: React.DragEvent<any>,
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,23 @@ import AlertDropdown from "../../../../alerts/alertDropDown";
|
||||||
import { alertContext } from "../../../../contexts/alertContext";
|
import { alertContext } from "../../../../contexts/alertContext";
|
||||||
import ImportModal from "../../../../modals/importModal";
|
import ImportModal from "../../../../modals/importModal";
|
||||||
import ExportModal from "../../../../modals/exportModal";
|
import ExportModal from "../../../../modals/exportModal";
|
||||||
|
import { typesContext } from "../../../../contexts/typesContext";
|
||||||
|
|
||||||
export default function TabsManagerComponent() {
|
export default function TabsManagerComponent() {
|
||||||
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } =
|
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } =
|
||||||
useContext(TabsContext);
|
useContext(TabsContext);
|
||||||
const { openPopUp } = useContext(PopUpContext);
|
const { openPopUp } = useContext(PopUpContext);
|
||||||
|
const {templates} = useContext(typesContext)
|
||||||
const AlertWidth = 256;
|
const AlertWidth = 256;
|
||||||
const { dark, setDark } = useContext(darkContext);
|
const { dark, setDark } = useContext(darkContext);
|
||||||
const { notificationCenter, setNotificationCenter } =
|
const { notificationCenter, setNotificationCenter } =
|
||||||
useContext(alertContext);
|
useContext(alertContext);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//create the first flow
|
//create the first flow
|
||||||
if (flows.length === 0) {
|
if (flows.length === 0&& Object.keys(templates).length>0) {
|
||||||
addFlow();
|
addFlow();
|
||||||
}
|
}
|
||||||
}, [addFlow, flows.length]);
|
}, [addFlow, flows.length,templates]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full w-full flex flex-col">
|
<div className="h-full w-full flex flex-col">
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ var _ = require("lodash");
|
||||||
export default function FlowPage({ flow }:{flow:FlowType}) {
|
export default function FlowPage({ flow }:{flow:FlowType}) {
|
||||||
let { updateFlow, incrementNodeId} =
|
let { updateFlow, incrementNodeId} =
|
||||||
useContext(TabsContext);
|
useContext(TabsContext);
|
||||||
const { types, reactFlowInstance, setReactFlowInstance } =
|
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||||
useContext(typesContext);
|
useContext(typesContext);
|
||||||
const reactFlowWrapper = useRef(null);
|
const reactFlowWrapper = useRef(null);
|
||||||
|
|
||||||
|
|
@ -180,7 +180,7 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||||
{Object.keys(types).length > 0 ? (
|
{Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import {
|
||||||
} from "@heroicons/react/24/outline";
|
} from "@heroicons/react/24/outline";
|
||||||
import { Connection, Edge, Node, ReactFlowInstance } from "reactflow";
|
import { Connection, Edge, Node, ReactFlowInstance } from "reactflow";
|
||||||
import { FlowType } from "./types/flow";
|
import { FlowType } from "./types/flow";
|
||||||
import { APIClassType } from "./types/api";
|
|
||||||
var _ = require('lodash')
|
var _ = require('lodash')
|
||||||
|
|
||||||
export function classNames(...classes:Array<string>) {
|
export function classNames(...classes:Array<string>) {
|
||||||
|
|
@ -390,6 +389,21 @@ export function removeApiKeys(flow:FlowType):FlowType{
|
||||||
return cleanFLow
|
return cleanFLow
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateTemplate(newTemplate:APIClassType,oldTemplate:APIClassType){
|
export function updateObject<T extends Record<string, any>>(reference: T, objectToUpdate: T): T {
|
||||||
|
let clonedObject = _.cloneDeep(objectToUpdate)
|
||||||
|
// Loop through each key in the object to update
|
||||||
|
for (const key in clonedObject) {
|
||||||
|
// If the key is not in the reference object, delete it
|
||||||
|
if (!(key in reference)) {
|
||||||
|
delete clonedObject[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Loop through each key in the reference object
|
||||||
|
for (const key in reference) {
|
||||||
|
// If the key is not in the object to update, add it
|
||||||
|
if (!(key in clonedObject)) {
|
||||||
|
clonedObject[key] = reference[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return clonedObject;
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue