Started working on the ability to add workflow templates

This commit is contained in:
Colin Dawson 2026-02-10 21:50:35 +00:00
parent 86ce578ce4
commit 9696e0bcce
5 changed files with 216 additions and 1 deletions

View File

@ -7,6 +7,7 @@
"AddDomain": "Add Domain",
"Address": "Address",
"AddUser": "Add User",
"AddWorkflowTemplate": "Add Workflow Template",
"Admin": "Admin",
"Allowed": "Allowed",
"AnEmailWithPasswordResetLinkHasBeenSent": "An email with a password reset link has been sent.",
@ -55,7 +56,7 @@
"e-suiteLogo": "e-suite logo",
"Edit": "Edit",
"EditDomain": "Edit Domain",
"EditUser": "Edit User",
"EditWorkflowTemplate": "Edit Workflow Template",
"EFlowAppId": "e-flow AppId",
"EFlowCategoryId": "e-flow CategoryId",
"EFlowHostname": "e-flow hostname",
@ -201,6 +202,7 @@
"ValidIssuer": "Valid Issuer",
"Version": "Version",
"VersionCannotBeNull": "Version cannot be null",
"WorkflowTemplateName": "Workflow Template Name",
"WorkflowTemplates": "Workflow Templates",
"Year": "Year",
"YourProfileSettingsHaveBeenSaved": "Your profile settings have been saved"

View File

@ -45,6 +45,7 @@ import SsoManager from "./modules/manager/ssoManager/ssoManager";
import SsoProviderDetails from "./modules/manager/ssoManager/SsoProviderDetails";
import { Namespaces } from "./i18n/i18n";
import WorkflowTemplateManager from "./modules/manager/workflowTemplates/WorkflowTemplateManager";
import WorkflowTemplateDetails from "./modules/manager/workflowTemplates/WorkflowTemplateDetails";
function GetSecureRoutes() {
const { t } = useTranslation<typeof Namespaces.Common>();
@ -371,6 +372,14 @@ function GetSecureRoutes() {
</Mainframe>
}
/>
<Route
path="/workflowTemplates/add"
element={
<Mainframe title={t("WorkflowTemplates")}>
<WorkflowTemplateDetails editMode={false} />
</Mainframe>
}
/>
<Route
path="/workflowTemplates"
element={

View File

@ -0,0 +1,151 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../i18n/i18n";
import HorizontalTabs from "../../../components/common/HorizionalTabs";
import Tab from "../../../components/common/Tab";
import GeneralTab from "./components/GeneralTab";
import { Navigate, useParams } from "react-router-dom";
import templateVersionsService, {
CreateWorkflowTemplateVersion,
} from "./services/WorkflowTemplateService";
import Loading from "../../../components/common/Loading";
import { toast } from "react-toastify";
import Joi from "joi";
import {
renderButton,
renderError,
} from "../../../components/common/formHelpers";
import { useForm } from "../../../components/common/useForm";
const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
editMode,
}) => {
const { t } = useTranslation<typeof Namespaces.Common>();
const { userId } = useParams<{ userId: string }>();
// useForm promoted to the parent
const form = useForm({
loaded: false,
data: {
name: "",
} as CreateWorkflowTemplateVersion,
errors: {},
redirect: "",
});
const heading = editMode
? t("EditWorkflowTemplate")
: t("AddWorkflowTemplate");
// -----------------------------
// Joi schema (same pattern as original GeneralTab)
// -----------------------------
form.schema = {
name: Joi.string().required().max(450).label(t("WorkflowTemplateName")),
};
// -----------------------------
// Load existing template (edit mode)
// -----------------------------
useEffect(() => {
const load = async () => {
const newData: CreateWorkflowTemplateVersion = { ...form.state.data };
if (editMode && userId) {
try {
const loadedData = await templateVersionsService.getTemplateVersion(
BigInt(userId),
);
if (loadedData) {
newData.name = loadedData.name ?? "";
}
} catch (ex: any) {
form.handleGeneralError(ex);
}
}
form.setState({ ...form.state, loaded: true, data: newData });
};
load();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editMode, userId]);
// -----------------------------
// Save handler (called by useForm.handleSubmit)
// -----------------------------
const doSubmit = async (buttonName: string) => {
try {
const { name } = form.state.data;
if (editMode) {
await templateVersionsService.putTemplateVersion({ name });
toast.info(t("WorkflowTemplateEdited"));
} else {
await templateVersionsService.postTemplateVersion({ name });
toast.info(t("WorkflowTemplateAdded"));
}
if (buttonName === "save") {
form.setState({ ...form.state, redirect: "/workflowTemplates" });
}
} catch (ex: any) {
form.handleGeneralError(ex);
}
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
form.handleSubmit(e, doSubmit);
};
const { loaded, redirect, errors, data } = form.state;
if (redirect) return <Navigate to={redirect} />;
// -----------------------------
// Tabs
// -----------------------------
const tabs = [
<Tab key="general" id="general" label={t("General")}>
<GeneralTab
data={data}
errors={errors}
isEditMode={editMode}
onFieldChange={form.handleChange}
/>
</Tab>,
<Tab key="tasks" id="tasks" label={t("Tasks")}>
<div>Tasks editor coming soon</div>
</Tab>,
<Tab key="fields" id="fields" label={t("Fields")}>
<div>Fields editor coming soon</div>
</Tab>,
<Tab key="visualFlow" id="visualFlow" label={t("Visual Flow")}>
<div>Visual Flow editor coming soon</div>
</Tab>,
];
return (
<Loading loaded={loaded}>
<div>
<form onSubmit={handleSubmit}>
<h1>{heading}</h1>
<div style={{ marginBottom: "1rem" }}>
{editMode && renderButton(t("Save"), errors, "apply")}
{renderButton(t("SaveAndClose"), errors, "save")}
</div>
{renderError("_general", errors)}
<HorizontalTabs hashSegment={0}>{tabs}</HorizontalTabs>
</form>
</div>
</Loading>
);
};
export default WorkflowTemplateDetails;

View File

@ -0,0 +1,49 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../../i18n/i18n";
import { InputType } from "../../../../components/common/Input";
import {
renderInput,
renderError,
} from "../../../../components/common/formHelpers";
import { WorkflowTemplateDraft } from "../WorkflowTemplateDetails";
interface GeneralTabProps {
data: WorkflowTemplateDraft;
errors: Record<string, string>;
isEditMode: boolean;
onFieldChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
const GeneralTab: React.FC<GeneralTabProps> = ({
data,
errors,
onFieldChange,
}) => {
const { t } = useTranslation<typeof Namespaces.Common>();
const labelName = t("WorkflowTemplateName");
return (
<div>
{renderError("_general", errors)}
{renderInput(
"name",
labelName,
data,
errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
onFieldChange,
)}
</div>
);
};
export default GeneralTab;

View File

@ -25,6 +25,10 @@ export type ReadWorkflowTemplateVersion = {
status: string;
};
export type CreateWorkflowTemplateVersion = {
name: string;
};
export async function getTemplates(
page: number,
pageSize: number,