feat(validate): add debounced validation for nodes in GenericNode component

fix(validate): set validation status to "success" or "error" based on response status
feat(validate): add state for validation status and outline color in GenericNode component
refactor(validate): use useDebouncedCallback hook for debouncing validation function
refactor(validate): simplify useEffect dependencies in GenericNode component
This commit is contained in:
Gabriel Almeida 2023-04-25 14:31:57 -03:00
commit 7af5f68861
2 changed files with 180 additions and 163 deletions

View file

@ -46,7 +46,7 @@ def post_validate_node(node_id: str, data: dict):
node = graph.get_node(node_id) node = graph.get_node(node_id)
if node is not None: if node is not None:
_ = node.build() _ = node.build()
return node.params return str(node.params)
raise Exception(f"Node {node_id} not found") raise Exception(f"Node {node_id} not found")
except Exception as e: except Exception as e:
logger.exception(e) logger.exception(e)

View file

@ -1,4 +1,5 @@
import { TrashIcon } from "@heroicons/react/24/outline"; import { TrashIcon } from "@heroicons/react/24/outline";
import { useDebouncedCallback } from "use-debounce";
import { import {
classNames, classNames,
nodeColors, nodeColors,
@ -10,7 +11,7 @@ import { typesContext } from "../../contexts/typesContext";
import { useContext, useState, useEffect, useRef } from "react"; import { useContext, useState, useEffect, useRef } from "react";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { useCallback } from 'react'; import { useCallback } from "react";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -23,52 +24,68 @@ export default function GenericNode({
const showError = useRef(true); const showError = useRef(true);
const { types, deleteNode } = useContext(typesContext); const { types, deleteNode } = useContext(typesContext);
const Icon = nodeIcons[types[data.type]]; const Icon = nodeIcons[types[data.type]];
const [validationStatus, setValidationStatus] = useState("idle");
// State for outline color // State for outline color
const [isGreenOutline, setIsGreenOutline] = useState(false); const [isGreenOutline, setIsGreenOutline] = useState(false);
const [isRedOutline, setIsRedOutline] = useState(false); const [isRedOutline, setIsRedOutline] = useState(false);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const validateNode = useCallback(async () => { const debouncedValidateNode = useDebouncedCallback(async () => {
// Check if the validationStatus is "success"
if (validationStatus === "success") return;
try { try {
const response = await fetch(`/validate/node/${data.id}`, { const response = await fetch(`/validate/node/${data.id}`, {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify(reactFlowInstance.toObject()), body: JSON.stringify(reactFlowInstance.toObject()),
}); });
if (response.status === 200) { if (response.status === 200) {
setIsGreenOutline(true); setValidationStatus("success");
setIsRedOutline(false);
} else if (response.status === 500) { } else if (response.status === 500) {
setIsRedOutline(true); setValidationStatus("error");
setIsGreenOutline(false);
} }
} catch (error) { } catch (error) {
console.error('Error validating node:', error); console.error("Error validating node:", error);
setIsRedOutline(true); setValidationStatus("error");
} }
}, [data.id, reactFlowInstance]); }, 1000);
const validateNode = useCallback(() => {
debouncedValidateNode();
}, [debouncedValidateNode]);
useEffect(() => { useEffect(() => {
validateNode(); validateNode();
}, [ }, [
validateNode, validateNode,
...Object.values(data.node.template).flatMap((t) => Object.values(t)), ...Object.values(data.node.template).flatMap((t) => Object.values(t)),
]); ]);
useEffect(() => { useEffect(() => {
if (isGreenOutline) { if (validationStatus === "success") {
setIsGreenOutline(true);
setIsRedOutline(false);
setTimeout(() => { setTimeout(() => {
setIsGreenOutline(false); setIsGreenOutline(false);
}, 1000); }, 1000);
} else if (validationStatus === "error") {
setIsRedOutline(true);
setIsGreenOutline(false);
} else {
setIsGreenOutline(false);
setIsRedOutline(false);
} }
}, [isGreenOutline]); }, [validationStatus]);
const outlineColor = isGreenOutline ? 'animate-pulse-green' : isRedOutline ? 'border-red-outline' : ''; const outlineColor = isGreenOutline
? "animate-pulse-green"
: isRedOutline
? "border-red-outline"
: "";
if (!Icon) { if (!Icon) {
if (showError.current) { if (showError.current) {