From 4c8ff7378b69cdf7b8c1a8cf89f4ec15999bd7ef Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Mon, 16 Mar 2026 14:24:27 +0000 Subject: [PATCH] Started simplifying the Assignee capability editor, want to make it generic and data driven --- ...AssigneesOfIApprovalTaskAssigneeEditor.tsx | 244 ------------------ .../AssigneesOfITaskAssigneeEditor.tsx | 2 +- .../components/capabilityEditorRegistry.ts | 2 - 3 files changed, 1 insertion(+), 247 deletions(-) delete mode 100644 src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfIApprovalTaskAssigneeEditor.tsx diff --git a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfIApprovalTaskAssigneeEditor.tsx b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfIApprovalTaskAssigneeEditor.tsx deleted file mode 100644 index d4f0889..0000000 --- a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfIApprovalTaskAssigneeEditor.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import React from "react"; -import UserPicker from "../../../../../components/pickers/UserPicker"; -import { - GeneralIdRef, - MakeGeneralIdRef, -} from "../../../../../utils/GeneralIdRef"; -import { TaskDefinition } from "../../services/WorkflowTemplateService"; -import { - CapabilityEditorProps, - capabilityEditorRegistryEntry, - defaultsContext, -} from "../useCapabilityDefaults"; -import Button, { ButtonType } from "../../../../../components/common/Button"; -import ValidationErrorIcon from "../../../../../components/validationErrorIcon"; -import ErrorBlock from "../../../../../components/common/ErrorBlock"; -import RolePicker from "../../../../../components/pickers/RolePicker"; -import { getCurrentUser } from "../../../../frame/services/authenticationService"; -import RaciPicker from "../../../../../components/pickers/RaciPicker"; - -// --------------------------- -// Domain Interfaces -// --------------------------- - -export interface IApprovalTaskAssignee { - role?: GeneralIdRef | null; - user?: GeneralIdRef | null; - raci: string; // Raci enum as string - allowNoVerdict: boolean; - bypassable: boolean; -} - -export interface IApprovalTaskAssigneesCapability { - assignees: IApprovalTaskAssignee[]; -} - -// --------------------------- -// Editor Component -// --------------------------- - -export const AssigneesOfIApprovalTaskAssigneeEditor: React.FC< - CapabilityEditorProps -> = (props) => { - const { task, onChange, fieldErrors } = props; - const assignees = (task.config.assignees ?? []) as IApprovalTaskAssignee[]; - - function updateAssignee(index: number, updated: IApprovalTaskAssignee) { - const clone = structuredClone(task); - const list = clone.config.assignees ?? []; - list[index] = updated; - clone.config.assignees = list; - onChange(clone); - } - - function addAssignee() { - const clone = structuredClone(task); - const list = clone.config.assignees ?? []; - list.push({ - role: null, - user: null, - raci: "Responsible", - allowNoVerdict: false, - bypassable: false, - }); - clone.config.assignees = list; - onChange(clone); - } - - function removeAssignee(index: number) { - const clone = structuredClone(task); - const list = clone.config.assignees ?? []; - clone.config.assignees = list.filter((_, i) => i !== index); - onChange(clone); - } - - const guid = task.config.guid as string; - - const domain = MakeGeneralIdRef(getCurrentUser()?.domainid); - - return ( -
- Select assigness (you can select either a role or a user) - - - - - - - - - - - - - - {assignees.map((assignee, index) => ( - - - - - - - - - - ))} - -
RoleUserRACIAllow No VerdictBypassable - -
- - - - updateAssignee(index, { ...assignee, role: val }) - } - /> - - - updateAssignee(index, { ...assignee, user: val }) - } - /> - - - updateAssignee(index, { ...assignee, raci: val }) - } - /> - allowNoVerdict goes here.Bypass goes here. - -
- - Object.keys(fieldErrors ?? {}).some( - (key) => key === `${guid}.assignees`, - ), - ) - .join("; ")} - /> -
- ); -}; - -// --------------------------- -// Validation -// --------------------------- - -const runValidation = ( - task: TaskDefinition, - tasks: TaskDefinition[], -): Promise> => { - const errors: Record = {}; - const guid = task.config.guid as string; - const assignees = task.config.assignees as ITaskAssignee[] | undefined; - - if (!assignees || assignees.length === 0) { - errors[`${guid}.assignees`] = "At least one assignee is required."; - return Promise.resolve(errors); - } - - assignees.forEach((a, i) => { - const noUserSelected = !a.user || a.user?.id === BigInt(0); - const noRoleSelected = !a.role || a.role?.id === BigInt(0); - - if (!noUserSelected && !noRoleSelected) { - errors[`${guid}.assignees[${i}].user`] = - "Cannot select both a user and a role."; - errors[`${guid}.assignees[${i}].role`] = - "Cannot select both a user and a role."; - } else { - if (!(!noUserSelected || !noRoleSelected)) { - if (noUserSelected) { - errors[`${guid}.assignees[${i}].user`] = "A user must be selected."; - } - - if (noRoleSelected) { - errors[`${guid}.assignees[${i}].role`] = "A role must be selected."; - } - } - } - - if (!a.raci) { - errors[`${guid}.assignees[${i}].raci`] = "RACI is required."; - } - }); - - return errors; -}; - -// --------------------------- -// Defaults Assignment -// --------------------------- - -export function defaultsAssignment( - task: TaskDefinition, - tasks: TaskDefinition[], - ctx: defaultsContext, -) { - task.config.assignees = [{ raci: "Responsible" } as ITaskAssignee]; - //task.config.assignees = []; -} - -// --------------------------- -// Registry Entry -// --------------------------- - -export const assigneesOfIApprovalTaskAssigneeRegistryEntry: capabilityEditorRegistryEntry = - { - match: (cap) => cap === "IAssignees", - Editor: AssigneesOfIApprovalTaskAssigneeEditor, - DefaultsAssignment: defaultsAssignment, - ValidationRunner: runValidation, - }; diff --git a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx index 0f20cc2..92e49a7 100644 --- a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx +++ b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx @@ -229,7 +229,7 @@ export function defaultsAssignment( export const assigneesOfITaskAssigneeRegistryEntry: capabilityEditorRegistryEntry = { - match: (cap) => cap === "IAssignees", + match: (cap) => cap.startsWith("IAssignees<"), Editor: AssigneesOfITaskAssigneeEditor, DefaultsAssignment: defaultsAssignment, ValidationRunner: runValidation, diff --git a/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts b/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts index 870d1e7..f84fc2a 100644 --- a/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts +++ b/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts @@ -6,7 +6,6 @@ import { outcomeOfApprovalVerdictRegistryEntry } from "./CapabilityEditors/Outco import { budgetEditorRegistryEntry } from "./CapabilityEditors/BudgetEditorRegistryEntry"; import { bypassableEditorRegistryEntry } from "./CapabilityEditors/BypassableEditor"; import { createStageEditorRegistryEntry } from "./CapabilityEditors/StageEditor"; -import { assigneesOfIApprovalTaskAssigneeRegistryEntry } from "./CapabilityEditors/AssigneesOfIApprovalTaskAssigneeEditor"; type CapabilityEditorRegistry = Array; @@ -15,7 +14,6 @@ export const capabilityEditorRegistry: CapabilityEditorRegistry = [ tagsEditorRegistryEntry, budgetEditorRegistryEntry, assigneesOfITaskAssigneeRegistryEntry, - assigneesOfIApprovalTaskAssigneeRegistryEntry, outcomeOfApprovalVerdictRegistryEntry, // IFormTemplate: null, //ToDo implement this bypassableEditorRegistryEntry,