Added community page, fixed styling of buttons, added promise at AddFlow

This commit is contained in:
Lucas Oliveira 2023-06-13 19:09:24 -03:00
commit 1a92ceca7e
13 changed files with 301 additions and 150 deletions

View file

@ -82,41 +82,7 @@ export const MenuBar = ({ setRename, rename, flows, tabId }) => {
<ChevronDown className="w-4 h-4" />
</DropdownMenuTrigger>
<DropdownMenuContent className="w-44">
<DropdownMenuLabel>File</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => {
openPopUp(<ImportModal />);
}}
>
<Upload className="w-4 h-4 mr-2" />
Import
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
openPopUp(<ExportModal />);
}}
>
<Download className="w-4 h-4 mr-2" />
Export
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
openPopUp(<ApiModal flow={current_flow} />);
}}
>
<Code className="w-4 h-4 mr-2" />
Code
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuLabel>Edit</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => {
handleSaveFlow(current_flow);
}}
>
<Save className="w-4 h-4 mr-2" />
Save
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setRename(true);

View file

@ -1,4 +1,4 @@
import { SunIcon, MoonIcon, BellIcon } from "lucide-react";
import { SunIcon, MoonIcon, BellIcon, Home, Users2 } from "lucide-react";
import { useContext, useState, useEffect } from "react";
import { FaGithub } from "react-icons/fa";
import { Button } from "../ui/button";
@ -9,7 +9,7 @@ import { darkContext } from "../../contexts/darkContext";
import { PopUpContext } from "../../contexts/popUpContext";
import { typesContext } from "../../contexts/typesContext";
import MenuBar from "./components/menuBar";
import { Link, useParams } from "react-router-dom";
import { Link, useLocation, useParams } from "react-router-dom";
export default function Header() {
const { flows, addFlow, tabId } = useContext(TabsContext);
@ -21,6 +21,7 @@ export default function Header() {
const [rename, setRename] = useState(false);
const { notificationCenter, setNotificationCenter, setErrorData } =
useContext(alertContext);
const location = useLocation();
return (
<div className="w-full h-12 flex justify-between items-center border-b bg-muted">
<div className="flex gap-2 justify-start items-center w-96">
@ -36,6 +37,20 @@ export default function Header() {
/>
)}
</div>
<div className="flex gap-2 items-center">
<Link to="/">
<Button variant={location.pathname === "/" ? "primary" : "secondary"} size="sm">
<Home className="w-4 mr-2" />
My Projects
</Button>
</Link>
<Link to="/community">
<Button variant={location.pathname === "/community" ? "primary" : "secondary"} size="sm">
<Users2 className="w-4 mr-2" />
Community Examples
</Button>
</Link>
</div>
<div className="flex justify-end px-2 w-96">
<div className="ml-auto mr-2 flex gap-5">
<Button

View file

@ -13,6 +13,8 @@ const buttonVariants = cva(
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input hover:bg-accent hover:text-accent-foreground",
primary:
"bg-background text-secondary-foreground hover:bg-background/80",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",

View file

@ -8,7 +8,7 @@ const Card = React.forwardRef<
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-all",
"rounded-lg flex flex-col justify-between border bg-card text-card-foreground shadow-sm hover:shadow-lg transition-all",
className
)}
{...props}
@ -35,7 +35,7 @@ const CardTitle = React.forwardRef<
<h3
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
"text-base font-semibold leading-tight tracking-tight",
className
)}
{...props}

View file

@ -33,7 +33,7 @@ const TabsContextInitialValue: TabsContextType = {
setTabId: (index: string) => {},
flows: [],
removeFlow: (id: string) => {},
addFlow: (flowData?: any) => {},
addFlow: async (flowData?: any) => "",
updateFlow: (newFlow: FlowType) => {},
incrementNodeId: () => uuidv4(),
downloadFlow: (flow: FlowType) => {},
@ -415,21 +415,27 @@ export function TabsProvider({ children }: { children: ReactNode }) {
reactFlowInstance.setEdges(edges);
}
const addFlow = (flow?: FlowType) => {
const addFlow = async (flow?: FlowType): Promise<String> => {
let flowData = extractDataFromFlow(flow);
// Create a new flow with a default name if no flow is provided.
const newFlow = createNewFlow(flowData, flow);
saveFlowToDatabase(newFlow)
.then((id) => {
// Change the id to the new id.
newFlow.id = id.id;
})
.then(() => {
// Add the new flow to the list of flows.
addFlowToLocalState(newFlow);
});
try {
const id = await saveFlowToDatabase(newFlow);
// Change the id to the new id.
newFlow.id = id.id;
// Add the new flow to the list of flows.
addFlowToLocalState(newFlow);
// Return the id
return id.id;
} catch (error) {
// Handle the error if needed
console.error('Error while adding flow:', error);
throw error; // Re-throw the error so the caller can handle it if needed
}
};
const extractDataFromFlow = (flow) => {

View file

@ -0,0 +1,81 @@
import { Trash2, ExternalLink, GitFork } from "lucide-react";
import { useContext } from "react";
import { Link, useNavigate } from "react-router-dom";
import { alertContext } from "../../../../contexts/alertContext";
import { TabsContext } from "../../../../contexts/tabsContext";
import { updateFlowInDatabase } from "../../../../controllers/API";
import { FlowType } from "../../../../types/flow";
import { gradients } from "../../../../utils";
import { CardTitle, CardDescription, CardFooter, Card, CardHeader } from "../../../../components/ui/card";
import { Button } from "../../../../components/ui/button";
export const CardComponent = ({ flow, id }: { flow: FlowType; id: string }) => {
const { setErrorData } = useContext(alertContext);
const { updateFlow, addFlow } = useContext(TabsContext);
const navigate = useNavigate();
function handleSaveFlow(flow) {
try {
updateFlowInDatabase(flow);
updateFlow(flow);
// updateFlowStyleInDataBase(flow);
} catch (err) {
setErrorData(err);
}
}
return (
<Card className="group">
<CardHeader>
<CardTitle className="flex justify-between items-start">
<div className="flex gap-4 items-center">
<span
className={
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
}
>
</span>
<span className="flex-1 truncate-doubleline">
{flow.name}
</span>
</div>
</CardTitle>
<CardDescription className="pt-2 pb-2">
<div className="truncate-doubleline">
This flow creates an agent that accesses a department store database
and APIs to monitor customer activity and overall storage.
{/* {flow.description} */}
</div>
</CardDescription>
</CardHeader>
<CardFooter>
<div className="flex gap-2 w-full justify-between items-end">
<div className="flex flex-wrap gap-2">
{/* <Badge variant="secondary">Agent</Badge>
<Badge variant="secondary">
<div className="w-3">
<OpenAiIcon />
</div>
<span className="text-base">&nbsp;</span>OpenAI+
</Badge> */}
</div>
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
onClick={() => {
addFlow(flow).then((id) => {
navigate("/flow/"+id);
});
}}
>
<GitFork className="w-4 mr-2" />
Fork Example
</Button>
</div>
</CardFooter>
</Card>
);
};

View file

@ -0,0 +1,92 @@
import {
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "../../components/ui/tabs";
import ExtraSidebar from "../../components/ExtraSidebarComponent";
import { ReactFlowProvider } from "reactflow";
import FlowPage from "../FlowPage";
import { useContext, useEffect, useState } from "react";
import {
SunIcon,
MoonIcon,
BellIcon,
GithubIcon,
Download,
Upload,
Plus,
Home,
Users2,
} from "lucide-react";
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 { Button } from "../../components/ui/button";
import { FaGithub } from "react-icons/fa";
import _ from "lodash";
import {
getExamples,
updateFlowInDatabase,
uploadFlowsToDatabase,
} from "../../controllers/API";
import { MenuBar } from "../../components/headerComponent/components/menuBar";
import { FlowType } from "../../types/flow";
import { CardComponent } from "./components/cardComponent";
export default function CommunityPage() {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow } =
useContext(TabsContext);
useEffect(() => {
setTabId("");
}, []);
const { setErrorData } = useContext(alertContext);
const [loadingExamples, setLoadingExamples] = useState(false);
const [examples, setExamples] = useState<FlowType[]>([]);
function handleExamples() {
setLoadingExamples(true);
getExamples()
.then((result) => {
setLoadingExamples(false);
setExamples(result);
})
.catch((error) =>
setErrorData({
title: "there was an error loading examples, please try again",
list: [error.message],
})
);
}
useEffect(() => {
handleExamples();
}, []);
return (
<div className="w-full h-full flex overflow-auto flex-col bg-muted px-16">
<div className="w-full flex justify-between py-12 pb-8 px-6">
<span className="text-2xl flex items-center justify-center gap-2 font-semibold">
<Users2 className="w-6" />
Community Examples
</span>
<div className="flex gap-2">
<a href="https://github.com/logspace-ai/langflow_examples">
<Button variant="primary">
<GithubIcon className="w-4 mr-2" />
Add Your Example
</Button>
</a>
</div>
</div>
<div className="w-full p-4 grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{!loadingExamples &&
examples.map((flow, idx) => (
<CardComponent key={idx} flow={flow} id={flow.id} />
))}
</div>
</div>
);
}

View file

@ -17,6 +17,7 @@ import ImportModal from "../../../../modals/importModal";
import ExportModal from "../../../../modals/exportModal";
import ApiModal from "../../../../modals/ApiModal";
import { TabsContext } from "../../../../contexts/tabsContext";
import { Separator } from "../../../../components/ui/separator";
export default function ExtraSidebar() {
const { data } = useContext(typesContext);
@ -62,7 +63,7 @@ export default function ExtraSidebar() {
return (
<div className="flex flex-col overflow-auto scrollbar-hide h-full border-r">
<div className="mt-2 w-full flex gap-2 justify-between px-2 items-center">
<div className="mt-2 mb-2 w-full flex gap-2 justify-between px-2 items-center">
<ShadTooltip delayDuration={1000} content="Import" side="top">
<button
className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center rounded-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
@ -108,6 +109,7 @@ export default function ExtraSidebar() {
</button>
</ShadTooltip>
</div>
<Separator />
<div className="relative mt-2 flex items-center mb-2 mx-2">
<input
type="text"

View file

@ -1,33 +1,15 @@
import React, { useContext, useState } from "react";
import {
ArrowTopRightOnSquareIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import { Edit, Trash } from "lucide-react";
import { OpenAiIcon } from "../../../../icons/OpenAi";
import { Button } from "../../../../components/ui/button";
import { Badge } from "../../../../components/ui/badge";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "../../../../components/ui/card";
import { FlowType } from "../../../../types/flow";
import RenameLabel from "../../../../components/ui/rename-label";
import _ from "lodash";
import { TabsContext } from "../../../../contexts/tabsContext";
import { alertContext } from "../../../../contexts/alertContext";
import { updateFlowInDatabase } from "../../../../controllers/API";
import { Trash2, ExternalLink } from "lucide-react";
import { useContext } from "react";
import { Link } from "react-router-dom";
export const CardComponent = ({
flow,
id,
}: {
flow: FlowType;
id: string;
}) => {
import { alertContext } from "../../../../contexts/alertContext";
import { TabsContext } from "../../../../contexts/tabsContext";
import { updateFlowInDatabase } from "../../../../controllers/API";
import { FlowType } from "../../../../types/flow";
import { gradients } from "../../../../utils";
import { CardTitle, CardDescription, CardFooter, Card, CardHeader } from "../../../../components/ui/card";
import { Button } from "../../../../components/ui/button";
export const CardComponent = ({ flow, id }: { flow: FlowType; id: string }) => {
const { setErrorData } = useContext(alertContext);
const { updateFlow, removeFlow, setTabId } = useContext(TabsContext);
function handleSaveFlow(flow) {
@ -39,76 +21,62 @@ export const CardComponent = ({
setErrorData(err);
}
}
const [rename, setRename] = useState(false);
return (
<Card className="group">
<CardHeader>
<CardTitle className="flex justify-between items-start">
<div className="flex gap-4 items-center">
<RenameLabel
value={flow.name}
setValue={(value) => {
if (value !== "") {
let newFlow = _.cloneDeep(flow);
newFlow.name = value;
handleSaveFlow(newFlow);
}
}}
rename={rename}
setRename={setRename}
/>
</div>
<div className="flex gap-5">
<Link to={`/flow/${flow.id}`}>
<Edit
className="w-4"
onClick={() => {
setTabId(id);
}}
/>
</Link>
<button onClick={() => {removeFlow(flow.id)}}>
<Trash
className="w-4"
/>
</button>
</div>
</CardTitle>
<CardDescription className="pt-2 pb-2 h-10">
{/* <div className="flex gap-2"> */}
<RenameLabel
className="truncate-doubleline w-full h-full"
placeholder="Description"
value={flow.description || "Description"}
setValue={(value) => {
if (value !== "") {
let newFlow = _.cloneDeep(flow);
newFlow.description = value;
handleSaveFlow(newFlow);
<span
className={
"rounded-full w-8 h-8 flex items-center justify-center text-2xl " +
gradients[parseInt(flow.id.slice(0, 6), 16) % gradients.length]
}
>
</span>
<span className="flex-1 truncate-doubleline">
{flow.name}
</span>
</div>
<button
onClick={() => {
removeFlow(flow.id);
}}
rename={rename}
setRename={setRename}
/>
{/* </div> */}
>
<Trash2 className="w-5 text-primary opacity-0 group-hover:opacity-100 transition-all" />
</button>
</CardTitle>
<CardDescription className="pt-2 pb-2">
<div className="truncate-doubleline">
This flow creates an agent that accesses a department store database
and APIs to monitor customer activity and overall storage.
{/* {flow.description} */}
</div>
</CardDescription>
</CardHeader>
<CardFooter>
<div className="flex gap-2 w-full justify-end items-end">
{/* <div className="flex flex-wrap gap-2">
<Badge variant="secondary">{idx === 0 ? "Agent" : "Tool"}</Badge>
{idx === 0 && (
<Badge variant="secondary">
<div className="w-3">
<OpenAiIcon />
</div>
<span className="text-base">&nbsp;</span>OpenAI+
</Badge>
)}
</div> */}
<div className="flex gap-2 w-full justify-between items-end">
<div className="flex flex-wrap gap-2">
{/* <Badge variant="secondary">Agent</Badge>
<Badge variant="secondary">
<div className="w-3">
<OpenAiIcon />
</div>
<span className="text-base">&nbsp;</span>OpenAI+
</Badge> */}
</div>
<Link to={"/flow/" + id}>
<Button
variant="outline"
size="sm"
className="whitespace-nowrap "
>
<ExternalLink className="w-4 mr-2" />
Edit Flow
</Button>
</Link>
</div>
</CardFooter>
</Card>

View file

@ -8,7 +8,7 @@ import ExtraSidebar from "../../components/ExtraSidebarComponent";
import { ReactFlowProvider } from "reactflow";
import FlowPage from "../FlowPage";
import { useContext, useEffect, useState } from "react";
import { SunIcon, MoonIcon, BellIcon, GithubIcon, Download, Upload, Plus } from "lucide-react";
import { SunIcon, MoonIcon, BellIcon, GithubIcon, Download, Upload, Plus, Home } from "lucide-react";
import { TabsContext } from "../../contexts/tabsContext";
import AlertDropdown from "../../alerts/alertDropDown";
import { alertContext } from "../../contexts/alertContext";
@ -21,8 +21,8 @@ import { FaGithub } from "react-icons/fa";
import _ from "lodash";
import { updateFlowInDatabase, uploadFlowsToDatabase } from "../../controllers/API";
import { CardComponent } from "./components/cardComponent";
import { MenuBar } from "../../components/headerComponent/components/menuBar";
import { CardComponent } from "./components/cardComponent";
export default function HomePage() {
const {
flows,
@ -36,26 +36,26 @@ export default function HomePage() {
}, [])
return (
<div
className="w-full h-full flex overflow-auto flex-col bg-muted"
className="w-full h-full flex overflow-auto flex-col bg-muted px-16"
>
<div className="w-full flex justify-between py-12 px-8">
<span className="text-xl font-semibold">
Flows
<div className="w-full flex justify-between py-12 pb-8 px-6">
<span className="text-2xl flex items-center justify-center gap-2 font-semibold">
<Home className="w-6" />My Projects
</span>
<div className="flex gap-2">
<Button variant="outline" onClick={() => {
<Button variant="primary" onClick={() => {
downloadFlows();
}}>
<Download className="w-4 mr-2" />
Download Database
</Button>
<Button variant="outline" onClick={() => {uploadFlows();}}>
<Button variant="primary" onClick={() => {uploadFlows();}}>
<Upload className="w-4 mr-2" />
Upload Database
</Button>
<Button variant="outline" onClick={() => {addFlow();}}>
<Button variant="primary" onClick={() => {addFlow();}}>
<Plus className="w-4 mr-2" />
Add Flow
New Project
</Button>
</div>
</div>

View file

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

View file

@ -5,7 +5,7 @@ export type TabsContextType = {
setTabId: (index: string) => void;
flows: Array<FlowType>;
removeFlow: (id: string) => void;
addFlow: (flowData?: FlowType, newFlow?: boolean) => void;
addFlow: (flowData?: FlowType, newFlow?: boolean) => Promise<String>;
updateFlow: (newFlow: FlowType) => void;
incrementNodeId: () => string;
downloadFlow: (flow: FlowType) => void;

View file

@ -206,6 +206,23 @@ export const nodeIcons: {
unknown: QuestionMarkCircleIcon,
};
export const gradients = [
"bg-gradient-to-br from-gray-800 via-rose-700 to-violet-900",
"bg-gradient-to-br from-green-200 via-green-300 to-blue-500",
"bg-gradient-to-br from-yellow-200 via-yellow-400 to-yellow-700",
"bg-gradient-to-br from-green-200 via-green-400 to-purple-700",
"bg-gradient-to-br from-blue-100 via-blue-300 to-blue-500",
"bg-gradient-to-br from-purple-400 to-yellow-400",
"bg-gradient-to-br from-red-800 via-yellow-600 to-yellow-500",
"bg-gradient-to-br from-blue-300 via-green-200 to-yellow-300",
"bg-gradient-to-br from-blue-700 via-blue-800 to-gray-900",
"bg-gradient-to-br from-green-300 to-purple-400",
"bg-gradient-to-br from-yellow-200 via-pink-200 to-pink-400",
"bg-gradient-to-br from-green-500 to-green-700",
"bg-gradient-to-br from-rose-400 via-fuchsia-500 to-indigo-500",
"bg-gradient-to-br from-sky-400 to-blue-500",
];
export const bgColors = {
white: "bg-white",
red: "bg-red-100",