aboutsummaryrefslogtreecommitdiff
path: root/packages/exchange-backoffice-ui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-05-10 15:01:56 -0300
committerSebastian <sebasjm@gmail.com>2023-05-10 15:01:56 -0300
commit55a1e8c6e1ab34702525aadf18b1acce2d06e616 (patch)
treedf430fee9ba715fc64c342052b43c1b66a532cd7 /packages/exchange-backoffice-ui
parentf281803f1e555b8e8c1e76612b1f6b7128033cd6 (diff)
downloadwallet-core-55a1e8c6e1ab34702525aadf18b1acce2d06e616.tar.xz
fix form
Diffstat (limited to 'packages/exchange-backoffice-ui')
-rw-r--r--packages/exchange-backoffice-ui/src/App.tsx10
-rw-r--r--packages/exchange-backoffice-ui/src/Dashborad.tsx35
-rw-r--r--packages/exchange-backoffice-ui/src/Form.tsx906
-rw-r--r--packages/exchange-backoffice-ui/src/HeroSections.tsx1
-rw-r--r--packages/exchange-backoffice-ui/src/forms/FormProvider.tsx27
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputArray.tsx10
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputChoice.tsx4
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputDate.tsx3
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputLine.tsx4
-rw-r--r--packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx13
-rw-r--r--packages/exchange-backoffice-ui/src/forms/useField.ts23
11 files changed, 524 insertions, 512 deletions
diff --git a/packages/exchange-backoffice-ui/src/App.tsx b/packages/exchange-backoffice-ui/src/App.tsx
index 65b58a344..95926e634 100644
--- a/packages/exchange-backoffice-ui/src/App.tsx
+++ b/packages/exchange-backoffice-ui/src/App.tsx
@@ -1,16 +1,12 @@
+import { TranslationProvider } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
-import { HeroSections } from "./HeroSections.js";
-import "./scss/main.css";
import { Dashboard } from "./Dashborad.js";
-import { Form } from "./Form.js";
-import { TranslationProvider } from "@gnu-taler/web-util/browser";
+import "./scss/main.css";
export function App(): VNode {
return (
<TranslationProvider source={{}}>
- <Dashboard>
- <Form />
- </Dashboard>
+ <Dashboard />
</TranslationProvider>
);
}
diff --git a/packages/exchange-backoffice-ui/src/Dashborad.tsx b/packages/exchange-backoffice-ui/src/Dashborad.tsx
index a540caa8d..19ea4a31c 100644
--- a/packages/exchange-backoffice-ui/src/Dashborad.tsx
+++ b/packages/exchange-backoffice-ui/src/Dashborad.tsx
@@ -16,7 +16,8 @@ import {
ChevronDownIcon,
MagnifyingGlassIcon,
} from "@heroicons/react/20/solid";
-import { useState } from "preact/hooks";
+import { useRef, useState } from "preact/hooks";
+import { Form } from "./Form.js";
const navigation = [
{ name: "Dashboard", href: "#", icon: HomeIcon, current: true },
@@ -43,9 +44,10 @@ function classNames(...classes: string[]) {
export function Dashboard({
children,
}: {
- children: ComponentChildren;
+ children?: ComponentChildren;
}): VNode {
const [sidebarOpen, setSidebarOpen] = useState(false);
+ const logRef = useRef<HTMLPreElement>(null);
return (
<>
@@ -53,6 +55,7 @@ export function Dashboard({
<Transition.Root show={sidebarOpen} as={Fragment}>
<Dialog
as="div"
+ /* @ts-ignore */
class="relative z-50 lg:hidden"
onClose={setSidebarOpen}
>
@@ -195,9 +198,12 @@ export function Dashboard({
alt="Your Company"
/>
</div>
+ <div class="text-white text-sm">
+ <pre ref={logRef}></pre>
+ </div>
<nav class="flex flex-1 flex-col">
<ul role="list" class="flex flex-1 flex-col gap-y-7">
- <li>
+ {/* <li>
<ul role="list" class="-mx-2 space-y-1">
{navigation.map((item) => (
<li key={item.name}>
@@ -224,8 +230,8 @@ export function Dashboard({
</li>
))}
</ul>
- </li>
- <li>
+ </li> */}
+ {/* <li>
<div class="text-xs font-semibold leading-6 text-indigo-200">
Your teams
</div>
@@ -249,7 +255,7 @@ export function Dashboard({
</li>
))}
</ul>
- </li>
+ </li> */}
<li class="mt-auto">
<a
href="#"
@@ -314,7 +320,11 @@ export function Dashboard({
/>
{/* Profile dropdown */}
- <Menu as="div" class="relative">
+ <Menu
+ as="div"
+ /* @ts-ignore */
+ class="relative"
+ >
<Menu.Button class="-m-1.5 flex items-center p-1.5">
<span class="sr-only">Open user menu</span>
<img
@@ -368,7 +378,16 @@ export function Dashboard({
</div>
<main class="py-10">
- <div class="px-4 sm:px-6 lg:px-8">{children}</div>
+ <div class="px-4 sm:px-6 lg:px-8">
+ <div class="mx-auto max-w-3xl">
+ <Form
+ onUpdate={(v) => {
+ if (!logRef.current) return;
+ logRef.current.innerHTML = JSON.stringify(v, undefined, 1);
+ }}
+ />
+ </div>
+ </div>
</main>
</div>
</div>
diff --git a/packages/exchange-backoffice-ui/src/Form.tsx b/packages/exchange-backoffice-ui/src/Form.tsx
index 53458824d..e8c6af369 100644
--- a/packages/exchange-backoffice-ui/src/Form.tsx
+++ b/packages/exchange-backoffice-ui/src/Form.tsx
@@ -1,8 +1,8 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h } from "preact";
-import { useState } from "preact/hooks";
-import { FormProvider } from "./forms/FormProvider.js";
+import { useRef, useState } from "preact/hooks";
+import { FormProvider, FormState } from "./forms/FormProvider.js";
import { DoubleColumnForm, RenderAllFieldsByUiConfig } from "./forms/forms.js";
import { CircleStackIcon } from "@heroicons/react/24/outline";
@@ -218,232 +218,234 @@ const firstForm: DoubleColumnForm = [
name: "filler.when",
pattern: "dd/MM/yyyy",
label: "Date" as TranslatedString,
+ help: "format 'dd/MM/yyyy'" as TranslatedString,
},
},
],
},
- {
- title: "Information on customer" as TranslatedString,
- description:
- "The customer is the person with whom the member concludes the contract with regard to the financial service provided (civil law). Does the member act as director of a domiciliary company, this domiciliary company is the customer." as TranslatedString,
- fields: [
- {
- type: "choiceStacked",
- props: {
- name: "customerType",
- label: "Type of customer" as TranslatedString,
- required: true,
- choices: [
- {
- label: "Natural person" as TranslatedString,
- value: "natural",
- },
- {
- label: "Legal entity" as TranslatedString,
- value: "legal",
- },
- ],
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.fullName",
- label: "Full name" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.address",
- label: "Residential address" as TranslatedString,
- required: true,
- },
- },
- {
- type: "integer",
- props: {
- name: "naturalCustomer.telephone",
- label: "Telephone" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.email",
- label: "E-mail" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.dateOfBirth",
- label: "Date of birth" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.nationality",
- label: "Nationality" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.document",
- label: "Identification document" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.companyName",
- label: "Company name" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.office",
- label: "Registered office" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "naturalCustomer.companyDocument",
- label: "Company identification document" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.companyName",
- label: "Company name" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.domicile",
- label: "Domicile" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.contactPerson",
- label: "Contact person" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.telephone",
- label: "Telephone" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.email",
- label: "E-mail" as TranslatedString,
- },
- },
- {
- type: "text",
- props: {
- name: "legalCustomer.document",
- label: "Identification document" as TranslatedString,
- },
- },
- ],
- },
- {
- title:
- "Information on the natural persons who establish the business relationship for legal entities and partnerships" as TranslatedString,
- description:
- "For legal entities and partnerships the identity of the natural persons who establish the business relationship must be verified." as TranslatedString,
- fields: [
- {
- type: "array",
- props: {
- name: "businessEstablisher",
- label: "Persons" as TranslatedString,
- required: true,
- tooltip: "hola" as TranslatedString,
- placeholder: "this is the placeholder" as TranslatedString,
- fields: [
- {
- type: "text",
- props: {
- name: "fullName",
- label: "Full name" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "address",
- label: "Residential address" as TranslatedString,
- required: true,
- },
- },
- {
- type: "date",
- props: {
- name: "dateOfBirth",
- label: "Date of birth" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "nationality",
- label: "Nationality" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "typeOfAuthorization",
- label:
- "Type of authorization (signatory of representation)" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "document",
- label: "Identification document" as TranslatedString,
- required: true,
- },
- },
- {
- type: "text",
- props: {
- name: "powerOfAttorneyArrangements",
- label: "Residential address" as TranslatedString,
- required: true,
- },
- },
- ],
- labelField: "fullName",
- },
- },
- ],
- },
+ // {
+ // title: "Information on customer" as TranslatedString,
+ // description:
+ // "The customer is the person with whom the member concludes the contract with regard to the financial service provided (civil law). Does the member act as director of a domiciliary company, this domiciliary company is the customer." as TranslatedString,
+ // fields: [
+ // {
+ // type: "choiceStacked",
+ // props: {
+ // name: "customerType",
+ // label: "Type of customer" as TranslatedString,
+ // required: true,
+ // choices: [
+ // {
+ // label: "Natural person" as TranslatedString,
+ // value: "natural",
+ // },
+ // {
+ // label: "Legal entity" as TranslatedString,
+ // value: "legal",
+ // },
+ // ],
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.fullName",
+ // label: "Full name" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.address",
+ // label: "Residential address" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "integer",
+ // props: {
+ // name: "naturalCustomer.telephone",
+ // label: "Telephone" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.email",
+ // label: "E-mail" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.dateOfBirth",
+ // label: "Date of birth" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.nationality",
+ // label: "Nationality" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.document",
+ // label: "Identification document" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.companyName",
+ // label: "Company name" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.office",
+ // label: "Registered office" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "naturalCustomer.companyDocument",
+ // label: "Company identification document" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.companyName",
+ // label: "Company name" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.domicile",
+ // label: "Domicile" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.contactPerson",
+ // label: "Contact person" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.telephone",
+ // label: "Telephone" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.email",
+ // label: "E-mail" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "legalCustomer.document",
+ // label: "Identification document" as TranslatedString,
+ // },
+ // },
+ // ],
+ // },
+ // {
+ // title:
+ // "Information on the natural persons who establish the business relationship for legal entities and partnerships" as TranslatedString,
+ // description:
+ // "For legal entities and partnerships the identity of the natural persons who establish the business relationship must be verified." as TranslatedString,
+ // fields: [
+ // {
+ // type: "array",
+ // props: {
+ // name: "businessEstablisher",
+ // label: "Persons" as TranslatedString,
+ // required: true,
+ // tooltip: "hola" as TranslatedString,
+ // placeholder: "this is the placeholder" as TranslatedString,
+ // fields: [
+ // {
+ // type: "text",
+ // props: {
+ // name: "fullName",
+ // label: "Full name" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "address",
+ // label: "Residential address" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "date",
+ // props: {
+ // name: "dateOfBirth",
+ // label: "Date of birth" as TranslatedString,
+ // required: true,
+ // help: "format 'dd/MM/yyyy'" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "nationality",
+ // label: "Nationality" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "typeOfAuthorization",
+ // label:
+ // "Type of authorization (signatory of representation)" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "document",
+ // label: "Identification document" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "powerOfAttorneyArrangements",
+ // label: "Residential address" as TranslatedString,
+ // required: true,
+ // },
+ // },
+ // ],
+ // labelField: "fullName",
+ // },
+ // },
+ // ],
+ // },
{
title: "Acceptance of business relationship" as TranslatedString,
fields: [
@@ -453,6 +455,7 @@ const firstForm: DoubleColumnForm = [
name: "acceptance.when",
pattern: "dd/MM/yyyy",
label: "Date (conclusion of contract)" as TranslatedString,
+ help: "format 'dd/MM/yyyy'" as TranslatedString,
},
},
{
@@ -522,6 +525,7 @@ const firstForm: DoubleColumnForm = [
name: "acceptance.language",
label: "Languages" as TranslatedString,
choices: languageList,
+ unique: true,
},
},
{
@@ -533,256 +537,244 @@ const firstForm: DoubleColumnForm = [
},
],
},
- {
- title:
- "Information on the beneficial owner of the assets and/or controlling person" as TranslatedString,
- description:
- "Establishment of the beneficial owner of the assets and/or controlling person" as TranslatedString,
- fields: [
- {
- type: "choiceStacked",
- props: {
- name: "establishment",
- label: "The customer is" as TranslatedString,
- required: true,
- choices: [
- {
- label:
- "a natural person and there are no doubts that this person is the sole beneficial owner of the assets" as TranslatedString,
- value: "natural",
- },
- {
- label:
- "a foundation (or a similar construct; incl. underlying companies)" as TranslatedString,
- value: "foundation",
- },
- {
- label: "a trust (incl. underlying companies)" as TranslatedString,
- value: "trust",
- },
- {
- label:
- "a life insurance policy with separately managed accounts/securities accounts" as TranslatedString,
- value: "insurance-wrapper",
- },
- {
- label: "all other cases" as TranslatedString,
- value: "other",
- },
- ],
- },
+ // {
+ // title:
+ // "Information on the beneficial owner of the assets and/or controlling person" as TranslatedString,
+ // description:
+ // "Establishment of the beneficial owner of the assets and/or controlling person" as TranslatedString,
+ // fields: [
+ // {
+ // type: "choiceStacked",
+ // props: {
+ // name: "establishment",
+ // label: "The customer is" as TranslatedString,
+ // required: true,
+ // choices: [
+ // {
+ // label:
+ // "a natural person and there are no doubts that this person is the sole beneficial owner of the assets" as TranslatedString,
+ // value: "natural",
+ // },
+ // {
+ // label:
+ // "a foundation (or a similar construct; incl. underlying companies)" as TranslatedString,
+ // value: "foundation",
+ // },
+ // {
+ // label: "a trust (incl. underlying companies)" as TranslatedString,
+ // value: "trust",
+ // },
+ // {
+ // label:
+ // "a life insurance policy with separately managed accounts/securities accounts" as TranslatedString,
+ // value: "insurance-wrapper",
+ // },
+ // {
+ // label: "all other cases" as TranslatedString,
+ // value: "other",
+ // },
+ // ],
+ // },
+ // },
+ // ],
+ // },
+ // {
+ // title:
+ // "Evaluation with regard to embargo procedures/terrorism lists on establishing the business relationship" as TranslatedString,
+ // description:
+ // "Verification whether the customer, beneficial owners of the assets, controlling persons, authorized representatives or other involved persons are listed on an embargo/terrorism list (date of verification/result)" as TranslatedString,
+ // fields: [
+ // {
+ // type: "textArea",
+ // props: {
+ // name: "embargoEvaluation",
+ // help: "The evaluation must be made at the beginning of the business relationship and has to be repeated in the case of permanent business relationship every time the according lists are updated." as TranslatedString,
+ // label: "Evaluation" as TranslatedString,
+ // },
+ // },
+ // ],
+ // },
+ // {
+ // title:
+ // "In the case of cash transactions/occasional customers: Information on type and purpose of business relationship" as TranslatedString,
+ // description:
+ // "These details are only necessary for occasional customers, i.e. money exchange, money and asset transfer or other cash transactions provided that no customer profile (VQF doc. No. 902.5) is created" as TranslatedString,
+ // fields: [
+ // {
+ // type: "choiceStacked",
+ // props: {
+ // name: "cashTransactions.typeOfBusiness",
+ // label: "Type of business relationship" as TranslatedString,
+ // choices: [
+ // {
+ // label: "Money exchange" as TranslatedString,
+ // value: "money-exchange",
+ // },
+ // {
+ // label: "Money and asset transfer" as TranslatedString,
+ // value: "money-and-asset-transfer",
+ // },
+ // {
+ // label:
+ // "Other cash transactions. Specify below" as TranslatedString,
+ // value: "other",
+ // },
+ // ],
+ // },
+ // },
+ // {
+ // type: "text",
+ // props: {
+ // name: "cashTransactions.otherTypeOfBusiness",
+ // required: true,
+ // label: "Specify other cash transactions:" as TranslatedString,
+ // },
+ // },
+ // {
+ // type: "textArea",
+ // props: {
+ // name: "cashTransactions.purpose",
+ // label:
+ // "Purpose of the business relationship (purpose of service requested)" as TranslatedString,
+ // },
+ // },
+ // ],
+ // },
+ // {
+ // title: "Enclosures" as TranslatedString,
+ // fields: [
+ // {
+ // type: "text",
+ // props: {
+ // label: "Customer identification documents" as TranslatedString,
+ // name: "ASd",
+ // },
+ // },
+ // ],
+ // },
+];
+
+function formBehavior(v: Form902_1e.Form): FormState<Form902_1e.Form> {
+ return {
+ filler: {
+ fullName: {
+ disabled: true,
},
- ],
- },
- {
- title:
- "Evaluation with regard to embargo procedures/terrorism lists on establishing the business relationship" as TranslatedString,
- description:
- "Verification whether the customer, beneficial owners of the assets, controlling persons, authorized representatives or other involved persons are listed on an embargo/terrorism list (date of verification/result)" as TranslatedString,
- fields: [
- {
- type: "textArea",
- props: {
- name: "embargoEvaluation",
- help: "The evaluation must be made at the beginning of the business relationship and has to be repeated in the case of permanent business relationship every time the according lists are updated." as TranslatedString,
- label: "Evaluation" as TranslatedString,
- },
+ when: {
+ disabled: true,
},
- ],
- },
-
- {
- title:
- "In the case of cash transactions/occasional customers: Information on type and purpose of business relationship" as TranslatedString,
- description:
- "These details are only necessary for occasional customers, i.e. money exchange, money and asset transfer or other cash transactions provided that no customer profile (VQF doc. No. 902.5) is created" as TranslatedString,
- fields: [
- {
- type: "choiceStacked",
- props: {
- name: "cashTransactions.typeOfBusiness",
- label: "Type of business relationship" as TranslatedString,
- choices: [
- {
- label: "Money exchange" as TranslatedString,
- value: "money-exchange",
- },
- {
- label: "Money and asset transfer" as TranslatedString,
- value: "money-and-asset-transfer",
- },
- {
- label:
- "Other cash transactions. Specify below" as TranslatedString,
- value: "other",
- },
- ],
- },
+ },
+ acceptance: {
+ thirdPartyFullName: {
+ hidden: v.acceptance?.typeOfCorrespondence !== "correspondence-address",
},
- {
- type: "text",
- props: {
- name: "cashTransactions.otherTypeOfBusiness",
- required: true,
- label: "Specify other cash transactions:" as TranslatedString,
- },
+ thirdPartyAddress: {
+ hidden: v.acceptance?.typeOfCorrespondence !== "correspondence-address",
},
- {
- type: "textArea",
- props: {
- name: "cashTransactions.purpose",
- label:
- "Purpose of the business relationship (purpose of service requested)" as TranslatedString,
- },
+ },
+ cashTransactions: {
+ otherTypeOfBusiness: {
+ hidden: v.cashTransactions?.typeOfBusiness !== "other",
},
- ],
- },
- {
- title: "Enclosures" as TranslatedString,
- fields: [
- {
- type: "text",
- props: {
- label: "Customer identification documents" as TranslatedString,
- name: "ASd",
- },
+ },
+ naturalCustomer: {
+ fullName: {
+ hidden: v.customerType !== "natural",
},
- ],
- },
-];
+ address: {
+ hidden: v.customerType !== "natural",
+ },
+ telephone: {
+ hidden: v.customerType !== "natural",
+ },
+ email: {
+ hidden: v.customerType !== "natural",
+ },
+ dateOfBirth: {
+ hidden: v.customerType !== "natural",
+ },
+ nationality: {
+ hidden: v.customerType !== "natural",
+ },
+ document: {
+ hidden: v.customerType !== "natural",
+ },
+ companyName: {
+ hidden: v.customerType !== "natural",
+ },
+ office: {
+ hidden: v.customerType !== "natural",
+ },
+ companyDocument: {
+ hidden: v.customerType !== "natural",
+ },
+ },
+ legalCustomer: {
+ companyName: {
+ hidden: v.customerType !== "legal",
+ },
+ contactPerson: {
+ hidden: v.customerType !== "legal",
+ },
+ document: {
+ hidden: v.customerType !== "legal",
+ },
+ domicile: {
+ hidden: v.customerType !== "legal",
+ },
+ email: {
+ hidden: v.customerType !== "legal",
+ },
+ telephone: {
+ hidden: v.customerType !== "legal",
+ },
+ },
+ };
+}
-export function Form() {
+export function Form({ onUpdate }: { onUpdate: (d: any) => void }) {
const { i18n } = useTranslationContext();
- const formState = useState<Form902_1e.Form>({
+ const initial: Form902_1e.Form = {
filler: {
fullName: "Sebastian Marchano",
when: {
t_ms: Date.now(),
},
},
- // acceptance: {
- // language: ["spa"],
- // },
- // businessEstablisher: [
- // {
- // document: "2121",
- // fullName: "sebastian marchano",
- // },
- // {
- // document: "3131",
- // fullName: "romina cordoba",
- // },
- // ],
- } as Form902_1e.Form);
-
+ } as Form902_1e.Form;
return (
- <FormProvider
- state={formState}
- computeFormState={(v) => {
- return {
- filler: {
- fullName: {
- disabled: true,
- },
- when: {
- disabled: true,
- },
- },
- acceptance: {
- thirdPartyFullName: {
- hidden:
- v.acceptance?.typeOfCorrespondence !== "correspondence-address",
- },
- thirdPartyAddress: {
- hidden:
- v.acceptance?.typeOfCorrespondence !== "correspondence-address",
- },
- },
- cashTransactions: {
- otherTypeOfBusiness: {
- hidden: v.cashTransactions?.typeOfBusiness !== "other",
- },
- },
- naturalCustomer: {
- fullName: {
- hidden: v.customerType !== "natural",
- },
- address: {
- hidden: v.customerType !== "natural",
- },
- telephone: {
- hidden: v.customerType !== "natural",
- },
- email: {
- hidden: v.customerType !== "natural",
- },
- dateOfBirth: {
- hidden: v.customerType !== "natural",
- },
- nationality: {
- hidden: v.customerType !== "natural",
- },
- document: {
- hidden: v.customerType !== "natural",
- },
- companyName: {
- hidden: v.customerType !== "natural",
- },
- office: {
- hidden: v.customerType !== "natural",
- },
- companyDocument: {
- hidden: v.customerType !== "natural",
- },
- },
- legalCustomer: {
- companyName: {
- hidden: v.customerType !== "legal",
- },
- contactPerson: {
- hidden: v.customerType !== "legal",
- },
- document: {
- hidden: v.customerType !== "legal",
- },
- domicile: {
- hidden: v.customerType !== "legal",
- },
- email: {
- hidden: v.customerType !== "legal",
- },
- telephone: {
- hidden: v.customerType !== "legal",
- },
- },
- };
- }}
- >
- <div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
- {firstForm.map((section) => {
- return (
- <div 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 sm:rounded-xl md:col-span-2">
- <div class="px-4 py-6 sm:p-8">
- <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
- <RenderAllFieldsByUiConfig fields={section.fields} />
+ <Fragment>
+ <FormProvider
+ initialValue={initial}
+ onUpdate={onUpdate}
+ computeFormState={formBehavior}
+ >
+ <div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
+ {firstForm.map((section) => {
+ return (
+ <div 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 sm:rounded-xl md:col-span-2">
+ <div class="px-4 py-6 sm:p-8">
+ <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
+ <RenderAllFieldsByUiConfig fields={section.fields} />
+ </div>
</div>
</div>
</div>
- </div>
- );
- })}
- </div>
- </FormProvider>
+ );
+ })}
+ </div>
+ </FormProvider>
+ </Fragment>
);
}
diff --git a/packages/exchange-backoffice-ui/src/HeroSections.tsx b/packages/exchange-backoffice-ui/src/HeroSections.tsx
index d7890b2b9..bdfaa49d4 100644
--- a/packages/exchange-backoffice-ui/src/HeroSections.tsx
+++ b/packages/exchange-backoffice-ui/src/HeroSections.tsx
@@ -59,6 +59,7 @@ export function HeroSections() {
</nav>
<Dialog
as="div"
+ /* @ts-ignore */
class="lg:hidden"
open={mobileMenuOpen}
onClose={setMobileMenuOpen}
diff --git a/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx b/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx
index c9b6783e6..ebb8e3dce 100644
--- a/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/FormProvider.tsx
@@ -1,18 +1,18 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { ComponentChildren, VNode, createContext, h } from "preact";
-import { StateUpdater, useMemo } from "preact/hooks";
+import { MutableRef, StateUpdater, useRef } from "preact/hooks";
export interface FormType<T> {
- initialValue: Partial<T>;
- value: Partial<T>;
- onUpdate: StateUpdater<T>;
+ value: MutableRef<Partial<T>>;
+ initialValue?: Partial<T>;
+ onUpdate?: StateUpdater<T>;
computeFormState?: (v: T) => FormState<T>;
}
//@ts-ignore
export const FormContext = createContext<FormType<any>>({});
-type FormState<T> = {
+export type FormState<T> = {
[field in keyof T]?: T[field] extends AbsoluteTime
? Partial<InputFieldState>
: T[field] extends object
@@ -33,21 +33,20 @@ export interface InputFieldState {
export function FormProvider<T>({
children,
- state,
+ initialValue,
+ onUpdate,
computeFormState,
}: {
- state: [Partial<T>, StateUpdater<T>];
+ initialValue?: Partial<T>;
+ onUpdate?: (v: Partial<T>) => void;
computeFormState?: (v: T) => FormState<T>;
children: ComponentChildren;
}): VNode {
- const [value, onUpdate] = state;
- const initialValue = useMemo(() => value, []);
- const contextValue = useMemo(
- () => ({ initialValue, value, onUpdate, computeFormState }),
- [value, onUpdate, computeFormState],
- );
+ const value = useRef(initialValue ?? {});
return (
- <FormContext.Provider value={contextValue}>
+ <FormContext.Provider
+ value={{ initialValue, value, onUpdate, computeFormState }}
+ >
<form>{children}</form>
</FormContext.Provider>
);
diff --git a/packages/exchange-backoffice-ui/src/forms/InputArray.tsx b/packages/exchange-backoffice-ui/src/forms/InputArray.tsx
index f60ed4160..2447c9989 100644
--- a/packages/exchange-backoffice-ui/src/forms/InputArray.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/InputArray.tsx
@@ -17,10 +17,9 @@ export function InputArray(
const [selectedIndex, setSelected] = useState<number | undefined>(undefined);
const selected =
selectedIndex === undefined ? undefined : list[selectedIndex];
- const formState = useState(selected ?? {});
+ const [subForm, updateSubForm] = useState(selected ?? {});
useEffect(() => {
- const [, update] = formState;
- update(selected);
+ updateSubForm(selected);
}, [selected]);
return (
<div class="sm:col-span-6">
@@ -118,7 +117,7 @@ export function InputArray(
})}
</div>
{selectedIndex !== undefined && (
- <FormProvider state={formState}>
+ <FormProvider initialValue={subForm} onUpdate={updateSubForm}>
<div class="px-4 py-6">
<div class="grid grid-cols-1 gap-y-8 ">
<RenderAllFieldsByUiConfig fields={fields} />
@@ -149,8 +148,7 @@ export function InputArray(
type="button"
onClick={() => {
const newValue = [...list];
- const [confirmed] = formState;
- newValue.splice(selectedIndex, 1, confirmed);
+ newValue.splice(selectedIndex, 1, subForm);
onChange(newValue);
setSelected(undefined);
}}
diff --git a/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx b/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx
index dae5ff34a..0e1b32cba 100644
--- a/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/InputChoice.tsx
@@ -43,7 +43,7 @@ export function InputChoiceStacked(
}
return (
- <div class="sm:col-span-4">
+ <div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
label={label}
required={required}
@@ -81,7 +81,7 @@ export function InputChoiceStacked(
<input
type="radio"
name="server-size"
- value={choice.value}
+ defaultValue={choice.value}
onClick={(e) => {
onChange(choice.value);
}}
diff --git a/packages/exchange-backoffice-ui/src/forms/InputDate.tsx b/packages/exchange-backoffice-ui/src/forms/InputDate.tsx
index c9e1421f8..00dd59996 100644
--- a/packages/exchange-backoffice-ui/src/forms/InputDate.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/InputDate.tsx
@@ -18,12 +18,11 @@ export function InputDate(
converter={{
fromStringUI: (v) => {
if (!v) return { t_ms: "never" };
- console.log("from", v);
const t_ms = parse(v, pattern, Date.now()).getTime();
return { t_ms };
},
toStringUI: (v) => {
- return v === undefined
+ return !v || !v.t_ms
? ""
: v.t_ms === "never"
? "never"
diff --git a/packages/exchange-backoffice-ui/src/forms/InputLine.tsx b/packages/exchange-backoffice-ui/src/forms/InputLine.tsx
index 0870e885a..2a5245ddf 100644
--- a/packages/exchange-backoffice-ui/src/forms/InputLine.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/InputLine.tsx
@@ -243,7 +243,7 @@ export function InputLine<T>(props: { type: string } & UIFormProps<T>): VNode {
onChange(fromString(e.currentTarget.value));
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(value)}
+ defaultValue={toString(value)}
disabled={state.disabled}
aria-invalid={showError}
// aria-describedby="email-error"
@@ -262,7 +262,7 @@ export function InputLine<T>(props: { type: string } & UIFormProps<T>): VNode {
onChange(fromString(e.currentTarget.value));
}}
placeholder={placeholder ? placeholder : undefined}
- value={toString(value)}
+ defaultValue={toString(value)}
disabled={state.disabled}
aria-invalid={showError}
// aria-describedby="email-error"
diff --git a/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx b/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx
index 375f8da93..05733fe19 100644
--- a/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx
+++ b/packages/exchange-backoffice-ui/src/forms/InputSelectMultiple.tsx
@@ -7,9 +7,11 @@ import { useState } from "preact/hooks";
export function InputSelectMultiple(
props: {
choices: Choice[];
+ unique?: boolean;
} & UIFormProps<Array<string>>,
): VNode {
- const { name, label, choices, placeholder, tooltip, required } = props;
+ const { name, label, choices, placeholder, tooltip, required, unique } =
+ props;
const { value, onChange } = useField<{ [s: string]: Array<string> }>(name);
const [filter, setFilter] = useState<string | undefined>(undefined);
@@ -26,7 +28,7 @@ export function InputSelectMultiple(
return regex.test(v.label);
});
return (
- <div class="sm:col-span-4">
+ <div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
label={label}
required={required}
@@ -101,18 +103,19 @@ export function InputSelectMultiple(
role="listbox"
>
{filteredChoices.map((v, idx) => {
- let clazz =
- "relative flex border p-4 focus:outline-none disabled:text-grey";
return (
<li
class="relative cursor-pointer select-none py-2 pl-3 pr-9 text-gray-900 hover:text-white hover:bg-indigo-600"
id="option-0"
role="option"
onClick={() => {
+ setFilter(undefined);
+ if (unique && list.indexOf(v.value) !== -1) {
+ return;
+ }
const newValue = [...list];
newValue.splice(0, 0, v.value);
onChange(newValue);
- setFilter(undefined);
}}
// tabindex="-1"
diff --git a/packages/exchange-backoffice-ui/src/forms/useField.ts b/packages/exchange-backoffice-ui/src/forms/useField.ts
index 6f7b84112..f54dc7465 100644
--- a/packages/exchange-backoffice-ui/src/forms/useField.ts
+++ b/packages/exchange-backoffice-ui/src/forms/useField.ts
@@ -13,14 +13,14 @@ export function useField<T>(name: keyof T): InputFieldHandler<T[keyof T]> {
initialValue,
value: formValue,
computeFormState,
- onUpdate,
+ onUpdate: notifyUpdate,
} = useContext(FormContext);
type P = typeof name;
type V = T[P];
- const [isDirty, setDirty] = useState(false);
- const formState = computeFormState ? computeFormState(formValue) : {};
+ const formState = computeFormState ? computeFormState(formValue.current) : {};
- const fieldValue = readField(formValue, String(name)) as V;
+ const fieldValue = readField(formValue.current, String(name)) as V;
+ const [currentValue, setCurrentValue] = useState<any | undefined>(undefined);
const fieldState = readField<Partial<InputFieldState>>(
formState,
String(name),
@@ -35,16 +35,21 @@ export function useField<T>(name: keyof T): InputFieldHandler<T[keyof T]> {
};
function onChange(value: V): void {
- setDirty(true);
- return onUpdate((prev: any) => {
- return setValueDeeper(prev, String(name).split("."), value);
- });
+ setCurrentValue(value);
+ formValue.current = setValueDeeper(
+ formValue.current,
+ String(name).split("."),
+ value,
+ );
+ if (notifyUpdate) {
+ notifyUpdate(formValue.current);
+ }
}
return {
value: fieldValue,
onChange,
- isDirty,
+ isDirty: currentValue !== undefined,
state,
};
}