aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-09-06 13:54:33 -0300
committerSebastian <sebasjm@gmail.com>2024-09-06 13:54:58 -0300
commitc2efc802b2789cb47a6b0a54fc1672b98ee37db2 (patch)
treede5535184952ba20b77dd481ae152a077be8b547
parentd671ef0f4cfa7d17b43b265501ae595882549f17 (diff)
downloadwallet-core-c2efc802b2789cb47a6b0a54fc1672b98ee37db2.tar.xz
search account in aml form and remove name from dynamic forms
-rw-r--r--packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx12
-rw-r--r--packages/aml-backoffice-ui/src/Routing.tsx6
-rw-r--r--packages/aml-backoffice-ui/src/forms/simplest.ts9
-rw-r--r--packages/aml-backoffice-ui/src/hooks/form.ts19
-rw-r--r--packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx6
-rw-r--r--packages/aml-backoffice-ui/src/pages/Cases.tsx151
-rw-r--r--packages/aml-backoffice-ui/src/pages/CreateAccount.tsx12
-rw-r--r--packages/aml-backoffice-ui/src/pages/Search.tsx298
-rw-r--r--packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx16
-rw-r--r--packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx10
-rw-r--r--packages/kyc-ui/src/forms/nameAndBirthdate.ts59
-rw-r--r--packages/kyc-ui/src/forms/personal-info.ts7
-rw-r--r--packages/kyc-ui/src/forms/simplest.ts9
-rw-r--r--packages/web-util/src/forms/FormProvider.tsx2
-rw-r--r--packages/web-util/src/forms/InputAbsoluteTime.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputAmount.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputArray.stories.tsx5
-rw-r--r--packages/web-util/src/forms/InputArray.tsx10
-rw-r--r--packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputChoiceStacked.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputFile.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputInteger.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputLine.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputSelectMultiple.stories.tsx6
-rw-r--r--packages/web-util/src/forms/InputSelectOne.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputText.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputTextArea.stories.tsx3
-rw-r--r--packages/web-util/src/forms/InputToggle.stories.tsx3
-rw-r--r--packages/web-util/src/forms/forms.ts6
-rw-r--r--packages/web-util/src/forms/ui-form.ts11
30 files changed, 498 insertions, 189 deletions
diff --git a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
index ca7f9b0b5..b7741d4c7 100644
--- a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
+++ b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
@@ -33,7 +33,12 @@ import {
getLabelForPreferences,
usePreferences,
} from "./hooks/preferences.js";
-import { HomeIcon, PeopleIcon, ToInvestigateIcon } from "./pages/Cases.js";
+import {
+ HomeIcon,
+ PeopleIcon,
+ SearchIcon,
+ ToInvestigateIcon,
+} from "./pages/Cases.js";
/**
* mapping route to view
@@ -236,6 +241,11 @@ function Navigation(): VNode {
label: i18n.str`Investigation`,
},
{ route: privatePages.active, Icon: HomeIcon, label: i18n.str`Active` },
+ {
+ route: privatePages.search,
+ Icon: SearchIcon,
+ label: i18n.str`Search`,
+ },
];
const { path } = useNavigationContext();
return (
diff --git a/packages/aml-backoffice-ui/src/Routing.tsx b/packages/aml-backoffice-ui/src/Routing.tsx
index b99314889..c7f9a40fe 100644
--- a/packages/aml-backoffice-ui/src/Routing.tsx
+++ b/packages/aml-backoffice-ui/src/Routing.tsx
@@ -31,6 +31,7 @@ import { Officer } from "./pages/Officer.js";
import { CaseDetails } from "./pages/CaseDetails.js";
import { CaseUpdate, SelectForm } from "./pages/CaseUpdate.js";
import { HandleAccountNotReady } from "./pages/HandleAccountNotReady.js";
+import { Search } from "./pages/Search.js";
export function Routing(): VNode {
const session = useOfficer();
@@ -95,6 +96,7 @@ function PublicRounting(): VNode {
export const privatePages = {
profile: urlPattern(/\/profile/, () => "#/profile"),
+ search: urlPattern(/\/search/, () => "#/search"),
investigation: urlPattern(/\/investigation/, () => "#/investigation"),
active: urlPattern(/\/active/, () => "#/active"),
caseUpdate: urlPattern<{ cid: string; type: string }>(
@@ -116,7 +118,6 @@ function PrivateRouting(): VNode {
const location = useCurrentLocation(privatePages);
useEffect(() => {
if (location.name === undefined) {
- console.log("asd")
navigateTo(privatePages.profile.url({}));
}
}, [location]);
@@ -145,6 +146,9 @@ function PrivateRouting(): VNode {
case "active": {
return <Cases />;
}
+ case "search": {
+ return <Search />;
+ }
default:
assertUnreachable(location);
}
diff --git a/packages/aml-backoffice-ui/src/forms/simplest.ts b/packages/aml-backoffice-ui/src/forms/simplest.ts
index 4cd781b74..215b0ba51 100644
--- a/packages/aml-backoffice-ui/src/forms/simplest.ts
+++ b/packages/aml-backoffice-ui/src/forms/simplest.ts
@@ -29,8 +29,7 @@ export const v1 = (i18n: InternationalizationAPI): DoubleColumnForm => ({
fields: [
{
type: "textArea",
- id: ".comment" as UIHandlerId,
- name: "comment",
+ id: "comment" as UIHandlerId,
label: i18n.str`Comment`,
},
],
@@ -59,8 +58,7 @@ export function resolutionSection(
fields: [
{
type: "choiceHorizontal",
- id: ".state" as UIHandlerId,
- name: "state",
+ id: "state" as UIHandlerId,
label: i18n.str`New state`,
converterId: "TalerExchangeApi.AmlState",
choices: [
@@ -80,9 +78,8 @@ export function resolutionSection(
},
{
type: "amount",
- id: ".threshold" as UIHandlerId,
+ id: "threshold" as UIHandlerId,
currency: "NETZBON",
- name: "threshold",
converterId: "Taler.Amount",
label: i18n.str`New threshold`,
},
diff --git a/packages/aml-backoffice-ui/src/hooks/form.ts b/packages/aml-backoffice-ui/src/hooks/form.ts
index 70b2db571..375dbb190 100644
--- a/packages/aml-backoffice-ui/src/hooks/form.ts
+++ b/packages/aml-backoffice-ui/src/hooks/form.ts
@@ -126,14 +126,14 @@ export function useFormState<T>(
shape: Array<UIHandlerId>,
defaultValue: RecursivePartial<FormValues<T>>,
check: (f: RecursivePartial<FormValues<T>>) => FormStatus<T>,
-): [FormHandler<T>, FormStatus<T>] {
+): { handler: FormHandler<T>; status: FormStatus<T> } {
const [form, updateForm] =
useState<RecursivePartial<FormValues<T>>>(defaultValue);
const status = check(form);
const handler = constructFormHandler(shape, form, updateForm, status.errors);
- return [handler, status];
+ return { handler, status };
}
interface Tree<T> extends Record<string, Tree<T> | T> {}
@@ -163,7 +163,10 @@ export function setValueDeeper(object: any, names: string[], value: any): any {
if (object === undefined) {
return undefinedIfEmpty({ [head]: setValueDeeper({}, rest, value) });
}
- return undefinedIfEmpty({ ...object, [head]: setValueDeeper(object[head] ?? {}, rest, value) });
+ return undefinedIfEmpty({
+ ...object,
+ [head]: setValueDeeper(object[head] ?? {}, rest, value),
+ });
}
export function getShapeFromFields(
@@ -179,10 +182,7 @@ export function getShapeFromFields(
}
shape.push(field.id);
} else if (field.type === "group") {
- Array.prototype.push.apply(
- shape,
- getShapeFromFields(field.fields),
- );
+ Array.prototype.push.apply(shape, getShapeFromFields(field.fields));
}
});
return shape;
@@ -204,10 +204,7 @@ export function getRequiredFields(
}
shape.push(field.id);
} else if (field.type === "group") {
- Array.prototype.push.apply(
- shape,
- getRequiredFields(field.fields),
- );
+ Array.prototype.push.apply(shape, getRequiredFields(field.fields));
}
});
return shape;
diff --git a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
index 2708dba4c..87f1aed5f 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
@@ -117,7 +117,7 @@ export function CaseUpdate({
);
});
- const [form, state] = useFormState<FormType>(shape, initial, (st) => {
+ const { handler, status } = useFormState<FormType>(shape, initial, (st) => {
const partialErrors = undefinedIfEmpty<FormErrors<FormType>>({
state: st.state === undefined ? i18n.str`required` : undefined,
threshold: !st.threshold ? i18n.str`required` : undefined,
@@ -143,7 +143,7 @@ export function CaseUpdate({
};
});
- const validatedForm = state.status !== "ok" ? undefined : state.result;
+ const validatedForm = status.status !== "ok" ? undefined : status.result;
const submitHandler =
validatedForm === undefined
@@ -224,7 +224,7 @@ export function CaseUpdate({
fields={convertUiField(
i18n,
section.fields,
- form,
+ handler,
getConverterById,
)}
/>
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index c229850b1..c7191332a 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -21,22 +21,18 @@ import {
} from "@gnu-taler/taler-util";
import {
Attention,
- ErrorLoading,
- InputChoiceHorizontal,
Loading,
- UIHandlerId,
- amlStateConverter,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
-import { useCurrentDecisions, useCurrentDecisionsUnderInvestigation } from "../hooks/decisions.js";
+import {
+ useCurrentDecisions,
+ useCurrentDecisionsUnderInvestigation,
+} from "../hooks/decisions.js";
import { privatePages } from "../Routing.js";
-import { FormErrors, RecursivePartial, useFormState } from "../hooks/form.js";
-import { undefinedIfEmpty } from "./CreateAccount.js";
-import { Officer } from "./Officer.js";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
-import { useState } from "preact/hooks";
+import { Officer } from "./Officer.js";
type FormType = {
// state: TalerExchangeApi.AmlState;
@@ -48,7 +44,7 @@ export function CasesUI({
onNext,
filtered,
}: {
- filtered: boolean,
+ filtered: boolean;
onFirstPage?: () => void;
onNext?: () => void;
records: TalerExchangeApi.AmlDecision[];
@@ -93,17 +89,20 @@ export function CasesUI({
return (
<div>
<div class="sm:flex sm:items-center">
- {filtered ?
+ {filtered ? (
<div class="px-2 sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">
<i18n.Translate>Cases under investigation</i18n.Translate>
</h1>
<p class="mt-2 text-sm text-gray-700 w-80">
<i18n.Translate>
- A list of all the accounts which are waiting for a deicison to be made.
+ A list of all the accounts which are waiting for a deicison to
+ be made.
</i18n.Translate>
</p>
- </div> : <div class="px-2 sm:flex-auto">
+ </div>
+ ) : (
+ <div class="px-2 sm:flex-auto">
<h1 class="text-base font-semibold leading-6 text-gray-900">
<i18n.Translate>Cases</i18n.Translate>
</h1>
@@ -113,7 +112,7 @@ export function CasesUI({
</i18n.Translate>
</p>
</div>
- }
+ )}
</div>
<div class="mt-8 flow-root">
<div class="overflow-x-auto">
@@ -155,7 +154,11 @@ export function CasesUI({
</div>
</td>
<td class="whitespace-nowrap px-3 py-5 text-sm text-gray-900">
- {r.to_investigate ? <span title="require investigation"><ToInvestigateIcon /></span> : undefined}
+ {r.to_investigate ? (
+ <span title="require investigation">
+ <ToInvestigateIcon />
+ </span>
+ ) : undefined}
</td>
</tr>
);
@@ -189,7 +192,8 @@ export function Cases() {
<Fragment>
<Attention type="danger" title={i18n.str`Operation denied`}>
<i18n.Translate>
- This account signature is wrong, contact administrator or create a new one.
+ This account signature is wrong, contact administrator or create
+ a new one.
</i18n.Translate>
</Attention>
<Officer />
@@ -200,27 +204,26 @@ export function Cases() {
return (
<Fragment>
<Attention type="danger" title={i18n.str`Operation denied`}>
- <i18n.Translate>
- This account is not known.
- </i18n.Translate>
- </Attention>
- <Officer />
- </Fragment>
- );
- }
- case HttpStatusCode.Conflict: {
- return (
- <Fragment>
- <Attention type="danger" title={i18n.str`Operation denied`}>
- <i18n.Translate>
- This account doesn't have access. Request account activation
- sending your public key.
- </i18n.Translate>
+ <i18n.Translate>This account is not known.</i18n.Translate>
</Attention>
<Officer />
</Fragment>
);
}
+ case HttpStatusCode.Conflict:
+ {
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account doesn't have access. Request account activation
+ sending your public key.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ }
return <Officer />;
default:
assertUnreachable(list);
@@ -233,10 +236,10 @@ export function Cases() {
records={list.body}
onFirstPage={list.isFirstPage ? undefined : list.loadFirst}
onNext={list.isLastPage ? undefined : list.loadNext}
- // filter={stateFilter}
- // onChangeFilter={(d) => {
- // setStateFilter(d);
- // }}
+ // filter={stateFilter}
+ // onChangeFilter={(d) => {
+ // setStateFilter(d);
+ // }}
/>
);
}
@@ -258,7 +261,8 @@ export function CasesUnderInvestigation() {
<Fragment>
<Attention type="danger" title={i18n.str`Operation denied`}>
<i18n.Translate>
- This account signature is wrong, contact administrator or create a new one.
+ This account signature is wrong, contact administrator or create
+ a new one.
</i18n.Translate>
</Attention>
<Officer />
@@ -269,27 +273,26 @@ export function CasesUnderInvestigation() {
return (
<Fragment>
<Attention type="danger" title={i18n.str`Operation denied`}>
- <i18n.Translate>
- This account is not known.
- </i18n.Translate>
- </Attention>
- <Officer />
- </Fragment>
- );
- }
- case HttpStatusCode.Conflict: {
- return (
- <Fragment>
- <Attention type="danger" title={i18n.str`Operation denied`}>
- <i18n.Translate>
- This account doesn't have access. Request account activation
- sending your public key.
- </i18n.Translate>
+ <i18n.Translate>This account is not known.</i18n.Translate>
</Attention>
<Officer />
</Fragment>
);
}
+ case HttpStatusCode.Conflict:
+ {
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account doesn't have access. Request account activation
+ sending your public key.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ }
return <Officer />;
default:
assertUnreachable(list);
@@ -302,10 +305,10 @@ export function CasesUnderInvestigation() {
records={list.body}
onFirstPage={list.isFirstPage ? undefined : list.loadFirst}
onNext={list.isLastPage ? undefined : list.loadNext}
- // filter={stateFilter}
- // onChangeFilter={(d) => {
- // setStateFilter(d);
- // }}
+ // filter={stateFilter}
+ // onChangeFilter={(d) => {
+ // setStateFilter(d);
+ // }}
/>
);
}
@@ -316,12 +319,23 @@ export function CasesUnderInvestigation() {
// </svg>
// }
export const ToInvestigateIcon = () => (
- <svg title="requires investigation" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 w-6">
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
+ <svg
+ title="requires investigation"
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="size-6 w-6"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"
+ />
</svg>
);
-
export const PeopleIcon = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -356,6 +370,23 @@ export const HomeIcon = () => (
</svg>
);
+export const SearchIcon = () => (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ stroke-width="1.5"
+ stroke="currentColor"
+ class="w-6 h-6"
+ >
+ <path
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
+ />
+ </svg>
+);
+
function Pagination({
onFirstPage,
onNext,
diff --git a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
index 87310aa27..328d8459b 100644
--- a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
@@ -89,7 +89,9 @@ function createFormValidator(
};
}
-export function undefinedIfEmpty<T extends object | undefined>(obj: T): T | undefined {
+export function undefinedIfEmpty<T extends object | undefined>(
+ obj: T,
+): T | undefined {
if (obj === undefined) return undefined;
return Object.keys(obj).some(
(k) => (obj as Record<string, T>)[k] !== undefined,
@@ -105,7 +107,7 @@ export function CreateAccount(): VNode {
const [notification, withErrorHandler] = useLocalNotificationHandler();
- const [form, status] = useFormState<FormType>(
+ const { handler, status } = useFormState<FormType>(
[".password", ".repeat"] as Array<UIHandlerId>,
{
password: undefined,
@@ -118,7 +120,7 @@ export function CreateAccount(): VNode {
status.status === "fail" || officer.state !== "not-found"
? undefined
: withErrorHandler(
- async () => officer.create(form.password!.value!),
+ async () => officer.create(handler.password!.value!),
() => {},
);
return (
@@ -148,7 +150,7 @@ export function CreateAccount(): VNode {
name="password"
type="password"
required
- handler={form.password}
+ handler={handler.password}
/>
</div>
@@ -158,7 +160,7 @@ export function CreateAccount(): VNode {
name="repeat"
type="password"
required
- handler={form.repeat}
+ handler={handler.repeat}
/>
</div>
diff --git a/packages/aml-backoffice-ui/src/pages/Search.tsx b/packages/aml-backoffice-ui/src/pages/Search.tsx
new file mode 100644
index 000000000..047e56180
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/pages/Search.tsx
@@ -0,0 +1,298 @@
+/*
+ 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 {
+ convertUiField,
+ getConverterById,
+ InternationalizationAPI,
+ RenderAllFieldsByUiConfig,
+ UIFormElementConfig,
+ UIHandlerId,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { h } from "preact";
+import {
+ FormErrors,
+ FormStatus,
+ FormValues,
+ getShapeFromFields,
+ RecursivePartial,
+ useFormState,
+} from "../hooks/form.js";
+import { useOfficer } from "../hooks/officer.js";
+import { undefinedIfEmpty } from "./CreateAccount.js";
+import { HandleAccountNotReady } from "./HandleAccountNotReady.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
+
+interface FormType {
+ paytoType: "generic" | "iban" | "x-taler-bank";
+}
+
+export function Search() {
+ const officer = useOfficer();
+ const { i18n } = useTranslationContext();
+
+ const paytoForm = useFormState(
+ getShapeFromFields(paytoTypeField(i18n)),
+ { paytoType: "generic" },
+ createFormValidator(i18n),
+ );
+
+ const secondFieldSet =
+ paytoForm.status.status !== "ok"
+ ? []
+ : paytoForm.status.result.paytoType === "iban"
+ ? ibanFields(i18n)
+ : paytoForm.status.result.paytoType === "x-taler-bank"
+ ? talerBankFields(i18n)
+ : genericFields(i18n);
+
+ const secondForm = useFormState(
+ getShapeFromFields(secondFieldSet),
+ {},
+ createFormValidator(i18n),
+ );
+
+ if (officer.state !== "ready") {
+ return <HandleAccountNotReady officer={officer} />;
+ }
+
+ return (
+ <div>
+ <h1 class="my-2 text-3xl font-bold tracking-tight text-gray-900 ">
+ <i18n.Translate>Search account</i18n.Translate>
+ </h1>
+ <form
+ class="space-y-6"
+ noValidate
+ onSubmit={(e) => {
+ e.preventDefault();
+ }}
+ autoCapitalize="none"
+ autoCorrect="off"
+ >
+ <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3">
+ <RenderAllFieldsByUiConfig
+ fields={convertUiField(
+ i18n,
+ paytoTypeField(i18n),
+ paytoForm.handler,
+ getConverterById,
+ )}
+ />
+ </div>
+ </form>
+
+ <form
+ class="space-y-6"
+ noValidate
+ onSubmit={(e) => {
+ e.preventDefault();
+ }}
+ autoCapitalize="none"
+ autoCorrect="off"
+ >
+ <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3">
+ <RenderAllFieldsByUiConfig
+ fields={convertUiField(
+ i18n,
+ secondFieldSet,
+ secondForm.handler,
+ getConverterById,
+ )}
+ />
+ </div>
+ </form>
+ </div>
+ );
+}
+
+function createFormValidator(i18n: InternationalizationAPI) {
+ return function check(
+ state: RecursivePartial<FormValues<FormType>>,
+ ): FormStatus<FormType> {
+ const errors = undefinedIfEmpty<FormErrors<FormType>>({
+ paytoType: !state?.paytoType ? i18n.str`required` : undefined,
+ });
+
+ if (errors === undefined) {
+ const result: FormType = {
+ paytoType: state.paytoType! as any,
+ };
+ return {
+ status: "ok",
+ result,
+ errors,
+ };
+ }
+ const result: RecursivePartial<FormType> = {
+ paytoType: state?.paytoType,
+ };
+ return {
+ status: "fail",
+ result,
+ errors,
+ };
+ };
+}
+
+const paytoTypeField: (
+ i18n: InternationalizationAPI,
+) => UIFormElementConfig[] = (i18n) => [
+ {
+ id: "paytoType" as UIHandlerId,
+ type: "choiceHorizontal",
+ required: true,
+ choices: [
+ {
+ value: "generic",
+ label: i18n.str`Generic Payto:// URI`,
+ },
+ {
+ value: "iban",
+ label: i18n.str`IBAN`,
+ },
+ {
+ value: "x-taler-bank",
+ label: i18n.str`Taler Bank`,
+ },
+ ],
+ label: `Account type`,
+ },
+];
+
+const receiverName: (i18n: InternationalizationAPI) => UIFormElementConfig = (
+ i18n,
+) => ({
+ id: "receiverName" as UIHandlerId,
+ type: "text",
+ required: true,
+ label: `Owner's name`,
+ help: i18n.str`It should match the bank account name.`,
+ placeholder: i18n.str`John Doe`,
+});
+
+const genericFields: (
+ i18n: InternationalizationAPI,
+) => UIFormElementConfig[] = (i18n) => [
+ {
+ id: "paytoText" as UIHandlerId,
+ type: "textArea",
+ required: true,
+ label: `Payto URI`,
+ help: i18n.str`As defined by RFC 8905`,
+ placeholder: i18n.str`payto://`,
+ },
+ receiverName(i18n),
+];
+const ibanFields: (i18n: InternationalizationAPI) => UIFormElementConfig[] = (
+ i18n,
+) => [
+ {
+ id: "account" as UIHandlerId,
+ type: "text",
+ required: true,
+ label: `Account`,
+ help: i18n.str`International Bank Account Number`,
+ placeholder: i18n.str`DE1231231231`,
+ validator: (value) => validateIBAN(value, i18n),
+ },
+ receiverName(i18n),
+];
+
+const talerBankFields: (
+ i18n: InternationalizationAPI,
+) => UIFormElementConfig[] = (i18n) => [
+ {
+ id: "account" as UIHandlerId,
+ type: "text",
+ required: true,
+ label: `Bank account`,
+ help: i18n.str`Bank account id`,
+ placeholder: i18n.str`DE123123123`,
+ },
+ {
+ id: "hostname" as UIHandlerId,
+ type: "text",
+ required: true,
+ label: `Hostname`,
+ validator: (value) => validateTalerBank(value, i18n),
+ help: i18n.str`Without the scheme, may include subpath: bank.com, bank.com/path/`,
+ placeholder: i18n.str`bank.demo.taler.net`,
+ },
+ receiverName(i18n),
+];
+
+function validateIBAN(
+ iban: string,
+ i18n: ReturnType<typeof useTranslationContext>["i18n"],
+): TranslatedString | undefined {
+ // Check total length
+ if (iban.length < 4)
+ return i18n.str`IBAN numbers usually have more that 4 digits`;
+ if (iban.length > 34)
+ return i18n.str`IBAN numbers usually have less that 34 digits`;
+
+ const A_code = "A".charCodeAt(0);
+ const Z_code = "Z".charCodeAt(0);
+ const IBAN = iban.toUpperCase();
+
+ // check supported country
+ // const code = IBAN.substr(0, 2);
+ // const found = code in COUNTRY_TABLE;
+ // if (!found) return i18n.str`IBAN country code not found`;
+
+ // 2.- Move the four initial characters to the end of the string
+ const step2 = IBAN.substr(4) + iban.substr(0, 4);
+ const step3 = Array.from(step2)
+ .map((letter) => {
+ const code = letter.charCodeAt(0);
+ if (code < A_code || code > Z_code) return letter;
+ return `${letter.charCodeAt(0) - "A".charCodeAt(0) + 10}`;
+ })
+ .join("");
+
+ function calculate_iban_checksum(str: string): number {
+ const numberStr = str.substr(0, 5);
+ const rest = str.substr(5);
+ const number = parseInt(numberStr, 10);
+ const result = number % 97;
+ if (rest.length > 0) {
+ return calculate_iban_checksum(`${result}${rest}`);
+ }
+ return result;
+ }
+
+ const checksum = calculate_iban_checksum(step3);
+ if (checksum !== 1)
+ return i18n.str`IBAN number is invalid, checksum is wrong`;
+ return undefined;
+}
+
+const DOMAIN_REGEX =
+ /^[a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9-_](?:\.[a-zA-Z0-9-_]{2,})+(:[0-9]+)?(\/[a-zA-Z0-9-.]+)*\/?$/;
+
+function validateTalerBank(
+ addr: string,
+ i18n: InternationalizationAPI,
+): TranslatedString | undefined {
+ try {
+ const valid = DOMAIN_REGEX.test(addr);
+ if (valid) return undefined;
+ } catch (e) {
+ console.log(e);
+ }
+ return i18n.str`This is not a valid host.`;
+}
diff --git a/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx b/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
index 21c14fee3..7374125b0 100644
--- a/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
+++ b/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
@@ -20,7 +20,6 @@ import {
TranslatedString,
} from "@gnu-taler/taler-util";
import {
- DefaultForm,
FormConfiguration,
RenderAllFieldsByUiConfig,
UIFormElementConfig,
@@ -31,8 +30,8 @@ import {
} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
-import { AmlEvent } from "./CaseDetails.js";
import { getShapeFromFields, useFormState } from "../hooks/form.js";
+import { AmlEvent } from "./CaseDetails.js";
/**
* the exchange doesn't hava a consistent api
@@ -78,7 +77,6 @@ export function ShowConsolidated({
type: "text",
label: key as TranslatedString,
id: `${key}.value` as UIHandlerId,
- name: `${key}.value`,
disabled: true,
help: `At ${
field.since.t_ms === "never"
@@ -92,13 +90,11 @@ export function ShowConsolidated({
: undefined!,
],
};
- const shape: Array<UIHandlerId> = [];
-
- formConfig.design.forEach((section) => {
- Array.prototype.push.apply(shape, getShapeFromFields(section.fields));
- });
+ const shape: Array<UIHandlerId> = formConfig.design.flatMap((field) =>
+ getShapeFromFields(field.fields),
+ );
- const [form, state] = useFormState<{}>(shape, fixed, (result) => {
+ const { handler } = useFormState<{}>(shape, fixed, (result) => {
return { status: "ok", errors: undefined, result };
});
@@ -130,7 +126,7 @@ export function ShowConsolidated({
fields={convertUiField(
i18n,
section.fields,
- form,
+ handler,
getConverterById,
)}
/>
diff --git a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
index 084e639bf..72656bb98 100644
--- a/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/UnlockAccount.tsx
@@ -19,7 +19,7 @@ import {
LocalNotificationBanner,
UIHandlerId,
useLocalNotificationHandler,
- useTranslationContext
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { FormErrors, useFormState } from "../hooks/form.js";
@@ -36,7 +36,7 @@ export function UnlockAccount(): VNode {
const officer = useOfficer();
const [notification, withErrorHandler] = useLocalNotificationHandler();
- const [form, status] = useFormState<FormType>(
+ const { handler, status } = useFormState<FormType>(
[".password"] as Array<UIHandlerId>,
{
password: undefined,
@@ -64,7 +64,7 @@ export function UnlockAccount(): VNode {
status.status === "fail" || officer.state !== "locked"
? undefined
: withErrorHandler(
- async () => officer.tryUnlock(form.password!.value!),
+ async () => officer.tryUnlock(handler.password!.value!),
() => {},
);
@@ -94,14 +94,13 @@ export function UnlockAccount(): VNode {
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px] ">
<div class="bg-gray-100 px-6 py-6 shadow sm:rounded-lg sm:px-12">
-
<div class="mb-4">
<InputLine<FormType, "password">
label={i18n.str`Password`}
name="password"
type="password"
required
- handler={form.password}
+ handler={handler.password}
/>
</div>
@@ -115,7 +114,6 @@ export function UnlockAccount(): VNode {
<i18n.Translate>Unlock</i18n.Translate>
</Button>
</div>
-
</div>
<Button
type="button"
diff --git a/packages/kyc-ui/src/forms/nameAndBirthdate.ts b/packages/kyc-ui/src/forms/nameAndBirthdate.ts
index 4e4cb17e6..dc4aa52e5 100644
--- a/packages/kyc-ui/src/forms/nameAndBirthdate.ts
+++ b/packages/kyc-ui/src/forms/nameAndBirthdate.ts
@@ -14,34 +14,31 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
- import type {
- DoubleColumnForm,
- DoubleColumnFormSection,
- InternationalizationAPI,
- UIHandlerId,
- } from "@gnu-taler/web-util/browser";
-
- export const nameAndDob = (i18n: InternationalizationAPI): DoubleColumnForm => ({
- type: "double-column" as const,
- design: [
- {
- title: i18n.str`Simple form`,
- fields: [
- {
- type: "textArea",
- id: ".full_name" as UIHandlerId,
- name: "full_name",
- label: i18n.str`Full Name`,
- },
- {
- type: "textArea",
- id: ".birthdate" as UIHandlerId,
- name: "birthdate",
- label: i18n.str`Birthdate`,
- },
- ],
- },
- ],
- });
-
- \ No newline at end of file
+import type {
+ DoubleColumnForm,
+ InternationalizationAPI,
+ UIHandlerId,
+} from "@gnu-taler/web-util/browser";
+
+export const nameAndDob = (
+ i18n: InternationalizationAPI,
+): DoubleColumnForm => ({
+ type: "double-column" as const,
+ design: [
+ {
+ title: i18n.str`Simple form`,
+ fields: [
+ {
+ type: "textArea",
+ id: "full_name" as UIHandlerId,
+ label: i18n.str`Full Name`,
+ },
+ {
+ type: "textArea",
+ id: "birthdate" as UIHandlerId,
+ label: i18n.str`Birthdate`,
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/kyc-ui/src/forms/personal-info.ts b/packages/kyc-ui/src/forms/personal-info.ts
index 657fe603f..799a8eb3a 100644
--- a/packages/kyc-ui/src/forms/personal-info.ts
+++ b/packages/kyc-ui/src/forms/personal-info.ts
@@ -16,7 +16,6 @@
import type {
DoubleColumnForm,
- DoubleColumnFormSection,
InternationalizationAPI,
UIHandlerId,
} from "@gnu-taler/web-util/browser";
@@ -40,8 +39,7 @@ export const personalInfo = (
// },
{
type: "choiceStacked",
- name: "trucker",
- id: ".trucker" as UIHandlerId,
+ id: "trucker" as UIHandlerId,
required: true,
label: i18n.str`Are you a cross-border truck driver?`,
choices: [
@@ -57,9 +55,8 @@ export const personalInfo = (
},
{
type: "amount",
- id: ".money" as UIHandlerId,
+ id: "money" as UIHandlerId,
currency: "YEIN",
- name: "money",
converterId: "Taler.Amount",
label: i18n.str`How much is in your pockets?`,
},
diff --git a/packages/kyc-ui/src/forms/simplest.ts b/packages/kyc-ui/src/forms/simplest.ts
index aa0646959..9cadbc95c 100644
--- a/packages/kyc-ui/src/forms/simplest.ts
+++ b/packages/kyc-ui/src/forms/simplest.ts
@@ -29,8 +29,7 @@ export const simplest = (i18n: InternationalizationAPI): DoubleColumnForm => ({
fields: [
{
type: "textArea",
- id: ".comment" as UIHandlerId,
- name: "comment",
+ id: "comment" as UIHandlerId,
label: i18n.str`Comment`,
},
],
@@ -47,8 +46,7 @@ export function resolutionSection(
fields: [
{
type: "choiceHorizontal",
- id: ".state" as UIHandlerId,
- name: "state",
+ id: "state" as UIHandlerId,
label: i18n.str`New state`,
converterId: "TalerExchangeApi.AmlState",
choices: [
@@ -68,9 +66,8 @@ export function resolutionSection(
},
{
type: "amount",
- id: ".threshold" as UIHandlerId,
+ id: "threshold" as UIHandlerId,
currency: "NETZBON",
- name: "threshold",
converterId: "Taler.Amount",
label: i18n.str`New threshold`,
},
diff --git a/packages/web-util/src/forms/FormProvider.tsx b/packages/web-util/src/forms/FormProvider.tsx
index 5e08efb32..fe886030a 100644
--- a/packages/web-util/src/forms/FormProvider.tsx
+++ b/packages/web-util/src/forms/FormProvider.tsx
@@ -14,7 +14,7 @@ export interface FormType<T extends object> {
computeFormState?: (v: Partial<T>) => FormState<T>;
}
-export const FormContext = createContext<FormType<any>| undefined>(undefined);
+export const FormContext = createContext<FormType<any> | undefined>(undefined);
/**
* Map of {[field]:FieldUIOptions}
diff --git a/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx b/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx
index dcaf1cec9..2f67f952f 100644
--- a/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx
+++ b/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "absoluteTimeText",
label: "label of the field" as TranslatedString,
- id: ".today" as UIHandlerId,
- name: "today",
+ id: "today" as UIHandlerId,
pattern: "dd/MM/yyyy HH:mm",
},
],
diff --git a/packages/web-util/src/forms/InputAmount.stories.tsx b/packages/web-util/src/forms/InputAmount.stories.tsx
index 052b22813..425824b75 100644
--- a/packages/web-util/src/forms/InputAmount.stories.tsx
+++ b/packages/web-util/src/forms/InputAmount.stories.tsx
@@ -52,9 +52,8 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "amount",
label: "label of the field" as TranslatedString,
- id: ".amount" as UIHandlerId,
+ id: "amount" as UIHandlerId,
currency: "ARS",
- name: "amount",
},
],
},
diff --git a/packages/web-util/src/forms/InputArray.stories.tsx b/packages/web-util/src/forms/InputArray.stories.tsx
index 31bad66a4..558e2e2aa 100644
--- a/packages/web-util/src/forms/InputArray.stories.tsx
+++ b/packages/web-util/src/forms/InputArray.stories.tsx
@@ -60,10 +60,9 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "array",
label: "People" as TranslatedString,
- name: "comment",
fields: [],
- id: ".name" as UIHandlerId,
- labelFieldId: "name" as UIHandlerId,
+ id: "name" as UIHandlerId,
+ labelFieldId: "ame" as UIHandlerId,
},
],
},
diff --git a/packages/web-util/src/forms/InputArray.tsx b/packages/web-util/src/forms/InputArray.tsx
index d90028508..58f403035 100644
--- a/packages/web-util/src/forms/InputArray.tsx
+++ b/packages/web-util/src/forms/InputArray.tsx
@@ -99,7 +99,7 @@ export function InputArray<T extends object, K extends keyof T>(
const [selectedIndex, setSelected] = useState<number | undefined>(undefined);
const selected =
selectedIndex === undefined ? undefined : list[selectedIndex];
-
+
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
@@ -110,7 +110,7 @@ export function InputArray<T extends object, K extends keyof T>(
<div class="-space-y-px rounded-md bg-white ">
{list.map((v, idx) => {
- const label = getValueDeeper(v, labelField.split("."))
+ const label = getValueDeeper(v, labelField.split("."));
return (
<Option
label={label as TranslatedString}
@@ -204,8 +204,6 @@ export function InputArray<T extends object, K extends keyof T>(
);
}
-
-
export function getValueDeeper(
object: Record<string, any>,
names: string[],
@@ -218,9 +216,7 @@ export function getValueDeeper(
return getValueDeeper(object, rest);
}
if (object === undefined) {
- return ""
+ return "";
}
return getValueDeeper(object[head], rest);
}
-
-
diff --git a/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx b/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx
index 4d3745fe5..1de160605 100644
--- a/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx
+++ b/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "choiceHorizontal",
label: "label of the field" as TranslatedString,
- id: ".commnet" as UIHandlerId,
- name: "comment",
+ id: "commnet" as UIHandlerId,
choices: [
{
label: "first choice" as TranslatedString,
diff --git a/packages/web-util/src/forms/InputChoiceStacked.stories.tsx b/packages/web-util/src/forms/InputChoiceStacked.stories.tsx
index 8d57b3689..5ee893b96 100644
--- a/packages/web-util/src/forms/InputChoiceStacked.stories.tsx
+++ b/packages/web-util/src/forms/InputChoiceStacked.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "choiceStacked",
label: "label of the field" as TranslatedString,
- name: "comment",
- id: ".comment" as UIHandlerId,
+ id: "comment" as UIHandlerId,
choices: [
{
label: "first choice" as TranslatedString,
diff --git a/packages/web-util/src/forms/InputFile.stories.tsx b/packages/web-util/src/forms/InputFile.stories.tsx
index 591b4e2af..58fb16835 100644
--- a/packages/web-util/src/forms/InputFile.stories.tsx
+++ b/packages/web-util/src/forms/InputFile.stories.tsx
@@ -52,9 +52,8 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "file",
label: "label of the field" as TranslatedString,
- name: "comment",
required: true,
- id: ".comment" as UIHandlerId,
+ id: "comment" as UIHandlerId,
accept: ".png",
tooltip:
"this is a very long tooltip that explain what the field does without being short" as TranslatedString,
diff --git a/packages/web-util/src/forms/InputInteger.stories.tsx b/packages/web-util/src/forms/InputInteger.stories.tsx
index e6b78c242..fd7fc39ed 100644
--- a/packages/web-util/src/forms/InputInteger.stories.tsx
+++ b/packages/web-util/src/forms/InputInteger.stories.tsx
@@ -46,8 +46,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "integer",
label: "label of the field" as TranslatedString,
- name: "age",
- id: ".comment" as UIHandlerId,
+ id: "comment" as UIHandlerId,
tooltip: "just numbers" as TranslatedString,
},
],
diff --git a/packages/web-util/src/forms/InputLine.stories.tsx b/packages/web-util/src/forms/InputLine.stories.tsx
index 16530ff2b..32b26329b 100644
--- a/packages/web-util/src/forms/InputLine.stories.tsx
+++ b/packages/web-util/src/forms/InputLine.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "text",
label: "label of the field" as TranslatedString,
- name: "comment",
- id: ".comment" as UIHandlerId,
+ id: "comment" as UIHandlerId,
},
],
},
diff --git a/packages/web-util/src/forms/InputSelectMultiple.stories.tsx b/packages/web-util/src/forms/InputSelectMultiple.stories.tsx
index a85259786..210ab1b2b 100644
--- a/packages/web-util/src/forms/InputSelectMultiple.stories.tsx
+++ b/packages/web-util/src/forms/InputSelectMultiple.stories.tsx
@@ -54,8 +54,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "selectMultiple",
label: "allow diplicates" as TranslatedString,
- name: "pets",
- id: ".pets" as UIHandlerId,
+ id: "pets" as UIHandlerId,
placeholder: "search..." as TranslatedString,
choices: [
{
@@ -75,8 +74,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "selectMultiple",
label: "unique values" as TranslatedString,
- name: "things",
- id: ".things" as UIHandlerId,
+ id: "things" as UIHandlerId,
unique: true,
placeholder: "search..." as TranslatedString,
choices: [
diff --git a/packages/web-util/src/forms/InputSelectOne.stories.tsx b/packages/web-util/src/forms/InputSelectOne.stories.tsx
index f6b2354c5..56284f4ab 100644
--- a/packages/web-util/src/forms/InputSelectOne.stories.tsx
+++ b/packages/web-util/src/forms/InputSelectOne.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "selectOne",
label: "label of the field" as TranslatedString,
- name: "things",
- id: ".things" as UIHandlerId,
+ id: "things" as UIHandlerId,
placeholder: "search..." as TranslatedString,
choices: [
{
diff --git a/packages/web-util/src/forms/InputText.stories.tsx b/packages/web-util/src/forms/InputText.stories.tsx
index 8e2b415ac..3fa171655 100644
--- a/packages/web-util/src/forms/InputText.stories.tsx
+++ b/packages/web-util/src/forms/InputText.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "text",
label: "label of the field" as TranslatedString,
- id: ".comment" as UIHandlerId,
- name: "comment",
+ id: "comment" as UIHandlerId,
},
],
},
diff --git a/packages/web-util/src/forms/InputTextArea.stories.tsx b/packages/web-util/src/forms/InputTextArea.stories.tsx
index d36e87fc5..c3df4f186 100644
--- a/packages/web-util/src/forms/InputTextArea.stories.tsx
+++ b/packages/web-util/src/forms/InputTextArea.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "text",
label: "label of the field" as TranslatedString,
- id: ".comment" as UIHandlerId,
- name: "comment",
+ id: "comment" as UIHandlerId,
},
],
},
diff --git a/packages/web-util/src/forms/InputToggle.stories.tsx b/packages/web-util/src/forms/InputToggle.stories.tsx
index ae9161645..7ce3cec5a 100644
--- a/packages/web-util/src/forms/InputToggle.stories.tsx
+++ b/packages/web-util/src/forms/InputToggle.stories.tsx
@@ -52,8 +52,7 @@ const form: FlexibleForm_Deprecated<TargetObject> = {
{
type: "toggle",
label: "label of the field" as TranslatedString,
- name: "comment",
- id: ".comment" as UIHandlerId,
+ id: "comment" as UIHandlerId,
},
],
},
diff --git a/packages/web-util/src/forms/forms.ts b/packages/web-util/src/forms/forms.ts
index b37363a62..3b56081ef 100644
--- a/packages/web-util/src/forms/forms.ts
+++ b/packages/web-util/src/forms/forms.ts
@@ -334,12 +334,13 @@ function converInputFieldsProps(
p: UIFormFieldBaseConfig,
getConverterById: GetConverterById,
) {
+ const names = p.id.split(".");
return {
converter: getConverterById(p.converterId, p),
- handler: getValueDeeper2(form, p.id.split(".")),
- name: p.name,
+ handler: getValueDeeper2(form, names),
required: p.required,
disabled: p.disabled,
+ name: names[names.length - 1],
help: p.help,
placeholder: p.placeholder,
tooltip: p.tooltip,
@@ -355,7 +356,6 @@ function converBaseFieldsProps(
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}`,
diff --git a/packages/web-util/src/forms/ui-form.ts b/packages/web-util/src/forms/ui-form.ts
index 012499d6d..f26e08f3b 100644
--- a/packages/web-util/src/forms/ui-form.ts
+++ b/packages/web-util/src/forms/ui-form.ts
@@ -12,7 +12,9 @@ import {
codecOptional,
Integer,
TalerProtocolTimestamp,
+ TranslatedString,
} from "@gnu-taler/taler-util";
+import { InternationalizationAPI } from "../index.browser.js";
export type FormConfiguration = DoubleColumnForm;
@@ -134,9 +136,6 @@ export type UIFieldElementDescription = {
/* short text to be shown close to the field, usually below and dimmer*/
help?: string;
- /* name of the field, useful for a11y */
- name: string;
-
/* if the field should be initially hidden */
hidden?: boolean;
@@ -162,6 +161,11 @@ export type UIFormFieldBaseConfig = UIFieldElementDescription & {
*/
converterId?: string;
+ /* return an error message if the value is not valid, returns un undefined
+ if there is no error
+ */
+ validator?: (value: string) => TranslatedString | undefined;
+
/* property id of the form */
id: UIHandlerId;
};
@@ -181,7 +185,6 @@ const codecForUIFormFieldBaseDescriptionTemplate = <
.property("hidden", codecOptional(codecForBoolean()))
.property("help", codecOptional(codecForString()))
.property("label", codecForString())
- .property("name", codecForString())
.property("tooltip", codecOptional(codecForString()));
const codecForUIFormFieldBaseConfigTemplate = <