244 lines
7.1 KiB
TypeScript
244 lines
7.1 KiB
TypeScript
import React, { useEffect, useState, useCallback } 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,
|
|
ReadWorkflowTemplateVersion,
|
|
TaskDefinition,
|
|
} from "./services/WorkflowTemplateService";
|
|
import { toast } from "react-toastify";
|
|
import Joi from "joi";
|
|
import {
|
|
renderButton,
|
|
renderError,
|
|
} from "../../../components/common/formHelpers";
|
|
import ErrorBlock from "../../../components/common/ErrorBlock";
|
|
import authentication from "../../frame/services/authenticationService";
|
|
import { GeneralIdRef, MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
|
|
import TasksTab from "./components/TasksTab";
|
|
import { useFormWithGuard } from "../../../components/common/useFormRouter";
|
|
import VisualiserTab from "./components/VisualisetTab";
|
|
|
|
const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
|
|
editMode,
|
|
}) => {
|
|
const { t } = useTranslation(Namespaces.Common);
|
|
const { workflowTemplateId } = useParams<{ workflowTemplateId: string }>();
|
|
const [activeTab, setActiveTab] = React.useState("general");
|
|
|
|
const [taskValidation, setTaskValidation] = useState<Record<string, boolean>>(
|
|
{},
|
|
);
|
|
|
|
// useForm promoted to the parent
|
|
const form = useFormWithGuard({
|
|
loaded: false,
|
|
data: {
|
|
workflowName: "",
|
|
domainId: {} as GeneralIdRef,
|
|
activityNameTemplate: "",
|
|
description: "",
|
|
tasks: [] as TaskDefinition[],
|
|
} as CreateWorkflowTemplateVersion | ReadWorkflowTemplateVersion,
|
|
errors: {},
|
|
redirect: "",
|
|
});
|
|
|
|
const heading = editMode
|
|
? t("EditWorkflowTemplate")
|
|
: t("AddWorkflowTemplate");
|
|
|
|
// -----------------------------
|
|
// Joi schema (same pattern as original GeneralTab)
|
|
// -----------------------------
|
|
form.schema = {
|
|
workflowName: Joi.string()
|
|
.required()
|
|
.max(450)
|
|
.label(t("WorkflowTemplateName")),
|
|
activityNameTemplate: Joi.string()
|
|
.required()
|
|
.max(450)
|
|
.label(t("ActivityNameTemplate")),
|
|
description: Joi.string().required().allow("").label(t("Description")),
|
|
domainId: Joi.required(),
|
|
tasks: Joi.array().required(),
|
|
deleted: Joi.boolean().optional(),
|
|
guid: Joi.string().optional(),
|
|
id: Joi.number().optional(),
|
|
lastUpdated: Joi.date().optional(),
|
|
version: Joi.number().optional(),
|
|
workflowId: Joi.optional(),
|
|
};
|
|
|
|
const load = async () => {
|
|
let newData: CreateWorkflowTemplateVersion | ReadWorkflowTemplateVersion = {
|
|
...form.state.data,
|
|
};
|
|
|
|
if (editMode && workflowTemplateId) {
|
|
try {
|
|
const loadedData = await templateVersionsService.getTemplateVersion(
|
|
BigInt(workflowTemplateId),
|
|
);
|
|
|
|
if (loadedData) {
|
|
newData = loadedData;
|
|
}
|
|
} catch (ex: any) {
|
|
form.handleGeneralError(ex);
|
|
}
|
|
} else {
|
|
const user = authentication.getCurrentUser();
|
|
newData.domainId = MakeGeneralIdRef(user?.domainid);
|
|
}
|
|
|
|
form.setState({ ...form.state, loaded: true, data: newData });
|
|
};
|
|
|
|
// -----------------------------
|
|
// Load existing template (edit mode)
|
|
// -----------------------------
|
|
useEffect(() => {
|
|
load();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [editMode, workflowTemplateId]);
|
|
|
|
// -----------------------------
|
|
// Save handler (called by useForm.handleSubmit)
|
|
// -----------------------------
|
|
const doSubmit = async (buttonName: string) => {
|
|
try {
|
|
const { data } = form.state;
|
|
|
|
var json = JSON.stringify(data);
|
|
|
|
if (editMode) {
|
|
await templateVersionsService.putTemplateVersion(
|
|
data as ReadWorkflowTemplateVersion,
|
|
);
|
|
toast.info(t("WorkflowTemplateEdited"));
|
|
} else {
|
|
await templateVersionsService.postTemplateVersion(
|
|
data as CreateWorkflowTemplateVersion,
|
|
);
|
|
toast.info(t("WorkflowTemplateAdded"));
|
|
}
|
|
|
|
if (buttonName === "save") {
|
|
form.setState({ ...form.state, redirect: "/workflowTemplates" });
|
|
}
|
|
|
|
form.markAsSaved();
|
|
load();
|
|
} catch (ex: any) {
|
|
form.handleGeneralError(ex);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
form.handleSubmit(e, doSubmit);
|
|
};
|
|
|
|
//const [tasksValid, setTasksValid] = useState(true);
|
|
|
|
const handleTaskValidate = useCallback((taskId: string, isValid: boolean) => {
|
|
setTaskValidation((prev) => {
|
|
const updated = { ...prev, [taskId]: isValid };
|
|
return updated;
|
|
});
|
|
}, []);
|
|
|
|
const { redirect, errors: formErrors, data } = form.state;
|
|
if (redirect) return <Navigate to={redirect} />;
|
|
|
|
let errors = { ...formErrors };
|
|
|
|
const generalTabValid = !(
|
|
errors["domainId"] ||
|
|
errors["workflowName"] ||
|
|
errors["activityNameTemplate"] ||
|
|
errors["description"]
|
|
);
|
|
|
|
const tasksValid = Object.values(taskValidation).every((v) => v === true);
|
|
|
|
if (!tasksValid) {
|
|
errors["tasks"] = t("TasksValidationError");
|
|
}
|
|
|
|
// -----------------------------
|
|
// Tabs
|
|
// -----------------------------
|
|
const tabs = [
|
|
<Tab
|
|
key="general"
|
|
id="general"
|
|
label={t("General")}
|
|
hasError={!generalTabValid}
|
|
>
|
|
<GeneralTab
|
|
data={data}
|
|
errors={errors}
|
|
isEditMode={editMode}
|
|
onFieldChange={form.handleChange}
|
|
handleDomainPickerChange={(name, values) =>
|
|
form.handlePickerChange(name, values[0].value)
|
|
}
|
|
/>
|
|
</Tab>,
|
|
|
|
<Tab key="tasks" id="tasks" label={t("Tasks")} hasError={!tasksValid}>
|
|
<TasksTab
|
|
data={data}
|
|
errors={errors}
|
|
isEditMode={editMode}
|
|
onTasksChange={form.handleTasksChange}
|
|
onValidate={handleTaskValidate}
|
|
taskValidation={taskValidation}
|
|
/>
|
|
</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")}>
|
|
<VisualiserTab data={data} taskValidation={taskValidation} />
|
|
</Tab>,
|
|
];
|
|
|
|
return (
|
|
<div className="full-height-container">
|
|
<form onSubmit={handleSubmit}>
|
|
<h1>{heading}</h1>
|
|
|
|
<div style={{ marginBottom: "1rem" }}>
|
|
{editMode && renderButton(t("Save"), errors, "apply")}
|
|
{renderButton(t("SaveAndClose"), errors, "save")}
|
|
</div>
|
|
|
|
{Object.keys(errors).length > 0 && (
|
|
<ErrorBlock error={t("ResolveAllValidation")} />
|
|
)}
|
|
|
|
{renderError("_general", errors)}
|
|
|
|
<HorizontalTabs
|
|
hashSegment={0}
|
|
activeTab={activeTab}
|
|
onTabChange={setActiveTab}
|
|
>
|
|
{tabs}
|
|
</HorizontalTabs>
|
|
</form>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default WorkflowTemplateDetails;
|