From 83cf83b6fc900bba3120336b8f1915f5621fe0d6 Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Sat, 31 Jan 2026 17:56:57 +0000 Subject: [PATCH] Form.tsx is gone, useForm hook is the replacement, --- src/components/common/Form.tsx | 957 ------------------ src/components/common/TemplateFiller.tsx | 358 ++++--- src/modules/frame/components/LoginForm.tsx | 388 +++---- .../specifications/SpecificationsDetails.tsx | 10 +- src/modules/profile/Profile.tsx | 508 ++++++---- 5 files changed, 741 insertions(+), 1480 deletions(-) delete mode 100644 src/components/common/Form.tsx diff --git a/src/components/common/Form.tsx b/src/components/common/Form.tsx deleted file mode 100644 index de63740..0000000 --- a/src/components/common/Form.tsx +++ /dev/null @@ -1,957 +0,0 @@ -import React from "react"; -import Joi from "joi"; -import Input, { InputType } from "./Input"; -import ToggleSlider from "./ToggleSlider"; -import Select from "./Select"; -import Option from "./option"; -import { GeneralIdRef } from "../../utils/GeneralIdRef"; -import SequencePicker from "../pickers/SequencePicker"; -import GlossaryPicker from "../pickers/GlossaryPicker"; -import CustomFieldsEditor, { CustomFieldEditorAdd, CustomFieldEditorDelete } from "./CustomFieldsEditor"; -import { CustomField, numberParams, textParams } from "../../modules/manager/customfields/services/customFieldsService"; -import FormTemplatePicker from "../pickers/FormTemplatePicker"; -import { CustomFieldValue, CustomFieldValues, Glossary } from "../../modules/manager/glossary/services/glossaryService"; -import TemplateEditor from "./TemplateEditor"; -import DomainPicker from "../pickers/DomainPicker"; -import UserPicker from "../pickers/UserPicker"; -import Button, { ButtonType } from "./Button"; -import Expando from "./expando"; -import ErrorBlock from "./ErrorBlock"; -import SsoProviderPicker from "../pickers/SsoProviderPicker"; - -export interface FormError { - [key: string]: string; -} - -export interface FormData { - [key: string]: string | number | boolean | CustomFieldValue[] | GeneralIdRef | CustomField[] | bigint | Glossary | null | undefined; -} - -export interface businessValidationError { - path: string; - message: string; -} - -export interface businessValidationResult { - details: businessValidationError[]; -} - -export interface propertyValue { - name: string; - value: string | boolean | number; -} - -export interface joiSchema { - [key: string]: object; -} - -export interface FormState { - loaded: boolean; - data: FormData; - customFields?: CustomField[]; - errors: FormError; - redirect?: string; -} - -export interface Match

{ - params: P; - isExact: boolean; - path: string; - url: string; -} - -export interface State { - from: Location; -} - -export interface LocationProps { - hash: string; - pathname: string; - search: string; - state: State; -} - -export interface FormProps

{ - location: LocationProps; - match: Match

; - staticContext?: any; -} - -class Form, FS extends FormState> extends React.Component { - schema: joiSchema = {}; - - validate = (data: FormData) => { - let options: Joi.ValidationOptions = { - context: {}, - abortEarly: false, - }; - - const { customFields } = this.state; - - let schema = this.schema; - if (customFields !== undefined) { - for (const customfield of customFields) { - const name = "customfield_" + customfield.id; - switch (customfield.fieldType) { - case "Number": - if (customfield.parameters !== undefined) { - const parameters: numberParams = JSON.parse(customfield.parameters!); - - options.context![name + "_minEntries"] = customfield.minEntries; - if (parameters.minValue) options.context![name + "_minValue"] = Number(parameters.minValue); - if (parameters.maxValue) options.context![name + "_maxValue"] = Number(parameters.maxValue); - - let minCheck = options.context![name + "_minValue"] - ? Joi.number() - .empty("") - .min(options.context![name + "_minValue"]) - : Joi.number().empty(""); - let maxCheck = options.context![name + "_maxValue"] - ? Joi.number() - .empty("") - .max(options.context![name + "_maxValue"]) - : Joi.number().empty(""); - - schema[name] = Joi.array() - .min(1) - .items( - Joi.object({ - displayValue: Joi.string().allow(""), - value: Joi.when("$" + name + "_minEntries", { - is: 0, - then: Joi.number().empty(""), - otherwise: Joi.number().required(), - }) - .when("$" + name + "_minValue", { - is: Joi.number(), - then: minCheck, - }) - .when("$" + name + "_maxValue", { - is: Joi.number(), - then: maxCheck, - }) - .label(customfield.name), - }) - ); - } else { - schema[name] = Joi.optional().label(customfield.name); - } - break; - default: - schema[name] = Joi.optional().label(customfield.name); - } - } - } - - const joiSchema = Joi.object(schema); - const { error } = joiSchema.validate(data, options); - let errors: FormError = {}; - if (error) { - if (error.details === undefined) { - errors[error.name] = error.message; - } else { - for (let item of error.details) { - errors[item.path[0]] = item.message; - } - } - } - - return errors; - }; - - GetCustomFieldValues = (customField: CustomField) => { - const name = "customfield_" + customField.id; - - const { data } = this.state; - - const codedValue = data[name]; - - let values: CustomFieldValue[] = []; - - switch (customField.fieldType) { - case "FormTemplate": - if (codedValue !== undefined) { - const formTemplateValue = { value: JSON.stringify(codedValue as GeneralIdRef) }; - values.push(formTemplateValue); - } - break; - case "Sequence": - if (codedValue !== undefined) { - values = codedValue as CustomFieldValue[]; - } - break; - case "Glossary": - if (codedValue !== undefined) { - values = codedValue as CustomFieldValue[]; - } - break; - case "Domain": - if (codedValue !== undefined) { - values = codedValue as CustomFieldValue[]; - } - break; - case "Text": - const textParameters: textParams = JSON.parse(customField.parameters!); - if (textParameters.multiLine) { - const textValue = { - value: codedValue === undefined ? customField.defaultValue : codedValue, - displayValue: codedValue === undefined ? customField.defaultValue : codedValue, - } as CustomFieldValue; - values.push(textValue); - } else { - if (codedValue === undefined) { - const numberValue = { - value: customField.defaultValue, - displayValue: customField.defaultValue, - } as CustomFieldValue; - values.push(numberValue); - } else { - values = codedValue as CustomFieldValue[]; - } - } - break; - case "Number": - if (codedValue === undefined) { - const numberValue = { - value: customField.defaultValue, - displayValue: customField.defaultValue, - } as CustomFieldValue; - values.push(numberValue); - } else { - values = codedValue as CustomFieldValue[]; - } - - break; - default: - const textValue = { value: codedValue === undefined ? customField.defaultValue : String((codedValue as CustomFieldValue[])[0].displayValue) }; - values.push(textValue); - break; - } - - return values; - }; - - CustomFieldValues = () => { - const { customFields } = this.state; - - let result: CustomFieldValues[] = []; - - if (customFields === undefined) { - return result; - } - - for (const customfield of customFields) { - const values = this.GetCustomFieldValues(customfield); - - const id: GeneralIdRef = { - id: customfield.id, - guid: customfield.guid, - }; - - const newItem: CustomFieldValues = { - id, - values, - }; - - result.push(newItem); - } - - return result; - }; - - setCustomFieldValues(data: object, customFieldValues: CustomFieldValues[], customFields: CustomField[]) { - if (customFieldValues !== undefined) { - for (const x of customFieldValues) { - const customfieldName = "customfield_" + x.id.id; - - switch (this.getCustomFieldType(x, customFields).toLowerCase()) { - case "glossary": - case "domain": - case "number": - case "text": - (data as any)[customfieldName] = x.values.map((x) => { - return { - displayValue: x.displayValue, - value: x.value, - }; - }); - break; - case "formtemplate": - case "multilinetext": - (data as any)[customfieldName] = x.values[0].value; - break; - default: - (data as any)[customfieldName] = x.values; - break; - } - } - } - } - - getCustomFieldType = (field: CustomFieldValues, childCustomFieldDefinition: CustomField[]): string => { - const fieldDefinition = childCustomFieldDefinition.filter((x) => x.id === field.id.id)[0]; - - if (fieldDefinition.parameters) { - const textParameters: textParams = JSON.parse(fieldDefinition.parameters!); - if (textParameters.multiLine) return "multilinetext"; - } - - return fieldDefinition.fieldType; - }; - - handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - - const submitEvent = e.nativeEvent as SubmitEvent; - - const submitter = submitEvent.submitter as any; - - const errors = this.validate(this.state.data); - this.setState({ errors: errors }); - - const disabled = Object.keys(errors).length > 0; - - if (disabled) return; - - this.doSubmit(submitter.name); - }; - - doSubmit = async (buttonName: string) => {}; - - handleGeneralError = (ex: any) => { - const errors: FormError = { ...this.state.errors }; - - if (ex.response) { - errors._general = ex.response.data.detail; - } else { - errors._general = ex.message; - } - - this.setState({ errors }); - }; - - handleChange = (e: React.ChangeEvent) => { - const input = e.currentTarget; - - const data: FormData = { ...this.state.data }; - - if ((input as any).type === InputType.checkbox) { - data[input.name] = !data[input.name]; - } else data[input.name] = input.value; - - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleTextAreaChange = (e: React.ChangeEvent) => { - const input = e.currentTarget; - - const data: FormData = { ...this.state.data }; - - data[input.name] = input.value; - - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleCustomFieldChange = (e: React.ChangeEvent) => { - const input = e.currentTarget; - - const data: FormData = { ...this.state.data }; - - switch ((input as any).type) { - case InputType.checkbox: - data[input.name] = !data[input.name]; - break; - default: - const customFieldValue: CustomFieldValue = { - displayValue: input.value, - value: input.value, - }; - - data[input.name] = [customFieldValue]; - break; - } - - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleTemplateEditorChange = (name: string, value: string) => { - const data: FormData = { ...this.state.data }; - - data[name] = value; - - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleSelectChange = (e: React.ChangeEvent) => { - const input = e.currentTarget; - - const data: FormData = { ...this.state.data }; - data[input.name] = input.value; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handlePickerChange = (name: string, value: GeneralIdRef) => { - const data: FormData = { ...this.state.data }; - data[name] = value; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleDomainPickerChange = (name: string, values: CustomFieldValue[]) => { - const data: FormData = { ...this.state.data }; - data[name] = values; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleGlossaryPickerChange = (name: string, values: CustomFieldValue[]) => { - const data: FormData = { ...this.state.data }; - data[name] = values; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleTemplateFormPickerChange = (name: string, value: GeneralIdRef) => { - const data: FormData = { ...this.state.data }; - data[name] = value; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleUserPickerChange = (name: string, value: GeneralIdRef) => { - const data: FormData = { ...this.state.data }; - data[name] = value; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleSsoProviderPickerChange = (name: string, value: GeneralIdRef) => { - const data: FormData = { ...this.state.data }; - data[name] = value; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - handleToggleChange = (e: React.ChangeEvent) => { - const input = e.currentTarget; - const { name, checked } = input; - - const data: FormData = { ...this.state.data }; - data[name] = checked; - const errors = this.validate(data); - - this.setState({ data, errors }); - }; - - renderButton( - label: string, - name?: string, - onClick?: (keyValue: any) => {}, - testid?: string, - enabled: boolean = true, - buttonType: ButtonType = ButtonType.primary, - overrideErrorChecking: boolean = false - ) { - const { errors } = this.state; - - let disabled = !enabled || Object.keys(errors).filter((x) => !x.startsWith("_")).length > 0; - - if (overrideErrorChecking) disabled = !enabled; - - return ( - - ); - } - - renderError(name: string) { - const { errors } = this.state; - - return ; - } - - renderInput( - name: string, - label: string, - type: InputType = InputType.text, - readOnly = false, - defaultValue: string = "", - placeHolder: string = "", - maxLength: number = 0, - visible: boolean = true, - autoComplete: string | undefined = undefined - ) { - const { data, errors } = this.state; - - let value = data[name]; - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } else if (typeof value === "number") { - cleanValue = String(value); - } else if (typeof value === "boolean") { - cleanValue = String(value); - } else if (value as CustomFieldValue) { - cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString(); - } - - if (readOnly) { - return ( - - ); - } else { - return ( - - ); - } - } - - renderInputWithChangeEvent( - name: string, - label: string, - type: InputType = InputType.text, - readOnly = false, - handleChangeEvent: any, - defaultValue: string = "", - placeHolder: string = "", - maxLength: number = 0 - ) { - const { data, errors } = this.state; - - let value = data[name]; - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } else if (typeof value === "number") { - cleanValue = String(value); - } else if (typeof value === "boolean") { - cleanValue = String(value); - } else if (value as CustomFieldValue) { - cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString(); - } - - if (readOnly) { - return ( - - ); - } else { - return ( - - ); - } - } - - renderInputNumber(name: string, label: string, readOnly = false, defaultValue: string = "", min?: number, max?: number, step: number = 1) { - const { data, errors } = this.state; - - let value = data[name]; - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } else if (typeof value === "number") { - cleanValue = String(value); - } else if (typeof value === "boolean") { - cleanValue = String(value); - } else if (value as CustomFieldValue) { - cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value.toString(); - } - - if (readOnly) { - return ; - } else { - return ( - - ); - } - } - - renderInputTextarea(includeLabel: boolean, name: string, label: string, readOnly = false, defaultValue: string = "") { - const { data, errors } = this.state; - - let value = data[name]; - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } - - if (readOnly) { - return ( - - ); - } else { - return ( - - ); - } - } - - renderCustomFieldInput(includeLabel: boolean, name: string, label: string, type: InputType = InputType.text, readOnly = false, defaultValue: string = "") { - const { data, errors } = this.state; - - let value = data[name]; - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } else if (typeof value === "number") { - cleanValue = String(value); - } else if (typeof value === "boolean") { - cleanValue = String(value); - } else if (value as CustomFieldValue) { - const customFieldValue = value as CustomFieldValue[]; - cleanValue = customFieldValue[0].displayValue ?? customFieldValue[0].value?.toString(); - } - - if (readOnly) { - return ; - } else { - return ( - - ); - } - } - - renderCustomFieldNumber( - includeLabel: boolean, - name: string, - label: string, - readOnly = false, - defaultValue: string = "", - min?: number, - max?: number, - step: number = 1 - ) { - const { data, errors } = this.state; - - let values: CustomFieldValue[] = data[name] as CustomFieldValue[]; - - let value: CustomFieldValue | undefined = undefined; - - if (values) { - if (values.length > 0) { - value = values[0]; - } - } - - let cleanValue: string | undefined; - if (value === undefined) { - cleanValue = defaultValue; - } else if (typeof value === "string") { - cleanValue = value as string; - } else if (typeof value === "number") { - cleanValue = String(value); - } else if (value as CustomFieldValue) { - cleanValue = (value as CustomFieldValue).displayValue ?? (value as CustomFieldValue).value?.toString(); - } - - if (readOnly) { - return ; - } else { - return ( - - ); - } - } - - renderTemplateEditor(className: string, name: string, label: string, allowCustomFields: boolean) { - const { data } = this.state; - - let value = data[name] as string; - - return ( -

- - -
- ); - } - - renderSelect(name: string, label: string, options: Option[]) { - const { data, errors } = this.state; - - return