268 lines
8.2 KiB
TypeScript
268 lines
8.2 KiB
TypeScript
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useImperativeHandle,
|
|
useRef,
|
|
forwardRef,
|
|
} from "react";
|
|
import { GeneralIdRef, MakeGeneralIdRef } from "../../utils/GeneralIdRef";
|
|
import formsService, {
|
|
CreateFormInstance,
|
|
EditFormInstance,
|
|
} from "../../modules/manager/forms/services/formsService";
|
|
import parse, { HTMLReactParserOptions, domToReact } from "html-react-parser";
|
|
import { CustomField } from "../../modules/manager/customfields/services/customFieldsService";
|
|
import { toast } from "react-toastify";
|
|
import { renderCustomField } from "./formHelpers";
|
|
import { CustomFieldValue } from "../../modules/manager/glossary/services/glossaryService";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Namespaces } from "../../i18n/i18n";
|
|
import { useFormWithGuard } from "./useFormRouter";
|
|
|
|
interface TemplateFillerProps {
|
|
templateId?: GeneralIdRef;
|
|
formInstanceId?: GeneralIdRef;
|
|
onValidationChanged?: () => void;
|
|
}
|
|
|
|
export interface TemplateFillerHandle {
|
|
hasValidationErrors: () => boolean;
|
|
Save: () => Promise<any>;
|
|
}
|
|
|
|
interface TemplateState {
|
|
name?: string;
|
|
templateId?: GeneralIdRef;
|
|
version?: bigint;
|
|
definition?: string;
|
|
}
|
|
|
|
const TemplateFiller = forwardRef<TemplateFillerHandle, TemplateFillerProps>(
|
|
({ templateId, formInstanceId, onValidationChanged }, ref) => {
|
|
const form = useFormWithGuard({
|
|
loaded: false,
|
|
data: {},
|
|
errors: {},
|
|
redirect: "",
|
|
});
|
|
|
|
const [template, setTemplate] = React.useState<TemplateState>({
|
|
name: undefined,
|
|
templateId: undefined,
|
|
version: undefined,
|
|
definition: undefined,
|
|
});
|
|
|
|
const prevHasErrorsRef = useRef<boolean>(false);
|
|
|
|
form.schema = {};
|
|
|
|
const loadTemplate = useCallback(async () => {
|
|
let loadedData: any;
|
|
|
|
if (templateId !== undefined) {
|
|
loadedData = await formsService.getForm(
|
|
templateId?.id,
|
|
templateId?.guid,
|
|
);
|
|
loadedData.templateId = undefined;
|
|
loadedData.customFieldValues = undefined;
|
|
loadedData.updatedVersion = undefined;
|
|
} else if (formInstanceId !== undefined) {
|
|
loadedData = await formsService.getFormInstance(
|
|
formInstanceId?.id,
|
|
formInstanceId?.guid,
|
|
);
|
|
} else {
|
|
loadedData = {
|
|
name: undefined,
|
|
id: undefined,
|
|
guid: undefined,
|
|
version: undefined,
|
|
definition: undefined,
|
|
customFieldDefinitions: undefined,
|
|
templateId: undefined,
|
|
customFieldValues: undefined,
|
|
updatedVersion: undefined,
|
|
};
|
|
}
|
|
|
|
const newTemplate: TemplateState = {
|
|
name: loadedData.name,
|
|
templateId: MakeGeneralIdRef(loadedData.id, loadedData.guid),
|
|
version: loadedData.version,
|
|
definition: loadedData.definition,
|
|
};
|
|
|
|
const newCustomFields = loadedData.customFieldDefinitions as
|
|
| CustomField[]
|
|
| undefined;
|
|
const newData = { ...form.state.data } as Record<string, any>;
|
|
|
|
form.setCustomFieldValues(
|
|
newData,
|
|
loadedData.customFieldValues,
|
|
newCustomFields ?? [],
|
|
);
|
|
|
|
form.setState({
|
|
loaded: true,
|
|
data: newData,
|
|
customFields: newCustomFields,
|
|
});
|
|
setTemplate(newTemplate);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [templateId, formInstanceId]);
|
|
|
|
useEffect(() => {
|
|
void loadTemplate();
|
|
}, [loadTemplate]);
|
|
|
|
useEffect(() => {
|
|
if (!onValidationChanged) return;
|
|
|
|
const hasErrors = Object.keys(form.state.errors).length > 0;
|
|
if (prevHasErrorsRef.current !== hasErrors) {
|
|
prevHasErrorsRef.current = hasErrors;
|
|
onValidationChanged();
|
|
}
|
|
}, [form.state.errors, onValidationChanged]);
|
|
|
|
const handleCustomFieldChange = useCallback(
|
|
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
if (e.target instanceof HTMLTextAreaElement) {
|
|
form.handleTextAreaChange(
|
|
e as React.ChangeEvent<HTMLTextAreaElement>,
|
|
);
|
|
} else {
|
|
form.handleCustomFieldChange(
|
|
e as React.ChangeEvent<HTMLInputElement>,
|
|
);
|
|
}
|
|
},
|
|
[form],
|
|
);
|
|
|
|
const handleCustomFieldPickerChange = useCallback(
|
|
(name: string, value: GeneralIdRef | CustomFieldValue[]) => {
|
|
if (Array.isArray(value)) {
|
|
form.handleGlossaryPickerChange(name, value);
|
|
} else {
|
|
form.handlePickerChange(name, value as GeneralIdRef);
|
|
}
|
|
},
|
|
[form],
|
|
);
|
|
|
|
const getCustomFieldType = useCallback(
|
|
(field: CustomFieldValue, customFields: CustomField[]) => {
|
|
return form.getCustomFieldType(field as any, customFields);
|
|
},
|
|
[form],
|
|
);
|
|
|
|
const parseDefinition = useCallback(
|
|
(definition: string, customFieldDefinitions: CustomField[]) => {
|
|
const options: HTMLReactParserOptions = {
|
|
replace: (domNode) => {
|
|
const domNodeAsAny: any = domNode;
|
|
if (domNodeAsAny.name === "span") {
|
|
if (domNodeAsAny.attribs.fieldtype === "CustomField") {
|
|
const customField = customFieldDefinitions.filter(
|
|
(x) => x.guid === domNodeAsAny.attribs.guid,
|
|
)[0];
|
|
return renderCustomField(
|
|
customField,
|
|
false,
|
|
form.state.data,
|
|
form.state.errors,
|
|
handleCustomFieldChange,
|
|
handleCustomFieldPickerChange,
|
|
getCustomFieldType,
|
|
);
|
|
}
|
|
} else if (domNodeAsAny.name === "p") {
|
|
return (
|
|
<div className="p">
|
|
{domToReact(domNodeAsAny.children, options)}
|
|
</div>
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
return parse(definition, options);
|
|
},
|
|
[
|
|
form.state.data,
|
|
form.state.errors,
|
|
handleCustomFieldChange,
|
|
handleCustomFieldPickerChange,
|
|
getCustomFieldType,
|
|
],
|
|
);
|
|
|
|
const hasValidationErrors = useCallback((): boolean => {
|
|
return Object.keys(form.state.errors).length > 0;
|
|
}, [form.state.errors]);
|
|
|
|
const { t } = useTranslation(Namespaces.Common);
|
|
|
|
const Save = useCallback(async () => {
|
|
const { errors } = form.state;
|
|
|
|
if (Object.keys(errors).length > 0) {
|
|
toast.error(t("ThereAreErrorsOnTheForm"));
|
|
throw new Error(t("ThereAreErrorsOnTheForm") as string);
|
|
}
|
|
|
|
const customFieldValues = form.CustomFieldValues();
|
|
if (formInstanceId !== undefined) {
|
|
if (template.templateId === undefined)
|
|
throw Error(t("TemplateIdCannotBeNull") as string);
|
|
if (template.version === undefined)
|
|
throw Error(t("VersionCannotBeNull") as string);
|
|
|
|
const editFormInstance: EditFormInstance = {
|
|
formInstanceId,
|
|
templateId: template.templateId,
|
|
version: template.version,
|
|
customFieldValues,
|
|
};
|
|
await formsService.putFormInstance(editFormInstance);
|
|
} else {
|
|
if (
|
|
template.templateId !== undefined &&
|
|
template.version !== undefined
|
|
) {
|
|
const formInstance: CreateFormInstance = {
|
|
templateId: template.templateId,
|
|
version: template.version,
|
|
customFieldValues,
|
|
};
|
|
return await formsService.postFormInstance(formInstance);
|
|
}
|
|
throw new Error(t("TemplateUnknown") as string);
|
|
}
|
|
}, [form, template, formInstanceId, t]);
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
hasValidationErrors,
|
|
Save,
|
|
}));
|
|
|
|
const { loaded, customFields } = form.state;
|
|
|
|
let parsedDefinition: any;
|
|
if (template.definition && customFields)
|
|
parsedDefinition = parseDefinition(template.definition, customFields);
|
|
else parsedDefinition = <></>;
|
|
|
|
return <div className="ck-content form-editor">{parsedDefinition}</div>;
|
|
},
|
|
);
|
|
|
|
TemplateFiller.displayName = "TemplateFiller";
|
|
|
|
export default TemplateFiller;
|