From f30120d448def08ae4564b4c1313926d817de48c Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Sat, 14 Feb 2026 13:36:41 +0000 Subject: [PATCH] Task validation now returns it's state when navigating away from the tab. --- .../WorkflowTemplateDetails.tsx | 35 ++++++++++---- .../components/TasksEditor.tsx | 2 +- .../workflowTemplates/components/TasksTab.tsx | 46 +++++++++++-------- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx index 3bee394..5fc6f3b 100644 --- a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx +++ b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx @@ -30,6 +30,10 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ const { userId } = useParams<{ userId: string }>(); const [activeTab, setActiveTab] = React.useState("general"); + const [taskValidation, setTaskValidation] = useState>( + {}, + ); + // useForm promoted to the parent const form = useFormWithGuard({ loaded: false, @@ -124,11 +128,21 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ form.handleSubmit(e, doSubmit); }; - const [tasksValid, setTasksValid] = useState(true); + //const [tasksValid, setTasksValid] = useState(true); - const handleTasksValidate = (isValid: boolean) => { - console.log("Test", isValid); - setTasksValid(isValid); + const handleTaskValidate = (taskId: string, isValid: boolean) => { + setTaskValidation((prev) => { + const updated = { ...prev, [taskId]: isValid }; + + // Compute overall validity + //const allValid = Object.values(updated).every((v) => v === true); + + // Bubble up to parent + //setTasksValid(allValid); + //setTaskValidation(updated); + + return updated; + }); }; const { loaded, redirect, errors: formErrors, data } = form.state; @@ -136,10 +150,6 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ let errors = { ...formErrors }; - if (!tasksValid) { - errors["tasks"] = t("TasksValidationError"); - } - const generalTabValid = !( errors["domainId"] || errors["name"] || @@ -147,6 +157,12 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ errors["description"] ); + const tasksValid = Object.values(taskValidation).every((v) => v === true); + + if (!tasksValid) { + errors["tasks"] = t("TasksValidationError"); + } + // ----------------------------- // Tabs // ----------------------------- @@ -172,7 +188,8 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ errors={errors} isEditMode={editMode} onTasksChange={form.handleTasksChange} - onValidate={handleTasksValidate} + onValidate={handleTaskValidate} + taskValidation={taskValidation} /> , diff --git a/src/modules/manager/workflowTemplates/components/TasksEditor.tsx b/src/modules/manager/workflowTemplates/components/TasksEditor.tsx index 9c44b90..e49d5b5 100644 --- a/src/modules/manager/workflowTemplates/components/TasksEditor.tsx +++ b/src/modules/manager/workflowTemplates/components/TasksEditor.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useState } from "react"; import { TaskDefinition } from "../services/WorkflowTemplateService"; import { TaskCoreEditor } from "./CapabilityEditors/TaskCoreEditor"; diff --git a/src/modules/manager/workflowTemplates/components/TasksTab.tsx b/src/modules/manager/workflowTemplates/components/TasksTab.tsx index c54d6c0..0da3ce7 100644 --- a/src/modules/manager/workflowTemplates/components/TasksTab.tsx +++ b/src/modules/manager/workflowTemplates/components/TasksTab.tsx @@ -11,7 +11,8 @@ interface TasksTabProps { errors: Record; isEditMode: boolean; onTasksChange: (name: string, value: TaskDefinition[]) => void; - onValidate: (isValid: boolean) => void; + onValidate: (taskId: string, isValid: boolean) => void; + taskValidation: Record; } const TasksTab: React.FC = ({ @@ -20,26 +21,10 @@ const TasksTab: React.FC = ({ isEditMode, onTasksChange, onValidate, + taskValidation, }) => { const tasks = data.tasks; const [selectedTask, setSelectedTask] = useState(null); - const [taskValidation, setTaskValidation] = useState>( - {}, - ); - - const handleTaskValidate = (taskId: string, isValid: boolean) => { - setTaskValidation((prev) => { - const updated = { ...prev, [taskId]: isValid }; - - // Compute overall validity - const allValid = Object.values(updated).every((v) => v === true); - - // Bubble up to parent - onValidate(allValid); - - return updated; - }); - }; useEffect(() => { if (tasks.length === 0) { @@ -72,6 +57,29 @@ const TasksTab: React.FC = ({ } }, [tasks, selectedTask]); + useEffect(() => { + // Don't override user selection + if (selectedTask) return; + + if (tasks.length === 0) { + setSelectedTask(null); + return; + } + + // Find first invalid task + const firstInvalid = tasks.find( + (t) => taskValidation[t.config.guid as string] === false, + ); + + if (firstInvalid) { + setSelectedTask(firstInvalid); + return; + } + + // Otherwise select first task + setSelectedTask(tasks[0]); + }, [tasks, taskValidation, selectedTask]); + const handleTasksChange = (newTasks: TaskDefinition[]) => { // Update the parent form state onTasksChange("tasks", newTasks); @@ -102,7 +110,7 @@ const TasksTab: React.FC = ({ handleTasksChange(newTasks); setSelectedTask(updatedTask); }} - onValidate={handleTaskValidate} + onValidate={onValidate} /> )}