aboutsummaryrefslogtreecommitdiff
path: root/packages/aml-backoffice-ui/src/hooks/form.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/aml-backoffice-ui/src/hooks/form.ts')
-rw-r--r--packages/aml-backoffice-ui/src/hooks/form.ts291
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;
}