diff --git a/src/components/pickers/UserPicker.tsx b/src/components/pickers/UserPicker.tsx index 216ac18..3dde886 100644 --- a/src/components/pickers/UserPicker.tsx +++ b/src/components/pickers/UserPicker.tsx @@ -11,6 +11,7 @@ interface UserPickerProps { value: any; domain?: GeneralIdRef; onChange?: (name: string, value: GeneralIdRef) => void; + includeLabel?: boolean; } export default function UserPicker({ @@ -20,6 +21,7 @@ export default function UserPicker({ value, domain, onChange, + includeLabel = true, }: UserPickerProps) { const [options, setOptions] = useState(undefined); @@ -59,6 +61,7 @@ export default function UserPicker({ options={options} includeBlankFirstEntry={true} onChange={handleChange} + includeLabel={includeLabel} /> ); } diff --git a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx index 5fc6f3b..7a26b71 100644 --- a/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx +++ b/src/modules/manager/workflowTemplates/WorkflowTemplateDetails.tsx @@ -104,7 +104,11 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({ // ----------------------------- const doSubmit = async (buttonName: string) => { try { - //const { name } = form.state.data; + const { data } = form.state; + + var json = JSON.stringify(data); + + console.log("Submitting workflow template:", json); if (editMode) { //await templateVersionsService.putTemplateVersion({ name }); diff --git a/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx new file mode 100644 index 0000000..b0aabc9 --- /dev/null +++ b/src/modules/manager/workflowTemplates/components/CapabilityEditors/AssigneesOfITaskAssigneeEditor.tsx @@ -0,0 +1,214 @@ +import React from "react"; +import UserPicker from "../../../../../components/pickers/UserPicker"; +import { GeneralIdRef } from "../../../../../utils/GeneralIdRef"; +import { TaskDefinition } from "../../services/WorkflowTemplateService"; +import { + CapabilityEditorProps, + capabilityEditorRegistryEntry, + defaultsContext, +} from "../useCapabilityDefaults"; +import Button, { ButtonType } from "../../../../../components/common/Button"; + +// TODO: Replace with your real RolePicker and RaciPicker +const RolePicker = (props: any) => ( +
+ + props.onChange(props.name, { id: BigInt(e.target.value) }) + } + /> +
+); + +const RaciPicker = (props: any) => ( +
+ +
+); + +// --------------------------- +// Domain Interfaces +// --------------------------- + +export interface ITaskAssignee { + role?: GeneralIdRef | null; + contact?: GeneralIdRef | null; + raci: string; // Raci enum as string +} + +export interface IAssigneesCapability { + assignees: ITaskAssignee[]; +} + +// --------------------------- +// Editor Component +// --------------------------- + +export const AssigneesOfITaskAssigneeEditor: React.FC = ( + props, +) => { + const { task, onChange, fieldErrors } = props; + const assignees = (task.config.assignees ?? []) as ITaskAssignee[]; + + function updateAssignee(index: number, updated: ITaskAssignee) { + 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, + contact: null, + raci: "Responsible", + }); + 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); + } + + return ( +
+ Select assigness (you can select either a role or a contact) + + + + + + + + + + + {assignees.map((assignee, index) => ( + + + + + + + ))} + +
RoleContactRACI + +
+ + updateAssignee(index, { ...assignee, role: val }) + } + /> + + + updateAssignee(index, { ...assignee, contact: val }) + } + /> + + + updateAssignee(index, { ...assignee, raci: val }) + } + /> + + +
+
+ ); +}; + +// --------------------------- +// Validation +// --------------------------- + +const runValidation = ( + task: TaskDefinition, + tasks: TaskDefinition[], +): Record => { + const errors: Record = {}; + const assignees = task.config.assignees as ITaskAssignee[] | undefined; + + if (!assignees || assignees.length === 0) { + errors["assignees"] = "At least one assignee is required."; + return errors; + } + + assignees.forEach((a, i) => { + if (!a.role && !a.contact) { + //errors[`assignees[${i}]`] = "Either role or contact must be selected."; + errors[`assignees`] = "Either role or contact must be selected."; + } + + if (!a.raci) { + //errors[`assignees[${i}].raci`] = "RACI is required."; + errors[`assignees`] = "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 AssigneesOfITaskAssigneeRegistryEntry: capabilityEditorRegistryEntry = + { + 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 f6889f5..2d2b7e6 100644 --- a/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts +++ b/src/modules/manager/workflowTemplates/components/capabilityEditorRegistry.ts @@ -1,4 +1,5 @@ import { tagsEditorRegistryEntry } from "./CapabilityEditors/TagsEditor"; +import { AssigneesOfITaskAssigneeRegistryEntry } from "./CapabilityEditors/AssigneesOfITaskAssigneeEditor"; import { taskCoreEditorRegistryEntry } from "./CapabilityEditors/TaskCoreEditor"; import { capabilityEditorRegistryEntry } from "./useCapabilityDefaults"; @@ -8,4 +9,5 @@ export const capabilityEditorRegistry: Record< > = { ITask: taskCoreEditorRegistryEntry, ITags: tagsEditorRegistryEntry, + "IAssignees": AssigneesOfITaskAssigneeRegistryEntry, };