Form.tsx is gone, useForm hook is the replacement,

This commit is contained in:
Colin Dawson 2026-01-31 17:56:57 +00:00
parent dce95c8345
commit 83cf83b6fc
5 changed files with 741 additions and 1480 deletions

View File

@ -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<P> {
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<P> {
location: LocationProps;
match: Match<P>;
staticContext?: any;
}
class Form<P, FP extends FormProps<P>, FS extends FormState> extends React.Component<FP, FS> {
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<HTMLFormElement>) => {
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<HTMLInputElement>) => {
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<HTMLTextAreaElement>) => {
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<HTMLInputElement>) => {
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<HTMLSelectElement>) => {
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<HTMLInputElement>) => {
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 (
<Button testid={testid} disabled={disabled} name={name ?? label} buttonType={buttonType} onClick={onClick}>
{label}
</Button>
);
}
renderError(name: string) {
const { errors } = this.state;
return <ErrorBlock error={errors[name]} />;
}
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 (
<Input
includeLabel={true}
type={type}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
maxLength={maxLength}
readOnly
placeHolder={placeHolder}
hidden={!visible}
autoComplete={autoComplete}
/>
);
} else {
return (
<Input
includeLabel={true}
type={type}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
maxLength={maxLength}
onChange={this.handleChange}
placeHolder={placeHolder}
hidden={!visible}
autoComplete={autoComplete}
/>
);
}
}
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 (
<Input
includeLabel={true}
type={type}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
maxLength={maxLength}
readOnly
placeHolder={placeHolder}
/>
);
} else {
return (
<Input
includeLabel={true}
type={type}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
maxLength={maxLength}
onChange={handleChangeEvent}
placeHolder={placeHolder}
/>
);
}
}
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 <Input includeLabel={true} type={InputType.number} name={name} label={label} value={cleanValue} error={errors[name]} readOnly />;
} else {
return (
<Input
includeLabel={true}
type={InputType.number}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
min={min}
max={max}
step={step}
onChange={this.handleChange}
/>
);
}
}
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 (
<Input
includeLabel={includeLabel}
type={InputType.textarea}
key={name}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
readOnly
/>
);
} else {
return (
<Input
includeLabel={includeLabel}
type={InputType.textarea}
key={name}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
onTextAreaChange={this.handleTextAreaChange}
/>
);
}
}
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 <Input includeLabel={includeLabel} type={type} name={name} label={label} value={cleanValue} error={errors[name]} readOnly />;
} else {
return (
<Input
includeLabel={includeLabel}
type={type}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
onChange={this.handleCustomFieldChange}
/>
);
}
}
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 <Input includeLabel={includeLabel} type={InputType.number} name={name} label={label} value={cleanValue} error={errors[name]} readOnly />;
} else {
return (
<Input
includeLabel={includeLabel}
type={InputType.number}
name={name}
label={label}
value={cleanValue}
error={errors[name]}
min={min}
max={max}
step={step}
onChange={this.handleCustomFieldChange}
/>
);
}
}
renderTemplateEditor(className: string, name: string, label: string, allowCustomFields: boolean) {
const { data } = this.state;
let value = data[name] as string;
return (
<div>
<label htmlFor={name}>{label}</label>
<TemplateEditor className={className} name={name} data={value} onChange={this.handleTemplateEditorChange} showFields={allowCustomFields} />
</div>
);
}
renderSelect(name: string, label: string, options: Option[]) {
const { data, errors } = this.state;
return <Select name={name} label={label} value={data[name]} options={options} error={errors[name]} onChange={this.handleSelectChange} />;
}
renderToggle(name: string, label: string) {
const { data, errors } = this.state;
return <ToggleSlider name={name} label={label} defaultChecked={Boolean(data[name])} error={errors[name]} onChange={this.handleToggleChange} />;
}
renderSequencePicker(includeLabel: boolean, name: string, label: string) {
const { data, errors } = this.state;
return (
<SequencePicker includeLabel={includeLabel} name={name} label={label} value={data[name]} error={errors[name]} onChange={this.handlePickerChange} />
);
}
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 (
<GlossaryPicker
includeLabel={includeLabel}
name={name}
label={label}
maxEntries={maxEntries}
values={glossaryValues}
error={errors[name]}
rootItem={refElementId}
onChange={this.handleGlossaryPickerChange}
/>
);
}
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 (
<DomainPicker
includeLabel={includeLabel}
name={name}
label={label}
minEntries={minEntries}
maxEntries={maxEntries}
values={domainValues}
error={errors[name]}
onChange={this.handleDomainPickerChange}
/>
);
}
renderTemplatePicker(includeLabel: boolean, name: string, label: string) {
const { data, errors } = this.state;
const templateValue: GeneralIdRef = data[name] as any as GeneralIdRef;
return (
<FormTemplatePicker
includeLabel={includeLabel}
name={name}
label={label}
value={templateValue}
error={errors[name]}
onChange={this.handleTemplateFormPickerChange}
/>
);
}
renderUserPicker(name: string, label: string) {
const { data, errors } = this.state;
const glossaryValue: GeneralIdRef | undefined = data[name] as any as GeneralIdRef;
return <UserPicker name={name} label={label} value={glossaryValue} error={errors[name]} onChange={this.handleUserPickerChange} />;
}
renderSsoProviderPicker(name: string, label: string) {
const { data, errors } = this.state;
const glossaryValue: GeneralIdRef | undefined = data[name] as any as GeneralIdRef;
return <SsoProviderPicker name={name} label={label} value={glossaryValue} error={errors[name]} onChange={this.handleSsoProviderPickerChange} />;
}
renderCustomFieldsEditor(name: string, label: string, selected: CustomField[], onAdd: CustomFieldEditorAdd, onDelete: CustomFieldEditorDelete) {
const { data, errors } = this.state;
return (
<CustomFieldsEditor name={name} label={label} value={data[name] as []} error={errors[name]} exclude={selected} onAdd={onAdd} onDelete={onDelete} />
);
}
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 (
<Expando name={name} title={title} error={errors[name]}>
{content}
</Expando>
);
}
}
export default Form;

View File

@ -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,79 +12,58 @@ 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: {
export interface TemplateFillerHandle {
hasValidationErrors: () => boolean;
Save: () => Promise<any>;
}
interface TemplateState {
name?: string;
templateId?: GeneralIdRef;
version?: bigint;
definition?: string;
};
}
class TemplateFiller extends Form<
TemplateFillerProps,
any,
TemplateFillerState
> {
state: TemplateFillerState = {
const TemplateFiller = forwardRef<TemplateFillerHandle, TemplateFillerProps>(
({ templateId, formInstanceId, onValidationChanged }, ref) => {
const form = useForm({
loaded: false,
customFields: undefined,
template: {
data: {},
errors: {},
redirect: "",
});
const [template, setTemplate] = React.useState<TemplateState>({
name: undefined,
templateId: undefined,
version: undefined,
definition: undefined,
},
data: {},
errors: {},
};
});
schema = {};
const prevHasErrorsRef = useRef<boolean>(false);
componentDidMount(): void {
this.loadTemplate();
}
form.schema = {};
componentDidUpdate(
prevProps: Readonly<TemplateFillerProps>,
prevState: Readonly<TemplateFillerState>,
snapshot?: any,
): void {
if (
prevProps.formInstanceId !== this.props.formInstanceId ||
prevProps.templateId !== this.props.templateId
)
this.loadTemplate();
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();
}
}
}
loadTemplate = async () => {
const { templateId, formInstanceId } = this.props;
const loadTemplate = useCallback(async () => {
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 = await formsService.getForm(
templateId?.id,
templateId?.guid,
);
loadedData.templateId = undefined;
loadedData.customFieldValues = undefined;
loadedData.updatedVersion = undefined;
@ -87,7 +72,6 @@ class TemplateFiller extends Form<
formInstanceId?.id,
formInstanceId?.guid,
);
console.log("formInstanceId", loadedData);
} else {
loadedData = {
name: undefined,
@ -102,23 +86,81 @@ class TemplateFiller extends Form<
};
}
const { template, data } = this.state;
template.name = loadedData.name;
template.templateId = MakeGeneralIdRef(loadedData.id, loadedData.guid);
template.version = loadedData.version;
template.definition = loadedData.definition;
const customFields = loadedData.customFieldDefinitions;
this.setCustomFieldValues(data, loadedData.customFieldValues, customFields);
this.setState({ loaded: true, template, customFields, data });
const newTemplate: TemplateState = {
name: loadedData.name,
templateId: MakeGeneralIdRef(loadedData.id, loadedData.guid),
version: loadedData.version,
definition: loadedData.definition,
};
parseDefinition = (
definition: string,
customFieldDefinitions: CustomField[],
) => {
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);
}, [templateId, formInstanceId, form]);
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;
@ -127,7 +169,15 @@ class TemplateFiller extends Form<
const customField = customFieldDefinitions.filter(
(x) => x.guid === domNodeAsAny.attribs.guid,
)[0];
return this.renderCustomField(customField, false);
return renderCustomField(
customField,
false,
form.state.data,
form.state.errors,
handleCustomFieldChange,
handleCustomFieldPickerChange,
getCustomFieldType,
);
}
} else if (domNodeAsAny.name === "p") {
return (
@ -140,60 +190,68 @@ class TemplateFiller extends Form<
};
return parse(definition, options);
};
},
[
form.state.data,
form.state.errors,
handleCustomFieldChange,
handleCustomFieldPickerChange,
getCustomFieldType,
],
);
hasValidationErrors = (): boolean => {
const { errors } = this.state;
const hasValidationErrors = useCallback((): boolean => {
return Object.keys(form.state.errors).length > 0;
}, [form.state.errors]);
const result = Object.keys(errors).length > 0;
return result;
};
async Save() {
const { errors } = this.state;
const { templateId, version } = this.state.template;
const { formInstanceId } = this.props;
const Save = useCallback(async () => {
const { errors } = form.state;
if (Object.keys(errors).length > 0) {
toast.error("There are errors on the form");
throw new Error("There are errors on the form");
}
const customFieldValues = this.CustomFieldValues();
const customFieldValues = form.CustomFieldValues();
if (formInstanceId !== undefined) {
if (templateId === undefined) throw Error("TemplateId cannot be null");
if (version === undefined) throw Error("Version cannot be null");
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,
version,
templateId: template.templateId,
version: template.version,
customFieldValues,
};
await formsService.putFormInstance(editFormInstance);
} else {
if (templateId !== undefined && version !== undefined) {
//const customFieldValues = this.CustomFieldValues();
if (
template.templateId !== undefined &&
template.version !== undefined
) {
const formInstance: CreateFormInstance = {
templateId,
version,
templateId: template.templateId,
version: template.version,
customFieldValues,
};
return await formsService.postFormInstance(formInstance);
} else throw new Error("template unknown");
}
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<
<div className="ck-content form-editor">{parsedDefinition}</div>
</Loading>
);
}
}
},
);
TemplateFiller.displayName = "TemplateFiller";
export default TemplateFiller;

View File

@ -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<any, any, LoginFormState> {
state = {
const form = useForm({
loaded: true,
passwordMaxLength: 255,
isInNextStage: false,
emailSent: false,
data: {
username: "",
password: "",
@ -36,9 +26,10 @@ class LoginForm extends Form<any, any, LoginFormState> {
securityCode: "",
},
errors: {},
};
redirect: "",
});
schema = {
const schema = {
username: Joi.string()
.required()
.email({ tlds: { allow: false } })
@ -49,46 +40,16 @@ class LoginForm extends Form<any, any, LoginFormState> {
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);
}
};
useEffect(() => {
window.location.replace("/login");
}, []);
handleForgetPassword = async () => {
const performLogin = useCallback(
async (data: any) => {
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(
const result = await authentication.login(
data.username,
data.password,
data.securityCode,
@ -96,35 +57,65 @@ class LoginForm extends Form<any, any, LoginFormState> {
);
switch (result) {
case 1: //requires tfa
const { data } = this.state;
case 1: {
const nextData = { ...form.state.data };
if (data.tfaNeeded === true) {
//TFA removal Request accepted.
} else {
data.tfaNeeded = true;
this.setState({ data });
if (!nextData.tfaNeeded) {
nextData.tfaNeeded = true;
form.setState({ data: nextData });
}
break;
case 2: //logged in
}
case 2:
window.location.href = "/";
break;
default:
break; //treat at though not logged in.
break;
}
} catch (ex: any) {
this.handleGeneralError(ex);
form.handleGeneralError(ex);
}
},
[form],
);
const doSubmit = async () => {
const { data } = form.state;
await performLogin(data);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
form.handleSubmit(e, async () => doSubmit());
};
const handleNextClick = async () => {
const data = { ...form.state.data };
const validationResult = schema.username.validate(data.username);
if (validationResult.error === undefined) {
setIsInNextStage(true);
}
};
render() {
window.location.replace("/login");
const handleForgetPassword = async () => {
try {
await authentication.forgotPassword(form.state.data.username as string);
setEmailSent(true);
const nextData = { ...form.state.data, username: "", password: "" };
form.setState({ data: nextData });
} catch (ex: any) {
form.handleGeneralError(ex);
}
};
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 authenticationWorkAround = async () => {
const data = { ...form.state.data, requestTfaRemoval: true };
await performLogin(data);
form.setState({ data });
};
const { tfaNeeded, requestTfaRemoval } = form.state.data as any;
const result = schema.username.validate(form.state.data.username);
const validEmail = result.error === undefined;
if (authentication.getCurrentUser()) return <Navigate to="/" />;
@ -137,43 +128,51 @@ class LoginForm extends Form<any, any, LoginFormState> {
const loginPanel = (
<>
<form onSubmit={this.handleSubmit}>
{this.renderInput(
<form onSubmit={handleSubmit}>
{renderInput(
"username",
"",
form.state.data,
form.state.errors,
InputType.text,
isInNextStage,
undefined,
"",
"Email",
undefined,
undefined,
0,
true,
"username",
form.handleChange,
)}
{this.renderInput(
{renderInput(
"password",
"",
form.state.data,
form.state.errors,
InputType.password,
emailSent,
undefined,
"",
"Password",
passwordMaxLength,
isInNextStage,
"current-password",
form.handleChange,
)}
{!isInNextStage &&
this.renderButton(
renderButton(
"Next",
"login",
this.handleNextClick,
form.state.errors,
"next",
handleNextClick,
"login",
validEmail,
ButtonType.primary,
true,
)}
{isInNextStage && (
<div className="clickables">
{this.renderButton(
{renderButton(
"Login",
form.state.errors,
"login",
undefined,
"login",
@ -184,10 +183,11 @@ class LoginForm extends Form<any, any, LoginFormState> {
</form>
{isInNextStage && (
<div className="forgottenLink">
{this.renderButton(
{renderButton(
"Forgotten Password",
form.state.errors,
"forgot-password",
this.handleForgetPassword,
handleForgetPassword,
"forgot-password",
validEmail,
ButtonType.secondary,
@ -200,16 +200,29 @@ class LoginForm extends Form<any, any, LoginFormState> {
If you have a registered account, you will receive an email.
</div>
)}
{this.renderError("_general")}
{renderError("_general", form.state.errors)}
</>
);
const tfaPanel = (
<form onSubmit={this.handleSubmit}>
{this.renderError("_general")}
{this.renderInput("securityCode", "Authenticate")}
{this.renderButton("Authenticate")}
<Link to="#" onClick={this.authenticationWorkAround}>
<form onSubmit={handleSubmit}>
{renderError("_general", form.state.errors)}
{renderInput(
"securityCode",
"Authenticate",
form.state.data,
form.state.errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
{renderButton("Authenticate", form.state.errors, "authenticate")}
<Link to="#" onClick={authenticationWorkAround}>
My Authenticator is not working
</Link>
</form>
@ -224,7 +237,6 @@ class LoginForm extends Form<any, any, LoginFormState> {
: loginPanel}
</div>
);
}
}
};
export default LoginForm;

View File

@ -5,7 +5,9 @@ import { toast } from "react-toastify";
import { useForm } from "../../../components/common/useForm";
import { InputType } from "../../../components/common/Input";
import Loading from "../../../components/common/Loading";
import TemplateFiller from "../../../components/common/TemplateFiller";
import TemplateFiller, {
TemplateFillerHandle,
} from "../../../components/common/TemplateFiller";
import { GeneralIdRef, MakeGeneralIdRef } from "../../../utils/GeneralIdRef";
import {
CustomFieldValue,
@ -31,7 +33,7 @@ const SpecificationsDetails: React.FC<SpecificationsDetailsProps> = ({
siteId: string;
specificationId?: string;
}>();
const TemplateFillerRef = useRef<TemplateFiller>(null);
const TemplateFillerRef = useRef<TemplateFillerHandle>(null);
const labelName = "Name";
const labelPrintSpecification = "Print Specification";
@ -257,7 +259,9 @@ const SpecificationsDetails: React.FC<SpecificationsDetailsProps> = ({
<TemplateFiller
templateId={formTemplate}
formInstanceId={form.state.data.formInstanceId}
formInstanceId={
form.state.data.formInstanceId as GeneralIdRef | undefined
}
ref={TemplateFillerRef}
onValidationChanged={handleValidationChanged}
/>

View File

@ -1,33 +1,38 @@
import React from "react";
import React, { useEffect, useState } from "react";
import Joi from "joi";
import { toast } from "react-toastify";
import Form, { FormState, FormData } from "../../components/common/Form";
import { useForm } from "../../components/common/useForm";
import profileService from "./services/profileService";
import { InputType } from "../../components/common/Input";
import { TwoFactorAuthenticationSettings } from "./models/TwoFactorAuthenticationSettings";
import Loading from "../../components/common/Loading";
import {
renderError,
renderButton,
renderInput,
renderToggle,
renderDropSection,
} from "../../components/common/formHelpers";
export interface ProfileStateData extends FormData {
firstName: string;
middleNames: string;
lastName: string;
email: string;
newPassword: string;
confirmPassword: string;
originalUsingTwoFactorAuthentication: boolean;
usingTwoFactorAuthentication: boolean;
tfaCode: string;
}
const Profile: React.FC = () => {
const labelFirstName = "First Name";
const labelMiddleNames = "Middle Name(s)";
const labelLastName = "Last Name";
const labelEmail = "E-Mail";
const labelNewPassword = "New Password";
const labelConfirmPassword = "Confirm Password";
const labelUsingTwoFactorAuthentication = "2 Factor Authentication";
const labelTfaCode = "Authentication code";
const labelApply = "Save";
export interface ProfileState extends FormState {
data: ProfileStateData;
twoFactorAuthenticationSettings: TwoFactorAuthenticationSettings;
}
const [twoFactorAuthenticationSettings, setTwoFactorAuthenticationSettings] =
useState<TwoFactorAuthenticationSettings>({
manualEntrySetupCode: "",
qrCodeImageUrl: "",
});
class Profile extends Form<any, any, ProfileState> {
state = {
const form = useForm({
loaded: false,
passwordMaxLenght : 255,
data: {
firstName: "",
middleNames: "",
@ -37,35 +42,21 @@ class Profile extends Form<any, any, ProfileState> {
confirmPassword: "",
originalUsingTwoFactorAuthentication: false,
usingTwoFactorAuthentication: false,
tfaCode: ""
tfaCode: "",
},
errors: {},
twoFactorAuthenticationSettings: {
manualEntrySetupCode: "",
qrCodeImageUrl: "",
},
};
redirect: "",
});
labelFirstName = "First Name";
labelMiddleNames = "Middle Name(s)";
labelLastName = "Last Name";
labelEmail = "E-Mail";
labelNewPassword = "New Password";
labelConfirmPassword = "Confirm Password";
labelUsingTwoFactorAuthentication = "2 Factor Authentication";
labelTfaCode = "Authentication code";
labelApply = "Save";
schema = {
firstName: Joi.string().required().label(this.labelFirstName),
middleNames: Joi.string().allow("").required().label(this.labelMiddleNames),
lastName: Joi.string().required().label(this.labelLastName),
form.schema = {
firstName: Joi.string().required().label(labelFirstName),
middleNames: Joi.string().allow("").required().label(labelMiddleNames),
lastName: Joi.string().required().label(labelLastName),
email: Joi.string()
.required()
.email({ tlds: { allow: false } })
.label(this.labelEmail),
newPassword: Joi.string().allow("").min(5).label(this.labelNewPassword),
.label(labelEmail),
newPassword: Joi.string().allow("").min(5).label(labelNewPassword),
confirmPassword: Joi.string()
.when("newPassword", {
is: "",
@ -76,11 +67,11 @@ class Profile extends Form<any, any, ProfileState> {
return e;
}),
})
.label(this.labelConfirmPassword),
.label(labelConfirmPassword),
originalUsingTwoFactorAuthentication: Joi.boolean().required(),
usingTwoFactorAuthentication: Joi.boolean().required().label(this.labelUsingTwoFactorAuthentication),
usingTwoFactorAuthentication: Joi.boolean()
.required()
.label(labelUsingTwoFactorAuthentication),
tfaCode: Joi.string()
.when("originalUsingTwoFactorAuthentication", {
is: Joi.ref("usingTwoFactorAuthentication"),
@ -91,21 +82,30 @@ class Profile extends Form<any, any, ProfileState> {
.length(6)
.required()
.error(() => {
const e = new Error("You must enter the code from the authenicator");
const e = new Error(
"You must enter the code from the authenicator",
);
e.name = "tfaCode";
return e;
}),
otherwise: Joi.allow("").optional(),
}),
})
.label(this.labelTfaCode),
.label(labelTfaCode),
};
async componentDidMount() {
const loadProfile = async () => {
try {
const profile = await profileService.getMyProfile();
if (profile) {
const { firstName, middleNames, lastName, email, usingTwoFactorAuthentication, twoFactorAuthenticationSettings } = profile;
const {
firstName,
middleNames,
lastName,
email,
usingTwoFactorAuthentication,
twoFactorAuthenticationSettings,
} = profile;
const data = {
firstName,
@ -116,39 +116,61 @@ class Profile extends Form<any, any, ProfileState> {
confirmPassword: "",
originalUsingTwoFactorAuthentication: usingTwoFactorAuthentication,
usingTwoFactorAuthentication,
tfaCode: ""
tfaCode: "",
};
this.setState({ loaded: true, data, twoFactorAuthenticationSettings });
}
}
catch(ex: any) {
this.handleGeneralError(ex);
form.setState({ loaded: true, data });
setTwoFactorAuthenticationSettings(twoFactorAuthenticationSettings);
}
} catch (ex: any) {
form.handleGeneralError(ex);
}
};
doSubmit = async (buttonName : string) => {
useEffect(() => {
void loadProfile();
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const doSubmit = async (buttonName: string) => {
try {
const { firstName, middleNames, lastName, email, usingTwoFactorAuthentication, tfaCode, newPassword, confirmPassword } = this.state.data;
const {
firstName,
middleNames,
lastName,
email,
usingTwoFactorAuthentication,
tfaCode,
newPassword,
confirmPassword,
} = form.state.data;
let password = "";
if (newPassword === confirmPassword) password = newPassword as string;
if (newPassword === confirmPassword) password = newPassword;
const response = await profileService.putMyProfile(firstName, middleNames, lastName, email, usingTwoFactorAuthentication, tfaCode, password);
const response = await profileService.putMyProfile(
firstName as string,
middleNames as string,
lastName as string,
email as string,
usingTwoFactorAuthentication as boolean,
tfaCode as string,
password,
);
if (response) {
await this.componentDidMount();
await loadProfile();
toast.info("Your profile settings have been saved");
}
}
catch(ex: any) {
this.handleGeneralError(ex);
} catch (ex: any) {
form.handleGeneralError(ex);
}
};
render() {
const { loaded, twoFactorAuthenticationSettings, passwordMaxLenght } = this.state;
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
form.handleSubmit(e, doSubmit);
};
const { usingTwoFactorAuthentication, newPassword } = this.state.data;
const { loaded } = form.state;
const { usingTwoFactorAuthentication, newPassword } = form.state.data;
const passwordMaxLength = 255;
const tfaEnabled = usingTwoFactorAuthentication ? "Enabled" : "Disabled";
@ -157,48 +179,168 @@ class Profile extends Form<any, any, ProfileState> {
tfaImageBlock = (
<React.Fragment>
<label>{twoFactorAuthenticationSettings.manualEntrySetupCode}</label>
<img src={twoFactorAuthenticationSettings.qrCodeImageUrl} alt={twoFactorAuthenticationSettings.manualEntrySetupCode} />
<img
src={twoFactorAuthenticationSettings.qrCodeImageUrl}
alt={twoFactorAuthenticationSettings.manualEntrySetupCode}
/>
</React.Fragment>
);
let tfaSection : JSX.Element;
tfaSection = (
const tfaSection = (
<div>
{this.renderToggle("usingTwoFactorAuthentication", this.labelUsingTwoFactorAuthentication)}
{renderToggle(
"usingTwoFactorAuthentication",
labelUsingTwoFactorAuthentication,
form.state.data,
form.state.errors,
form.handleToggleChange,
)}
{tfaImageBlock}
{this.renderInput("tfaCode", this.labelTfaCode)}
{renderInput(
"tfaCode",
labelTfaCode,
form.state.data,
form.state.errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
</div>
);
let passwordSection = <React.Fragment>{this.renderInput("newPassword", this.labelNewPassword, InputType.password)}</React.Fragment>;
let passwordSection = (
<React.Fragment>
{renderInput(
"newPassword",
labelNewPassword,
form.state.data,
form.state.errors,
InputType.password,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
</React.Fragment>
);
if (newPassword !== "")
passwordSection = (
<React.Fragment>
{this.renderInput("newPassword", this.labelNewPassword, InputType.password, false, undefined, "", passwordMaxLenght)}
{this.renderInput("confirmPassword", this.labelConfirmPassword, InputType.password, false, undefined, "", passwordMaxLenght)}
{renderInput(
"newPassword",
labelNewPassword,
form.state.data,
form.state.errors,
InputType.password,
false,
"",
"",
passwordMaxLength,
true,
undefined,
form.handleChange,
)}
{renderInput(
"confirmPassword",
labelConfirmPassword,
form.state.data,
form.state.errors,
InputType.password,
false,
"",
"",
passwordMaxLength,
true,
undefined,
form.handleChange,
)}
</React.Fragment>
);
return (
<Loading loaded={loaded}>
<h1>Profile</h1>
<form onSubmit={this.handleSubmit}>
{this.renderError("_general")}
{this.renderInput("email", this.labelEmail, InputType.text, true)}
{this.renderInput("firstName", this.labelFirstName)}
{this.renderInput("middleNames", this.labelMiddleNames)}
{this.renderInput("lastName", this.labelLastName)}
<form onSubmit={handleSubmit}>
{renderError("_general", form.state.errors)}
{renderInput(
"email",
labelEmail,
form.state.data,
form.state.errors,
InputType.text,
true,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
{renderInput(
"firstName",
labelFirstName,
form.state.data,
form.state.errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
{renderInput(
"middleNames",
labelMiddleNames,
form.state.data,
form.state.errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
{renderInput(
"lastName",
labelLastName,
form.state.data,
form.state.errors,
InputType.text,
false,
"",
"",
0,
true,
undefined,
form.handleChange,
)}
{passwordSection}
{this.renderDropSection("turnOnTfa", <label>Two Factor Authentication {tfaEnabled}</label>, tfaSection)}
{renderDropSection(
"turnOnTfa",
<label>Two Factor Authentication {tfaEnabled}</label>,
tfaSection,
form.state.errors,
)}
<br />
{this.renderButton(this.labelApply)}
{renderButton(labelApply, form.state.errors, "apply")}
</form>
</Loading>
);
}
}
};
export default Profile;