merge playground

This commit is contained in:
cristhianzl 2024-05-02 10:39:46 -03:00
commit aa3ea07c6a
39 changed files with 1850 additions and 1333 deletions

File diff suppressed because it is too large Load diff

View file

@ -20,18 +20,21 @@ import {
CardHeader,
CardTitle,
} from "../ui/card";
import Loading from "../ui/loading";
export default function CollectionCardComponent({
data,
authorized = true,
disabled = false,
button,
onClick,
onDelete,
playground,
}: {
data: storeComponent;
authorized?: boolean;
disabled?: boolean;
onClick?: () => void;
button?: JSX.Element;
playground?: boolean;
onDelete?: () => void;
@ -40,6 +43,7 @@ export default function CollectionCardComponent({
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const setValidApiKey = useStoreStore((state) => state.updateValidApiKey);
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
const isStore = false;
const [loading, setLoading] = useState(false);
const [loadingLike, setLoadingLike] = useState(false);
@ -51,6 +55,8 @@ export default function CollectionCardComponent({
data?.downloads_count ?? 0
);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const setNodes = useFlowStore((state) => state.setNodes);
const setEdges = useFlowStore((state) => state.setEdges);
@ -58,15 +64,28 @@ export default function CollectionCardComponent({
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId
);
const [loadingPlayground, setLoadingPlayground] = useState(false);
const name = data.is_component ? "Component" : "Flow";
async function getFlowData() {
const res = await getComponent(data.id);
const newFlow = cloneFLowWithParent(res, res.id, data.is_component, true);
return newFlow;
}
useEffect(() => {
if (currentFlowId && playground) {
setNodes(currentFlow?.data?.nodes ?? []);
setEdges(currentFlow?.data?.edges ?? []);
if (openPlayground) {
setNodes(currentFlow?.data?.nodes ?? [], true);
setEdges(currentFlow?.data?.edges ?? [], true);
} else {
setNodes([], true);
setEdges([], true);
cleanFlowPool();
}
}
}, [currentFlowId]);
}, [openPlayground]);
useEffect(() => {
if (data) {
@ -147,250 +166,325 @@ export default function CollectionCardComponent({
}
return (
<Card
className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
disabled ? "pointer-events-none opacity-50" : ""
)}
>
<div>
<CardHeader>
<div>
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
<IconComponent
className={cn(
"flex-shrink-0",
data.is_component
? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon"
)}
name={data.is_component ? "ToyBrick" : "Group"}
/>
<ShadTooltip content={data.name}>
<div className="w-full truncate">{data.name}</div>
</ShadTooltip>
{data?.metadata !== undefined && (
<div className="flex gap-3">
{data.private && (
<ShadTooltip content="Private">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="Lock" className="h-4 w-4" />
</span>
</ShadTooltip>
<>
<Card
className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : ""
)}
onClick={onClick}
>
<div>
<CardHeader>
<div>
<CardTitle className="flex w-full items-center justify-between gap-3 text-xl">
<IconComponent
className={cn(
"flex-shrink-0",
data.is_component
? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon"
)}
{!data.is_component && (
<ShadTooltip content="Components">
name={data.is_component ? "ToyBrick" : "Group"}
/>
<ShadTooltip content={data.name}>
<div className="w-full truncate">{data.name}</div>
</ShadTooltip>
{data?.metadata !== undefined && (
<div className="flex gap-3">
{data.private && (
<ShadTooltip content="Private">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="Lock" className="h-4 w-4" />
</span>
</ShadTooltip>
)}
{!data.is_component && (
<ShadTooltip content="Components">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="ToyBrick" className="h-4 w-4" />
<span data-testid={`total-${data.name}`}>
{data?.metadata?.total ?? 0}
</span>
</span>
</ShadTooltip>
)}
<ShadTooltip content="Likes">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="ToyBrick" className="h-4 w-4" />
<span data-testid={`total-${data.name}`}>
{data?.metadata?.total ?? 0}
<IconComponent
name="Heart"
className={cn("h-4 w-4 ")}
/>
<span data-testid={`likes-${data.name}`}>
{likes_count ?? 0}
</span>
</span>
</ShadTooltip>
)}
<ShadTooltip content="Likes">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="Heart" className={cn("h-4 w-4 ")} />
<span data-testid={`likes-${data.name}`}>
{likes_count ?? 0}
<ShadTooltip content="Downloads">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent
name="DownloadCloud"
className="h-4 w-4"
/>
<span data-testid={`downloads-${data.name}`}>
{downloads_count ?? 0}
</span>
</span>
</span>
</ShadTooltip>
<ShadTooltip content="Downloads">
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<IconComponent name="DownloadCloud" className="h-4 w-4" />
<span data-testid={`downloads-${data.name}`}>
{downloads_count ?? 0}
</span>
</span>
</ShadTooltip>
</div>
)}
</ShadTooltip>
</div>
)}
{onDelete && data?.metadata === undefined && (
<DeleteConfirmationModal
onConfirm={() => {
onDelete();
{onDelete && data?.metadata === undefined && (
<DeleteConfirmationModal
onConfirm={() => {
onDelete();
}}
>
<IconComponent
name="Trash2"
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
/>
</DeleteConfirmationModal>
)}
</CardTitle>
</div>
<div className="flex gap-2">
{data.user_created && data.user_created.username && (
<span className="text-sm text-primary">
by <b>{data.user_created.username}</b>
{data.last_tested_version && (
<>
{" "}
|{" "}
<span className="text-xs">
{" "}
⛓︎ v{data.last_tested_version}
</span>
</>
)}
</span>
)}
<div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags &&
data.tags.length > 0 &&
data.tags.map((tag, index) => (
<Badge
key={index}
variant="outline"
size="xq"
className="text-muted-foreground"
>
{tag.name}
</Badge>
))}
</div>
</div>
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
</CardDescription>
</CardHeader>
</div>
<CardFooter>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2">
{playground && data?.metadata !== undefined ? (
<Button
disabled={loadingPlayground}
key={data.id}
tabIndex={-1}
variant="outline"
size="sm"
className="gap-2 whitespace-nowrap"
data-testid={"playground-flow-button-" + data.id}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setLoadingPlayground(true);
if (getFlowById(data.id)) {
setCurrentFlowId(data.id);
setOpenPlayground(true);
setLoadingPlayground(false);
} else {
getFlowData().then((res) => {
setCurrentFlow(res);
setOpenPlayground(true);
setLoadingPlayground(false);
});
}
}}
>
<IconComponent
name="Trash2"
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
/>
</DeleteConfirmationModal>
{!loadingPlayground ? (
<IconComponent
name="BotMessageSquareIcon"
className="h-4 w-4 select-none"
/>
) : (
<Loading className="h-4 w-4 text-medium-indigo" />
)}
Playground
</Button>
) : (
<div></div>
)}
</CardTitle>
</div>
{data.user_created && data.user_created.username && (
<span className="text-sm text-primary">
by <b>{data.user_created.username}</b>
{data.last_tested_version && (
<>
{" "}
|{" "}
<span className="text-xs">
{" "}
⛓︎ v{data.last_tested_version}
</span>
</>
)}
</span>
)}
<CardDescription className="pb-2 pt-2">
<div className="truncate-doubleline">{data.description}</div>
</CardDescription>
</CardHeader>
</div>
<CardFooter>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2">
<div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags &&
data.tags.length > 0 &&
data.tags.map((tag, index) => (
<Badge
key={index}
variant="outline"
size="xq"
className="text-muted-foreground"
>
{tag.name}
</Badge>
))}
</div>
{data.liked_by_count != undefined && (
<div className="flex gap-0.5">
{onDelete && data?.metadata !== undefined ? (
<ShadTooltip
content={
authorized ? "Delete" : "Please review your API key."
}
>
<DeleteConfirmationModal
onConfirm={() => {
onDelete();
}}
{data.liked_by_count != undefined && (
<div className="flex gap-0.5">
{onDelete && data?.metadata !== undefined ? (
<ShadTooltip
content={
authorized ? "Delete" : "Please review your API key."
}
>
<DeleteConfirmationModal
onConfirm={() => {
onDelete();
}}
>
<Button
variant="ghost"
size="icon"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
>
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</DeleteConfirmationModal>
</ShadTooltip>
) : (
<ShadTooltip
content={
authorized ? "Like" : "Please review your API key."
}
>
<Button
disabled={loadingLike}
variant="ghost"
size="icon"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
}
onClick={() => {
if (!authorized) {
return;
}
handleLike();
}}
data-testid={`like-${data.name}`}
>
<IconComponent
name="Trash2"
name="Heart"
className={cn(
"h-5 w-5",
liked_by_user
? "fill-destructive stroke-destructive"
: "",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</DeleteConfirmationModal>
</ShadTooltip>
) : (
</ShadTooltip>
)}
<ShadTooltip
content={
authorized ? "Like" : "Please review your API key."
authorized
? isStore
? "Download"
: "Install Locally"
: "Please review your API key."
}
>
<Button
disabled={loadingLike}
disabled={loading}
variant="ghost"
size="icon"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "")
(!authorized ? " cursor-not-allowed" : "") +
(!loading ? " p-0.5" : "")
}
onClick={() => {
if (!authorized) {
if (loading || !authorized) {
return;
}
handleLike();
handleInstall();
}}
data-testid={`like-${data.name}`}
data-testid={`install-${data.name}`}
>
<IconComponent
name="Heart"
name={
loading ? "Loader2" : isStore ? "Download" : "Plus"
}
className={cn(
"h-5 w-5",
liked_by_user
? "fill-destructive stroke-destructive"
: "",
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
!authorized ? " text-ring" : ""
)}
/>
</Button>
</ShadTooltip>
)}
<ShadTooltip
content={
authorized
? isStore
? "Download"
: "Install Locally"
: "Please review your API key."
}
>
<Button
disabled={loading}
variant="ghost"
size="icon"
className={
"whitespace-nowrap" +
(!authorized ? " cursor-not-allowed" : "") +
(!loading ? " p-0.5" : "")
</div>
)}
{button && button}
{playground && data?.metadata === undefined && (
<Button
disabled={loadingPlayground}
key={data.id}
tabIndex={-1}
variant="outline"
size="sm"
className="gap-2 whitespace-nowrap"
data-testid={"playground-flow-button-" + data.id}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setLoadingPlayground(true);
if (getFlowById(data.id)) {
setCurrentFlowId(data.id);
setOpenPlayground(true);
setLoadingPlayground(false);
} else {
getFlowData().then((res) => {
setCurrentFlow(res);
setOpenPlayground(true);
setLoadingPlayground(false);
});
}
onClick={() => {
if (loading || !authorized) {
return;
}
handleInstall();
}}
data-testid={`install-${data.name}`}
>
}}
>
{!loadingPlayground ? (
<IconComponent
name={loading ? "Loader2" : isStore ? "Download" : "Plus"}
className={cn(
loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
!authorized ? " text-ring" : ""
)}
name="BotMessageSquareIcon"
className="h-4 w-4 select-none"
/>
</Button>
</ShadTooltip>
</div>
)}
{button && button}
{playground && (
<Button
tabIndex={-1}
variant="outline"
size="sm"
className="whitespace-nowrap "
data-testid={"playground-flow-button-" + data.id}
onClick={() => {
setCurrentFlowId(data.id);
setOpenPlayground(true);
}}
>
<IconComponent
name="ExternalLink"
className="main-page-nav-button select-none"
/>
Playground
{openPlayground && (
<IOModal open={openPlayground} setOpen={setOpenPlayground}>
<></>
</IOModal>
)}
</Button>
)}
) : (
<Loading className="h-4 w-4 text-medium-indigo" />
)}
Playground
</Button>
)}
</div>
</div>
</div>
</CardFooter>
</Card>
</CardFooter>
</Card>
{openPlayground && (
<IOModal
cleanOnClose={true}
open={openPlayground}
setOpen={setOpenPlayground}
>
<></>
</IOModal>
)}
</>
);
}

View file

@ -87,15 +87,15 @@ export default function FlowToolbar(): JSX.Element {
}
>
<div className="flex">
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
<div className="flex h-full w-full gap-1 rounded-sm transition-all">
{hasIO ? (
<IOModal open={open} setOpen={setOpen} disable={!hasIO}>
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-500 ease-in-out ease-in-out hover:bg-hover">
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold transition-all duration-500 ease-in-out hover:bg-hover">
<ForwardedIconComponent
name="Zap"
className={"message-button-icon h-5 w-5 transition-all"}
name="BotMessageSquareIcon"
className={" h-5 w-5 transition-all"}
/>
Run
Playground
</div>
</IOModal>
) : (

View file

@ -1,5 +1,5 @@
import { AxiosResponse } from "axios";
import { ReactFlowJsonObject } from "reactflow";
import { AxiosRequestConfig, AxiosResponse } from "axios";
import { Edge, ReactFlowJsonObject,Node } from "reactflow";
import { BASE_URL_API } from "../../constants/constants";
import { api } from "../../controllers/API/api";
import {
@ -906,17 +906,26 @@ export async function updateGlobalVariable(
export async function getVerticesOrder(
flowId: string,
startNodeId?: string | null,
stopNodeId?: string | null
stopNodeId?: string | null,
nodes?:Node[],
Edges?:Edge[]
): Promise<AxiosResponse<VerticesOrderTypeAPI>> {
// nodeId is optional and is a query parameter
// if nodeId is not provided, the API will return all vertices
const config = {};
const config:AxiosRequestConfig<any> = {};
if (stopNodeId) {
config["params"] = { stop_component_id: stopNodeId };
} else if (startNodeId) {
config["params"] = { start_component_id: startNodeId };
}
return await api.get(`${BASE_URL_API}build/${flowId}/vertices`, config);
const data = {
data:{}
}
if(nodes && Edges){
data["data"]["nodes"] = nodes
data["data"]["edges"] = Edges
}
return await api.post(`${BASE_URL_API}build/${flowId}/vertices`,data, config);
}
export async function postBuildVertex(

View file

@ -83,12 +83,6 @@ export default function IOModal({
return updateVerticesOrder(currentFlow!.id, null);
}
useEffect(() => {
if (open) {
updateVertices();
}
}, [open, currentFlow]);
async function sendMessage(count = 1): Promise<void> {
if (isBuilding) return;
setIsBuilding(true);
@ -132,9 +126,9 @@ export default function IOModal({
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}
<BaseModal.Header description={CHAT_FORM_DIALOG_SUBTITLE}>
<div className="flex items-center">
<span className="pr-2">Interaction Panel</span>
<span className="pr-2">Playground</span>
<IconComponent
name="prompts"
name="BotMessageSquareIcon"
className="h-6 w-6 pl-1 text-foreground"
aria-hidden="true"
/>

View file

@ -86,6 +86,7 @@ export default function StoreApiKeyModal({
<Form.Root
onSubmit={(event) => {
event.preventDefault();
handleSaveKey();
}}
>
<div className="grid gap-5">
@ -131,9 +132,6 @@ export default function StoreApiKeyModal({
<Button
data-testid="api-key-save-button-store"
className="mt-8"
onClick={() => {
handleSaveKey();
}}
>
Save
</Button>

View file

@ -6,18 +6,25 @@ import { useDarkStore } from "../../stores/darkStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import Page from "./components/PageComponent";
import ExtraSidebar from "./components/extraSidebarComponent";
import useFlowStore from "../../stores/flowStore";
export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId
);
const version = useDarkStore((state) => state.version);
const setOnFlowPage = useFlowStore((state) => state.setOnFlowPage);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const { id } = useParams();
// Set flow tab id
useEffect(() => {
setCurrentFlowId(id!);
setOnFlowPage(true);
return () => {
setOnFlowPage(false);
};
}, [id]);
return (
<>

View file

@ -56,7 +56,7 @@ export default function ComponentsComponent({
const start = (pageIndex - 1) * pageSize;
const end = start + pageSize;
setData(all.slice(start, end));
}, [flows, isLoading, pageIndex, pageSize]);
}, [flows,isLoading, pageIndex, pageSize]);
const [data, setData] = useState<FlowType[]>([]);
@ -146,30 +146,11 @@ export default function ComponentsComponent({
}}
key={idx}
data={item}
disabled={isLoading}
button={
!is_component ? (
<Link to={"/flow/" + item.id}>
<Button
tabIndex={-1}
variant="outline"
size="sm"
className="whitespace-nowrap "
data-testid={
"edit-flow-button-" + item.id + "-" + idx
}
>
<IconComponent
name="ExternalLink"
className="main-page-nav-button select-none"
/>
Edit Flow
</Button>
</Link>
) : (
<></>
)
data-testid={
"edit-flow-button-" + item.id + "-" + idx
}
onClick={!is_component ? () => {navigate("/flow/" + item.id);} : undefined}
disabled={isLoading}
playground={!is_component}
/>
))

View file

@ -0,0 +1,64 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { getComponent } from "../../controllers/API";
import cloneFLowWithParent from "../../utils/storeUtils";
import LoadingComponent from "../../components/loadingComponent";
import useFlowStore from "../../stores/flowStore";
import IOModal from "../../modals/IOModal";
export default function PlaygroundPage() {
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const getFlowById = useFlowsManagerStore((state) => state.getFlowById);
const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const setNodes = useFlowStore((state) => state.setNodes);
const setEdges = useFlowStore((state) => state.setEdges);
const cleanFlowPool = useFlowStore((state) => state.CleanFlowPool);
const { id } = useParams();
const [loading, setLoading] = useState(true);
async function getFlowData() {
const res = await getComponent(id!);
const newFlow = cloneFLowWithParent(res, res.id, false, true);
return newFlow;
}
// Set flow tab id
useEffect(() => {
console.log("id", id);
if (getFlowById(id!)) {
setCurrentFlowId(id!);
}
else {
getFlowData().then((flow) => {
setCurrentFlow(flow);
});
}
}, [id]);
useEffect(() => {
if (currentFlow) {
setNodes(currentFlow?.data?.nodes ?? [], true);
setEdges(currentFlow?.data?.edges ?? [], true);
cleanFlowPool();
setLoading(false);
}
return () => {
setNodes([], true);
setEdges([], true);
cleanFlowPool();
};
}, [currentFlow]);
return (
<div className="w-full h-full flex flex-col align-middle items-center justify-center">
{loading ? <div><LoadingComponent remSize={24}></LoadingComponent></div> :
<IOModal open={true}setOpen={()=>{}} isPlayground>
<></>
</IOModal>}
</div>
)
}

View file

@ -9,7 +9,7 @@ import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { Link, useParams } from "react-router-dom";
import { Link, useNavigate, useParams } from "react-router-dom";
import { TagsSelector } from "../../components/tagsSelectorComponent";
import { Badge } from "../../components/ui/badge";
import {
@ -65,6 +65,8 @@ export default function StorePage(): JSX.Element {
const [searchNow, setSearchNow] = useState("");
const [selectFilter, setSelectFilter] = useState("all");
const navigate = useNavigate();
useEffect(() => {
if (!loadingApiKey) {
if (!hasApiKey) {
@ -149,6 +151,11 @@ export default function StorePage(): JSX.Element {
.catch((err) => {
if (err.response?.status === 403 || err.response?.status === 401) {
setValidApiKey(false);
} else if (
err.response?.data?.detail ===
"Unexpected error: Object of type UUID is not JSON serializable"
) {
navigate("/store/" + inputText);
} else {
setSearchData([]);
setTotalRowsCount(0);
@ -371,6 +378,10 @@ export default function StorePage(): JSX.Element {
data={item}
authorized={validApiKey}
disabled={loading}
playground={
item.last_tested_version?.includes("1.0.0") &&
!item.is_component
}
/>
</>
);

View file

@ -17,6 +17,7 @@ import ViewPage from "./pages/ViewPage";
import DeleteAccountPage from "./pages/deleteAccountPage";
import LoginPage from "./pages/loginPage";
import SignUp from "./pages/signUpPage";
import PlaygroundPage from "./pages/Playground";
const Router = () => {
const navigate = useNavigate();
@ -65,7 +66,13 @@ const Router = () => {
</ProtectedRoute>
}
/>
<Route path="/playground/:id/">
element={
<Route path="" element={<ProtectedRoute>
<PlaygroundPage />
</ProtectedRoute>} />
}
</Route>
<Route path="/flow/:id/">
<Route
path=""

View file

@ -44,9 +44,12 @@ import { getInputsAndOutputs } from "../utils/storeUtils";
import useAlertStore from "./alertStore";
import { useDarkStore } from "./darkStore";
import useFlowsManagerStore from "./flowsManagerStore";
import FlowPage from "../pages/FlowPage";
// this is our useStore hook that we can use in our components to get parts of the store and call actions
const useFlowStore = create<FlowStoreType>((set, get) => ({
onFlowPage: false,
setOnFlowPage:(FlowPage=>set({onFlowPage:FlowPage})),
flowState: undefined,
flowBuildStatus: {},
nodes: [],
@ -149,7 +152,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
edges: applyEdgeChanges(changes, get().edges),
});
},
setNodes: (change) => {
setNodes: (change,skipSave=false) => {
let newChange = typeof change === "function" ? change(get().nodes) : change;
let newEdges = cleanEdges(newChange, get().edges);
const { inputs, outputs } = getInputsAndOutputs(newChange);
@ -164,7 +167,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
});
const flowsManager = useFlowsManagerStore.getState();
if (!get().isBuilding) {
if (!get().isBuilding && !skipSave && get().onFlowPage) {
flowsManager.autoSaveCurrentFlow(
newChange,
newEdges,
@ -172,7 +175,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
);
}
},
setEdges: (change) => {
setEdges: (change,skipSave=false) => {
let newChange = typeof change === "function" ? change(get().edges) : change;
set({
edges: newChange,
@ -180,7 +183,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
});
const flowsManager = useFlowsManagerStore.getState();
if (!get().isBuilding) {
if (!get().isBuilding && !skipSave && get().onFlowPage) {
flowsManager.autoSaveCurrentFlow(
get().nodes,
newChange,
@ -560,6 +563,8 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING);
},
onValidateNodes: validateSubgraph,
nodes: !get().onFlowPage ? get().nodes : undefined,
edges: !get().onFlowPage ? get().edges : undefined,
});
get().setIsBuilding(false);
get().revertBuiltStatusFromBuilding();

View file

@ -46,6 +46,16 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
set({ examples });
},
currentFlowId: "",
setCurrentFlow: (flow: FlowType) => {
set((state) => ({
currentFlow: flow,
currentFlowId: flow.id,
}));
},
getFlowById: (id: string) => {
return get().flows.find((flow) => flow.id === id);
},
setCurrentFlowId: (currentFlowId: string) => {
set((state) => ({
currentFlowId,

View file

@ -594,6 +594,8 @@ export type IOModalPropsType = {
open: boolean;
setOpen: (open: boolean) => void;
disable?: boolean;
isPlayground?: boolean;
cleanOnClose?: boolean;
};
export type buttonBoxPropsType = {

View file

@ -45,6 +45,8 @@ export type FlowPoolType = {
};
export type FlowStoreType = {
onFlowPage: boolean;
setOnFlowPage: (onFlowPage: boolean) => void;
flowPool: FlowPoolType;
inputs: Array<{ type: string; id: string; displayName: string }>;
outputs: Array<{ type: string; id: string; displayName: string }>;
@ -74,8 +76,8 @@ export type FlowStoreType = {
edges: Edge[];
onNodesChange: OnNodesChange;
onEdgesChange: OnEdgesChange;
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
setNodes: (update: Node[] | ((oldState: Node[]) => Node[]),skipSave?:boolean) => void;
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[]),skipSave?:boolean) => void;
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
getNode: (id: string) => Node | undefined;
deleteNode: (nodeId: string | Array<string>) => void;

View file

@ -2,6 +2,7 @@ import { Edge, Node, Viewport, XYPosition } from "reactflow";
import { FlowType } from "../../flow";
export type FlowsManagerStoreType = {
getFlowById: (id: string) => FlowType | undefined;
flows: Array<FlowType>;
setFlows: (flows: FlowType[]) => void;
currentFlow: FlowType | undefined;
@ -50,6 +51,7 @@ export type FlowsManagerStoreType = {
takeSnapshot: () => void;
examples: Array<FlowType>;
setExamples: (examples: FlowType[]) => void;
setCurrentFlow: (flow: FlowType) => void;
};
export type UseUndoRedoOptions = {

View file

@ -5,6 +5,7 @@ import useAlertStore from "../stores/alertStore";
import useFlowStore from "../stores/flowStore";
import { VertexBuildTypeAPI } from "../types/api";
import { VertexLayerElementType } from "../types/zustand/flow";
import { Edge, Node } from "reactflow";
type BuildVerticesParams = {
flowId: string; // Assuming FlowType is the type for your flow
@ -21,6 +22,8 @@ type BuildVerticesParams = {
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
onBuildStart?: (idList: VertexLayerElementType[]) => void;
onValidateNodes?: (nodes: string[]) => void;
nodes?: Node[];
edges?: Edge[];
};
function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
@ -48,7 +51,9 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI {
export async function updateVerticesOrder(
flowId: string,
startNodeId?: string | null,
stopNodeId?: string | null
stopNodeId?: string | null,
nodes?:Node[],
edges?:Edge[]
): Promise<{
verticesLayers: VertexLayerElementType[][];
verticesIds: string[];
@ -59,7 +64,7 @@ export async function updateVerticesOrder(
const setErrorData = useAlertStore.getState().setErrorData;
let orderResponse;
try {
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId);
orderResponse = await getVerticesOrder(flowId, startNodeId, stopNodeId, nodes, edges);
} catch (error: any) {
setErrorData({
title: "Oops! Looks like you missed something",
@ -101,6 +106,8 @@ export async function buildVertices({
onBuildError,
onBuildStart,
onValidateNodes,
nodes,
edges,
}: BuildVerticesParams) {
let verticesBuild = useFlowStore.getState().verticesBuild;
// if startNodeId and stopNodeId are provided
@ -113,7 +120,9 @@ export async function buildVertices({
let verticesOrderResponse = await updateVerticesOrder(
flowId,
startNodeId,
stopNodeId
stopNodeId,
nodes,
edges
);
if (onValidateNodes) {
try {

View file

@ -1,4 +1,4 @@
import { cloneDeep } from "lodash";
import { cloneDeep, uniqueId } from "lodash";
import { Node } from "reactflow";
import { FlowType, NodeDataType } from "../types/flow";
import { isInputNode, isOutputNode } from "./reactflowUtils";
@ -6,11 +6,17 @@ import { isInputNode, isOutputNode } from "./reactflowUtils";
export default function cloneFLowWithParent(
flow: FlowType,
parent: string,
is_component: boolean
is_component: boolean,
keepId=false
) {
let childFLow = cloneDeep(flow);
childFLow.parent = parent;
childFLow.id = "";
if(!keepId){
childFLow.id = "";
}
else{
childFLow.id = uniqueId()+"-"+childFLow.id;
}
childFLow.is_component = is_component;
return childFLow;
}

View file

@ -136,6 +136,7 @@ import {
X,
XCircle,
Zap,
PlaySquare
} from "lucide-react";
import { FaApple, FaGithub } from "react-icons/fa";
import { AWSIcon } from "../icons/AWS";
@ -144,7 +145,7 @@ import { AnthropicIcon } from "../icons/Anthropic";
import { AstraDBIcon } from "../icons/AstraDB";
import { AzureIcon } from "../icons/Azure";
import { BingIcon } from "../icons/Bing";
import { BotMessageSquareIcon } from "../icons/BotMessageSquare";
import { BotMessageSquareIcon} from "../icons/BotMessageSquare";
import { ChromaIcon } from "../icons/ChromaIcon";
import { CohereIcon } from "../icons/Cohere";
import { ElasticsearchIcon } from "../icons/ElasticsearchStore";
@ -292,7 +293,8 @@ export const nodeIconsLucide: iconsType = {
ListFlows: Group,
ClearMessageHistory: FileClock,
Python: PythonIcon,
ChatOutput: BotMessageSquareIcon,
ChatOutput: MessagesSquare,
BotMessageSquareIcon,
ChatInput: MessagesSquare,
inputs: Download,
outputs: Upload,