taler-typescript-core

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

commit 1937095f39078b379152997590f756f0aa4b12b5
parent f214fa8477bc52e602bf2f329817390e7df03a24
Author: Sebastian <sebasjm@gmail.com>
Date:   Sun, 23 Feb 2025 20:06:16 -0300

test form

Diffstat:
Mpackages/kyc-ui/src/Routing.tsx | 10+++++++++-
Mpackages/kyc-ui/src/app.tsx | 12+++++++-----
Mpackages/kyc-ui/src/context/ui-forms.ts | 27+++++++++++++--------------
Mpackages/kyc-ui/src/forms/index.ts | 3+--
Mpackages/kyc-ui/src/pages/FillForm.tsx | 10+++-------
Mpackages/kyc-ui/src/pages/Frame.tsx | 5++++-
Apackages/kyc-ui/src/pages/TriggerForms.tsx | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/web-util/src/forms/fields/InputSelectOne.stories.tsx | 3++-
Mpackages/web-util/src/forms/fields/InputSelectOne.tsx | 7++++++-
9 files changed, 143 insertions(+), 32 deletions(-)

diff --git a/packages/kyc-ui/src/Routing.tsx b/packages/kyc-ui/src/Routing.tsx @@ -29,6 +29,7 @@ import { Frame } from "./pages/Frame.js"; import { Start } from "./pages/Start.js"; import { useSessionState } from "./hooks/session.js"; import { TriggerKyc } from "./pages/TriggerKyc.js"; +import { TriggerForms } from "./pages/TriggerForms.js"; export function Routing(): VNode { // check session and defined if this is @@ -44,6 +45,10 @@ export const publicPages = { completed: urlPattern(/\/completed/, () => `#/completed`), start: urlPattern(/\/start/, () => `#/start`), triggerKyc: urlPattern(/\/test\/trigger-kyc/, () => `#/test/trigger-kyc`), + triggerForm: urlPattern( + /\/test\/show-forms\/(?<fid>[a-zA-Z0-9-_]+)/, + ({ fid }) => `#/test/show-forms/${fid}`, + ), }; function safeGetParam( @@ -135,12 +140,15 @@ function PublicRounting(): VNode { return ( <TriggerKyc onKycStarted={(token) => { - start(token) + start(token); navigateTo(publicPages.start.url({})); }} /> ); } + case "triggerForm": { + return <TriggerForms formId={location.values.fid} />; + } default: assertUnreachable(location); } diff --git a/packages/kyc-ui/src/app.tsx b/packages/kyc-ui/src/app.tsx @@ -34,22 +34,24 @@ import { VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { SWRConfig } from "swr"; import { Routing } from "./Routing.js"; +import { NotifierProvider } from "./context/notifier.js"; import { SettingsProvider } from "./context/settings.js"; +import { UiFormsProvider } from "./context/ui-forms.js"; +import { revalidateKycInfo } from "./hooks/kyc.js"; import { strings } from "./i18n/strings.js"; import { Frame } from "./pages/Frame.js"; import { KycUiSettings, fetchSettings } from "./settings.js"; -import { revalidateKycInfo } from "./hooks/kyc.js"; -import { fetchUiForms, UiFormsProvider } from "./context/ui-forms.js"; -import { NotifierProvider } from "./context/notifier.js"; const WITH_LOCAL_STORAGE_CACHE = false; export function App(): VNode { const [settings, setSettings] = useState<KycUiSettings>(); - const [forms, setForms] = useState<UiForms>(); + const [forms, setForms] = useState<UiForms>({ + forms: [], + }); useEffect(() => { fetchSettings(setSettings); - fetchUiForms(setForms); + // fetchUiForms(setForms); }, []); if (!settings || !forms) return <Loading />; diff --git a/packages/kyc-ui/src/context/ui-forms.ts b/packages/kyc-ui/src/context/ui-forms.ts @@ -58,19 +58,18 @@ function removeUndefineField<T extends object>(obj: T): T { } export function fetchUiForms(listener: (s: UiForms) => void): void { - listener(defaultForms); + fetch("./forms.json") + .then((resp) => resp.json()) + .then((json) => codecForUIForms().decode(json)) + .then((result) => + listener({ + ...defaultForms, + ...removeUndefineField(result), + }), + ) + .catch((e) => { + console.log("failed to fetch forms", e); + listener(defaultForms); + }); return; - // fetch("./forms.json") - // .then((resp) => resp.json()) - // .then((json) => codecForUIForms().decode(json)) - // .then((result) => - // listener({ - // ...defaultForms, - // ...removeUndefineField(result), - // }), - // ) - // .catch((e) => { - // console.log("failed to fetch forms", e); - // listener(defaultForms); - // }); } diff --git a/packages/kyc-ui/src/forms/index.ts b/packages/kyc-ui/src/forms/index.ts @@ -27,10 +27,9 @@ import { VQF_902_5, VQF_902_9, } from "@gnu-taler/web-util/browser"; -import { simplest } from "./simplest.js"; import { acceptTos } from "./accept-tos.js"; import { nameAndDob } from "./nameAndBirthdate.js"; -import { personalInfo } from "./personal-info.js"; +import { simplest } from "./simplest.js"; const TALER_SCREEN_ID = 105; diff --git a/packages/kyc-ui/src/pages/FillForm.tsx b/packages/kyc-ui/src/pages/FillForm.tsx @@ -71,11 +71,9 @@ export function FillForm({ onComplete, }: Props): VNode { const { i18n } = useTranslationContext(); - const { config, lib } = useExchangeApiContext(); - // const { forms } = useUiFormsContext(); + const { lib } = useExchangeApiContext(); const [notification, withErrorHandler] = useLocalNotificationHandler(); const [preferences] = usePreferences(); - const notifier = useNotifierContext(); const customForm = requirement.context && "form" in requirement.context @@ -90,17 +88,15 @@ export function FillForm({ const { forms } = useUiFormsContext(); const allForms = customForm ? [...forms, customForm] : forms; const theForm = searchForm(i18n, allForms, formId, requirement.context); + const reqId = requirement.id; if (!theForm) { return <div>form with id {formId} not found</div>; } - const reqId = requirement.id; if (!reqId) { return <div>no id for this form, can't upload</div>; } - const { handler, status } = useForm<FormType>(theForm.config, { - // CUSTOMER_ID: "123", - }); + const { handler, status } = useForm<FormType>(theForm.config, {}); const validatedForm = status.status !== "ok" ? undefined : status.result; const submitHandler = diff --git a/packages/kyc-ui/src/pages/Frame.tsx b/packages/kyc-ui/src/pages/Frame.tsx @@ -81,7 +81,10 @@ export function Frame({ children }: { children: ComponentChildren }): VNode { sites={ !preferences.showDebugInfo ? [] - : [["Test kyc", publicPages.triggerKyc.url({})]] + : [ + ["Test kyc", publicPages.triggerKyc.url({})], + ["Test Forms", publicPages.triggerForm.url({})], + ] } > <li> diff --git a/packages/kyc-ui/src/pages/TriggerForms.tsx b/packages/kyc-ui/src/pages/TriggerForms.tsx @@ -0,0 +1,98 @@ +/* + This file is part of GNU Taler + (C) 2022-2024 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +import { + FormMetadata, + FormUI, + LocalNotificationBanner, + UIHandlerId, + useExchangeApiContext, + useForm, + useLocalNotificationHandler, + useTranslationContext, +} from "@gnu-taler/web-util/browser"; +import { Fragment, h, VNode } from "preact"; +import { preloadedForms } from "../forms/index.js"; + +const TALER_SCREEN_ID = 102; + +type FormType = { + form: string; +}; +type Props = { + formId?: string; +}; +export function TriggerForms({ formId }: Props): VNode { + const { i18n } = useTranslationContext(); + const [notification, withErrorHandler, notify] = + useLocalNotificationHandler(); + const { config, lib } = useExchangeApiContext(); + + const pf = preloadedForms(i18n, {}); + + const theForm: FormMetadata = { + id: "asd", + version: 1, + label: i18n.str`Trigger KYC balance`, + config: { + type: "single-column", + fields: [ + { + id: "form" as UIHandlerId, + type: "selectOne", + label: i18n.str`Form`, + help: i18n.str`You can also use the formId in the UR after "/test/show-forms/$FORM_ID"`, + required: true, + choices: pf.map((form) => { + return { + label: `${form.id}: ${form.label}`, + value: form.id, + }; + }), + }, + ], + }, + }; + + console.log("ASDASD", formId); + const { handler, status } = useForm<FormType>(theForm.config, { + form: formId, + }); + + const selected = !status.result?.form + ? undefined + : pf.find((f) => f.id === status.result.form); + return ( + <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"> + <FormUI handler={handler} design={theForm.config} /> + </div> + {!selected ? undefined : <ShowForm form={selected} />} + </div> + ); +} + +function ShowForm({ form }: { form: FormMetadata }) { + const { handler, status } = useForm<FormType>(form.config, {}); + + return ( + <Fragment> + <div class="space-y-10 divide-y -mt-5 divide-gray-900/10"> + <FormUI handler={handler} design={form.config} /> + </div> + </Fragment> + ); +} diff --git a/packages/web-util/src/forms/fields/InputSelectOne.stories.tsx b/packages/web-util/src/forms/fields/InputSelectOne.stories.tsx @@ -51,7 +51,8 @@ const design: FormDesign = { type: "selectOne", label: "label of the field" as TranslatedString, id: "things" as UIHandlerId, - placeholder: "search..." as TranslatedString, + placeholder: "search" as TranslatedString, + help: "search..." as TranslatedString, choices: [ { label: "one label" as TranslatedString, diff --git a/packages/web-util/src/forms/fields/InputSelectOne.tsx b/packages/web-util/src/forms/fields/InputSelectOne.tsx @@ -10,7 +10,7 @@ export function InputSelectOne<T extends object, K extends keyof T>( choices: ChoiceS<T[K]>[]; } & UIFormProps<T, K>, ): VNode { - const { label, choices, placeholder, tooltip, required } = props; + const { label, choices, placeholder, tooltip, required, help } = props; const { value, onChange, error } = props.handler ?? noHandlerPropsAndNoContextForField(props.name); @@ -124,6 +124,11 @@ export function InputSelectOne<T extends object, K extends keyof T>( )} </div> )} + {help && ( + <p class="mt-2 text-sm text-gray-500" id="email-description"> + {help} + </p> + )} {dirty !== undefined && error && ( <p class="mt-2 text-sm text-red-600" id="email-error"> {error}