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; } interface TemplateState { name?: string; templateId?: GeneralIdRef; version?: bigint; definition?: string; } const TemplateFiller = forwardRef( ({ templateId, formInstanceId, onValidationChanged }, ref) => { const form = useFormWithGuard({ loaded: false, data: {}, errors: {}, redirect: "", }); const [template, setTemplate] = React.useState({ name: undefined, templateId: undefined, version: undefined, definition: undefined, }); const prevHasErrorsRef = useRef(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; 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) => { if (e.target instanceof HTMLTextAreaElement) { form.handleTextAreaChange( e as React.ChangeEvent, ); } else { form.handleCustomFieldChange( e as React.ChangeEvent, ); } }, [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 (
{domToReact(domNodeAsAny.children, options)}
); } }, }; 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
{parsedDefinition}
; }, ); TemplateFiller.displayName = "TemplateFiller"; export default TemplateFiller;