diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index dfa505f91..c7b85c57c 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -7,6 +7,7 @@ import InputComponent from "../../components/inputComponent"; import { Button } from "../../components/ui/button"; import { Textarea } from "../../components/ui/textarea"; import { priorityFields } from "../../constants/constants"; +import { BuildStatus } from "../../constants/enums"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; @@ -71,7 +72,6 @@ export default function GenericNode({ setHandles(count); } - useEffect(() => { countHandles(); }, [data, data.node]); @@ -157,6 +157,78 @@ export default function GenericNode({ ); }; + const getIconPlayOrPauseComponent = (name, className) => ( + + ); + + const getStatusClassName = ( + validationStatus: validationStatusType | null, + isBuilding: boolean + ) => { + if (validationStatus && validationStatus.valid) { + return "green-status"; + } else if (validationStatus && !validationStatus.valid) { + return "red-status"; + } else if (!validationStatus || isBuilding) { + return "yellow-status"; + } else { + return "status-build-animation"; + } + }; + + const renderIconPlayOrPauseComponents = ( + buildStatus: BuildStatus | undefined, + validationStatus: validationStatusType | null, + isBuilding: boolean + ) => { + if (buildStatus === BuildStatus.BUILDING) { + return getIconPlayOrPauseComponent("Square", "red-status"); + } else { + const className = getStatusClassName(validationStatus, isBuilding); + return <>{getIconPlayOrPauseComponent("Play", className)}; + } + }; + + const getSpecificClassFromBuildStatus = ( + buildStatus: BuildStatus | undefined, + validationStatus: validationStatusType | null + ) => { + if ( + buildStatus === BuildStatus.BUILDED && + validationStatus && + !validationStatus.valid + ) { + return "border-none ring ring-red-300"; + } else if (buildStatus === BuildStatus.BUILDING) { + return "border-none ring"; + } else { + return ""; + } + }; + + const getNodeBorderClassName = ( + selected: boolean, + showNode: boolean, + buildStatus: BuildStatus | undefined, + validationStatus: validationStatusType | null + ) => { + return classNames( + getBaseBorderClass(selected), + getNodeSizeClass(showNode), + "generic-node-div", + getSpecificClassFromBuildStatus(buildStatus, validationStatus) + ); + }; + + const getBaseBorderClass = (selected) => + selected ? "border border-ring" : "border"; + + const getNodeSizeClass = (showNode) => + showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full"; + return ( <> @@ -181,10 +253,11 @@ export default function GenericNode({
{data.node?.beta && showNode && ( @@ -396,7 +469,8 @@ export default function GenericNode({
Building... ) : !validationStatus ? ( @@ -421,33 +495,11 @@ export default function GenericNode({ } >
- - - + {renderIconPlayOrPauseComponents( + data?.build_status, + validationStatus, + isBuilding + )}
diff --git a/src/frontend/src/constants/enums.ts b/src/frontend/src/constants/enums.ts index a7bed8fde..57476239c 100644 --- a/src/frontend/src/constants/enums.ts +++ b/src/frontend/src/constants/enums.ts @@ -6,3 +6,9 @@ export enum TypeModal { TEXT = 1, PROMPT = 2, } + +export enum BuildStatus { + BUILDING = "BUILDING", + TO_BUILD = "TO_BUILD", + BUILDED = "BUILDED", +} diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx index c85e77a0d..80ab8c06b 100644 --- a/src/frontend/src/modals/formModal/index.tsx +++ b/src/frontend/src/modals/formModal/index.tsx @@ -205,7 +205,6 @@ export default function FormModal({ if (Array.isArray(data) && data.length > 0) { //set chat history setChatHistory((_) => { - console.log(data); let newChatHistory: ChatMessageType[] = []; for (let i = 0; i < data.length; i++) { if (data[i].type === "prompt" && data[i].prompt) { diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index f14b9558e..92dc11ee8 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -11,6 +11,7 @@ import { } from "reactflow"; import { create } from "zustand"; import { INPUT_TYPES, OUTPUT_TYPES } from "../constants/constants"; +import { BuildStatus } from "../constants/enums"; import { getFlowPool, updateFlowInDatabase } from "../controllers/API"; import { NodeDataType, @@ -380,6 +381,7 @@ const useFlowStore = create((set, get) => ({ const setNoticeData = useAlertStore.getState().setNoticeData; function handleBuildUpdate(data: any) { get().addDataToFlowPool(data.data[data.id], data.id); + useFlowStore.getState().updateBuildStatus([data.id], BuildStatus.BUILDED); } await updateFlowInDatabase({ data: { @@ -403,9 +405,14 @@ const useFlowStore = create((set, get) => ({ } }, onBuildUpdate: handleBuildUpdate, - onBuildError: (title, list) => { + onBuildError: (title, list, idList) => { + useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDED); + setErrorData({ list, title }); }, + onBuildStart: (idList) => { + useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING); + }, }); }, getFlow: () => { @@ -415,6 +422,15 @@ const useFlowStore = create((set, get) => ({ viewport: get().reactFlowInstance?.getViewport()!, }; }, + updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => { + nodeIdList.forEach((id) => { + const nodeToUpdate = get().nodes.find((node) => node.id === id); + if (nodeToUpdate) { + nodeToUpdate.data.build_status = status; + get().setNodes(get().nodes); + } + }); + }, })); export default useFlowStore; diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index 48db814cf..1286a90bc 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -1,4 +1,5 @@ import { Edge, Node, Viewport } from "reactflow"; +import { BuildStatus } from "../../constants/enums"; import { FlowType } from "../flow"; //kind and class are just representative names to represent the actual structure of the object received by the API export type APIDataType = { [key: string]: APIKindType }; @@ -36,6 +37,7 @@ export type APIClassType = { | CustomFieldsType | boolean | undefined; + build_status?: BuildStatus; }; export type TemplateVariableType = { diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index c3128b3f5..3a8563cf1 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -625,7 +625,7 @@ export type crashComponentPropsType = { export type validationStatusType = { id: string; - data: object; + data: object | any; params: string; progress: number; valid: boolean; diff --git a/src/frontend/src/types/flow/index.ts b/src/frontend/src/types/flow/index.ts index 544e78a45..5f2ed1dc4 100644 --- a/src/frontend/src/types/flow/index.ts +++ b/src/frontend/src/types/flow/index.ts @@ -1,4 +1,5 @@ import { ReactFlowJsonObject, XYPosition } from "reactflow"; +import { BuildStatus } from "../../constants/enums"; import { APIClassType } from "../api/index"; export type FlowType = { @@ -26,6 +27,7 @@ export type NodeDataType = { node?: APIClassType; id: string; output_types?: string[]; + build_status?: BuildStatus; }; // FlowStyleType is the type of the style object that is used to style the // Flow card with an emoji and a color. diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index a85965f16..429379155 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -7,6 +7,7 @@ import { ReactFlowInstance, Viewport, } from "reactflow"; +import { BuildStatus } from "../../../constants/enums"; import { FlowState } from "../../tabs"; export type chatInputType = { @@ -85,4 +86,5 @@ export type FlowStoreType = { unselectAll: () => void; buildFlow: (nodeId?: string) => Promise; getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; + updateBuildStatus: (nodeId: string[], status: BuildStatus) => void; }; diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 5b217b6d5..854a86f24 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -1,5 +1,7 @@ import { AxiosError } from "axios"; +import { BuildStatus } from "../constants/enums"; import { getVerticesOrder, postBuildVertex } from "../controllers/API"; +import useFlowStore from "../stores/flowStore"; import { VertexBuildTypeAPI } from "../types/api"; type BuildVerticesParams = { @@ -8,7 +10,8 @@ type BuildVerticesParams = { onProgressUpdate?: (progress: number) => void; // Replace number with the actual type if it's not a number onBuildUpdate?: (data: any) => void; // Replace any with the actual type of data onBuildComplete?: (allNodesValid: boolean) => void; - onBuildError?: (title, list) => void; + onBuildError?: (title, list, idList: string[]) => void; + onBuildStart?: (idList: string[]) => void; }; export async function buildVertices({ @@ -18,10 +21,12 @@ export async function buildVertices({ onBuildUpdate, onBuildComplete, onBuildError, + onBuildStart, }: BuildVerticesParams) { let orderResponse = await getVerticesOrder(flowId, nodeId); let verticesOrder: Array> = orderResponse.data.ids; let vertices: Array> = []; + if (nodeId) { for (let i = 0; i < verticesOrder.length; i += 1) { const innerArray = verticesOrder[i]; @@ -40,10 +45,13 @@ export async function buildVertices({ vertices = verticesOrder; } - // Set each vertex state to building + const verticesIds = vertices.flatMap((v) => v); + useFlowStore.getState().updateBuildStatus(verticesIds, BuildStatus.TO_BUILD); + // Set each vertex state to building const buildResults: Array = []; for (let i = 0; i < vertices.length; i += 1) { + if (onBuildStart) onBuildStart(vertices[i]); await Promise.all( vertices[i].map(async (id) => { try { @@ -54,7 +62,11 @@ export async function buildVertices({ let data = {}; if (!buildData.valid) { if (onBuildError) { - onBuildError("Error Building Component", [buildData.params]); + onBuildError( + "Error Building Component", + [buildData.params], + verticesIds + ); } } data[buildData.id] = buildData; @@ -64,10 +76,14 @@ export async function buildVertices({ } catch (error) { if (onBuildError) { console.log(error); - onBuildError("Error Building Component", [ - (error as AxiosError).response?.data?.detail ?? - "Unknown Error", - ]); + onBuildError( + "Error Building Component", + [ + (error as AxiosError).response?.data?.detail ?? + "Unknown Error", + ], + verticesIds + ); } } })