Work towards implementing a workflow template editor

This commit is contained in:
Colin Dawson 2026-02-09 22:05:29 +00:00
parent c30d0c74af
commit 86ce578ce4
6 changed files with 360 additions and 1 deletions

View File

@ -201,6 +201,7 @@
"ValidIssuer": "Valid Issuer",
"Version": "Version",
"VersionCannotBeNull": "Version cannot be null",
"WorkflowTemplates": "Workflow Templates",
"Year": "Year",
"YourProfileSettingsHaveBeenSaved": "Your profile settings have been saved"
}

View File

@ -44,6 +44,7 @@ import ErrorLogs from "./modules/errorLogs/errorLogs";
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";
function GetSecureRoutes() {
const { t } = useTranslation<typeof Namespaces.Common>();
@ -370,6 +371,14 @@ function GetSecureRoutes() {
</Mainframe>
}
/>
<Route
path="/workflowTemplates"
element={
<Mainframe title={t("WorkflowTemplates")}>
<WorkflowTemplateManager />
</Mainframe>
}
/>
<Route
path="/profile"
element={

View File

@ -44,6 +44,9 @@ const LeftMenu: React.FC = () => {
const viewField = authentication.hasAccess("ViewField");
const viewSequence = authentication.hasAccess("ViewSequence");
const viewSsoManager = authentication.hasAccess("ViewSsoProviders");
const ViewWorkflowTemplates = authentication.hasAccess(
"ViewWorkflowTemplates",
);
const viewAdmin =
viewUser ||
@ -51,7 +54,8 @@ const LeftMenu: React.FC = () => {
viewGlossary ||
viewFormTemplate ||
viewField ||
viewSequence;
viewSequence ||
ViewWorkflowTemplates;
const viewAuditLog = authentication.hasAccess("ViewAuditLog");
const viewBlockedIPAddresses = authentication.hasAccess(
@ -101,6 +105,12 @@ const LeftMenu: React.FC = () => {
{viewSsoManager && (
<LeftMenuItem to="/ssoManager" label={t("SsoManager")} />
)}
{ViewWorkflowTemplates && (
<LeftMenuItem
to="/workflowTemplates"
label={t("WorkflowTemplates")}
/>
)}
</LeftMenuSubMenu>
)}

View File

@ -0,0 +1,142 @@
import { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../i18n/i18n";
import Button, { ButtonType } from "../../../components/common/Button";
import Column from "../../../components/common/columns";
import Permission from "../../../components/common/Permission";
import { Paginated } from "../../../services/Paginated";
import Loading from "../../../components/common/Loading";
import workflowTemplatesService, {
ReadWorkflowTemplate,
} from "./services/WorkflowTemplateService";
import WorkflowTemplateManagerTable from "./components/WorkflowTemplateTable";
const WotkflowTemplateManager: React.FC = () => {
const { t } = useTranslation<typeof Namespaces.Common>();
const [loaded, setLoaded] = useState(false);
const [pagedData, setPagedData] = useState<Paginated<ReadWorkflowTemplate>>({
page: 1,
pageSize: 10,
count: 0,
totalPages: 1,
data: [],
});
const [sortColumn, setSortColumn] = useState<Column<ReadWorkflowTemplate>>({
key: "name",
label: t("Name"),
order: "asc",
});
const [filters, setFilters] = useState<Map<string, string>>(
new Map<string, string>(),
);
const changePage = useCallback(
async (page: number, pageSize: number) => {
const data = await workflowTemplatesService.getTemplates(
page,
pageSize,
sortColumn.key,
sortColumn.order === "asc",
filters,
);
if (data) {
setLoaded(true);
setPagedData(data);
} else {
setLoaded(false);
}
},
[filters, sortColumn.key, sortColumn.order],
);
const onSort = async (newSortColumn: Column<ReadWorkflowTemplate>) => {
const { page, pageSize } = pagedData;
const data = await workflowTemplatesService.getTemplates(
page,
pageSize,
newSortColumn.key,
newSortColumn.order === "asc",
filters,
);
if (data) {
setLoaded(true);
setPagedData(data);
setSortColumn(newSortColumn);
} else {
setLoaded(false);
}
};
const onSearch = async (name: string, value: string) => {
const { page, pageSize } = pagedData;
const newFilters = new Map(filters);
newFilters.set(name, value);
const data = await workflowTemplatesService.getTemplates(
page,
pageSize,
sortColumn.key,
sortColumn.order === "asc",
newFilters,
);
if (data) {
setLoaded(true);
setFilters(newFilters);
setPagedData(data);
} else {
setLoaded(false);
}
};
const onDelete = async (item?: ReadWorkflowTemplate) => {
const response = await workflowTemplatesService.deleteTemplateVersion(
item?.id,
item?.guid,
);
if (response) {
await changePage(pagedData.page, pagedData.pageSize);
}
};
useEffect(() => {
const loadInitial = async () => {
const data = await workflowTemplatesService.getTemplates(
1,
10,
"name",
true,
filters,
);
if (data) {
setLoaded(true);
setPagedData(data);
} else {
setLoaded(false);
}
};
void loadInitial();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
<Loading loaded={loaded}>
<Permission privilegeKey="AddOrganisation">
<Button buttonType={ButtonType.primary} to="add">
{t("Add")}
</Button>
</Permission>
<hr />
<WorkflowTemplateManagerTable
data={pagedData}
sortColumn={sortColumn}
onChangePage={changePage}
onSort={onSort}
onSearch={onSearch}
onDelete={onDelete}
/>
</Loading>
);
};
export default WotkflowTemplateManager;

View File

@ -0,0 +1,66 @@
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Namespaces } from "../../../../i18n/i18n";
import Column from "../../../../components/common/columns";
import Table, {
PublishedTableProps,
} from "../../../../components/common/Table";
import authentication from "../../../frame/services/authenticationService";
import { ReadWorkflowTemplate } from "../services/WorkflowTemplateService";
const WorkflowTemplateManagerTable: React.FC<
PublishedTableProps<ReadWorkflowTemplate>
> = ({ data, sortColumn, onChangePage, onSearch, onDelete, onSort }) => {
const { t } = useTranslation<typeof Namespaces.Common>();
const canViewSite = authentication.hasAccess("ViewSite");
const columns: Column<ReadWorkflowTemplate>[] = useMemo(
() => [
{
key: "name",
label: t("Name"),
order: "asc",
link: canViewSite ? "/site/{0}" : undefined,
},
],
[t, canViewSite],
);
const raiseSort = (sortCol: Column<ReadWorkflowTemplate>) => {
if (onSort !== undefined) onSort(sortCol);
};
const handleAuditParams = (item: any) => {
return {
entityName: "e_suite.Database.Core.Tables.Printer.Organisation",
primaryKey: `{"Id":${item.id}}`,
};
};
const editPath = authentication.hasAccess("EditOrganisation")
? "{0}"
: undefined;
const doDelete = authentication.hasAccess("DeleteOrganisation")
? onDelete
: undefined;
const showAudit = authentication.hasAccess("ViewAuditLog")
? handleAuditParams
: undefined;
return (
<Table
data={data}
keyName="id"
columns={columns}
sortColumn={sortColumn}
editPath={editPath}
onSort={raiseSort}
onChangePage={onChangePage}
onSearch={onSearch}
onDelete={doDelete}
onAuditParams={showAudit}
/>
);
};
export default WorkflowTemplateManagerTable;

View File

@ -0,0 +1,131 @@
import httpService from "../../../../services/httpService";
import { Paginated } from "../../../../services/Paginated";
import {
GeneralIdRef,
MakeGeneralIdRef,
MakeGeneralIdRefParams,
} from "../../../../utils/GeneralIdRef";
import MapToJson from "../../../../utils/MapToJson";
const apiEndpoint = "/WorkflowTemplate";
export type ReadWorkflowTemplate = {
id: bigint;
guid?: string;
name: string;
deleted: boolean;
version: bigint;
};
export type ReadWorkflowTemplateVersion = {
id: bigint;
guid?: string;
name: string;
address: string;
status: string;
};
export async function getTemplates(
page: number,
pageSize: number,
sortKey: string,
sortAscending: boolean,
filters?: Map<string, string>,
): Promise<Paginated<ReadWorkflowTemplate>> {
const filterString = MapToJson(filters);
const response = await httpService.get<Paginated<ReadWorkflowTemplate>>(
apiEndpoint + "/templates",
{
params: {
page: page,
pageSize: pageSize,
sortKey: sortKey,
sortAscending: sortAscending,
filters: filterString,
},
},
);
return response?.data;
}
export async function getTemplateVersions(
page: number,
pageSize: number,
sortKey: string,
sortAscending: boolean,
filters?: Map<string, string>,
): Promise<Paginated<ReadWorkflowTemplateVersion>> {
const filterString = MapToJson(filters);
const response = await httpService.get<
Paginated<ReadWorkflowTemplateVersion>
>(apiEndpoint + "/templateVersions", {
params: {
page: page,
pageSize: pageSize,
sortKey: sortKey,
sortAscending: sortAscending,
filters: filterString,
},
});
return response?.data;
}
export async function getTemplateVersion(id?: bigint, guid?: string) {
const params = MakeGeneralIdRefParams(id, guid);
const response = await httpService.get<ReadWorkflowTemplateVersion>(
apiEndpoint + "/templateVersion?" + params,
);
return response?.data;
}
export async function postTemplateVersion(
name: string,
address: string,
status: string,
): Promise<any> {
return await httpService.post(apiEndpoint + "/templateVersion", {
name,
address,
status,
});
}
export async function putTemplateVersion(
id: GeneralIdRef,
name: string,
address: string,
status: string,
): Promise<any> {
return await httpService.put(apiEndpoint + "/templateVersion", {
GeneralIdRef: id,
name,
address,
status,
});
}
export async function deleteTemplateVersion(
id?: bigint,
guid?: string,
): Promise<any> {
const generalIdRef = MakeGeneralIdRef(id, guid);
return await httpService.delete(apiEndpoint + "/templateVersion", {
data: generalIdRef,
});
}
const templateVersionsService = {
getTemplates,
getTemplateVersions,
getTemplateVersion,
postTemplateVersion,
putTemplateVersion,
deleteTemplateVersion,
};
export default templateVersionsService;