Improvements to the validation engine
This commit is contained in:
parent
4b3f65ec20
commit
226d402578
@ -89,6 +89,8 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
onChange(clone);
|
||||
}
|
||||
|
||||
const guid = task.config.guid as string;
|
||||
|
||||
return (
|
||||
<div>
|
||||
Select assigness (you can select either a role or a contact)
|
||||
@ -113,7 +115,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
name="role"
|
||||
label="Role"
|
||||
value={assignee.role}
|
||||
error={fieldErrors?.[`assignees[${index}].role`]}
|
||||
error={fieldErrors?.[`${guid}.assignees[${index}].role`]}
|
||||
onChange={(name: string, val: GeneralIdRef | null) =>
|
||||
updateAssignee(index, { ...assignee, role: val })
|
||||
}
|
||||
@ -125,7 +127,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
name="contact"
|
||||
label="Contact"
|
||||
value={assignee.contact}
|
||||
error={fieldErrors?.[`assignees[${index}].contact`]}
|
||||
error={fieldErrors?.[`${guid}.assignees[${index}].contact`]}
|
||||
onChange={(name: string, val: GeneralIdRef | null) =>
|
||||
updateAssignee(index, { ...assignee, contact: val })
|
||||
}
|
||||
@ -136,7 +138,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
name="raci"
|
||||
label="RACI"
|
||||
value={assignee.raci}
|
||||
error={fieldErrors?.[`assignees[${index}].raci`]}
|
||||
error={fieldErrors?.[`${guid}.assignees[${index}].raci`]}
|
||||
onChange={(name: string, val: string) =>
|
||||
updateAssignee(index, { ...assignee, raci: val })
|
||||
}
|
||||
@ -167,22 +169,30 @@ const runValidation = (
|
||||
tasks: TaskDefinition[],
|
||||
): Record<string, string> => {
|
||||
const errors: Record<string, string> = {};
|
||||
const guid = task.config.guid as string;
|
||||
const assignees = task.config.assignees as ITaskAssignee[] | undefined;
|
||||
|
||||
if (!assignees || assignees.length === 0) {
|
||||
errors["assignees"] = "At least one assignee is required.";
|
||||
errors[`${guid}.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.";
|
||||
const noContactSelected = !a.contact || a.contact?.id === BigInt(0);
|
||||
|
||||
if (noContactSelected) {
|
||||
errors[`${guid}.assignees[${i}].contact`] = "A contact must be selected.";
|
||||
}
|
||||
|
||||
if (!a.role && noContactSelected) {
|
||||
errors[`${guid}.assignees[${i}].role`] =
|
||||
"Either role or contact must be selected.";
|
||||
errors[`${guid}.assignees[${i}].contact`] =
|
||||
"Either role or contact must be selected.";
|
||||
}
|
||||
|
||||
if (!a.raci) {
|
||||
//errors[`assignees[${i}].raci`] = "RACI is required.";
|
||||
errors[`assignees`] = "RACI is required.";
|
||||
errors[`${guid}.assignees[${i}].raci`] = "RACI is required.";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { SelectableList } from "../../../../components/common/SelectableList";
|
||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { sortTasksTopologically } from "./workflowGraphUtils";
|
||||
import { useCapabilityDefaults } from "./useCapabilityDefaults";
|
||||
import { useCapabilityDefaults, validateTask } from "./useCapabilityDefaults";
|
||||
|
||||
interface TaskListProps {
|
||||
tasks: TaskDefinition[];
|
||||
@ -17,6 +17,7 @@ interface TaskListProps {
|
||||
onChange: (tasks: TaskDefinition[]) => void;
|
||||
selectedTask: TaskDefinition | null;
|
||||
onSelectTask: (task: TaskDefinition | null) => void;
|
||||
onValidate: (taskId: string, isValid: boolean) => void;
|
||||
}
|
||||
|
||||
const TaskList: React.FC<TaskListProps> = ({
|
||||
@ -26,6 +27,7 @@ const TaskList: React.FC<TaskListProps> = ({
|
||||
onChange,
|
||||
selectedTask,
|
||||
onSelectTask,
|
||||
onValidate,
|
||||
}) => {
|
||||
const runDefaults = useCapabilityDefaults(tasksMetadata);
|
||||
|
||||
@ -43,7 +45,12 @@ const TaskList: React.FC<TaskListProps> = ({
|
||||
runDefaults(capability, newTask, tasks);
|
||||
});
|
||||
|
||||
onChange([...tasks, newTask]);
|
||||
const updatedTasks = [...tasks, newTask];
|
||||
const errors = validateTask(newTask, updatedTasks, tasksMetadata);
|
||||
|
||||
onValidate(newTask.config.guid as string, Object.keys(errors).length === 0);
|
||||
|
||||
onChange(updatedTasks);
|
||||
onSelectTask(newTask);
|
||||
};
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
TaskMetadata,
|
||||
} from "../services/WorkflowTemplateService";
|
||||
import { capabilityEditorRegistry } from "./capabilityEditorRegistry";
|
||||
import { validateTask } from "./useCapabilityDefaults";
|
||||
|
||||
interface TaskEditorProps {
|
||||
task: TaskDefinition;
|
||||
@ -24,24 +25,6 @@ export const TaskEditor: React.FC<TaskEditorProps> = ({
|
||||
{},
|
||||
);
|
||||
|
||||
const validateTask = (task: TaskDefinition, tasks: TaskDefinition[]) => {
|
||||
const taskMeta = tasksMetadata.find((t) => t.taskType === task.type);
|
||||
|
||||
const errors: Record<string, string> = {};
|
||||
|
||||
for (const capability of taskMeta?.capabilities ?? []) {
|
||||
const entry = capabilityEditorRegistry[capability];
|
||||
|
||||
if (!entry?.ValidationRunner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validationErrors = entry.ValidationRunner(task, tasks);
|
||||
Object.assign(errors, validationErrors);
|
||||
}
|
||||
return errors;
|
||||
};
|
||||
|
||||
const handleTaskChange = (updatedTask: TaskDefinition) => {
|
||||
// Update the task list
|
||||
const updatedTasks = tasks.map((t) =>
|
||||
@ -49,7 +32,7 @@ export const TaskEditor: React.FC<TaskEditorProps> = ({
|
||||
);
|
||||
|
||||
// Run validation
|
||||
const errors = validateTask(updatedTask, updatedTasks);
|
||||
const errors = validateTask(updatedTask, updatedTasks, tasksMetadata);
|
||||
setFieldErrors(errors);
|
||||
|
||||
// Bubble validity up
|
||||
|
||||
@ -75,6 +75,7 @@ const TasksTab: React.FC<TasksTabProps> = ({
|
||||
onChange={handleTasksChange}
|
||||
selectedTask={selectedTask}
|
||||
onSelectTask={setSelectedTask}
|
||||
onValidate={onValidate}
|
||||
/>
|
||||
</div>
|
||||
{selectedTask && (
|
||||
|
||||
@ -35,9 +35,32 @@ export interface capabilityEditorRegistryEntry {
|
||||
ValidationRunner?: (
|
||||
task: TaskDefinition,
|
||||
tasks: TaskDefinition[],
|
||||
tasksMetadata: TaskMetadata[],
|
||||
) => Record<string, string>;
|
||||
}
|
||||
|
||||
export function validateTask(
|
||||
task: TaskDefinition,
|
||||
tasks: TaskDefinition[],
|
||||
tasksMetadata: TaskMetadata[],
|
||||
): Record<string, string> {
|
||||
const taskMeta = tasksMetadata.find((t) => t.taskType === task.type);
|
||||
|
||||
const errors: Record<string, string> = {};
|
||||
|
||||
for (const capability of taskMeta?.capabilities ?? []) {
|
||||
const entry = capabilityEditorRegistry[capability];
|
||||
|
||||
if (!entry?.ValidationRunner) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const validationErrors = entry.ValidationRunner(task, tasks);
|
||||
Object.assign(errors, validationErrors);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
export function useCapabilityDefaults(taskMetadata: TaskMetadata[]) {
|
||||
const { t: tTaskType } = useTranslation(Namespaces.TaskTypes);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user