From f530fc7efaffdd546d3567dd6ee4f6083b7d6916 Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Mon, 16 Feb 2026 22:16:18 +0000 Subject: [PATCH] Added a standard component for the validationErrorIcon. Fixed a rampant re rendering issue. --- src/Sass/old/_table.scss | 43 ++++++------ src/components/common/TabHeader.tsx | 9 +-- src/components/validationErrorIcon.tsx | 21 ++++++ .../WorkflowTemplateDetails.tsx | 14 +--- .../AssigneesOfITaskAssigneeEditor.tsx | 69 +++++++++++++------ .../workflowTemplates/components/TaskList.tsx | 11 +-- .../components/TasksEditor.tsx | 28 +++----- .../workflowTemplates/components/TasksTab.tsx | 2 +- 8 files changed, 111 insertions(+), 86 deletions(-) create mode 100644 src/components/validationErrorIcon.tsx diff --git a/src/Sass/old/_table.scss b/src/Sass/old/_table.scss index 51403d3..aeb5ded 100644 --- a/src/Sass/old/_table.scss +++ b/src/Sass/old/_table.scss @@ -1,36 +1,39 @@ -@import './global.scss'; +@import "./global.scss"; @mixin table { - background: $white; - border-radius: $sm; + background: $white; + border-radius: $sm; } .tableBackground { - @include table; - padding: $lge; + @include table; + padding: $lge; } .tableInfo { - display: inline-flex; - flex-direction: column; - justify-content: space-between; + display: inline-flex; + flex-direction: column; + justify-content: space-between; } table { - @include table; - display: table; - width: $full-width; - justify-content: left; - border: $thin-border + $--es-mono-100; + @include table; + display: table; + width: $full-width; + justify-content: left; + border: $thin-border + $--es-mono-100; } thead { - background-color: $background; - height: $th-height; - width: 100%; - th { - display: table-row; - justify-content: start; - } + background-color: $background; + height: $th-height; + width: 100%; + th { + display: table-row; + justify-content: start; + } } +.align-top { + vertical-align: top; +} diff --git a/src/components/common/TabHeader.tsx b/src/components/common/TabHeader.tsx index 2eb67e8..4836be6 100644 --- a/src/components/common/TabHeader.tsx +++ b/src/components/common/TabHeader.tsx @@ -1,6 +1,5 @@ -import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import React, { useCallback } from "react"; +import ValidationErrorIcon from "../validationErrorIcon"; interface TabHeaderProps { id: string; @@ -26,11 +25,7 @@ export default function TabHeader({ return (
  • {label} - {hasError && ( - - - - )} +
  • ); } diff --git a/src/components/validationErrorIcon.tsx b/src/components/validationErrorIcon.tsx new file mode 100644 index 0000000..6304521 --- /dev/null +++ b/src/components/validationErrorIcon.tsx @@ -0,0 +1,21 @@ +import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import React from "react"; + +interface ValidationErrorIconProps { + visible: boolean; +} + +const ValidationErrorIcon: React.FC = ({ + visible, +}) => { + if (!visible) return null; + + return ( + + + + ); +}; + +export default ValidationErrorIcon; diff --git a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx index 5f2055c..ee65f7a 100644 --- a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx +++ b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useCallback } from "react"; import { useTranslation } from "react-i18next"; import { Namespaces } from "../../../i18n/i18n"; import HorizontalTabs from "../../../components/common/HorizionalTabs"; @@ -131,20 +131,12 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ //const [tasksValid, setTasksValid] = useState(true); - const handleTaskValidate = (taskId: string, isValid: boolean) => { + const handleTaskValidate = useCallback((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; if (redirect) return ; diff --git a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx index b2ad3f1..da8987d 100644 --- a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx +++ b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx @@ -8,33 +8,39 @@ import { defaultsContext, } from "../useCapabilityDefaults"; import Button, { ButtonType } from "../../../../../components/common/Button"; +import ValidationErrorIcon from "../../../../../components/validationErrorIcon"; +import ErrorBlock from "../../../../../components/common/ErrorBlock"; // TODO: Replace with your real RolePicker and RaciPicker const RolePicker = (props: any) => ( -
    - - props.onChange(props.name, { id: BigInt(e.target.value) }) - } - /> +
    +
    + + props.onChange(props.name, { id: BigInt(e.target.value) }) + } + /> +
    ); const RaciPicker = (props: any) => ( -
    - +
    +
    + +
    ); @@ -97,6 +103,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC = ( + @@ -109,7 +116,16 @@ export const AssigneesOfITaskAssigneeEditor: React.FC = ( {assignees.map((assignee, index) => ( - + + -
    Role Contact RACI
    + + = ( } /> +
    + + Object.keys(fieldErrors ?? {}).some( + (key) => key === `${guid}.assignees`, + ), + ) + .join("; ")} + />
    ); }; diff --git a/src/modules/manager/workflowTemplates/components/TaskList.tsx b/src/modules/manager/workflowTemplates/components/TaskList.tsx index 218a5d9..d3d3f90 100644 --- a/src/modules/manager/workflowTemplates/components/TaskList.tsx +++ b/src/modules/manager/workflowTemplates/components/TaskList.tsx @@ -9,6 +9,7 @@ import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { sortTasksTopologically } from "./workflowGraphUtils"; import { useCapabilityDefaults, validateTask } from "./useCapabilityDefaults"; +import ValidationErrorIcon from "../../../../components/validationErrorIcon"; interface TaskListProps { tasks: TaskDefinition[]; @@ -70,11 +71,11 @@ const TaskList: React.FC = ({ <> {x.config.name as string} - {validTasksList[x.config.guid as string] === false && ( - - - - )} + { + + } )} onSelect={(item) => onSelectTask(item)} diff --git a/src/modules/manager/workflowTemplates/components/TasksEditor.tsx b/src/modules/manager/workflowTemplates/components/TasksEditor.tsx index 65f47c5..0b0be1b 100644 --- a/src/modules/manager/workflowTemplates/components/TasksEditor.tsx +++ b/src/modules/manager/workflowTemplates/components/TasksEditor.tsx @@ -27,26 +27,23 @@ export const TaskEditor: React.FC = ({ const runValidation = useCallback( ( - bubbleUp = true, taskToValidate: TaskDefinition, tasksList: TaskDefinition[], tasksMetadataList: TaskMetadata[], ) => { const errors = validateTask(taskToValidate, tasksList, tasksMetadataList); setFieldErrors(errors); - - if (bubbleUp) { - onValidate( - taskToValidate.config.guid as string, - Object.keys(errors).length === 0, - ); - } + onValidate( + taskToValidate.config.guid as string, + Object.keys(errors).length === 0, + ); }, + [onValidate], ); React.useEffect(() => { - runValidation(true, task, tasks, tasksMetadata); - }, [runValidation, task, tasks, tasksMetadata]); + runValidation(task, tasks, tasksMetadata); + }, [task.config.guid, runValidation]); const handleTaskChange = (updatedTask: TaskDefinition) => { // Update the task list @@ -54,16 +51,7 @@ export const TaskEditor: React.FC = ({ t.config.guid === updatedTask.config.guid ? updatedTask : t, ); - runValidation(true, updatedTask, updatedTasks, tasksMetadata); - // // Run validation - // const errors = validateTask(updatedTask, updatedTasks, tasksMetadata); - // setFieldErrors(errors); - - // // Bubble validity up - // onValidate( - // updatedTask.config.guid as string, - // Object.keys(errors).length === 0, - // ); + runValidation(updatedTask, updatedTasks, tasksMetadata); // Bubble updated task up onChange(updatedTask); diff --git a/src/modules/manager/workflowTemplates/components/TasksTab.tsx b/src/modules/manager/workflowTemplates/components/TasksTab.tsx index 3ab06f8..804a00a 100644 --- a/src/modules/manager/workflowTemplates/components/TasksTab.tsx +++ b/src/modules/manager/workflowTemplates/components/TasksTab.tsx @@ -66,7 +66,7 @@ const TasksTab: React.FC = ({ }; return ( -
    +