From f122151b12f388d7fa64f222fc1cbfee1c13f4b5 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Mon, 24 Apr 2023 13:43:52 -0300 Subject: [PATCH] fix(validate.py): raise an exception with a message when node is not found feat(GenericNode): add node validation with outline color feedback feat(tailwind.config.js): add styles for outline colors and animations --- src/backend/langflow/api/validate.py | 2 +- .../src/CustomNodes/GenericNode/index.tsx | 56 +++++++++++++- src/frontend/tailwind.config.js | 75 ++++++++++++------- 3 files changed, 100 insertions(+), 33 deletions(-) diff --git a/src/backend/langflow/api/validate.py b/src/backend/langflow/api/validate.py index 07f113a8b..d80010be4 100644 --- a/src/backend/langflow/api/validate.py +++ b/src/backend/langflow/api/validate.py @@ -47,7 +47,7 @@ def post_validate_node(node_id: str, data: dict): if node is not None: _ = node.build() return node.params - raise + raise Exception(f"Node {node_id} not found") except Exception as e: logger.exception(e) raise HTTPException(status_code=500, detail=str(e)) from e diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index ff13af901..c96591cc9 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -7,9 +7,10 @@ import { } from "../../utils"; import ParameterComponent from "./components/parameterComponent"; import { typesContext } from "../../contexts/typesContext"; -import { useContext, useRef } from "react"; +import { useContext, useState, useEffect, useRef } from "react"; import { NodeDataType } from "../../types/flow"; import { alertContext } from "../../contexts/alertContext"; +import { useCallback } from 'react'; export default function GenericNode({ data, @@ -22,6 +23,53 @@ export default function GenericNode({ const showError = useRef(true); const { types, deleteNode } = useContext(typesContext); const Icon = nodeIcons[types[data.type]]; + + // State for outline color + const [isGreenOutline, setIsGreenOutline] = useState(false); + const [isRedOutline, setIsRedOutline] = useState(false); + const { reactFlowInstance } = useContext(typesContext); + + const validateNode = useCallback(async () => { + try { + const response = await fetch(`/validate/node/${data.id}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(reactFlowInstance.toObject()), + }); + + if (response.status === 200) { + setIsGreenOutline(true); + setIsRedOutline(false); + } else if (response.status === 500) { + setIsRedOutline(true); + setIsGreenOutline(false); + } + } catch (error) { + console.error('Error validating node:', error); + setIsRedOutline(true); + } + }, [data.id, reactFlowInstance]); + + useEffect(() => { + validateNode(); + }, [ + validateNode, + ...Object.values(data.node.template).flatMap((t) => Object.values(t)), + + ]); + + useEffect(() => { + if (isGreenOutline) { + setTimeout(() => { + setIsGreenOutline(false); + }, 1000); + } + }, [isGreenOutline]); + + const outlineColor = isGreenOutline ? 'animate-pulse-green' : isRedOutline ? 'border-red-outline' : ''; + if (!Icon) { if (showError.current) { setErrorData({ @@ -34,9 +82,11 @@ export default function GenericNode({ deleteNode(data.id); return; } + return (