645 lines
19 KiB
TypeScript
645 lines
19 KiB
TypeScript
import Joi from "joi";
|
|
import React, { useEffect } from "react";
|
|
import { Navigate, useParams } from "react-router-dom";
|
|
import { toast } from "react-toastify";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Namespaces } from "../../../i18n/i18n";
|
|
import { InputType } from "../../../components/common/Input";
|
|
import {
|
|
renderInput,
|
|
renderButton,
|
|
renderError,
|
|
renderSelect,
|
|
renderInputNumber,
|
|
renderInputTextarea,
|
|
renderGlossaryPicker,
|
|
renderSequencePicker,
|
|
} from "../../../components/common/formHelpers";
|
|
import { MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
|
|
import customFieldsService, {
|
|
numberParams,
|
|
textParams,
|
|
} from "./services/customFieldsService";
|
|
import Option from "../../../components/common/option";
|
|
import { GeneralIdRef } from "./../../../utils/GeneralIdRef";
|
|
import {
|
|
CustomFieldValue,
|
|
SystemGlossaries,
|
|
} from "../glossary/services/glossaryService";
|
|
import Loading from "../../../components/common/Loading";
|
|
import { useFormWithGuard } from "../../../components/common/useFormRouter";
|
|
|
|
interface CustomFieldDetailsProps {
|
|
editMode?: boolean;
|
|
}
|
|
|
|
const CustomFieldDetails: React.FC<CustomFieldDetailsProps> = ({
|
|
editMode = false,
|
|
}) => {
|
|
const { customFieldId } = useParams<{ customFieldId: string }>();
|
|
const { t } = useTranslation(Namespaces.Common);
|
|
|
|
const labelName = t("Name");
|
|
const labelFieldType = t("FieldType");
|
|
const labelMultiLine = t("MultiLine");
|
|
const labelDefaultValue = t("DefaultValue");
|
|
const labelMinValue = t("MinimumValue");
|
|
const labelMaxValue = t("MaximumValue");
|
|
const labelStep = t("Step");
|
|
const labelRequired = t("Required");
|
|
const labelMinEntries = t("MinEntries");
|
|
const labelMaxEntries = t("MaxEntriesEmptyUnlimited");
|
|
let labelRefElementId = t("SequenceFormGlossary");
|
|
|
|
const labelApply = t("Save");
|
|
const labelSave = t("SaveAndClose");
|
|
|
|
const form = useFormWithGuard({
|
|
loaded: false,
|
|
data: {
|
|
name: "",
|
|
fieldType: "Text",
|
|
defaultValue: "",
|
|
multiLine: false,
|
|
minEntries: 0,
|
|
maxEntries: 1,
|
|
refElementId: undefined,
|
|
minValue: undefined,
|
|
maxValue: undefined,
|
|
step: undefined,
|
|
required: false,
|
|
},
|
|
errors: {},
|
|
redirect: "",
|
|
});
|
|
|
|
form.schema = {
|
|
name: Joi.string().required().max(450).label(labelName),
|
|
fieldType: Joi.string().required().label(labelFieldType),
|
|
multiLine: Joi.boolean().label(labelMultiLine),
|
|
minEntries: Joi.number().min(0).label(labelMinEntries),
|
|
maxEntries: Joi.number().empty("").label(labelMaxEntries),
|
|
refElementId: Joi.when("fieldType", {
|
|
is: Joi.string().valid("Sequence"),
|
|
then: Joi.object({
|
|
id: Joi.optional(),
|
|
guid: Joi.optional(),
|
|
}).required(),
|
|
}).when("fieldType", {
|
|
is: Joi.string().valid("Glossary"),
|
|
then: Joi.array()
|
|
.min(1)
|
|
.items(
|
|
Joi.object({
|
|
displayValue: Joi.string().optional(),
|
|
value: Joi.object({
|
|
id: Joi.optional(),
|
|
guid: Joi.optional(),
|
|
}).required(),
|
|
}),
|
|
)
|
|
.required(),
|
|
}),
|
|
minValue: Joi.number().allow("").label(labelMinValue),
|
|
maxValue: Joi.number().allow("").label(labelMaxValue),
|
|
step: Joi.number().optional().allow("").min(0).label(labelStep),
|
|
required: Joi.boolean().label(labelRequired),
|
|
|
|
//defaultValue: Joi.string().allow("").label(labelDefaultValue)
|
|
|
|
defaultValue: Joi.when("fieldType", {
|
|
is: Joi.string().valid("Number"),
|
|
then: Joi.when("minValue", {
|
|
is: Joi.any().valid(null, ""),
|
|
then: Joi.number(),
|
|
otherwise: Joi.number()
|
|
.min(Joi.ref("minValue"))
|
|
.message(t("DefaultMustBeGreaterThanOrEqualToMinimumValue")),
|
|
})
|
|
.when("maxValue", {
|
|
is: Joi.any().valid(null, ""),
|
|
then: Joi.number(),
|
|
otherwise: Joi.number()
|
|
.max(Joi.ref("maxValue"))
|
|
.message(t("DefaultMustBeLessThanOrEqualToMaximumValue")),
|
|
})
|
|
.allow(""),
|
|
otherwise: Joi.string().allow(""),
|
|
}).label(labelDefaultValue),
|
|
};
|
|
|
|
const isEditMode = () => {
|
|
return editMode;
|
|
};
|
|
|
|
useEffect(() => {
|
|
const loadData = async () => {
|
|
if (customFieldId !== undefined) {
|
|
try {
|
|
const loadedData = await customFieldsService.getField(
|
|
BigInt(customFieldId),
|
|
);
|
|
|
|
if (loadedData) {
|
|
const newData = { ...form.state.data };
|
|
newData.name = loadedData.name;
|
|
newData.fieldType = loadedData.fieldType;
|
|
newData.defaultValue = loadedData.defaultValue;
|
|
newData.minEntries = loadedData.minEntries;
|
|
newData.maxEntries = loadedData.maxEntries;
|
|
switch (newData.fieldType) {
|
|
case "Glossary":
|
|
let convertedRefElementId: CustomFieldValue = {
|
|
value: loadedData.refElementId,
|
|
};
|
|
|
|
newData.refElementId = [convertedRefElementId];
|
|
newData.required = loadedData.minEntries > 0;
|
|
break;
|
|
case "Sequence":
|
|
newData.refElementId = loadedData.refElementId;
|
|
break;
|
|
case "Domain":
|
|
newData.required = loadedData.minEntries > 0;
|
|
break;
|
|
}
|
|
|
|
if (loadedData.parameters !== undefined) {
|
|
switch (newData.fieldType) {
|
|
case "Number":
|
|
newData.required = loadedData.minEntries > 0;
|
|
const parameters: numberParams = JSON.parse(
|
|
loadedData.parameters,
|
|
);
|
|
newData.minValue = parameters.minValue ?? undefined;
|
|
newData.maxValue = parameters.maxValue ?? undefined;
|
|
newData.step = parameters.step ?? undefined;
|
|
break;
|
|
case "Text":
|
|
const textParameters: textParams = JSON.parse(
|
|
loadedData.parameters,
|
|
);
|
|
newData.multiLine = textParameters.multiLine ?? false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
form.setState({ loaded: true, data: newData });
|
|
} else {
|
|
form.setState({ loaded: false });
|
|
}
|
|
} catch (ex: any) {
|
|
form.handleGeneralError(ex);
|
|
}
|
|
}
|
|
|
|
if (!editMode) form.setState({ loaded: true });
|
|
};
|
|
|
|
loadData();
|
|
}, [customFieldId, editMode]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
|
|
const doSubmit = async (buttonName: string) => {
|
|
try {
|
|
const { name, fieldType } = form.state.data;
|
|
const nameStr = typeof name === "string" ? name : "";
|
|
const fieldTypeStr = typeof fieldType === "string" ? fieldType : "";
|
|
let { refElementId, defaultValue, minEntries, maxEntries, required } =
|
|
form.state.data;
|
|
let numberParams: numberParams | undefined = undefined;
|
|
let textParams: textParams | undefined = undefined;
|
|
let params;
|
|
let refElementIdValue: GeneralIdRef | undefined;
|
|
|
|
switch (fieldTypeStr) {
|
|
case "Sequence":
|
|
minEntries = 1;
|
|
maxEntries = 1;
|
|
defaultValue = "";
|
|
refElementIdValue = refElementId as GeneralIdRef;
|
|
break;
|
|
case "FormTemplate":
|
|
minEntries = 1;
|
|
maxEntries = 1;
|
|
defaultValue = "";
|
|
break;
|
|
case "Domain":
|
|
minEntries = required ? 1 : 0;
|
|
maxEntries = maxEntries === 0 ? undefined : maxEntries;
|
|
defaultValue = "";
|
|
break;
|
|
case "Glossary":
|
|
minEntries = required ? 1 : 0;
|
|
maxEntries = maxEntries === 0 ? undefined : maxEntries;
|
|
defaultValue = "";
|
|
refElementIdValue = (refElementId as CustomFieldValue[])[0]
|
|
.value as GeneralIdRef;
|
|
break;
|
|
case "Text":
|
|
minEntries = 1;
|
|
maxEntries = 1;
|
|
let { multiLine } = form.state.data;
|
|
textParams = { multiLine: Boolean(multiLine) };
|
|
params = textParams;
|
|
refElementIdValue = undefined;
|
|
break;
|
|
case "Number":
|
|
refElementIdValue = undefined;
|
|
let { minValue, maxValue, step } = form.state.data;
|
|
const minValueNum =
|
|
minValue === null || minValue === undefined || minValue === ""
|
|
? undefined
|
|
: Number(minValue);
|
|
const maxValueNum =
|
|
maxValue === null || maxValue === undefined || maxValue === ""
|
|
? undefined
|
|
: Number(maxValue);
|
|
const stepNum =
|
|
step === null || step === undefined || step === ""
|
|
? undefined
|
|
: Number(step);
|
|
numberParams = {
|
|
minValue: minValueNum,
|
|
maxValue: maxValueNum,
|
|
step: stepNum,
|
|
};
|
|
params = numberParams;
|
|
minEntries = required ? 1 : 0;
|
|
maxEntries = 1;
|
|
break;
|
|
default:
|
|
refElementIdValue = undefined;
|
|
}
|
|
|
|
const minEntriesValue =
|
|
typeof minEntries === "number"
|
|
? minEntries
|
|
: minEntries === undefined || minEntries === null || minEntries === ""
|
|
? 0
|
|
: Number(minEntries);
|
|
const cleanMaxEntries: Number | undefined =
|
|
maxEntries === "" ? undefined : Number(maxEntries);
|
|
|
|
const defaultValueStr =
|
|
typeof defaultValue === "string"
|
|
? defaultValue
|
|
: defaultValue === undefined || defaultValue === null
|
|
? ""
|
|
: String(defaultValue);
|
|
|
|
if (isEditMode()) {
|
|
var generalIdRef = MakeGeneralIdRef(BigInt(customFieldId!));
|
|
const response = await customFieldsService.putField(
|
|
generalIdRef,
|
|
nameStr,
|
|
fieldTypeStr,
|
|
defaultValueStr,
|
|
minEntriesValue,
|
|
cleanMaxEntries,
|
|
refElementIdValue,
|
|
params,
|
|
);
|
|
if (response) {
|
|
toast.info(t("CustomFieldEdited"));
|
|
}
|
|
} else {
|
|
const response = await customFieldsService.postField(
|
|
nameStr,
|
|
fieldTypeStr,
|
|
defaultValueStr,
|
|
minEntriesValue,
|
|
cleanMaxEntries,
|
|
refElementIdValue,
|
|
params,
|
|
);
|
|
if (response) {
|
|
toast.info(t("NewCustomFieldAdded"));
|
|
}
|
|
}
|
|
|
|
if (buttonName === "save") form.setState({ redirect: "/customfields" });
|
|
|
|
form.markAsSaved();
|
|
} catch (ex: any) {
|
|
form.handleGeneralError(ex);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
|
form.handleSubmit(e, doSubmit);
|
|
};
|
|
|
|
if (form.state.redirect) return <Navigate to={form.state.redirect} />;
|
|
|
|
const { fieldType } = form.state.data;
|
|
const fieldTypeValue = typeof fieldType === "string" ? fieldType : "";
|
|
|
|
let mode = t("Add");
|
|
if (isEditMode()) mode = t("Edit");
|
|
|
|
const fieldTypeOptions: Option[] = [
|
|
{ _id: "Text", name: t("Text") },
|
|
{ _id: "Number", name: t("Number") },
|
|
{ _id: "Sequence", name: t("Sequence") },
|
|
{ _id: "FormTemplate", name: t("FormTemplate") },
|
|
{ _id: "Glossary", name: t("Glossary") },
|
|
{ _id: "Domain", name: t("Domain") },
|
|
];
|
|
|
|
switch (fieldTypeValue) {
|
|
case "Sequence":
|
|
labelRefElementId = t("Sequence");
|
|
break;
|
|
case "FormTemplate":
|
|
labelRefElementId = t("Form");
|
|
break;
|
|
case "Glossary":
|
|
labelRefElementId = t("Glossary");
|
|
break;
|
|
}
|
|
|
|
return (
|
|
<Loading loaded={form.state.loaded}>
|
|
<h1>
|
|
{mode} {t("CustomField")}
|
|
</h1>
|
|
<form onSubmit={handleSubmit}>
|
|
{renderError("_general", form.state.errors)}
|
|
{renderInput(
|
|
"name",
|
|
labelName,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.text,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderSelect(
|
|
"fieldType",
|
|
labelFieldType,
|
|
form.state.data,
|
|
form.state.errors,
|
|
fieldTypeOptions,
|
|
form.handleSelectChange,
|
|
)}
|
|
{fieldTypeValue === "Domain" && (
|
|
<>
|
|
{renderInput(
|
|
"required",
|
|
labelRequired,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.checkbox,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInputNumber(
|
|
"maxEntries",
|
|
labelMaxEntries,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
undefined,
|
|
undefined,
|
|
1,
|
|
form.handleChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{fieldTypeValue === "Glossary" && (
|
|
<>
|
|
{renderGlossaryPicker(
|
|
true,
|
|
"refElementId",
|
|
labelRefElementId,
|
|
form.state.data,
|
|
form.state.errors,
|
|
1,
|
|
SystemGlossaries,
|
|
form.handleGlossaryPickerChange,
|
|
)}
|
|
{renderInput(
|
|
"required",
|
|
labelRequired,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.checkbox,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInputNumber(
|
|
"maxEntries",
|
|
labelMaxEntries,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
undefined,
|
|
undefined,
|
|
1,
|
|
form.handleChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{fieldTypeValue === "Sequence" && (
|
|
<>
|
|
{renderSequencePicker(
|
|
true,
|
|
"refElementId",
|
|
labelRefElementId,
|
|
form.state.data,
|
|
form.state.errors,
|
|
form.handlePickerChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{fieldTypeValue === "Text" && (
|
|
<>
|
|
{renderInput(
|
|
"multiLine",
|
|
labelMultiLine,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.checkbox,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{form.state.data.multiLine === true &&
|
|
renderInputTextarea(
|
|
true,
|
|
"defaultValue",
|
|
labelDefaultValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
form.handleTextAreaChange,
|
|
)}
|
|
{form.state.data.multiLine === false &&
|
|
renderInput(
|
|
"defaultValue",
|
|
labelDefaultValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.text,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{fieldTypeValue === "Number" && (
|
|
<>
|
|
{renderInput(
|
|
"required",
|
|
labelRequired,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.checkbox,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInputNumber(
|
|
"minValue",
|
|
labelMinValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
undefined,
|
|
undefined,
|
|
1,
|
|
form.handleChange,
|
|
)}
|
|
{renderInputNumber(
|
|
"maxValue",
|
|
labelMaxValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
undefined,
|
|
undefined,
|
|
1,
|
|
form.handleChange,
|
|
)}
|
|
{renderInput(
|
|
"step",
|
|
labelStep,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.number,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInputNumber(
|
|
"defaultValue",
|
|
labelDefaultValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
false,
|
|
"",
|
|
undefined,
|
|
undefined,
|
|
1,
|
|
form.handleChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{![
|
|
"Domain",
|
|
"Glossary",
|
|
"Sequence",
|
|
"FormTemplate",
|
|
"Text",
|
|
"Number",
|
|
].includes(fieldTypeValue) && (
|
|
<>
|
|
{renderInput(
|
|
"defaultValue",
|
|
labelDefaultValue,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.text,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInput(
|
|
"minEntries",
|
|
labelMinEntries,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.number,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
{renderInput(
|
|
"maxEntries",
|
|
labelMaxEntries,
|
|
form.state.data,
|
|
form.state.errors,
|
|
InputType.number,
|
|
false,
|
|
"",
|
|
"",
|
|
0,
|
|
true,
|
|
undefined,
|
|
form.handleChange,
|
|
)}
|
|
</>
|
|
)}
|
|
{isEditMode() && renderButton(labelApply, form.state.errors, "apply")}
|
|
{renderButton(labelSave, form.state.errors, "save")}
|
|
</form>
|
|
</Loading>
|
|
);
|
|
};
|
|
|
|
export default CustomFieldDetails;
|