diff options
Diffstat (limited to 'packages/aml-backoffice-ui/src/hooks')
-rw-r--r-- | packages/aml-backoffice-ui/src/hooks/form.ts | 291 |
1 files changed, 74 insertions, 217 deletions
diff --git a/packages/aml-backoffice-ui/src/hooks/form.ts b/packages/aml-backoffice-ui/src/hooks/form.ts index 033d1d950..e9194d86d 100644 --- a/packages/aml-backoffice-ui/src/hooks/form.ts +++ b/packages/aml-backoffice-ui/src/hooks/form.ts @@ -19,11 +19,14 @@ import { AmountJson, TalerExchangeApi, TranslatedString, - assertUnreachable, } from "@gnu-taler/taler-util"; -import { Addon, InternationalizationAPI, UIFieldBaseDescription, UIFieldHandler, UIFormField, UIFormFieldBaseConfig, UIFormFieldConfig, UIHandlerId } from "@gnu-taler/web-util/browser"; +import { + UIFieldHandler, + UIFormFieldConfig, + UIHandlerId, +} from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; -import { getConverterById } from "../utils/converter.js"; +import { undefinedIfEmpty } from "../pages/CreateAccount.js"; // export type UIField = { // value: string | undefined; @@ -61,10 +64,10 @@ export type FormErrors<T> = { : T[k] extends AmountJson ? TranslatedString : T[k] extends AbsoluteTime - ? TranslatedString - : T[k] extends TalerExchangeApi.AmlState ? TranslatedString - : FormErrors<T[k]>; + : T[k] extends TalerExchangeApi.AmlState + ? TranslatedString + : FormErrors<T[k]>; }; export type FormStatus<T> = @@ -85,17 +88,19 @@ function constructFormHandler<T>( updateForm: (d: RecursivePartial<FormValues<T>>) => void, errors: FormErrors<T> | undefined, ): FormHandler<T> { - const handler = shape.reduce((handleForm, fieldId) => { + const path = fieldId.split("."); - const path = fieldId.split(".") - function updater(newValue: unknown) { updateForm(setValueDeeper(form, path, newValue)); } - const currentValue = getValueDeeper<string>(form as any, path, undefined) - const currentError = getValueDeeper<TranslatedString>(errors as any, path, undefined) + const currentValue = getValueDeeper<string>(form as any, path, undefined); + const currentError = getValueDeeper<TranslatedString>( + errors as any, + path, + undefined, + ); const field: UIFieldHandler = { error: currentError, value: currentValue, @@ -103,14 +108,12 @@ function constructFormHandler<T>( state: {}, //FIXME: add the state of the field (hidden, ) }; - return setValueDeeper(handleForm, path, field) - + return setValueDeeper(handleForm, path, field); }, {} as FormHandler<T>); return handler; } - /** * FIXME: Consider sending this to web-utils * @@ -135,7 +138,7 @@ export function useFormState<T>( interface Tree<T> extends Record<string, Tree<T> | T> {} -function getValueDeeper<T>( +export function getValueDeeper<T>( object: Tree<T> | undefined, names: string[], notFoundValue?: T, @@ -146,225 +149,79 @@ function getValueDeeper<T>( return getValueDeeper(object, rest, notFoundValue); } if (object === undefined) { - return notFoundValue + return notFoundValue; } return getValueDeeper(object[head] as Tree<T>, rest, notFoundValue); } -function getValueDeeper2( - object: Record<string, any>, - names: string[], -): UIFieldHandler { - if (names.length === 0) return object as UIFieldHandler; - const [head, ...rest] = names; - if (!head) { - return getValueDeeper2(object, rest); - } - if (object === undefined) { - throw Error("handler not found"); - } - return getValueDeeper2(object[head], rest); -} - - -function setValueDeeper(object: any, names: string[], value: any): any { +export function setValueDeeper(object: any, names: string[], value: any): any { if (names.length === 0) return value; const [head, ...rest] = names; if (!head) { return setValueDeeper(object, rest, value); } if (object === undefined) { - return { [head]: setValueDeeper({}, rest, value) }; + return undefinedIfEmpty({ [head]: setValueDeeper({}, rest, value) }); } - return { ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) }; -} - -function getAddonById(_id: string | undefined): Addon { - return undefined!; -} - - -function converInputFieldsProps( - form: FormHandler<unknown>, - p: UIFormFieldBaseConfig, -) { - return { - converter: getConverterById(p.converterId, p), - handler: getValueDeeper2(form, p.id.split(".")), - name: p.name, - required: p.required, - disabled: p.disabled, - help: p.help, - placeholder: p.placeholder, - tooltip: p.tooltip, - label: p.label as TranslatedString, - }; + return undefinedIfEmpty({ ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) }); } -function converBaseFieldsProps( - i18n_: InternationalizationAPI, - p: UIFieldBaseDescription, -) { - return { - after: getAddonById(p.addonAfterId), - before: getAddonById(p.addonBeforeId), - hidden: p.hidden, - name: p.name, - help: i18n_.str`${p.help}`, - label: i18n_.str`${p.label}`, - tooltip: i18n_.str`${p.tooltip}`, - }; -} - -export function convertUiField( - i18n_: InternationalizationAPI, - fieldConfig: UIFormFieldConfig[], - form: FormHandler<unknown>, -): UIFormField[] { - return fieldConfig.map((config) => { - // NON input fields - switch (config.type) { - case "caption": { - const resp: UIFormField = { - type: config.type, - properties: converBaseFieldsProps(i18n_, config.properties), - }; - return resp; - } - case "group": { - const resp: UIFormField = { - type: config.type, - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - fields: convertUiField(i18n_, config.properties.fields, form), - }, - }; - return resp; +export function getShapeFromFields( + fields: UIFormFieldConfig[], +): Array<UIHandlerId> { + const shape: Array<UIHandlerId> = []; + fields.forEach((field) => { + if ("id" in field.properties) { + // FIXME: this should be a validation when loading the form + // consistency check + if (shape.indexOf(field.properties.id) !== -1) { + throw Error(`already present: ${field.properties.id}`); } + shape.push(field.properties.id); + } else if (field.type === "group") { + Array.prototype.push.apply( + shape, + getShapeFromFields(field.properties.fields), + ); } - // Input Fields - switch (config.type) { - case "array": { - return { - type: "array", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - labelField: config.properties.labelFieldId, - fields: convertUiField(i18n_, config.properties.fields, form), - }, - } as UIFormField; - } - case "absoluteTime": { - return { - type: "absoluteTime", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; - } - case "amount": { - return { - type: "amount", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; - } - case "choiceHorizontal": { - return { - type: "choiceHorizontal", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - choices: config.properties.choices, - }, - } as UIFormField; - } - case "choiceStacked": { - return { - type: "choiceStacked", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - choices: config.properties.choices, - - }, - }as UIFormField; - } - case "file":{ - console.log("ASDASD", config.properties.accept) - return { - type: "file", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - accept: config.properties.accept, - maxBites: config.properties.maxBytes, - }, - } as UIFormField; - } - case "integer":{ - return { - type: "integer", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; - } - case "selectMultiple":{ - return { - type: "selectMultiple", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - choices: config.properties.choices, - }, - } as UIFormField; - } - case "selectOne": { - return { - type: "selectOne", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - choices: config.properties.choices, - }, - } as UIFormField; - } - case "text": { - return { - type: "text", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; - } - case "textArea": { - return { - type: "text", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; - } - case "toggle": { - return { - type: "toggle", - properties: { - ...converBaseFieldsProps(i18n_, config.properties), - ...converInputFieldsProps(form, config.properties), - }, - } as UIFormField; + }); + return shape; +} + +export function getRequiredFields( + fields: UIFormFieldConfig[], +): Array<UIHandlerId> { + const shape: Array<UIHandlerId> = []; + fields.forEach((field) => { + if ("id" in field.properties) { + // FIXME: this should be a validation when loading the form + // consistency check + if (shape.indexOf(field.properties.id) !== -1) { + throw Error(`already present: ${field.properties.id}`); } - default: { - assertUnreachable(config); + if (!field.properties.required) { + return; } + shape.push(field.properties.id); + } else if (field.type === "group") { + Array.prototype.push.apply( + shape, + getRequiredFields(field.properties.fields), + ); } }); + return shape; +} +export function validateRequiredFields<FormType>( + errors: FormErrors<FormType> | undefined, + form: object, + fields: Array<UIHandlerId>, +): FormErrors<FormType> | undefined { + let result: FormErrors<FormType> | undefined = errors; + fields.forEach((f) => { + const path = f.split("."); + const v = getValueDeeper(form as any, path); + result = setValueDeeper(result, path, !v ? "required" : undefined); + }); + return result; } |