Form.tsx is gone, useForm hook is the replacement,
This commit is contained in:
parent
dce95c8345
commit
83cf83b6fc
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
|
||||
@ -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)}
|
||||
<br/>
|
||||
{this.renderButton(this.labelApply)}
|
||||
{renderDropSection(
|
||||
"turnOnTfa",
|
||||
<label>Two Factor Authentication {tfaEnabled}</label>,
|
||||
tfaSection,
|
||||
form.state.errors,
|
||||
)}
|
||||
<br />
|
||||
{renderButton(labelApply, form.state.errors, "apply")}
|
||||
</form>
|
||||
</Loading>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Profile;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user