diff --git a/src/components/common/Form.tsx b/src/components/common/Form.tsx
deleted file mode 100644
index de63740..0000000
--- a/src/components/common/Form.tsx
+++ /dev/null
@@ -1,957 +0,0 @@
-import React from "react";
-import Joi from "joi";
-import Input, { InputType } from "./Input";
-import ToggleSlider from "./ToggleSlider";
-import Select from "./Select";
-import Option from "./option";
-import { GeneralIdRef } from "../../utils/GeneralIdRef";
-import SequencePicker from "../pickers/SequencePicker";
-import GlossaryPicker from "../pickers/GlossaryPicker";
-import CustomFieldsEditor, { CustomFieldEditorAdd, CustomFieldEditorDelete } from "./CustomFieldsEditor";
-import { CustomField, numberParams, textParams } from "../../modules/manager/customfields/services/customFieldsService";
-import FormTemplatePicker from "../pickers/FormTemplatePicker";
-import { CustomFieldValue, CustomFieldValues, Glossary } from "../../modules/manager/glossary/services/glossaryService";
-import TemplateEditor from "./TemplateEditor";
-import DomainPicker from "../pickers/DomainPicker";
-import UserPicker from "../pickers/UserPicker";
-import Button, { ButtonType } from "./Button";
-import Expando from "./expando";
-import ErrorBlock from "./ErrorBlock";
-import SsoProviderPicker from "../pickers/SsoProviderPicker";
-
-export interface FormError {
- [key: string]: string;
-}
-
-export interface FormData {
- [key: string]: string | number | boolean | CustomFieldValue[] | GeneralIdRef | CustomField[] | bigint | Glossary | null | undefined;
-}
-
-export interface businessValidationError {
- path: string;
- message: string;
-}
-
-export interface businessValidationResult {
- details: businessValidationError[];
-}
-
-export interface propertyValue {
- name: string;
- value: string | boolean | number;
-}
-
-export interface joiSchema {
- [key: string]: object;
-}
-
-export interface FormState {
- loaded: boolean;
- data: FormData;
- customFields?: CustomField[];
- errors: FormError;
- redirect?: string;
-}
-
-export interface Match
{
- params: P;
- isExact: boolean;
- path: string;
- url: string;
-}
-
-export interface State {
- from: Location;
-}
-
-export interface LocationProps {
- hash: string;
- pathname: string;
- search: string;
- state: State;
-}
-
-export interface FormProps
{
- location: LocationProps;
- match: Match
;
- staticContext?: any;
-}
-
-class Form
, FS extends FormState> extends React.Component {
- schema: joiSchema = {};
-
- validate = (data: FormData) => {
- let options: Joi.ValidationOptions = {
- context: {},
- abortEarly: false,
- };
-
- const { customFields } = this.state;
-
- let schema = this.schema;
- if (customFields !== undefined) {
- for (const customfield of customFields) {
- const name = "customfield_" + customfield.id;
- switch (customfield.fieldType) {
- case "Number":
- if (customfield.parameters !== undefined) {
- const parameters: numberParams = JSON.parse(customfield.parameters!);
-
- options.context![name + "_minEntries"] = customfield.minEntries;
- if (parameters.minValue) options.context![name + "_minValue"] = Number(parameters.minValue);
- if (parameters.maxValue) options.context![name + "_maxValue"] = Number(parameters.maxValue);
-
- let minCheck = options.context![name + "_minValue"]
- ? Joi.number()
- .empty("")
- .min(options.context![name + "_minValue"])
- : Joi.number().empty("");
- let maxCheck = options.context![name + "_maxValue"]
- ? Joi.number()
- .empty("")
- .max(options.context![name + "_maxValue"])
- : Joi.number().empty("");
-
- schema[name] = Joi.array()
- .min(1)
- .items(
- Joi.object({
- displayValue: Joi.string().allow(""),
- value: Joi.when("$" + name + "_minEntries", {
- is: 0,
- then: Joi.number().empty(""),
- otherwise: Joi.number().required(),
- })
- .when("$" + name + "_minValue", {
- is: Joi.number(),
- then: minCheck,
- })
- .when("$" + name + "_maxValue", {
- is: Joi.number(),
- then: maxCheck,
- })
- .label(customfield.name),
- })
- );
- } else {
- schema[name] = Joi.optional().label(customfield.name);
- }
- break;
- default:
- schema[name] = Joi.optional().label(customfield.name);
- }
- }
- }
-
- const joiSchema = Joi.object(schema);
- const { error } = joiSchema.validate(data, options);
- let errors: FormError = {};
- if (error) {
- if (error.details === undefined) {
- errors[error.name] = error.message;
- } else {
- for (let item of error.details) {
- errors[item.path[0]] = item.message;
- }
- }
- }
-
- return errors;
- };
-
- GetCustomFieldValues = (customField: CustomField) => {
- const name = "customfield_" + customField.id;
-
- const { data } = this.state;
-
- const codedValue = data[name];
-
- let values: CustomFieldValue[] = [];
-
- switch (customField.fieldType) {
- case "FormTemplate":
- if (codedValue !== undefined) {
- const formTemplateValue = { value: JSON.stringify(codedValue as GeneralIdRef) };
- values.push(formTemplateValue);
- }
- break;
- case "Sequence":
- if (codedValue !== undefined) {
- values = codedValue as CustomFieldValue[];
- }
- break;
- case "Glossary":
- if (codedValue !== undefined) {
- values = codedValue as CustomFieldValue[];
- }
- break;
- case "Domain":
- if (codedValue !== undefined) {
- values = codedValue as CustomFieldValue[];
- }
- break;
- case "Text":
- const textParameters: textParams = JSON.parse(customField.parameters!);
- if (textParameters.multiLine) {
- const textValue = {
- value: codedValue === undefined ? customField.defaultValue : codedValue,
- displayValue: codedValue === undefined ? customField.defaultValue : codedValue,
- } as CustomFieldValue;
- values.push(textValue);
- } else {
- if (codedValue === undefined) {
- const numberValue = {
- value: customField.defaultValue,
- displayValue: customField.defaultValue,
- } as CustomFieldValue;
- values.push(numberValue);
- } else {
- values = codedValue as CustomFieldValue[];
- }
- }
- break;
- case "Number":
- if (codedValue === undefined) {
- const numberValue = {
- value: customField.defaultValue,
- displayValue: customField.defaultValue,
- } as CustomFieldValue;
- values.push(numberValue);
- } else {
- values = codedValue as CustomFieldValue[];
- }
-
- break;
- default:
- const textValue = { value: codedValue === undefined ? customField.defaultValue : String((codedValue as CustomFieldValue[])[0].displayValue) };
- values.push(textValue);
- break;
- }
-
- return values;
- };
-
- CustomFieldValues = () => {
- const { customFields } = this.state;
-
- let result: CustomFieldValues[] = [];
-
- if (customFields === undefined) {
- return result;
- }
-
- for (const customfield of customFields) {
- const values = this.GetCustomFieldValues(customfield);
-
- const id: GeneralIdRef = {
- id: customfield.id,
- guid: customfield.guid,
- };
-
- const newItem: CustomFieldValues = {
- id,
- values,
- };
-
- result.push(newItem);
- }
-
- return result;
- };
-
- setCustomFieldValues(data: object, customFieldValues: CustomFieldValues[], customFields: CustomField[]) {
- if (customFieldValues !== undefined) {
- for (const x of customFieldValues) {
- const customfieldName = "customfield_" + x.id.id;
-
- switch (this.getCustomFieldType(x, customFields).toLowerCase()) {
- case "glossary":
- case "domain":
- case "number":
- case "text":
- (data as any)[customfieldName] = x.values.map((x) => {
- return {
- displayValue: x.displayValue,
- value: x.value,
- };
- });
- break;
- case "formtemplate":
- case "multilinetext":
- (data as any)[customfieldName] = x.values[0].value;
- break;
- default:
- (data as any)[customfieldName] = x.values;
- break;
- }
- }
- }
- }
-
- getCustomFieldType = (field: CustomFieldValues, childCustomFieldDefinition: CustomField[]): string => {
- const fieldDefinition = childCustomFieldDefinition.filter((x) => x.id === field.id.id)[0];
-
- if (fieldDefinition.parameters) {
- const textParameters: textParams = JSON.parse(fieldDefinition.parameters!);
- if (textParameters.multiLine) return "multilinetext";
- }
-
- return fieldDefinition.fieldType;
- };
-
- handleSubmit = (e: React.FormEvent) => {
- e.preventDefault();
-
- const submitEvent = e.nativeEvent as SubmitEvent;
-
- const submitter = submitEvent.submitter as any;
-
- const errors = this.validate(this.state.data);
- this.setState({ errors: errors });
-
- const disabled = Object.keys(errors).length > 0;
-
- if (disabled) return;
-
- this.doSubmit(submitter.name);
- };
-
- doSubmit = async (buttonName: string) => {};
-
- handleGeneralError = (ex: any) => {
- const errors: FormError = { ...this.state.errors };
-
- if (ex.response) {
- errors._general = ex.response.data.detail;
- } else {
- errors._general = ex.message;
- }
-
- this.setState({ errors });
- };
-
- handleChange = (e: React.ChangeEvent) => {
- const input = e.currentTarget;
-
- const data: FormData = { ...this.state.data };
-
- if ((input as any).type === InputType.checkbox) {
- data[input.name] = !data[input.name];
- } else data[input.name] = input.value;
-
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleTextAreaChange = (e: React.ChangeEvent) => {
- const input = e.currentTarget;
-
- const data: FormData = { ...this.state.data };
-
- data[input.name] = input.value;
-
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleCustomFieldChange = (e: React.ChangeEvent) => {
- const input = e.currentTarget;
-
- const data: FormData = { ...this.state.data };
-
- switch ((input as any).type) {
- case InputType.checkbox:
- data[input.name] = !data[input.name];
- break;
- default:
- const customFieldValue: CustomFieldValue = {
- displayValue: input.value,
- value: input.value,
- };
-
- data[input.name] = [customFieldValue];
- break;
- }
-
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleTemplateEditorChange = (name: string, value: string) => {
- const data: FormData = { ...this.state.data };
-
- data[name] = value;
-
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleSelectChange = (e: React.ChangeEvent) => {
- const input = e.currentTarget;
-
- const data: FormData = { ...this.state.data };
- data[input.name] = input.value;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handlePickerChange = (name: string, value: GeneralIdRef) => {
- const data: FormData = { ...this.state.data };
- data[name] = value;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleDomainPickerChange = (name: string, values: CustomFieldValue[]) => {
- const data: FormData = { ...this.state.data };
- data[name] = values;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleGlossaryPickerChange = (name: string, values: CustomFieldValue[]) => {
- const data: FormData = { ...this.state.data };
- data[name] = values;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleTemplateFormPickerChange = (name: string, value: GeneralIdRef) => {
- const data: FormData = { ...this.state.data };
- data[name] = value;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleUserPickerChange = (name: string, value: GeneralIdRef) => {
- const data: FormData = { ...this.state.data };
- data[name] = value;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleSsoProviderPickerChange = (name: string, value: GeneralIdRef) => {
- const data: FormData = { ...this.state.data };
- data[name] = value;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- handleToggleChange = (e: React.ChangeEvent) => {
- const input = e.currentTarget;
- const { name, checked } = input;
-
- const data: FormData = { ...this.state.data };
- data[name] = checked;
- const errors = this.validate(data);
-
- this.setState({ data, errors });
- };
-
- renderButton(
- label: string,
- name?: string,
- onClick?: (keyValue: any) => {},
- testid?: string,
- enabled: boolean = true,
- buttonType: ButtonType = ButtonType.primary,
- overrideErrorChecking: boolean = false
- ) {
- const { errors } = this.state;
-
- let disabled = !enabled || Object.keys(errors).filter((x) => !x.startsWith("_")).length > 0;
-
- if (overrideErrorChecking) disabled = !enabled;
-
- return (
-
- );
- }
-
- renderError(name: string) {
- const { errors } = this.state;
-
- return ;
- }
-
- renderInput(
- name: string,
- label: string,
- type: InputType = InputType.text,
- readOnly = false,
- defaultValue: string = "",
- placeHolder: string = "",
- maxLength: number = 0,
- visible: boolean = true,
- autoComplete: string | undefined = undefined
- ) {
- const { data, errors } = this.state;
-
- let value = data[name];
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- } else if (typeof value === "number") {
- cleanValue = String(value);
- } else if (typeof value === "boolean") {
- cleanValue = String(value);
- } else if (value as CustomFieldValue) {
- cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString();
- }
-
- if (readOnly) {
- return (
-
- );
- } else {
- return (
-
- );
- }
- }
-
- renderInputWithChangeEvent(
- name: string,
- label: string,
- type: InputType = InputType.text,
- readOnly = false,
- handleChangeEvent: any,
- defaultValue: string = "",
- placeHolder: string = "",
- maxLength: number = 0
- ) {
- const { data, errors } = this.state;
-
- let value = data[name];
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- } else if (typeof value === "number") {
- cleanValue = String(value);
- } else if (typeof value === "boolean") {
- cleanValue = String(value);
- } else if (value as CustomFieldValue) {
- cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString();
- }
-
- if (readOnly) {
- return (
-
- );
- } else {
- return (
-
- );
- }
- }
-
- renderInputNumber(name: string, label: string, readOnly = false, defaultValue: string = "", min?: number, max?: number, step: number = 1) {
- const { data, errors } = this.state;
-
- let value = data[name];
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- } else if (typeof value === "number") {
- cleanValue = String(value);
- } else if (typeof value === "boolean") {
- cleanValue = String(value);
- } else if (value as CustomFieldValue) {
- cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString();
- }
-
- if (readOnly) {
- return ;
- } else {
- return (
-
- );
- }
- }
-
- renderInputTextarea(includeLabel: boolean, name: string, label: string, readOnly = false, defaultValue: string = "") {
- const { data, errors } = this.state;
-
- let value = data[name];
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- }
-
- if (readOnly) {
- return (
-
- );
- } else {
- return (
-
- );
- }
- }
-
- renderCustomFieldInput(includeLabel: boolean, name: string, label: string, type: InputType = InputType.text, readOnly = false, defaultValue: string = "") {
- const { data, errors } = this.state;
-
- let value = data[name];
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- } else if (typeof value === "number") {
- cleanValue = String(value);
- } else if (typeof value === "boolean") {
- cleanValue = String(value);
- } else if (value as CustomFieldValue) {
- const customFieldValue = value as CustomFieldValue[];
- cleanValue = customFieldValue[0].displayValue ?? customFieldValue[0].value?.toString();
- }
-
- if (readOnly) {
- return ;
- } else {
- return (
-
- );
- }
- }
-
- renderCustomFieldNumber(
- includeLabel: boolean,
- name: string,
- label: string,
- readOnly = false,
- defaultValue: string = "",
- min?: number,
- max?: number,
- step: number = 1
- ) {
- const { data, errors } = this.state;
-
- let values: CustomFieldValue[] = data[name] as CustomFieldValue[];
-
- let value: CustomFieldValue | undefined = undefined;
-
- if (values) {
- if (values.length > 0) {
- value = values[0];
- }
- }
-
- let cleanValue: string | undefined;
- if (value === undefined) {
- cleanValue = defaultValue;
- } else if (typeof value === "string") {
- cleanValue = value as string;
- } else if (typeof value === "number") {
- cleanValue = String(value);
- } else if (value as CustomFieldValue) {
- cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value?.toString();
- }
-
- if (readOnly) {
- return ;
- } else {
- return (
-
- );
- }
- }
-
- renderTemplateEditor(className: string, name: string, label: string, allowCustomFields: boolean) {
- const { data } = this.state;
-
- let value = data[name] as string;
-
- return (
-
-
-
-
- );
- }
-
- renderSelect(name: string, label: string, options: Option[]) {
- const { data, errors } = this.state;
-
- return ;
- }
-
- renderToggle(name: string, label: string) {
- const { data, errors } = this.state;
- return ;
- }
-
- renderSequencePicker(includeLabel: boolean, name: string, label: string) {
- const { data, errors } = this.state;
-
- return (
-
- );
- }
-
- renderGlossaryPicker(includeLabel: boolean, name: string, label: string, maxEntries?: number, refElementId?: GeneralIdRef) {
- const { data, errors } = this.state;
-
- const glossaryValues: CustomFieldValue[] | undefined = data[name] as any as CustomFieldValue[];
-
- return (
-
- );
- }
-
- renderDomainPicker(includeLabel: boolean, name: string, label: string, minEntries: number, maxEntries?: number) {
- const { data, errors } = this.state;
-
- const domainValues: CustomFieldValue[] | undefined = data[name] as any as CustomFieldValue[];
-
- return (
-
- );
- }
-
- renderTemplatePicker(includeLabel: boolean, name: string, label: string) {
- const { data, errors } = this.state;
-
- const templateValue: GeneralIdRef = data[name] as any as GeneralIdRef;
-
- return (
-
- );
- }
-
- renderUserPicker(name: string, label: string) {
- const { data, errors } = this.state;
-
- const glossaryValue: GeneralIdRef | undefined = data[name] as any as GeneralIdRef;
-
- return ;
- }
-
- renderSsoProviderPicker(name: string, label: string) {
- const { data, errors } = this.state;
-
- const glossaryValue: GeneralIdRef | undefined = data[name] as any as GeneralIdRef;
-
- return ;
- }
-
- renderCustomFieldsEditor(name: string, label: string, selected: CustomField[], onAdd: CustomFieldEditorAdd, onDelete: CustomFieldEditorDelete) {
- const { data, errors } = this.state;
-
- return (
-
- );
- }
-
- renderCustomFields(customFields: CustomField[] | undefined) {
- if (customFields === undefined) return <>>;
-
- let customFieldsBlock: JSX.Element[] = [];
- for (const customField of customFields) customFieldsBlock.push(this.renderCustomField(customField, true));
-
- return <>{customFieldsBlock.map((x) => x)}>;
- }
-
- renderCustomField(customField: CustomField, includeLabel: boolean) {
- switch (customField.fieldType.toLowerCase()) {
- case "text":
- const textParameters: textParams = JSON.parse(customField.parameters!);
- if (textParameters.multiLine) {
- return this.renderInputTextarea(includeLabel, "customfield_" + customField.id, customField.name, false, customField.defaultValue);
- } else {
- return this.renderCustomFieldInput(
- includeLabel,
- "customfield_" + customField.id,
- customField.name,
- InputType.text,
- false,
- customField.defaultValue
- );
- }
- case "sequence":
- return this.renderCustomFieldInput(includeLabel, "customfield_" + customField.id, customField.name, InputType.text, true);
- case "formtemplate":
- return this.renderTemplatePicker(includeLabel, "customfield_" + customField.id, customField.name);
- case "glossary":
- return this.renderGlossaryPicker(
- includeLabel,
- "customfield_" + customField.id,
- customField.name,
- customField.maxEntries,
- customField.refElementId
- );
- case "number":
- const numberParameters: numberParams = JSON.parse(customField.parameters!);
- return this.renderCustomFieldNumber(
- includeLabel,
- "customfield_" + customField.id,
- customField.name,
- false,
- customField.defaultValue,
- numberParameters.minValue ? Number(numberParameters.minValue) : undefined,
- numberParameters.maxValue ? Number(numberParameters.maxValue) : undefined,
- numberParameters.step ? Number(numberParameters.step) : undefined
- );
- case "domain":
- return this.renderDomainPicker(includeLabel, "customfield_" + customField.id, customField.name, customField.minEntries, customField.maxEntries);
- default:
- return <>{customField.name + " " + customField.fieldType}>;
- }
- }
-
- renderDropSection(name: string, title: JSX.Element, content: JSX.Element) {
- const { errors } = this.state;
-
- return (
-
- {content}
-
- );
- }
-}
-
-export default Form;
diff --git a/src/components/common/TemplateFiller.tsx b/src/components/common/TemplateFiller.tsx
index f93e687..b2dcbd1 100644
--- a/src/components/common/TemplateFiller.tsx
+++ b/src/components/common/TemplateFiller.tsx
@@ -1,4 +1,10 @@
-import React from "react";
+import React, {
+ useCallback,
+ useEffect,
+ useImperativeHandle,
+ useRef,
+ forwardRef,
+} from "react";
import { GeneralIdRef, MakeGeneralIdRef } from "../../utils/GeneralIdRef";
import formsService, {
CreateFormInstance,
@@ -6,194 +12,246 @@ import formsService, {
} from "../../modules/manager/forms/services/formsService";
import parse, { HTMLReactParserOptions, domToReact } from "html-react-parser";
import { CustomField } from "../../modules/manager/customfields/services/customFieldsService";
-import Form, { FormState } from "./Form";
import { toast } from "react-toastify";
import Loading from "./Loading";
+import { useForm } from "./useForm";
+import { renderCustomField } from "./formHelpers";
+import { CustomFieldValue } from "../../modules/manager/glossary/services/glossaryService";
interface TemplateFillerProps {
templateId?: GeneralIdRef;
formInstanceId?: GeneralIdRef;
- onValidationChanged?: () => {};
+ onValidationChanged?: () => void;
}
-interface TemplateFillerState extends FormState {
- customFields?: CustomField[];
- template: {
- name?: string;
- templateId?: GeneralIdRef;
- version?: bigint;
- definition?: string;
- };
+export interface TemplateFillerHandle {
+ hasValidationErrors: () => boolean;
+ Save: () => Promise;
}
-class TemplateFiller extends Form<
- TemplateFillerProps,
- any,
- TemplateFillerState
-> {
- state: TemplateFillerState = {
- loaded: false,
- customFields: undefined,
- template: {
+interface TemplateState {
+ name?: string;
+ templateId?: GeneralIdRef;
+ version?: bigint;
+ definition?: string;
+}
+
+const TemplateFiller = forwardRef(
+ ({ templateId, formInstanceId, onValidationChanged }, ref) => {
+ const form = useForm({
+ loaded: false,
+ data: {},
+ errors: {},
+ redirect: "",
+ });
+
+ const [template, setTemplate] = React.useState({
name: undefined,
templateId: undefined,
version: undefined,
definition: undefined,
- },
- data: {},
- errors: {},
- };
+ });
- schema = {};
+ const prevHasErrorsRef = useRef(false);
- componentDidMount(): void {
- this.loadTemplate();
- }
+ form.schema = {};
- componentDidUpdate(
- prevProps: Readonly,
- prevState: Readonly,
- snapshot?: any,
- ): void {
- if (
- prevProps.formInstanceId !== this.props.formInstanceId ||
- prevProps.templateId !== this.props.templateId
- )
- this.loadTemplate();
+ const loadTemplate = useCallback(async () => {
+ let loadedData: any;
- if (this.props.onValidationChanged) {
- const prevErrorCount = Object.keys(prevState.errors).length > 0;
- const errorCount = Object.keys(this.state.errors).length > 0;
-
- if (prevErrorCount !== errorCount) {
- this.props.onValidationChanged();
+ 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,
+ };
}
- }
- }
- loadTemplate = async () => {
- const { templateId, formInstanceId } = this.props;
- let loadedData: any;
-
- if (templateId !== undefined) {
- loadedData = await formsService.getForm(templateId?.id, templateId?.guid);
- //Get the form definiton for the template provided by templateId and load.
-
- loadedData.templateId = undefined;
- loadedData.customFieldValues = undefined;
- loadedData.updatedVersion = undefined;
- } else if (formInstanceId !== undefined) {
- loadedData = await formsService.getFormInstance(
- formInstanceId?.id,
- formInstanceId?.guid,
- );
- console.log("formInstanceId", loadedData);
- } 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 { template, data } = this.state;
+ const newCustomFields = loadedData.customFieldDefinitions as
+ | CustomField[]
+ | undefined;
+ const newData = { ...form.state.data } as Record;
- template.name = loadedData.name;
- template.templateId = MakeGeneralIdRef(loadedData.id, loadedData.guid);
- template.version = loadedData.version;
- template.definition = loadedData.definition;
- const customFields = loadedData.customFieldDefinitions;
+ form.setCustomFieldValues(
+ newData,
+ loadedData.customFieldValues,
+ newCustomFields ?? [],
+ );
- this.setCustomFieldValues(data, loadedData.customFieldValues, customFields);
+ form.setState({
+ loaded: true,
+ data: newData,
+ customFields: newCustomFields,
+ });
+ setTemplate(newTemplate);
+ }, [templateId, formInstanceId, form]);
- this.setState({ loaded: true, template, customFields, data });
- };
+ useEffect(() => {
+ void loadTemplate();
+ }, [loadTemplate]);
- parseDefinition = (
- 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 this.renderCustomField(customField, false);
- }
- } else if (domNodeAsAny.name === "p") {
- return (
-
- {domToReact(domNodeAsAny.children, options)}
-
+ 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],
+ );
- return parse(definition, options);
- };
+ const handleCustomFieldPickerChange = useCallback(
+ (name: string, value: GeneralIdRef | CustomFieldValue[]) => {
+ if (Array.isArray(value)) {
+ form.handleGlossaryPickerChange(name, value);
+ } else {
+ form.handlePickerChange(name, value as GeneralIdRef);
+ }
+ },
+ [form],
+ );
- hasValidationErrors = (): boolean => {
- const { errors } = this.state;
+ const getCustomFieldType = useCallback(
+ (field: CustomFieldValue, customFields: CustomField[]) => {
+ return form.getCustomFieldType(field as any, customFields);
+ },
+ [form],
+ );
- const result = Object.keys(errors).length > 0;
- return result;
- };
+ 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)}
+
+ );
+ }
+ },
+ };
- async Save() {
- const { errors } = this.state;
- const { templateId, version } = this.state.template;
- const { formInstanceId } = this.props;
+ return parse(definition, options);
+ },
+ [
+ form.state.data,
+ form.state.errors,
+ handleCustomFieldChange,
+ handleCustomFieldPickerChange,
+ getCustomFieldType,
+ ],
+ );
- if (Object.keys(errors).length > 0) {
- toast.error("There are errors on the form");
- throw new Error("There are errors on the form");
- }
+ const hasValidationErrors = useCallback((): boolean => {
+ return Object.keys(form.state.errors).length > 0;
+ }, [form.state.errors]);
- const customFieldValues = this.CustomFieldValues();
- if (formInstanceId !== undefined) {
- if (templateId === undefined) throw Error("TemplateId cannot be null");
+ const Save = useCallback(async () => {
+ const { errors } = form.state;
- if (version === undefined) throw Error("Version cannot be null");
+ if (Object.keys(errors).length > 0) {
+ toast.error("There are errors on the form");
+ throw new Error("There are errors on the form");
+ }
- const editFormInstance: EditFormInstance = {
- formInstanceId,
- templateId,
- version,
- customFieldValues,
- };
- await formsService.putFormInstance(editFormInstance);
- } else {
- if (templateId !== undefined && version !== undefined) {
- //const customFieldValues = this.CustomFieldValues();
- const formInstance: CreateFormInstance = {
- templateId,
- version,
+ const customFieldValues = form.CustomFieldValues();
+ if (formInstanceId !== undefined) {
+ if (template.templateId === undefined)
+ throw Error("TemplateId cannot be null");
+ if (template.version === undefined)
+ throw Error("Version cannot be null");
+
+ const editFormInstance: EditFormInstance = {
+ formInstanceId,
+ templateId: template.templateId,
+ version: template.version,
customFieldValues,
};
- return await formsService.postFormInstance(formInstance);
- } else throw new Error("template unknown");
- }
- }
+ 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("template unknown");
+ }
+ }, [form, template, formInstanceId]);
- render() {
- const { loaded, template, customFields } = this.state;
+ useImperativeHandle(ref, () => ({
+ hasValidationErrors,
+ Save,
+ }));
+
+ const { loaded, customFields } = form.state;
let parsedDefinition: any;
- if (template.definition)
- parsedDefinition = this.parseDefinition(
- template.definition,
- customFields!,
- );
+ if (template.definition && customFields)
+ parsedDefinition = parseDefinition(template.definition, customFields);
else parsedDefinition = <>>;
return (
@@ -201,7 +259,9 @@ class TemplateFiller extends Form<
{parsedDefinition}
);
- }
-}
+ },
+);
+
+TemplateFiller.displayName = "TemplateFiller";
export default TemplateFiller;
diff --git a/src/modules/frame/components/LoginForm.tsx b/src/modules/frame/components/LoginForm.tsx
index aaa7c09..0bac080 100644
--- a/src/modules/frame/components/LoginForm.tsx
+++ b/src/modules/frame/components/LoginForm.tsx
@@ -1,33 +1,23 @@
+import React, { useCallback, useEffect, useState } from "react";
import { Link, Navigate } from "react-router-dom";
import Joi from "joi";
-import Form, { FormState, FormData } from "../../../components/common/Form";
import authentication from "../services/authenticationService";
import { InputType } from "../../../components/common/Input";
import { ButtonType } from "../../../components/common/Button";
+import { useForm } from "../../../components/common/useForm";
+import {
+ renderButton,
+ renderError,
+ renderInput,
+} from "../../../components/common/formHelpers";
-//import '../../../Sass/login.scss';
+const LoginForm: React.FC = () => {
+ const [isInNextStage, setIsInNextStage] = useState(false);
+ const [emailSent, setEmailSent] = useState(false);
+ const passwordMaxLength = 255;
-export interface LoginFormStateData extends FormData {
- username: string;
- password: string;
- tfaNeeded: boolean;
- requestTfaRemoval: boolean;
- securityCode: string;
-}
-
-export interface LoginFormState extends FormState {
- passwordMaxLength: number;
- isInNextStage: boolean;
- emailSent: boolean;
- data: LoginFormStateData;
-}
-
-class LoginForm extends Form {
- state = {
+ const form = useForm({
loaded: true,
- passwordMaxLength: 255,
- isInNextStage: false,
- emailSent: false,
data: {
username: "",
password: "",
@@ -36,9 +26,10 @@ class LoginForm extends Form {
securityCode: "",
},
errors: {},
- };
+ redirect: "",
+ });
- schema = {
+ const schema = {
username: Joi.string()
.required()
.email({ tlds: { allow: false } })
@@ -49,182 +40,203 @@ class LoginForm extends Form {
securityCode: Joi.string().allow("").label("Authenticate"),
};
- doSubmit = async (buttonName: string) => {
- const { data } = this.state;
- await this.performLogin(data);
- };
+ form.schema = schema;
- handleNextClick = async (event: React.MouseEvent) => {
- const data: LoginFormStateData = { ...this.state.data };
- var validationResult = this.schema.username.validate(data.username);
- if (validationResult.error === undefined) {
- const stateData = this.state;
- stateData.isInNextStage = true;
- this.setState(stateData);
- }
- };
-
- handleForgetPassword = async () => {
- try {
- const stateData = this.state;
- await authentication.forgotPassword(stateData.data.username);
- stateData.emailSent = true;
- stateData.data.username = "";
- stateData.data.password = "";
- this.setState(stateData);
- } catch (ex: any) {
- this.handleGeneralError(ex);
- }
- };
-
- authenticationWorkAround = async () => {
- const data: LoginFormStateData = { ...this.state.data };
- data.requestTfaRemoval = true;
-
- await this.performLogin(data);
-
- this.setState({ data });
- };
-
- private async performLogin(data: LoginFormStateData) {
- try {
- let result = await authentication.login(
- data.username,
- data.password,
- data.securityCode,
- data.requestTfaRemoval,
- );
-
- switch (result) {
- case 1: //requires tfa
- const { data } = this.state;
-
- if (data.tfaNeeded === true) {
- //TFA removal Request accepted.
- } else {
- data.tfaNeeded = true;
-
- this.setState({ data });
- }
- break;
- case 2: //logged in
- window.location.href = "/";
- break;
- default:
- break; //treat at though not logged in.
- }
- } catch (ex: any) {
- this.handleGeneralError(ex);
- }
- }
-
- render() {
+ useEffect(() => {
window.location.replace("/login");
+ }, []);
- const { tfaNeeded, requestTfaRemoval } = this.state.data;
- const { isInNextStage, data, emailSent, passwordMaxLength } = this.state;
- const result = this.schema.username.validate(data.username);
- const validEmail = result.error === undefined ? true : false;
+ const performLogin = useCallback(
+ async (data: any) => {
+ try {
+ const result = await authentication.login(
+ data.username,
+ data.password,
+ data.securityCode,
+ data.requestTfaRemoval,
+ );
- if (authentication.getCurrentUser()) return ;
+ switch (result) {
+ case 1: {
+ const nextData = { ...form.state.data };
- const requestTfaRemovalPanel = (
-
- An email has been sent to you so that you can regain control of your
- account.
-
- );
+ if (!nextData.tfaNeeded) {
+ nextData.tfaNeeded = true;
+ form.setState({ data: nextData });
+ }
+ break;
+ }
+ case 2:
+ window.location.href = "/";
+ break;
+ default:
+ break;
+ }
+ } catch (ex: any) {
+ form.handleGeneralError(ex);
+ }
+ },
+ [form],
+ );
- const loginPanel = (
- <>
-