diff options
Diffstat (limited to 'packages/aml-backoffice-ui/src/pages')
-rw-r--r-- | packages/aml-backoffice-ui/src/pages/CaseDetails.tsx | 5 | ||||
-rw-r--r-- | packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx | 169 |
2 files changed, 160 insertions, 14 deletions
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx index 1ad8c9453..62c54d222 100644 --- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx +++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -34,18 +34,17 @@ import { import { DefaultForm, ErrorLoading, - FlexibleForm, InternationalizationAPI, Loading, - useTranslationContext, + useTranslationContext } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { privatePages } from "../Routing.js"; +import { FormMetadata, useUiFormsContext } from "../context/ui-forms.js"; import { useCaseDetails } from "../hooks/useCaseDetails.js"; import { ShowConsolidated } from "./ShowConsolidated.js"; -import { FormMetadata, useUiFormsContext } from "../context/ui-forms.js"; export type AmlEvent = | AmlFormEvent diff --git a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx index 64bfb90f1..f04d404d0 100644 --- a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx +++ b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx @@ -20,12 +20,14 @@ import { HttpStatusCode, TalerExchangeApi, TalerProtocolTimestamp, - assertUnreachable, + assertUnreachable } from "@gnu-taler/taler-util"; import { Button, + InternationalizationAPI, LocalNotificationBanner, RenderAllFieldsByUiConfig, + UIFieldHandler, UIFormField, useExchangeApiContext, useLocalNotificationHandler, @@ -33,12 +35,40 @@ import { } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { privatePages } from "../Routing.js"; -import { UIFormFieldConfig, useUiFormsContext } from "../context/ui-forms.js"; -import { useFormState } from "../hooks/form.js"; +import { + FormMetadata, + UIFormFieldConfig, + useUiFormsContext, +} from "../context/ui-forms.js"; +import { preloadedForms } from "../forms/index.js"; +import { FormHandler, useFormState } from "../hooks/form.js"; import { useOfficer } from "../hooks/officer.js"; import { Justification } from "./CaseDetails.js"; import { HandleAccountNotReady } from "./HandleAccountNotReady.js"; +function searchForm( + i18n: InternationalizationAPI, + forms: FormMetadata[], + formId: string, +): FormMetadata | undefined { + { + const found = forms.find((v) => v.id === formId); + if (found) return found; + } + { + const pf = preloadedForms(i18n); + const found = pf.find((v) => v.id === formId); + if (found) return found; + } + return undefined; +} + +type FormType = { + when: AbsoluteTime; + state: TalerExchangeApi.AmlState; + threshold: AmountJson; +}; + export function CaseUpdate({ account, type: formId, @@ -52,11 +82,10 @@ export function CaseUpdate({ lib: { exchange: api }, } = useExchangeApiContext(); - // const [notification, notify, handleError] = useLocalNotification(); const [notification, withErrorHandler] = useLocalNotificationHandler(); const { config } = useExchangeApiContext(); const { forms } = useUiFormsContext(); - const initial = { + const initial: FormType = { when: AbsoluteTime.now(), state: TalerExchangeApi.AmlState.pending, threshold: Amounts.zeroOfCurrency(config.currency), @@ -65,19 +94,31 @@ export function CaseUpdate({ if (officer.state !== "ready") { return <HandleAccountNotReady officer={officer} />; } - const theForm = forms.find((v) => v.id === formId); + const theForm = searchForm(i18n, forms, formId); if (!theForm) { return <div>form with id {formId} not found</div>; } - const [form, state] = useFormState(initial, (st) => { + let defaultValue = initial + theForm.config.design.forEach((section) => { + section.fields.forEach((field) => { + if ("id" in field.properties) { + const path = field.properties.id.split("."); + defaultValue = setValueDeeper(defaultValue, path, undefined); + } + }); + }); + + + const [form, state] = useFormState<FormType>(defaultValue, (st) => { return { status: "ok", result: st as any, errors: undefined, }; }); - + + console.log("FORM", form) const validatedForm = state.status === "fail" ? undefined : state.result; const submitHandler = @@ -125,8 +166,73 @@ export function CaseUpdate({ }, ); - function convertUiField(_f: UIFormFieldConfig[]): UIFormField[] { - return []; + function convertUiField( + fieldConfig: UIFormFieldConfig[], + form: FormHandler<unknown>, + ): UIFormField[] { + return fieldConfig.map((config) => { + switch (config.type) { + case "absoluteTime": { + return undefined!; + } + case "amount": { + return { + type: "amount", + properties: { + ...(config.properties as any), + handler: getValueDeeper(form, config.properties.id.split(".")), + }, + } as UIFormField; + } + case "array": { + return undefined!; + } + case "caption": { + return undefined!; + } + case "choiceHorizontal": { + return { + type: "choiceHorizontal", + properties: { + handler: getValueDeeper(form, config.properties.id.split(".")), + choices: config.properties.choices, + }, + } as UIFormField; + } + case "choiceStacked": + case "file": + case "group": + case "integer": + case "selectMultiple": + case "selectOne": { + return undefined!; + } + case "text": { + return { + type: "text", + properties: { + ...(config.properties as any), + handler: getValueDeeper(form, config.properties.id.split(".")), + }, + } as UIFormField; + } + case "textArea": { + return { + type: "text", + properties: { + ...(config.properties as any), + handler: getValueDeeper(form, config.properties.id.split(".")), + }, + } as UIFormField; + } + case "toggle": { + return undefined!; + } + default: { + assertUnreachable(config); + } + } + }); } return ( @@ -155,7 +261,7 @@ export function CaseUpdate({ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> <RenderAllFieldsByUiConfig key={i} - fields={convertUiField(section.fields)} + fields={convertUiField(section.fields, form)} /> </div> </div> @@ -185,7 +291,10 @@ export function CaseUpdate({ } export function SelectForm({ account }: { account: string }) { + const { i18n } = useTranslationContext(); const { forms } = useUiFormsContext(); + const pf = preloadedForms(i18n); + return ( <div> <pre>New form for account: {account.substring(0, 16)}...</pre> @@ -200,6 +309,44 @@ export function SelectForm({ account }: { account: string }) { </a> ); })} + {pf.map((form) => { + return ( + <a + key={form.id} + href={privatePages.caseUpdate.url({ cid: account, type: form.id })} + class="m-4 block rounded-md w-fit border-0 p-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-600" + > + {form.label} + </a> + ); + })} </div> ); } + +function getValueDeeper( + object: Record<string, any>, + names: string[], +): UIFieldHandler { + if (names.length === 0) return object as UIFieldHandler; + const [head, ...rest] = names; + if (!head) { + return getValueDeeper(object, rest); + } + if (object === undefined) { + throw Error("handler not found"); + } + return getValueDeeper(object[head], rest); +} + +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 { ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) }; +} |