958 lines
34 KiB
TypeScript
958 lines
34 KiB
TypeScript
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;
|