Added flow id and routes logic

This commit is contained in:
Lucas Oliveira 2023-06-12 18:19:05 -03:00
commit ceb9b1d155
9 changed files with 262 additions and 183 deletions

BIN
langflow.db Normal file

Binary file not shown.

View file

@ -1,7 +1,7 @@
import "reactflow/dist/style.css"; import "reactflow/dist/style.css";
import { useState, useEffect, useContext } from "react"; import { useState, useEffect, useContext } from "react";
import "./App.css"; import "./App.css";
import { useLocation } from "react-router-dom"; import { useLocation, useParams } from "react-router-dom";
import _ from "lodash"; import _ from "lodash";
import ErrorAlert from "./alerts/error"; import ErrorAlert from "./alerts/error";
@ -15,6 +15,8 @@ import CrashErrorComponent from "./components/CrashErrorComponent";
import { TabsContext } from "./contexts/tabsContext"; import { TabsContext } from "./contexts/tabsContext";
import MainPage from "./pages/MainPage"; import MainPage from "./pages/MainPage";
import { getVersion } from "./controllers/API"; import { getVersion } from "./controllers/API";
import Router from "./routes";
import Header from "./components/headerComponent";
export default function App() { export default function App() {
let { setCurrent, setShowSideBar, setIsStackedOpen } = let { setCurrent, setShowSideBar, setIsStackedOpen } =
@ -25,7 +27,7 @@ export default function App() {
setShowSideBar(true); setShowSideBar(true);
setIsStackedOpen(true); setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
const { hardReset } = useContext(TabsContext); const { hardReset, setTabIndex } = useContext(TabsContext);
const { const {
errorData, errorData,
errorOpen, errorOpen,
@ -38,6 +40,17 @@ export default function App() {
setSuccessOpen, setSuccessOpen,
} = useContext(alertContext); } = useContext(alertContext);
const { flows, addFlow } = useContext(TabsContext);
useEffect(() => {
//create the first flow
if (flows.length === 0) {
addFlow();
console.log("que");
}
}, [addFlow, flows.length]);
// Initialize state variable for the list of alerts // Initialize state variable for the list of alerts
const [alertsList, setAlertsList] = useState< const [alertsList, setAlertsList] = useState<
Array<{ Array<{
@ -122,7 +135,12 @@ export default function App() {
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >
<MainPage /> {flows.length !== 0 && (
<>
<Header />
<Router />
</>
)}
</ErrorBoundary> </ErrorBoundary>
<div></div> <div></div>
<div <div

View file

@ -0,0 +1,114 @@
import { SunIcon, MoonIcon, BellIcon } from "lucide-react";
import { useContext, useState, useEffect } from "react";
import { FaGithub } from "react-icons/fa";
import { Button } from "../ui/button";
import { TabsContext } from "../../contexts/tabsContext";
import AlertDropdown from "../../alerts/alertDropDown";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { typesContext } from "../../contexts/typesContext";
import MenuBar from "../../pages/MainPage/components/menuBar";
export default function Header(){
const {
flows,
addFlow,
removeFlow,
tabIndex,
setTabIndex,
uploadFlow,
downloadFlow,
} = useContext(TabsContext);
const { openPopUp } = useContext(PopUpContext);
const { templates } = useContext(typesContext);
const AlertWidth = 384;
const { dark, setDark } = useContext(darkContext);
const [activeTab, setActiveTab] = useState("myflow");
const [rename, setRename] = useState(false);
const { notificationCenter, setNotificationCenter, setErrorData } =
useContext(alertContext);
useEffect(() => {
//create the first flow
if (flows.length === 0 && Object.keys(templates).length > 0) {
addFlow();
}
}, [addFlow, flows.length, templates]);
return (
<div className="w-full h-16 flex justify-between items-center border-b">
<MenuBar
activeTab={activeTab}
setRename={setRename}
rename={rename}
flows={flows}
tabIndex={tabIndex}
/>
<div className="flex">
<Button>Explore</Button>
<Button>My Flows</Button>
</div>
<div className="flex justify-end px-2 w-96">
<div className="ml-auto mr-2 flex gap-5">
<Button
asChild
variant="outline"
className="text-gray-600 dark:text-gray-300 "
>
<a
href="https://github.com/logspace-ai/langflow"
target="_blank"
rel="noreferrer"
className="flex"
>
<FaGithub className="h-5 w-5 mr-2" />
Join The Community
</a>
</Button>
<button
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200"
onClick={() => {
setDark(!dark);
}}
>
{dark ? (
<SunIcon className="h-5 w-5" />
) : (
<MoonIcon className="h-5 w-5" />
)}
</button>
<button
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200 relative"
onClick={(event: React.MouseEvent<HTMLElement>) => {
setNotificationCenter(false);
const { top, left } = (
event.target as Element
).getBoundingClientRect();
openPopUp(
<>
<div
className="z-10 absolute"
style={{ top: top + 34, left: left - AlertWidth }}
>
<AlertDropdown />
</div>
<div className="h-screen w-screen fixed top-0 left-0"></div>
</>
);
}}
>
{notificationCenter && (
<div className="absolute w-1.5 h-1.5 rounded-full bg-destructive right-[3px]"></div>
)}
<BellIcon className="h-5 w-5" aria-hidden="true" />
</button>
<button>
<img
src="https://github.com/shadcn.png"
className="rounded-full w-8"
/>
</button>
</div>
</div>
</div>
)
}

View file

@ -5,22 +5,25 @@ import { LocationProvider } from "./locationContext";
import PopUpProvider from "./popUpContext"; import PopUpProvider from "./popUpContext";
import { TabsProvider } from "./tabsContext"; import { TabsProvider } from "./tabsContext";
import { TypesProvider } from "./typesContext"; import { TypesProvider } from "./typesContext";
import { ReactFlowProvider } from "reactflow";
export default function ContextWrapper({ children }: { children: ReactNode }) { export default function ContextWrapper({ children }: { children: ReactNode }) {
//element to wrap all context //element to wrap all context
return ( return (
<> <>
<DarkProvider> <ReactFlowProvider>
<TypesProvider> <DarkProvider>
<LocationProvider> <TypesProvider>
<AlertProvider> <LocationProvider>
<TabsProvider> <AlertProvider>
<PopUpProvider>{children}</PopUpProvider> <TabsProvider>
</TabsProvider> <PopUpProvider>{children}</PopUpProvider>
</AlertProvider> </TabsProvider>
</LocationProvider> </AlertProvider>
</TypesProvider> </LocationProvider>
</DarkProvider> </TypesProvider>
</DarkProvider>
</ReactFlowProvider>
</> </>
); );
} }

View file

@ -49,7 +49,7 @@ export default function ExtraSidebar() {
} }
return ( return (
<> <div className="flex flex-col">
<div className="relative mt-2 flex items-center mb-2 mx-2"> <div className="relative mt-2 flex items-center mb-2 mx-2">
<input <input
type="text" type="text"
@ -128,6 +128,6 @@ export default function ExtraSidebar() {
) )
)} )}
</div> </div>
</> </div>
); );
} }

View file

@ -18,6 +18,7 @@ import ReactFlow, {
useOnViewportChange, useOnViewportChange,
OnSelectionChangeParams, OnSelectionChangeParams,
OnNodesChange, OnNodesChange,
ReactFlowProvider,
} from "reactflow"; } from "reactflow";
import _ from "lodash"; import _ from "lodash";
import { locationContext } from "../../contexts/locationContext"; import { locationContext } from "../../contexts/locationContext";
@ -32,12 +33,13 @@ import { FlowType, NodeType } from "../../types/flow";
import { APIClassType } from "../../types/api"; import { APIClassType } from "../../types/api";
import { isValidConnection } from "../../utils"; import { isValidConnection } from "../../utils";
import useUndoRedo from "./hooks/useUndoRedo"; import useUndoRedo from "./hooks/useUndoRedo";
import { useParams } from "react-router-dom";
const nodeTypes = { const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
}; };
export default function FlowPage({ flow }: { flow: FlowType }) { export default function FlowPage() {
let { let {
updateFlow, updateFlow,
disableCopyPaste, disableCopyPaste,
@ -46,6 +48,10 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
paste, paste,
lastCopiedSelection, lastCopiedSelection,
setLastCopiedSelection, setLastCopiedSelection,
flows,
tabIndex,
setTabIndex
} = useContext(TabsContext); } = useContext(TabsContext);
const { types, reactFlowInstance, setReactFlowInstance, templates } = const { types, reactFlowInstance, setReactFlowInstance, templates } =
useContext(typesContext); useContext(typesContext);
@ -53,6 +59,18 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
const { takeSnapshot } = useUndoRedo(); const { takeSnapshot } = useUndoRedo();
const {id} = useParams();
useEffect(() => {
if(flows.length !== 0){
console.log(id, "o id é esse");
setTabIndex(flows.findIndex((flow) => flow.id === id) == -1 ? 0 : flows.findIndex((flow) => flow.id === id));
console.log("setou", flows.findIndex((flow) => flow.id === id))
console.log(flows[tabIndex])
}
}, [flows, id])
const [position, setPosition] = useState({ x: 0, y: 0 }); const [position, setPosition] = useState({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
useState<OnSelectionChangeParams>(null); useState<OnSelectionChangeParams>(null);
@ -111,28 +129,28 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
const { setExtraComponent, setExtraNavigation } = useContext(locationContext); const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const [nodes, setNodes, onNodesChange] = useNodesState( const [nodes, setNodes, onNodesChange] = useNodesState(
flow.data?.nodes ?? [] flows[tabIndex].data?.nodes ?? []
); );
const [edges, setEdges, onEdgesChange] = useEdgesState( const [edges, setEdges, onEdgesChange] = useEdgesState(
flow.data?.edges ?? [] flows[tabIndex].data?.edges ?? []
); );
const { setViewport } = useReactFlow(); const { setViewport } = useReactFlow();
const edgeUpdateSuccessful = useRef(true); const edgeUpdateSuccessful = useRef(true);
useEffect(() => { useEffect(() => {
if (reactFlowInstance && flow) { if (reactFlowInstance && flows[tabIndex]) {
flow.data = reactFlowInstance.toObject(); flows[tabIndex].data = reactFlowInstance.toObject();
updateFlow(flow); updateFlow(flows[tabIndex]);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodes, edges]); }, [nodes, edges]);
//update flow when tabs change //update flow when tabs change
useEffect(() => { useEffect(() => {
setNodes(flow?.data?.nodes ?? []); setNodes(flows[tabIndex]?.data?.nodes ?? []);
setEdges(flow?.data?.edges ?? []); setEdges(flows[tabIndex]?.data?.edges ?? []);
if (reactFlowInstance) { if (reactFlowInstance) {
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 }); setViewport(flows[tabIndex]?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 });
} }
}, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); }, [flows[tabIndex], reactFlowInstance, setEdges, setNodes, setViewport]);
//set extra sidebar //set extra sidebar
useEffect(() => { useEffect(() => {
setExtraComponent(<ExtraSidebar />); setExtraComponent(<ExtraSidebar />);
@ -312,55 +330,66 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
return ( return (
<div className="w-full h-full" ref={reactFlowWrapper}> <div className="h-full w-full flex basis-auto flex-1 overflow-hidden">
{Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? ( <ExtraSidebar />
<> <main className="h-full w-full flex-1 border-t border-gray-200 dark:border-gray-700 flex">
<ReactFlow {/* Primary column */}
nodes={nodes}
onMove={() => { <div className="w-full h-full" ref={reactFlowWrapper}>
updateFlow({ ...flow, data: reactFlowInstance.toObject() }); {Object.keys(templates).length > 0 &&
}} Object.keys(types).length > 0 ? (
edges={edges} <>
onPaneClick={() => { <ReactFlow
setDisableCopyPaste(false); nodes={nodes}
}} onMove={() => {
onPaneMouseLeave={() => { updateFlow({
setDisableCopyPaste(true); ...flows[tabIndex],
}} data: reactFlowInstance.toObject(),
onNodesChange={onNodesChange} });
onEdgesChange={onEdgesChangeMod} }}
onConnect={onConnect} edges={edges}
disableKeyboardA11y={true} onPaneClick={() => {
onLoad={setReactFlowInstance} setDisableCopyPaste(false);
onInit={setReactFlowInstance} }}
nodeTypes={nodeTypes} onPaneMouseLeave={() => {
onEdgeUpdate={onEdgeUpdate} setDisableCopyPaste(true);
onEdgeUpdateStart={onEdgeUpdateStart} }}
onEdgeUpdateEnd={onEdgeUpdateEnd} onNodesChange={onNodesChange}
onNodeDragStart={onNodeDragStart} onEdgesChange={onEdgesChangeMod}
onSelectionDragStart={onSelectionDragStart} onConnect={onConnect}
onSelectionEnd={onSelectionEnd} disableKeyboardA11y={true}
onSelectionStart={onSelectionStart} onLoad={setReactFlowInstance}
onEdgesDelete={onEdgesDelete} onInit={setReactFlowInstance}
connectionLineComponent={ConnectionLineComponent} nodeTypes={nodeTypes}
onDragOver={onDragOver} onEdgeUpdate={onEdgeUpdate}
onDrop={onDrop} onEdgeUpdateStart={onEdgeUpdateStart}
onNodesDelete={onDelete} onEdgeUpdateEnd={onEdgeUpdateEnd}
onSelectionChange={onSelectionChange} onNodeDragStart={onNodeDragStart}
nodesDraggable={!disableCopyPaste} onSelectionDragStart={onSelectionDragStart}
panOnDrag={!disableCopyPaste} onSelectionEnd={onSelectionEnd}
zoomOnDoubleClick={!disableCopyPaste} onSelectionStart={onSelectionStart}
selectNodesOnDrag={false} onEdgesDelete={onEdgesDelete}
className="theme-attribution" connectionLineComponent={ConnectionLineComponent}
> onDragOver={onDragOver}
<Background className="dark:bg-gray-900" /> onDrop={onDrop}
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls> onNodesDelete={onDelete}
</ReactFlow> onSelectionChange={onSelectionChange}
<Chat flow={flow} reactFlowInstance={reactFlowInstance} /> nodesDraggable={!disableCopyPaste}
</> panOnDrag={!disableCopyPaste}
) : ( zoomOnDoubleClick={!disableCopyPaste}
<></> selectNodesOnDrag={false}
)} className="theme-attribution"
>
<Background className="dark:bg-gray-900" />
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
</ReactFlow>
<Chat flow={flows[tabIndex]} reactFlowInstance={reactFlowInstance} />
</>
) : (
<></>
)}
</div>
</main>
</div> </div>
); );
} }

View file

@ -20,18 +20,17 @@ import _ from "lodash";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import { alertContext } from "../../../../contexts/alertContext"; import { alertContext } from "../../../../contexts/alertContext";
import { updateFlowInDatabase } from "../../../../controllers/API"; import { updateFlowInDatabase } from "../../../../controllers/API";
import { Link } from "react-router-dom";
export const CardComponent = ({ export const CardComponent = ({
flow, flow,
idx, idx,
removeFlow, removeFlow,
setTabIndex, setTabIndex,
setActiveTab,
}: { }: {
flow: FlowType; flow: FlowType;
idx: number; idx: number;
removeFlow: (id: string) => void; removeFlow: (id: string) => void;
setTabIndex: (idx: number) => void; setTabIndex: (idx: number) => void;
setActiveTab: (tab: string) => void;
}) => { }) => {
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const { updateFlow } = useContext(TabsContext); const { updateFlow } = useContext(TabsContext);
@ -65,13 +64,15 @@ export const CardComponent = ({
/> />
</div> </div>
<div className="flex gap-5"> <div className="flex gap-5">
<Link to={`/flow/${flow.id}`}>
<Edit <Edit
className="w-4" className="w-4"
onClick={() => { onClick={() => {
setTabIndex(idx); setTabIndex(idx);
setActiveTab("myflow");
}} }}
/> />
</Link>
<Trash <Trash
className="w-4" className="w-4"
onClick={() => { onClick={() => {

View file

@ -30,14 +30,11 @@ export default function HomePage() {
removeFlow, removeFlow,
tabIndex, tabIndex,
setTabIndex, setTabIndex,
uploadFlow,
downloadFlow,
} = useContext(TabsContext); } = useContext(TabsContext);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
const { templates } = useContext(typesContext); const { templates } = useContext(typesContext);
const AlertWidth = 384; const AlertWidth = 384;
const { dark, setDark } = useContext(darkContext); const { dark, setDark } = useContext(darkContext);
const [activeTab, setActiveTab] = useState("myflow");
const [rename, setRename] = useState(false); const [rename, setRename] = useState(false);
const { notificationCenter, setNotificationCenter, setErrorData } = const { notificationCenter, setNotificationCenter, setErrorData } =
useContext(alertContext); useContext(alertContext);
@ -50,106 +47,9 @@ export default function HomePage() {
return ( return (
flows.length !== 0 && ( flows.length !== 0 && (
<Tabs <div
defaultValue="myflow" className="w-full h-full flex flex-col bg-muted"
value={activeTab}
onValueChange={setActiveTab}
className="w-full h-full flex flex-col"
> >
<TabsList className="w-full h-16 flex justify-between items-center border-b">
<MenuBar
activeTab={activeTab}
setRename={setRename}
rename={rename}
flows={flows}
tabIndex={tabIndex}
/>
<div className="flex">
<TabsTrigger value="community">Explore</TabsTrigger>
<TabsTrigger value="myflows">My Flows</TabsTrigger>
</div>
<div className="flex justify-end px-2 w-96">
<div className="ml-auto mr-2 flex gap-5">
<Button
asChild
variant="outline"
className="text-gray-600 dark:text-gray-300 "
>
<a
href="https://github.com/logspace-ai/langflow"
target="_blank"
rel="noreferrer"
className="flex"
>
<FaGithub className="h-5 w-5 mr-2" />
Join The Community
</a>
</Button>
<button
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200"
onClick={() => {
setDark(!dark);
}}
>
{dark ? (
<SunIcon className="h-5 w-5" />
) : (
<MoonIcon className="h-5 w-5" />
)}
</button>
<button
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200 relative"
onClick={(event: React.MouseEvent<HTMLElement>) => {
setNotificationCenter(false);
const { top, left } = (
event.target as Element
).getBoundingClientRect();
openPopUp(
<>
<div
className="z-10 absolute"
style={{ top: top + 34, left: left - AlertWidth }}
>
<AlertDropdown />
</div>
<div className="h-screen w-screen fixed top-0 left-0"></div>
</>
);
}}
>
{notificationCenter && (
<div className="absolute w-1.5 h-1.5 rounded-full bg-destructive right-[3px]"></div>
)}
<BellIcon className="h-5 w-5" aria-hidden="true" />
</button>
<button>
<img
src="https://github.com/shadcn.png"
className="rounded-full w-8"
/>
</button>
</div>
</div>
</TabsList>
<TabsContent value="myflow" className="w-full h-full">
<div className="h-full w-full flex basis-auto flex-1 overflow-hidden">
<ExtraSidebar />
<main className="h-full w-full flex-1 border-t border-gray-200 dark:border-gray-700 flex">
{/* Primary column */}
<div className="w-full h-full">
<ReactFlowProvider>
{flows[tabIndex] ? (
<FlowPage flow={flows[tabIndex]}></FlowPage>
) : (
<></>
)}
</ReactFlowProvider>
</div>
</main>
</div>
</TabsContent>
<TabsContent value="myflows" className="w-full h-full bg-muted">
<div className="w-full p-4 grid gap-4 md:grid-cols-2 lg:grid-cols-4"> <div className="w-full p-4 grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{flows.map((flow, idx) => ( {flows.map((flow, idx) => (
<CardComponent <CardComponent
@ -157,12 +57,10 @@ export default function HomePage() {
idx={idx} idx={idx}
removeFlow={removeFlow} removeFlow={removeFlow}
setTabIndex={setTabIndex} setTabIndex={setTabIndex}
setActiveTab={setActiveTab}
/> />
))} ))}
</div> </div>
</TabsContent> </div>
</Tabs>
) )
); );
} }

View file

@ -0,0 +1,16 @@
import { Route, Routes } from "react-router-dom"
import HomePage from "./pages/MainPage";
import FlowPage from "./pages/FlowPage";
const Router = () => {
return(
<Routes>
<Route path="/" element={<HomePage/>}/>
<Route path="/flow/:id/">
<Route path="" element={<FlowPage/>}/>
</Route>
</Routes>
)
}
export default Router;