taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit ca62771fca54128c0fd0c1378a4db87c6da5d0fb
parent b1394a32c8245d05ef04e598705cd94ebaf68ccd
Author: Sebastian <sebasjm@gmail.com>
Date:   Fri, 10 Jan 2025 15:50:25 -0300

use the new form implementation in kyc

Diffstat:
Mpackages/kyc-ui/src/forms/VQF_902_1.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_11.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_12.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_13.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_14.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_15.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_4.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_5.ts | 8+++++---
Mpackages/kyc-ui/src/forms/VQF_902_9.ts | 8+++++---
Mpackages/kyc-ui/src/forms/accept-tos.ts | 5++---
Mpackages/kyc-ui/src/forms/nameAndBirthdate.ts | 6+++---
Mpackages/kyc-ui/src/forms/personal-info.ts | 6+++---
Mpackages/kyc-ui/src/forms/simplest.ts | 8+++++---
Mpackages/kyc-ui/src/pages/FillForm.tsx | 173++++---------------------------------------------------------------------------
Mpackages/kyc-ui/src/pages/TriggerKyc.tsx | 157++++++++++---------------------------------------------------------------------
15 files changed, 85 insertions(+), 342 deletions(-)

diff --git a/packages/kyc-ui/src/forms/VQF_902_1.ts b/packages/kyc-ui/src/forms/VQF_902_1.ts @@ -1,8 +1,8 @@ import { InternationalizationAPI, - DoubleColumnForm, UIHandlerId, SelectUiChoice, + DoubleColumnFormDesign, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; @@ -14,10 +14,12 @@ export function countryList(i18n: InternationalizationAPI): SelectUiChoice[] { ]; } -export function VQF_902_1(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_1( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_11.ts b/packages/kyc-ui/src/forms/VQF_902_11.ts @@ -1,14 +1,16 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; -export function VQF_902_11(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_11( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_12.ts b/packages/kyc-ui/src/forms/VQF_902_12.ts @@ -1,15 +1,17 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; import { countryList } from "./VQF_902_1.js"; -export function VQF_902_12(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_12( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_13.ts b/packages/kyc-ui/src/forms/VQF_902_13.ts @@ -1,15 +1,17 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; import { countryList } from "./VQF_902_1.js"; -export function VQF_902_13(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_13( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_14.ts b/packages/kyc-ui/src/forms/VQF_902_14.ts @@ -1,14 +1,16 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; -export function VQF_902_14(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_14( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_15.ts b/packages/kyc-ui/src/forms/VQF_902_15.ts @@ -1,15 +1,17 @@ import { + DoubleColumnFormDesign, InternationalizationAPI, - DoubleColumnForm, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; import { countryList } from "./VQF_902_1.js"; -export function VQF_902_15(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_15( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_4.ts b/packages/kyc-ui/src/forms/VQF_902_4.ts @@ -1,14 +1,16 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; -export function VQF_902_4(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_4( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_5.ts b/packages/kyc-ui/src/forms/VQF_902_5.ts @@ -1,14 +1,16 @@ import { + DoubleColumnFormDesign, InternationalizationAPI, - DoubleColumnForm, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; -export function VQF_902_5(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_5( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/VQF_902_9.ts b/packages/kyc-ui/src/forms/VQF_902_9.ts @@ -1,15 +1,17 @@ import { InternationalizationAPI, - DoubleColumnForm, + DoubleColumnFormDesign, UIHandlerId, } from "@gnu-taler/web-util/browser"; import { TalerAmlAttributes } from "./taler_form_attributes.js"; import { countryList } from "./VQF_902_1.js"; -export function VQF_902_9(i18n: InternationalizationAPI): DoubleColumnForm { +export function VQF_902_9( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign { return { type: "double-column", - design: [ + sections: [ { title: i18n.str`This form was completed by`, fields: [ diff --git a/packages/kyc-ui/src/forms/accept-tos.ts b/packages/kyc-ui/src/forms/accept-tos.ts @@ -15,9 +15,8 @@ */ import type { - DoubleColumnForm, InternationalizationAPI, - SingleColumnForm, + SingleColumnFormDesign, UIFormElementConfig, UIHandlerId, } from "@gnu-taler/web-util/browser"; @@ -29,7 +28,7 @@ function filterUndefined<T>(ar: Array<T | undefined>): Array<T> { export const acceptTos = ( i18n: InternationalizationAPI, context?: any, -): SingleColumnForm => ({ +): SingleColumnFormDesign => ({ type: "single-column" as const, fields: filterUndefined<UIFormElementConfig>([ { diff --git a/packages/kyc-ui/src/forms/nameAndBirthdate.ts b/packages/kyc-ui/src/forms/nameAndBirthdate.ts @@ -15,16 +15,16 @@ */ import type { - DoubleColumnForm, + DoubleColumnFormDesign, InternationalizationAPI, UIHandlerId, } from "@gnu-taler/web-util/browser"; export const nameAndDob = ( i18n: InternationalizationAPI, -): DoubleColumnForm => ({ +): DoubleColumnFormDesign => ({ type: "double-column" as const, - design: [ + sections: [ { title: i18n.str`Simple form`, fields: [ diff --git a/packages/kyc-ui/src/forms/personal-info.ts b/packages/kyc-ui/src/forms/personal-info.ts @@ -15,16 +15,16 @@ */ import type { - DoubleColumnForm, + DoubleColumnFormDesign, InternationalizationAPI, UIHandlerId, } from "@gnu-taler/web-util/browser"; export const personalInfo = ( i18n: InternationalizationAPI, -): DoubleColumnForm => ({ +): DoubleColumnFormDesign => ({ type: "double-column" as const, - design: [ + sections: [ { title: i18n.str`Simple form`, fields: [ diff --git a/packages/kyc-ui/src/forms/simplest.ts b/packages/kyc-ui/src/forms/simplest.ts @@ -15,15 +15,17 @@ */ import type { - DoubleColumnForm, + DoubleColumnFormDesign, DoubleColumnFormSection, InternationalizationAPI, UIHandlerId, } from "@gnu-taler/web-util/browser"; -export const simplest = (i18n: InternationalizationAPI): DoubleColumnForm => ({ +export const simplest = ( + i18n: InternationalizationAPI, +): DoubleColumnFormDesign => ({ type: "double-column" as const, - design: [ + sections: [ { title: i18n.str`Simple form`, fields: [ diff --git a/packages/kyc-ui/src/pages/FillForm.tsx b/packages/kyc-ui/src/pages/FillForm.tsx @@ -25,15 +25,15 @@ import { import { Button, FormMetadata, - FormProvider, + FormUI, InternationalizationAPI, LocalNotificationBanner, RenderAllFieldsByUiConfig, UIFormElementConfig, UIHandlerId, - convertUiField, getConverterById, useExchangeApiContext, + useForm, useLocalNotificationHandler, useTranslationContext, } from "@gnu-taler/web-util/browser"; @@ -105,69 +105,11 @@ export function FillForm({ if (!reqId) { return <div>no id for this form, can't upload</div>; } - const shape: Array<UIHandlerId> = []; - const requiredFields: Array<UIHandlerId> = []; - notifier.emit({ - type: "NEW_FORM", - payload: theForm.label, - }); - - switch (theForm.config.type) { - case "double-column": { - theForm.config.design.forEach((section) => { - Array.prototype.push.apply( - shape, - getShapeFromFields(section.fields, ""), - ); - Array.prototype.push.apply( - requiredFields, - getRequiredFields(section.fields), - ); - }); - break; - } - case "single-column": { - Array.prototype.push.apply( - shape, - getShapeFromFields(theForm.config.fields, ""), - ); - Array.prototype.push.apply( - requiredFields, - getRequiredFields(theForm.config.fields), - ); - } - } - - const [form, state] = useFormState<FormType>( - shape, - { - OFFICER_FULL_NAME: "asd", - FORM_FILLING_DATE: format(new Date(), "dd/MM/yyyy"), - CUSTOMER_ID: "123", - }, - (st) => { - const partialErrors = undefinedIfEmpty<FormErrors<FormType>>({}); - - const errors = undefinedIfEmpty<FormErrors<FormType> | undefined>( - validateRequiredFields(partialErrors, st, requiredFields), - ); - - if (errors === undefined) { - return { - status: "ok", - result: st as any, - errors: undefined, - }; - } - return { - status: "fail", - result: st as any, - errors, - }; - }, - ); - const validatedForm = state.status !== "ok" ? undefined : state.result; + const { handler, status } = useForm<FormType>(theForm.config, { + CUSTOMER_ID: "123", + }); + const validatedForm = status.status !== "ok" ? undefined : status.result; const submitHandler = validatedForm === undefined @@ -219,65 +161,7 @@ export function FillForm({ <div class="rounded-lg bg-white px-5 py-6 shadow m-4"> <LocalNotificationBanner notification={notification} /> <div class="space-y-10 divide-y -mt-5 divide-gray-900/10"> - {(function () { - switch (theForm.config.type) { - case "double-column": { - return theForm.config.design.map((section, i) => { - if (!section) return <Fragment />; - return ( - <div - key={i} - class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3" - > - <div class="px-4 sm:px-0"> - <h2 class="text-base font-semibold leading-7 text-gray-900"> - {section.title} - </h2> - {section.description && ( - <p class="mt-1 text-sm leading-6 text-gray-600"> - {section.description} - </p> - )} - </div> - <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2"> - <div class="p-3"> - <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> - <RenderAllFieldsByUiConfig - key={i} - fields={convertUiField( - i18n, - section.fields, - form, - getConverterById, - )} - /> - </div> - </div> - </div> - </div> - ); - }); - } - case "single-column": { - return ( - <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2"> - <div class="p-3"> - <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> - <RenderAllFieldsByUiConfig - fields={convertUiField( - i18n, - theForm.config.fields, - form, - getConverterById, - )} - /> - </div> - </div> - </div> - ); - } - } - })()} + <FormUI handler={handler} design={theForm.config} /> </div> {preferences.showDebugInfo ? ( @@ -288,7 +172,7 @@ export function FillForm({ </i18n.Translate> </p> <pre class="text-sm text-gray-300"> - {JSON.stringify(state.result, undefined, 2)} + {JSON.stringify(status.result, undefined, 2)} </pre> </div> ) : ( @@ -314,47 +198,6 @@ export function FillForm({ ); } -function getRequiredFields(fields: UIFormElementConfig[]): Array<UIHandlerId> { - const shape: Array<UIHandlerId> = []; - fields.forEach((field) => { - if ("id" in field) { - // FIXME: this should be a validation when loading the form - // consistency check - // if (shape.indexOf(field.id) !== -1) { - // throw Error(`already present: ${field.id}`); - // } - if (!field.required) { - return; - } - shape.push(field.id); - } else if (field.type === "group") { - Array.prototype.push.apply(shape, getRequiredFields(field.fields)); - } - }); - return shape; -} -function getShapeFromFields( - fields: UIFormElementConfig[], - parent: string, -): Array<UIHandlerId> { - const shape: Array<UIHandlerId> = []; - fields.forEach((field) => { - if ("id" in field) { - // FIXME: this should be a validation when loading the form - // consistency check - // if (shape.indexOf(field.id) !== -1) { - // throw Error(`already present: ${field.id}`); - // } - shape.push(field.id); - } else if (field.type === "group") { - Array.prototype.push.apply( - shape, - getShapeFromFields(field.fields, parent), - ); - } - }); - return shape; -} function searchForm( i18n: InternationalizationAPI, forms: FormMetadata[], diff --git a/packages/kyc-ui/src/pages/TriggerKyc.tsx b/packages/kyc-ui/src/pages/TriggerKyc.tsx @@ -14,38 +14,26 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ import { - Attention, - Button, - convertUiField, - FormMetadata, - getConverterById, - LocalNotificationBanner, - RenderAllFieldsByUiConfig, - UIHandlerId, - useExchangeApiContext, - useLocalNotificationHandler, - useTranslationContext, -} from "@gnu-taler/web-util/browser"; -import { Fragment, VNode, h } from "preact"; -import { - FormErrors, - getRequiredFields, - getShapeFromFields, - useFormState, - validateRequiredFields, -} from "../hooks/form.js"; -import { undefinedIfEmpty } from "./Start.js"; -import { AbsoluteTime, AccessToken, AmountJson, Amounts, - AmountString, assertUnreachable, - createNewOfficerAccount, createNewWalletKycAccount, HttpStatusCode, } from "@gnu-taler/taler-util"; +import { + Button, + FormMetadata, + FormUI, + LocalNotificationBanner, + UIHandlerId, + useExchangeApiContext, + useForm, + useLocalNotificationHandler, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; +import { Fragment, h, VNode } from "preact"; import { useEffect, useMemo, useState } from "preact/hooks"; type FormType = { @@ -67,7 +55,7 @@ export function TriggerKyc({ onKycStarted }: Props): VNode { label: i18n.str`Trigger KYC balance`, config: { type: "double-column", - design: [ + sections: [ { title: i18n.str`Trigger KYC Balance`, fields: [ @@ -85,58 +73,9 @@ export function TriggerKyc({ onKycStarted }: Props): VNode { }, }; - const shape: Array<UIHandlerId> = []; - const requiredFields: Array<UIHandlerId> = []; - - switch (theForm.config.type) { - case "double-column": { - theForm.config.design.forEach((section) => { - Array.prototype.push.apply(shape, getShapeFromFields(section.fields)); - Array.prototype.push.apply( - requiredFields, - getRequiredFields(section.fields), - ); - }); - break; - } - case "single-column": { - Array.prototype.push.apply( - shape, - getShapeFromFields(theForm.config.fields), - ); - Array.prototype.push.apply( - requiredFields, - getRequiredFields(theForm.config.fields), - ); - } - } - const [form, state] = useFormState<FormType>( - shape, - { - amount: Amounts.parseOrThrow(`${config.config.currency}:1000000`), - }, - (st) => { - const partialErrors = undefinedIfEmpty<FormErrors<FormType>>({}); - - const errors = undefinedIfEmpty<FormErrors<FormType> | undefined>( - validateRequiredFields(partialErrors, st, requiredFields), - ); - - if (errors === undefined) { - return { - status: "ok", - result: st as any, - errors: undefined, - }; - } - - return { - status: "fail", - result: st as any, - errors, - }; - }, - ); + const { handler, status } = useForm<FormType>(theForm.config, { + amount: Amounts.parseOrThrow(`${config.config.currency}:1000000`), + }); const accountPromise = useMemo(async () => { const resp = await lib.exchange.getSeed(); @@ -220,9 +159,9 @@ export function TriggerKyc({ onKycStarted }: Props): VNode { } const sendFormValue = - theForm === undefined || state.status === "fail" + theForm === undefined || status.status === "fail" ? undefined - : triggerAmount(state.result.amount); + : triggerAmount(status.result.amount); if (kycAccount) { return <div>loading...</div>; @@ -232,65 +171,7 @@ export function TriggerKyc({ onKycStarted }: Props): VNode { <div class="rounded-lg bg-white px-5 py-6 shadow m-4"> <LocalNotificationBanner notification={notification} /> <div class="space-y-10 divide-y -mt-5 divide-gray-900/10"> - {(function () { - switch (theForm.config.type) { - case "double-column": { - return theForm.config.design.map((section, i) => { - if (!section) return <Fragment />; - return ( - <div - key={i} - class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3" - > - <div class="px-4 sm:px-0"> - <h2 class="text-base font-semibold leading-7 text-gray-900"> - {section.title} - </h2> - {section.description && ( - <p class="mt-1 text-sm leading-6 text-gray-600"> - {section.description} - </p> - )} - </div> - <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2"> - <div class="p-3"> - <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> - <RenderAllFieldsByUiConfig - key={i} - fields={convertUiField( - i18n, - section.fields, - form, - getConverterById, - )} - /> - </div> - </div> - </div> - </div> - ); - }); - } - case "single-column": { - return ( - <div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2"> - <div class="p-3"> - <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6"> - <RenderAllFieldsByUiConfig - fields={convertUiField( - i18n, - theForm.config.fields, - form, - getConverterById, - )} - /> - </div> - </div> - </div> - ); - } - } - })()} + <FormUI handler={handler} design={theForm.config} /> </div> <div class="mt-6 flex items-center justify-end gap-x-6">