Now able to create a workflow template

This commit is contained in:
Colin Dawson 2026-02-26 20:29:56 +00:00
parent 22c80e82f2
commit 574f254dca
7 changed files with 54 additions and 61 deletions

View File

@ -38,7 +38,7 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
const form = useFormWithGuard({
loaded: false,
data: {
name: "",
workflowName: "",
domainId: {} as GeneralIdRef,
activityNameTemplate: "",
description: "",
@ -56,7 +56,10 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
// Joi schema (same pattern as original GeneralTab)
// -----------------------------
form.schema = {
name: Joi.string().required().max(450).label(t("WorkflowTemplateName")),
workflowName: Joi.string()
.required()
.max(450)
.label(t("WorkflowTemplateName")),
activityNameTemplate: Joi.string()
.required()
.max(450)
@ -80,7 +83,7 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
);
if (loadedData) {
newData.name = loadedData.name ?? "";
newData.workflowName = loadedData.workflowName ?? "";
}
} catch (ex: any) {
form.handleGeneralError(ex);
@ -112,7 +115,9 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
//await templateVersionsService.putTemplateVersion({ name });
toast.info(t("WorkflowTemplateEdited"));
} else {
//await templateVersionsService.postTemplateVersion({ name });
await templateVersionsService.postTemplateVersion(
data as CreateWorkflowTemplateVersion,
);
toast.info(t("WorkflowTemplateAdded"));
}
@ -146,7 +151,7 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
const generalTabValid = !(
errors["domainId"] ||
errors["name"] ||
errors["workflowName"] ||
errors["activityNameTemplate"] ||
errors["description"]
);

View File

@ -16,10 +16,6 @@ import ErrorBlock from "../../../../../components/common/ErrorBlock";
import RolePicker from "../../../../../components/pickers/RolePicker";
import { getCurrentUser } from "../../../../frame/services/authenticationService";
import RaciPicker from "../../../../../components/pickers/RaciPicker";
import VerdictPicker from "../../../../../components/pickers/VerdictPicker";
import { renderInput } from "../../../../../components/common/formHelpers";
import { InputType } from "../../../../../components/common/Input";
import { error } from "console";
// ---------------------------
// Domain Interfaces
@ -27,7 +23,7 @@ import { error } from "console";
export interface IApprovalTaskAssignee {
role?: GeneralIdRef | null;
contact?: GeneralIdRef | null;
user?: GeneralIdRef | null;
raci: string; // Raci enum as string
allowNoVerdict: boolean;
bypassable: boolean;
@ -60,7 +56,7 @@ export const AssigneesOfIApprovalTaskAssigneeEditor: React.FC<
const list = clone.config.assignees ?? [];
list.push({
role: null,
contact: null,
user: null,
raci: "Responsible",
allowNoVerdict: false,
bypassable: false,
@ -82,13 +78,13 @@ export const AssigneesOfIApprovalTaskAssigneeEditor: React.FC<
return (
<div>
Select assigness (you can select either a role or a contact)
Select assigness (you can select either a role or a user)
<table>
<thead>
<tr>
<th></th>
<th>Role</th>
<th>Contact</th>
<th>User</th>
<th>RACI</th>
<th>Allow No Verdict</th>
<th>Bypassable</th>
@ -106,7 +102,7 @@ export const AssigneesOfIApprovalTaskAssigneeEditor: React.FC<
<ValidationErrorIcon
visible={
!!fieldErrors?.[`${guid}.assignees[${index}].role`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].contact`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].user`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].raci`]
}
/>
@ -127,13 +123,13 @@ export const AssigneesOfIApprovalTaskAssigneeEditor: React.FC<
<td>
<UserPicker
includeLabel={false}
name="contact"
label="Contact"
value={assignee.contact}
name="user"
label="User"
value={assignee.user}
domain={domain}
error={fieldErrors?.[`${guid}.assignees[${index}].contact`]}
error={fieldErrors?.[`${guid}.assignees[${index}].user`]}
onChange={(name: string, val: GeneralIdRef | null) =>
updateAssignee(index, { ...assignee, contact: val })
updateAssignee(index, { ...assignee, user: val })
}
/>
</td>
@ -194,19 +190,18 @@ const runValidation = (
}
assignees.forEach((a, i) => {
const noContactSelected = !a.contact || a.contact?.id === BigInt(0);
const noUserSelected = !a.user || a.user?.id === BigInt(0);
const noRoleSelected = !a.role || a.role?.id === BigInt(0);
if (!noContactSelected && !noRoleSelected) {
errors[`${guid}.assignees[${i}].contact`] =
"Cannot select both a contact and a role.";
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 contact and a role.";
"Cannot select both a user and a role.";
} else {
if (!(!noContactSelected || !noRoleSelected)) {
if (noContactSelected) {
errors[`${guid}.assignees[${i}].contact`] =
"A contact must be selected.";
if (!(!noUserSelected || !noRoleSelected)) {
if (noUserSelected) {
errors[`${guid}.assignees[${i}].user`] = "A user must be selected.";
}
if (noRoleSelected) {

View File

@ -23,7 +23,7 @@ import RaciPicker from "../../../../../components/pickers/RaciPicker";
export interface ITaskAssignee {
role?: GeneralIdRef | null;
contact?: GeneralIdRef | null;
user?: GeneralIdRef | null;
raci: string; // Raci enum as string
}
@ -54,7 +54,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
const list = clone.config.assignees ?? [];
list.push({
role: null,
contact: null,
user: null,
raci: "Responsible",
});
clone.config.assignees = list;
@ -74,13 +74,13 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
return (
<div>
Select assigness (you can select either a role or a contact)
Select assigness (you can select either a role or a USER)
<table>
<thead>
<tr>
<th></th>
<th>Role</th>
<th>Contact</th>
<th>User</th>
<th>RACI</th>
<th>
<Button onClick={addAssignee} buttonType={ButtonType.secondary}>
@ -96,7 +96,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
<ValidationErrorIcon
visible={
!!fieldErrors?.[`${guid}.assignees[${index}].role`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].contact`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].user`] ||
!!fieldErrors?.[`${guid}.assignees[${index}].raci`]
}
/>
@ -117,13 +117,13 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
<td>
<UserPicker
includeLabel={false}
name="contact"
label="Contact"
value={assignee.contact}
name="user"
label="User"
value={assignee.user}
domain={domain}
error={fieldErrors?.[`${guid}.assignees[${index}].contact`]}
error={fieldErrors?.[`${guid}.assignees[${index}].user`]}
onChange={(name: string, val: GeneralIdRef | null) =>
updateAssignee(index, { ...assignee, contact: val })
updateAssignee(index, { ...assignee, user: val })
}
/>
</td>
@ -182,19 +182,18 @@ const runValidation = (
}
assignees.forEach((a, i) => {
const noContactSelected = !a.contact || a.contact?.id === BigInt(0);
const noUserSelected = !a.user || a.user?.id === BigInt(0);
const noRoleSelected = !a.role || a.role?.id === BigInt(0);
if (!noContactSelected && !noRoleSelected) {
errors[`${guid}.assignees[${i}].contact`] =
"Cannot select both a contact and a role.";
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 contact and a role.";
"Cannot select both a user and a role.";
} else {
if (!(!noContactSelected || !noRoleSelected)) {
if (noContactSelected) {
errors[`${guid}.assignees[${i}].contact`] =
"A contact must be selected.";
if (!(!noUserSelected || !noRoleSelected)) {
if (noUserSelected) {
errors[`${guid}.assignees[${i}].user`] = "A user must be selected.";
}
if (noRoleSelected) {

View File

@ -43,7 +43,7 @@ const GeneralTab: React.FC<GeneralTabProps> = ({
)}
{renderInput(
"name",
"workflowName",
t("WorkflowTemplateName"),
data,
errors,

View File

@ -17,10 +17,10 @@ const WorkflowTemplateManagerTable: React.FC<
const columns: Column<ReadWorkflowTemplate>[] = useMemo(
() => [
{
key: "name",
key: "workflowName",
label: t("Name"),
order: "asc",
link: canViewSite ? "/site/{0}" : undefined,
link: canViewSite ? "/workflowTemplates/{0}" : undefined,
},
],
[t, canViewSite],

View File

@ -15,8 +15,8 @@ export const capabilityEditorRegistry: Record<
ITask: taskCoreEditorRegistryEntry,
ITags: tagsEditorRegistryEntry,
IBudget: budgetEditorRegistryEntry,
"IAssignees<ITaskAssignee>": assigneesOfITaskAssigneeRegistryEntry,
"IAssignees<IApprovalTaskAssignee>":
"IAssignees<TaskAssignee>": assigneesOfITaskAssigneeRegistryEntry,
"IAssignees<ApprovalTaskAssignee>":
assigneesOfIApprovalTaskAssigneeRegistryEntry,
"IOutcome<ApprovalVerdict>": outcomeOfApprovalVerdictRegistryEntry,
// IFormTemplate: null, //ToDo implement this

View File

@ -33,7 +33,7 @@ export interface TaskDefinition<TConfig = Record<string, unknown>> {
}
export interface CreateWorkflowTemplateVersion extends FormData {
name: string;
workflowName: string;
domainId: GeneralIdRef;
activityNameTemplate: string;
description: string;
@ -105,15 +105,9 @@ export async function getTemplateVersion(id?: bigint, guid?: string) {
}
export async function postTemplateVersion(
name: string,
address: string,
status: string,
data: CreateWorkflowTemplateVersion,
): Promise<any> {
return await httpService.post(apiEndpoint + "/templateVersion", {
name,
address,
status,
});
return await httpService.post(apiEndpoint + "/templateVersion", data);
}
export async function putTemplateVersion(