second form
This commit is contained in:
parent
db03383325
commit
f4f798b1b4
@ -16,16 +16,22 @@ import {
|
|||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
} from "@heroicons/react/20/solid";
|
} from "@heroicons/react/20/solid";
|
||||||
import { useRef, useState } from "preact/hooks";
|
import { useEffect, useRef, useState } from "preact/hooks";
|
||||||
import { NiceForm } from "./NiceForm.js";
|
import { NiceForm } from "./NiceForm.js";
|
||||||
|
import { v1 as form_902_1e_v1 } from "./forms/902_1e.js";
|
||||||
|
import { v1 as form_902_11e_v1 } from "./forms/902_11e.js";
|
||||||
|
|
||||||
const navigation = [
|
const navigation = [
|
||||||
{ name: "Dashboard", href: "#", icon: HomeIcon, current: true },
|
{
|
||||||
{ name: "Team", href: "#", icon: UsersIcon, current: false },
|
name: "Identification form (902.1e)",
|
||||||
{ name: "Projects", href: "#", icon: FolderIcon, current: false },
|
icon: DocumentDuplicateIcon,
|
||||||
{ name: "Calendar", href: "#", icon: CalendarIcon, current: false },
|
impl: form_902_1e_v1,
|
||||||
{ name: "Documents", href: "#", icon: DocumentDuplicateIcon, current: false },
|
},
|
||||||
{ name: "Reports", href: "#", icon: ChartPieIcon, current: false },
|
{
|
||||||
|
name: "Operational legal entity or partnership (902.11e)",
|
||||||
|
icon: DocumentDuplicateIcon,
|
||||||
|
impl: form_902_11e_v1,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
const teams = [
|
const teams = [
|
||||||
{ id: 1, name: "Heroicons", href: "#", initial: "H", current: false },
|
{ id: 1, name: "Heroicons", href: "#", initial: "H", current: false },
|
||||||
@ -69,8 +75,24 @@ export function Dashboard({
|
|||||||
children?: ComponentChildren;
|
children?: ComponentChildren;
|
||||||
}): VNode {
|
}): VNode {
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||||
|
const [selectedForm, setSelectedForm] = useState(1);
|
||||||
const logRef = useRef<HTMLPreElement>(null);
|
const logRef = useRef<HTMLPreElement>(null);
|
||||||
|
const storedValue = {
|
||||||
|
when: {
|
||||||
|
t_ms: new Date().getTime(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
function showFormOnSidebar(v: any) {
|
||||||
|
if (!logRef.current) return;
|
||||||
|
logRef.current.innerHTML = JSON.stringify(v, undefined, 1);
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
// initial render
|
||||||
|
showFormOnSidebar(storedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
const showingFrom = navigation[selectedForm];
|
||||||
|
console.log(showingFrom);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
@ -139,12 +161,15 @@ export function Dashboard({
|
|||||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||||
<li>
|
<li>
|
||||||
<ul role="list" class="-mx-2 space-y-1">
|
<ul role="list" class="-mx-2 space-y-1">
|
||||||
{navigation.map((item) => (
|
{navigation.map((item, idx) => (
|
||||||
<li key={item.name}>
|
<li
|
||||||
|
key={item.name}
|
||||||
|
onClick={(e) => setSelectedForm(idx)}
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href={item.href}
|
href="#"
|
||||||
class={classNames(
|
class={classNames(
|
||||||
item.current
|
idx === selectedForm
|
||||||
? "bg-indigo-700 text-white"
|
? "bg-indigo-700 text-white"
|
||||||
: "text-indigo-200 hover:text-white hover:bg-indigo-700",
|
: "text-indigo-200 hover:text-white hover:bg-indigo-700",
|
||||||
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold",
|
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold",
|
||||||
@ -152,7 +177,7 @@ export function Dashboard({
|
|||||||
>
|
>
|
||||||
<item.icon
|
<item.icon
|
||||||
class={classNames(
|
class={classNames(
|
||||||
item.current
|
idx === selectedForm
|
||||||
? "text-white"
|
? "text-white"
|
||||||
: "text-indigo-200 group-hover:text-white",
|
: "text-indigo-200 group-hover:text-white",
|
||||||
"h-6 w-6 shrink-0",
|
"h-6 w-6 shrink-0",
|
||||||
@ -165,7 +190,7 @@ export function Dashboard({
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
{/* <li>
|
||||||
<div class="text-xs font-semibold leading-6 text-indigo-200">
|
<div class="text-xs font-semibold leading-6 text-indigo-200">
|
||||||
Your teams
|
Your teams
|
||||||
</div>
|
</div>
|
||||||
@ -189,7 +214,7 @@ export function Dashboard({
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li> */}
|
||||||
<li class="mt-auto">
|
<li class="mt-auto">
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
@ -220,19 +245,16 @@ export function Dashboard({
|
|||||||
alt="Your Company"
|
alt="Your Company"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-white text-sm">
|
|
||||||
<pre ref={logRef}></pre>
|
|
||||||
</div>
|
|
||||||
<nav class="flex flex-1 flex-col">
|
<nav class="flex flex-1 flex-col">
|
||||||
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
<ul role="list" class="flex flex-1 flex-col gap-y-7">
|
||||||
{/* <li>
|
<li>
|
||||||
<ul role="list" class="-mx-2 space-y-1">
|
<ul role="list" class="-mx-2 space-y-1">
|
||||||
{navigation.map((item) => (
|
{navigation.map((item, idx) => (
|
||||||
<li key={item.name}>
|
<li key={item.name} onClick={(e) => setSelectedForm(idx)}>
|
||||||
<a
|
<a
|
||||||
href={item.href}
|
href="#"
|
||||||
class={classNames(
|
class={classNames(
|
||||||
item.current
|
idx === selectedForm
|
||||||
? "bg-indigo-700 text-white"
|
? "bg-indigo-700 text-white"
|
||||||
: "text-indigo-200 hover:text-white hover:bg-indigo-700",
|
: "text-indigo-200 hover:text-white hover:bg-indigo-700",
|
||||||
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold",
|
"group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold",
|
||||||
@ -240,7 +262,7 @@ export function Dashboard({
|
|||||||
>
|
>
|
||||||
<item.icon
|
<item.icon
|
||||||
class={classNames(
|
class={classNames(
|
||||||
item.current
|
idx === selectedForm
|
||||||
? "text-white"
|
? "text-white"
|
||||||
: "text-indigo-200 group-hover:text-white",
|
: "text-indigo-200 group-hover:text-white",
|
||||||
"h-6 w-6 shrink-0",
|
"h-6 w-6 shrink-0",
|
||||||
@ -252,7 +274,7 @@ export function Dashboard({
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</li> */}
|
</li>
|
||||||
{/* <li>
|
{/* <li>
|
||||||
<div class="text-xs font-semibold leading-6 text-indigo-200">
|
<div class="text-xs font-semibold leading-6 text-indigo-200">
|
||||||
Your teams
|
Your teams
|
||||||
@ -292,6 +314,9 @@ export function Dashboard({
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="text-white text-sm">
|
||||||
|
<pre ref={logRef}></pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -403,10 +428,9 @@ export function Dashboard({
|
|||||||
<div class="px-4 sm:px-6 lg:px-8">
|
<div class="px-4 sm:px-6 lg:px-8">
|
||||||
<div class="mx-auto max-w-3xl">
|
<div class="mx-auto max-w-3xl">
|
||||||
<NiceForm
|
<NiceForm
|
||||||
onUpdate={(v) => {
|
initial={storedValue}
|
||||||
if (!logRef.current) return;
|
form={showingFrom.impl as any}
|
||||||
logRef.current.innerHTML = JSON.stringify(v, undefined, 1);
|
onUpdate={showFormOnSidebar}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,798 +1,29 @@
|
|||||||
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
|
|
||||||
import { useTranslationContext } from "@gnu-taler/web-util/browser";
|
import { useTranslationContext } from "@gnu-taler/web-util/browser";
|
||||||
import { Fragment, h } from "preact";
|
import { Fragment, h } from "preact";
|
||||||
import { useRef, useState } from "preact/hooks";
|
import { FlexibleForm } from "./forms/index.js";
|
||||||
import { FormProvider, FormState } from "./forms/FormProvider.js";
|
import { FormProvider } from "./handlers/FormProvider.js";
|
||||||
import { DoubleColumnForm, RenderAllFieldsByUiConfig } from "./forms/forms.js";
|
import { RenderAllFieldsByUiConfig } from "./handlers/forms.js";
|
||||||
import { CircleStackIcon } from "@heroicons/react/24/outline";
|
|
||||||
|
|
||||||
namespace Form902_1e {
|
export function NiceForm<T extends object>({
|
||||||
interface LegalEntityCustomer {
|
initial,
|
||||||
companyName: string;
|
onUpdate,
|
||||||
domicile: string;
|
form,
|
||||||
contactPerson: string;
|
}: {
|
||||||
telephone: string;
|
initial: Partial<T>;
|
||||||
email: string;
|
form: FlexibleForm<T>;
|
||||||
document: string;
|
onUpdate: (d: Partial<T>) => void;
|
||||||
documentAttachment: string;
|
}) {
|
||||||
}
|
|
||||||
interface NaturalCustomer {
|
|
||||||
fullName: string;
|
|
||||||
address: string;
|
|
||||||
telephone: string;
|
|
||||||
email: string;
|
|
||||||
dateOfBirth: AbsoluteTime;
|
|
||||||
nationality: string;
|
|
||||||
document: string;
|
|
||||||
documentAttachment: string;
|
|
||||||
companyName: string;
|
|
||||||
office: string;
|
|
||||||
companyDocument: string;
|
|
||||||
companyDocumentAttachment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Person {
|
|
||||||
fullName: string;
|
|
||||||
address: string;
|
|
||||||
dateOfBirth: AbsoluteTime;
|
|
||||||
nationality: string;
|
|
||||||
typeOfAuthorization: string;
|
|
||||||
document: string;
|
|
||||||
documentAttachment: string;
|
|
||||||
powerOfAttorneyArrangements: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Acceptance {
|
|
||||||
when: AbsoluteTime;
|
|
||||||
acceptedBy: "face-to-face" | "authenticated-copy";
|
|
||||||
typeOfCorrespondence: string;
|
|
||||||
language: string[];
|
|
||||||
furtherInformation: string;
|
|
||||||
thirdPartyFullName: string;
|
|
||||||
thirdPartyAddress: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Filler {
|
|
||||||
fullName: string;
|
|
||||||
when: AbsoluteTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface BeneficialOwner {
|
|
||||||
establishment:
|
|
||||||
| "natural-person"
|
|
||||||
| "foundation"
|
|
||||||
| "trust"
|
|
||||||
| "insurance-wrapper"
|
|
||||||
| "other";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CashTransactions {
|
|
||||||
typeOfBusiness: "money-exchange" | "money-and-asset-transfer" | "other";
|
|
||||||
otherTypeOfBusiness: string;
|
|
||||||
purpose: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Form {
|
|
||||||
filler: Filler;
|
|
||||||
customerType: "natural" | "legal";
|
|
||||||
naturalCustomer: NaturalCustomer;
|
|
||||||
legalCustomer: LegalEntityCustomer;
|
|
||||||
businessEstablisher: Array<Person>;
|
|
||||||
acceptance: Acceptance;
|
|
||||||
beneficialOwner: BeneficialOwner;
|
|
||||||
embargoEvaluation: string;
|
|
||||||
cashTransactions: CashTransactions;
|
|
||||||
// enclosures: Enclosures;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const languageList = [
|
|
||||||
{
|
|
||||||
label: "Mandarin Chinese" as TranslatedString,
|
|
||||||
value: "cmn",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Spanish" as TranslatedString,
|
|
||||||
value: "spa",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "English" as TranslatedString,
|
|
||||||
value: "eng",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Hindi" as TranslatedString,
|
|
||||||
value: "hin",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Portuguese" as TranslatedString,
|
|
||||||
value: "por",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Bengali" as TranslatedString,
|
|
||||||
value: "ben",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Russian" as TranslatedString,
|
|
||||||
value: "rus",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Japanese" as TranslatedString,
|
|
||||||
value: "jpn",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Yue" as TranslatedString,
|
|
||||||
value: "yue",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Vietnamese" as TranslatedString,
|
|
||||||
value: "vie",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Turkish" as TranslatedString,
|
|
||||||
value: "tur",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Wu" as TranslatedString,
|
|
||||||
value: "wuu",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Marathi" as TranslatedString,
|
|
||||||
value: "mar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Telugu" as TranslatedString,
|
|
||||||
value: "ten",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Korean" as TranslatedString,
|
|
||||||
value: "kor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "French" as TranslatedString,
|
|
||||||
value: "fra",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Tamil" as TranslatedString,
|
|
||||||
value: "tam",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Egyptian Arabic" as TranslatedString,
|
|
||||||
value: "arz",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Standard German" as TranslatedString,
|
|
||||||
value: "deu",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Urdu" as TranslatedString,
|
|
||||||
value: "urd",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Javanese" as TranslatedString,
|
|
||||||
value: "jav",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Punjabi" as TranslatedString,
|
|
||||||
value: "pan",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Italian" as TranslatedString,
|
|
||||||
value: "ita",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Gujarati" as TranslatedString,
|
|
||||||
value: "guj",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Iranian Persian" as TranslatedString,
|
|
||||||
value: "pes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Bhojpuri" as TranslatedString,
|
|
||||||
value: "bho",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Hausa" as TranslatedString,
|
|
||||||
value: "hau",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const form902_1e: DoubleColumnForm = [
|
|
||||||
{
|
|
||||||
title: "This form was completed by" as TranslatedString,
|
|
||||||
description:
|
|
||||||
"The customer has to be identified on entering into a permanent business relationship or on concluding a cash transaction, which meets the according threshold." as TranslatedString,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
props: {
|
|
||||||
name: "filler.fullName",
|
|
||||||
label: "Full name" as TranslatedString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "date",
|
|
||||||
props: {
|
|
||||||
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: "date",
|
|
||||||
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: "file",
|
|
||||||
props: {
|
|
||||||
name: "naturalCustomer.documentAttachment",
|
|
||||||
label: "Document attachment" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
maxBites: 2 * 1024 * 1024,
|
|
||||||
accept: ".png",
|
|
||||||
help: "Max size of 2 mega bytes" as TranslatedString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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: "file",
|
|
||||||
props: {
|
|
||||||
name: "naturalCustomer.companyDocumentAttachment",
|
|
||||||
label: "Document attachment" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
maxBites: 2 * 1024 * 1024,
|
|
||||||
accept: ".png",
|
|
||||||
help: "Max size of 2 mega bytes" 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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "file",
|
|
||||||
props: {
|
|
||||||
name: "legalCustomer.documentAttachment",
|
|
||||||
label: "Document attachment" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
maxBites: 2 * 1024 * 1024,
|
|
||||||
accept: ".png",
|
|
||||||
help: "Max size of 2 mega bytes" 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: "file",
|
|
||||||
props: {
|
|
||||||
name: "documentAttachment",
|
|
||||||
label: "Document attachment" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
maxBites: 2 * 1024 * 1024,
|
|
||||||
accept: ".png",
|
|
||||||
help: "Max size of 2 mega bytes" as TranslatedString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
props: {
|
|
||||||
name: "powerOfAttorneyArrangements",
|
|
||||||
label: "Residential address" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labelField: "fullName",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Acceptance of business relationship" as TranslatedString,
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: "date",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.when",
|
|
||||||
pattern: "dd/MM/yyyy",
|
|
||||||
label: "Date (conclusion of contract)" as TranslatedString,
|
|
||||||
help: "format 'dd/MM/yyyy'" as TranslatedString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "choiceStacked",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.acceptedBy",
|
|
||||||
label: "Accepted by" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
label: "Face-to-face meeting with customer" as TranslatedString,
|
|
||||||
value: "face-to-face",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label:
|
|
||||||
"Correspondence: authenticated copy of identification document obtained" as TranslatedString,
|
|
||||||
value: "correspondence-document",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label:
|
|
||||||
"Correspondence: residential address validated" as TranslatedString,
|
|
||||||
value: "correspondence-address",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "choiceStacked",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.typeOfCorrespondence",
|
|
||||||
label: "Type of correspondence" as TranslatedString,
|
|
||||||
choices: [
|
|
||||||
{
|
|
||||||
label: "to the customer" as TranslatedString,
|
|
||||||
value: "face-to-face",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "hold at bank" as TranslatedString,
|
|
||||||
value: "correspondence-document",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "to a third party" as TranslatedString,
|
|
||||||
value: "correspondence-address",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.thirdPartyFullName",
|
|
||||||
label: "Third party full name" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.thirdPartyAddress",
|
|
||||||
label: "Third party address" as TranslatedString,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "selectMultiple",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.language",
|
|
||||||
label: "Languages" as TranslatedString,
|
|
||||||
choices: languageList,
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "textArea",
|
|
||||||
props: {
|
|
||||||
name: "acceptance.furtherInformation",
|
|
||||||
label: "Further information" as TranslatedString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function formBehavior(v: Form902_1e.Form): FormState<Form902_1e.Form> {
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
documentAttachment: {
|
|
||||||
hidden: v.customerType !== "legal",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NiceForm({ onUpdate }: { onUpdate: (d: any) => void }) {
|
|
||||||
const { i18n } = useTranslationContext();
|
const { i18n } = useTranslationContext();
|
||||||
const initial: Form902_1e.Form = {
|
console.log("render");
|
||||||
filler: {
|
|
||||||
fullName: "Sebastian Marchano",
|
|
||||||
when: {
|
|
||||||
t_ms: Date.now(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
customerType: "natural",
|
|
||||||
naturalCustomer: {
|
|
||||||
fullName: "javier",
|
|
||||||
},
|
|
||||||
businessEstablisher: [
|
|
||||||
{
|
|
||||||
fullName: "sebastian marchano",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
} as Form902_1e.Form;
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<FormProvider
|
<FormProvider
|
||||||
initialValue={initial}
|
initialValue={initial}
|
||||||
onUpdate={onUpdate}
|
onUpdate={onUpdate}
|
||||||
computeFormState={formBehavior}
|
computeFormState={form.behavior}
|
||||||
>
|
>
|
||||||
<div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
|
<div class="space-y-10 divide-y -mt-5 divide-gray-900/10">
|
||||||
{form902_1e.map((section) => {
|
{form.design.map((section, i) => {
|
||||||
return (
|
return (
|
||||||
<div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-5 md:grid-cols-3">
|
<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">
|
<div class="px-4 sm:px-0">
|
||||||
@ -808,7 +39,10 @@ export function NiceForm({ onUpdate }: { onUpdate: (d: any) => void }) {
|
|||||||
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2">
|
<div class="bg-white shadow-sm ring-1 ring-gray-900/5 rounded-md md:col-span-2">
|
||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
<div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
||||||
<RenderAllFieldsByUiConfig fields={section.fields} />
|
<RenderAllFieldsByUiConfig
|
||||||
|
key={i}
|
||||||
|
fields={section.fields}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
147
packages/exchange-backoffice-ui/src/forms/902_11e.ts
Normal file
147
packages/exchange-backoffice-ui/src/forms/902_11e.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
|
||||||
|
import { FlexibleForm, languageList } from "./index.js";
|
||||||
|
import { FormState } from "../handlers/FormProvider.js";
|
||||||
|
|
||||||
|
export const v1: FlexibleForm<Form902_11e.Form> = {
|
||||||
|
versionId: "2023-05-15",
|
||||||
|
design: [
|
||||||
|
{
|
||||||
|
title:
|
||||||
|
"Establishing of the controlling person of operating legal entities and partnerships both not quoted on the stock exchange" as TranslatedString,
|
||||||
|
description:
|
||||||
|
"for operating legal entities and partnership that are contracting partner as well as analogously for operating legal entities and partnership that are beneficial owners." as TranslatedString,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "textArea",
|
||||||
|
props: {
|
||||||
|
name: "contractingPartner",
|
||||||
|
label: "Contracting partner" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "choiceStacked",
|
||||||
|
props: {
|
||||||
|
name: "declares",
|
||||||
|
label:
|
||||||
|
"The contracting partner hereby declares that" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
label:
|
||||||
|
"the person(s) listed below is/are holding 25% or more of the contracting partner's shares (capital shares or voting rights)" as TranslatedString,
|
||||||
|
value: "25-or-more",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label:
|
||||||
|
"if the capital shares or voting rights cannot be determined or in case there are no capital shares or voting rights 25% or more, the contracting partner hereby declares that the person(s) listed below is/are controlling the contracting partner in other ways" as TranslatedString,
|
||||||
|
value: "controlling-in-other-ways",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label:
|
||||||
|
"in case this/these person(s) cannot be determined or this/these person(s) does/do not exist, the contracting partner hereby declares that the person(s) listed below is/are the managing director(s)" as TranslatedString,
|
||||||
|
value: "managing-director",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: "lastName",
|
||||||
|
label: "Last name(s)" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "firstName",
|
||||||
|
label: "First name(s)" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "address",
|
||||||
|
label: "Actual address of domicile" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
labelField: "lastName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "choiceStacked",
|
||||||
|
props: {
|
||||||
|
name: "fiduciaryAssets",
|
||||||
|
label: "Fiduciary holding assets" as TranslatedString,
|
||||||
|
help: "Is a third person the beneficial owner of the assets held in the account/securities account?" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
label: "No" as TranslatedString,
|
||||||
|
value: "no",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Yes" as TranslatedString,
|
||||||
|
value: "yes",
|
||||||
|
description:
|
||||||
|
"The relevant information regarding the beneficial owner has to be obtained by filling in a separate VQF doc. No. 902.9" as TranslatedString,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "date",
|
||||||
|
props: {
|
||||||
|
name: "when",
|
||||||
|
pattern: "dd/MM/yyyy",
|
||||||
|
label: "Date" as TranslatedString,
|
||||||
|
help: "format 'dd/MM/yyyy'" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
behavior: function formBehavior(
|
||||||
|
v: Partial<Form902_11e.Form>,
|
||||||
|
): FormState<Form902_11e.Form> {
|
||||||
|
return {
|
||||||
|
person: {
|
||||||
|
hidden:
|
||||||
|
v.declares !== "controlling-in-other-ways" &&
|
||||||
|
v.declares !== "managing-director",
|
||||||
|
},
|
||||||
|
when: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Form902_11e {
|
||||||
|
interface Person {
|
||||||
|
lastName: string;
|
||||||
|
firstName: string;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
export interface Form {
|
||||||
|
contractingPartner: string;
|
||||||
|
declares: "25-or-more" | "controlling-in-other-ways" | "managing-director";
|
||||||
|
person: Person[];
|
||||||
|
fiduciaryAssets: "no" | "yes";
|
||||||
|
when: AbsoluteTime;
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
}
|
686
packages/exchange-backoffice-ui/src/forms/902_1e.ts
Normal file
686
packages/exchange-backoffice-ui/src/forms/902_1e.ts
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
|
||||||
|
import { FlexibleForm, languageList } from "./index.js";
|
||||||
|
import { FormState } from "../handlers/FormProvider.js";
|
||||||
|
|
||||||
|
export const v1: FlexibleForm<Form902_1e.Form> = {
|
||||||
|
versionId: "2023-05-15",
|
||||||
|
design: [
|
||||||
|
{
|
||||||
|
title: "This form was completed by" as TranslatedString,
|
||||||
|
description:
|
||||||
|
"The customer has to be identified on entering into a permanent business relationship or on concluding a cash transaction, which meets the according threshold." as TranslatedString,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "fullName",
|
||||||
|
label: "Full name" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "date",
|
||||||
|
props: {
|
||||||
|
name: "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: "date",
|
||||||
|
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: "file",
|
||||||
|
props: {
|
||||||
|
name: "naturalCustomer.documentAttachment",
|
||||||
|
label: "Document attachment" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
maxBites: 2 * 1024 * 1024,
|
||||||
|
accept: ".png",
|
||||||
|
help: "Max size of 2 mega bytes" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: "file",
|
||||||
|
props: {
|
||||||
|
name: "naturalCustomer.companyDocumentAttachment",
|
||||||
|
label: "Document attachment" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
maxBites: 2 * 1024 * 1024,
|
||||||
|
accept: ".png",
|
||||||
|
help: "Max size of 2 mega bytes" 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,
|
||||||
|
help: "Not older than 12 month" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "file",
|
||||||
|
props: {
|
||||||
|
name: "legalCustomer.documentAttachment",
|
||||||
|
label: "Document attachment" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
maxBites: 2 * 1024 * 1024,
|
||||||
|
accept: ".png",
|
||||||
|
help: "Max size of 2 mega bytes" 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: "file",
|
||||||
|
props: {
|
||||||
|
name: "documentAttachment",
|
||||||
|
label:
|
||||||
|
"Identification document attachment" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
maxBites: 2 * 1024 * 1024,
|
||||||
|
accept: ".png",
|
||||||
|
help: "Max size of 2 mega bytes" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "choiceStacked",
|
||||||
|
props: {
|
||||||
|
name: "powerOfAttorneyArrangements",
|
||||||
|
label: "Power of attorney arrangements" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
label: "CR extract" as TranslatedString,
|
||||||
|
value: "cr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Mandate" as TranslatedString,
|
||||||
|
value: "mandate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Other" as TranslatedString,
|
||||||
|
value: "other",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "powerOfAttorneyArrangementsOther",
|
||||||
|
label: "Power of attorney arrangements" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
labelField: "fullName",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Acceptance of business relationship" as TranslatedString,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: "date",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.when",
|
||||||
|
pattern: "dd/MM/yyyy",
|
||||||
|
label: "Date (conclusion of contract)" as TranslatedString,
|
||||||
|
help: "format 'dd/MM/yyyy'" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "choiceStacked",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.acceptedBy",
|
||||||
|
label: "Accepted by" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
label: "Face-to-face meeting with customer" as TranslatedString,
|
||||||
|
value: "face-to-face",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label:
|
||||||
|
"Correspondence: authenticated copy of identification document obtained" as TranslatedString,
|
||||||
|
value: "correspondence-document",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label:
|
||||||
|
"Correspondence: residential address validated" as TranslatedString,
|
||||||
|
value: "correspondence-address",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "choiceStacked",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.typeOfCorrespondence",
|
||||||
|
label: "Type of correspondence service" as TranslatedString,
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
label: "to the customer" as TranslatedString,
|
||||||
|
value: "customer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "hold at bank" as TranslatedString,
|
||||||
|
value: "bank",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "to the member" as TranslatedString,
|
||||||
|
value: "member",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "to a third party" as TranslatedString,
|
||||||
|
value: "third-party",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.thirdPartyFullName",
|
||||||
|
label: "Third party full name" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.thirdPartyAddress",
|
||||||
|
label: "Third party address" as TranslatedString,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "selectMultiple",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.language",
|
||||||
|
label: "Languages" as TranslatedString,
|
||||||
|
choices: languageList,
|
||||||
|
unique: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "textArea",
|
||||||
|
props: {
|
||||||
|
name: "acceptance.furtherInformation",
|
||||||
|
label: "Further information" as TranslatedString,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
behavior: function formBehavior(
|
||||||
|
v: Partial<Form902_1e.Form>,
|
||||||
|
): FormState<Form902_1e.Form> {
|
||||||
|
return {
|
||||||
|
fullName: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
when: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
businessEstablisher: {
|
||||||
|
elements: (v.businessEstablisher ?? []).map((be) => {
|
||||||
|
return {
|
||||||
|
powerOfAttorneyArrangementsOther: {
|
||||||
|
hidden: be.powerOfAttorneyArrangements !== "other",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
acceptance: {
|
||||||
|
thirdPartyFullName: {
|
||||||
|
hidden: v.acceptance?.typeOfCorrespondence !== "third-party",
|
||||||
|
},
|
||||||
|
thirdPartyAddress: {
|
||||||
|
hidden: v.acceptance?.typeOfCorrespondence !== "third-party",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
companyDocumentAttachment: {
|
||||||
|
hidden: v.customerType !== "natural",
|
||||||
|
},
|
||||||
|
documentAttachment: {
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
documentAttachment: {
|
||||||
|
hidden: v.customerType !== "legal",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Form902_1e {
|
||||||
|
interface LegalEntityCustomer {
|
||||||
|
companyName: string;
|
||||||
|
domicile: string;
|
||||||
|
contactPerson: string;
|
||||||
|
telephone: string;
|
||||||
|
email: string;
|
||||||
|
document: string;
|
||||||
|
documentAttachment: string;
|
||||||
|
}
|
||||||
|
interface NaturalCustomer {
|
||||||
|
fullName: string;
|
||||||
|
address: string;
|
||||||
|
telephone: string;
|
||||||
|
email: string;
|
||||||
|
dateOfBirth: AbsoluteTime;
|
||||||
|
nationality: string;
|
||||||
|
document: string;
|
||||||
|
documentAttachment: string;
|
||||||
|
companyName: string;
|
||||||
|
office: string;
|
||||||
|
companyDocument: string;
|
||||||
|
companyDocumentAttachment: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Person {
|
||||||
|
fullName: string;
|
||||||
|
address: string;
|
||||||
|
dateOfBirth: AbsoluteTime;
|
||||||
|
nationality: string;
|
||||||
|
typeOfAuthorization: string;
|
||||||
|
document: string;
|
||||||
|
documentAttachment: string;
|
||||||
|
powerOfAttorneyArrangements: "cr" | "mandate" | "other";
|
||||||
|
powerOfAttorneyArrangementsOther: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Acceptance {
|
||||||
|
when: AbsoluteTime;
|
||||||
|
acceptedBy: "face-to-face" | "authenticated-copy";
|
||||||
|
typeOfCorrespondence: string;
|
||||||
|
language: string[];
|
||||||
|
furtherInformation: string;
|
||||||
|
thirdPartyFullName: string;
|
||||||
|
thirdPartyAddress: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BeneficialOwner {
|
||||||
|
establishment:
|
||||||
|
| "natural-person"
|
||||||
|
| "foundation"
|
||||||
|
| "trust"
|
||||||
|
| "insurance-wrapper"
|
||||||
|
| "other";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CashTransactions {
|
||||||
|
typeOfBusiness: "money-exchange" | "money-and-asset-transfer" | "other";
|
||||||
|
otherTypeOfBusiness: string;
|
||||||
|
purpose: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Form {
|
||||||
|
when: AbsoluteTime;
|
||||||
|
fullName: string;
|
||||||
|
customerType: "natural" | "legal";
|
||||||
|
naturalCustomer: NaturalCustomer;
|
||||||
|
legalCustomer: LegalEntityCustomer;
|
||||||
|
businessEstablisher: Array<Person>;
|
||||||
|
acceptance: Acceptance;
|
||||||
|
beneficialOwner: BeneficialOwner;
|
||||||
|
embargoEvaluation: string;
|
||||||
|
cashTransactions: CashTransactions;
|
||||||
|
// enclosures: Enclosures;
|
||||||
|
}
|
||||||
|
}
|
120
packages/exchange-backoffice-ui/src/forms/index.ts
Normal file
120
packages/exchange-backoffice-ui/src/forms/index.ts
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { TranslatedString } from "@gnu-taler/taler-util";
|
||||||
|
import { FormState } from "../handlers/FormProvider.js";
|
||||||
|
import { DoubleColumnForm } from "../handlers/forms.js";
|
||||||
|
|
||||||
|
export interface FlexibleForm<T extends object> {
|
||||||
|
versionId: string;
|
||||||
|
design: DoubleColumnForm;
|
||||||
|
behavior: (form: Partial<T>) => FormState<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const languageList = [
|
||||||
|
{
|
||||||
|
label: "Mandarin Chinese" as TranslatedString,
|
||||||
|
value: "cmn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Spanish" as TranslatedString,
|
||||||
|
value: "spa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "English" as TranslatedString,
|
||||||
|
value: "eng",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hindi" as TranslatedString,
|
||||||
|
value: "hin",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Portuguese" as TranslatedString,
|
||||||
|
value: "por",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Bengali" as TranslatedString,
|
||||||
|
value: "ben",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Russian" as TranslatedString,
|
||||||
|
value: "rus",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Japanese" as TranslatedString,
|
||||||
|
value: "jpn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Yue" as TranslatedString,
|
||||||
|
value: "yue",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Vietnamese" as TranslatedString,
|
||||||
|
value: "vie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Turkish" as TranslatedString,
|
||||||
|
value: "tur",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Wu" as TranslatedString,
|
||||||
|
value: "wuu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Marathi" as TranslatedString,
|
||||||
|
value: "mar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Telugu" as TranslatedString,
|
||||||
|
value: "ten",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Korean" as TranslatedString,
|
||||||
|
value: "kor",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "French" as TranslatedString,
|
||||||
|
value: "fra",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Tamil" as TranslatedString,
|
||||||
|
value: "tam",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Egyptian Arabic" as TranslatedString,
|
||||||
|
value: "arz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Standard German" as TranslatedString,
|
||||||
|
value: "deu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Urdu" as TranslatedString,
|
||||||
|
value: "urd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Javanese" as TranslatedString,
|
||||||
|
value: "jav",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Punjabi" as TranslatedString,
|
||||||
|
value: "pan",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Italian" as TranslatedString,
|
||||||
|
value: "ita",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Gujarati" as TranslatedString,
|
||||||
|
value: "guj",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Iranian Persian" as TranslatedString,
|
||||||
|
value: "pes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Bhojpuri" as TranslatedString,
|
||||||
|
value: "bho",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hausa" as TranslatedString,
|
||||||
|
value: "hau",
|
||||||
|
},
|
||||||
|
];
|
@ -15,6 +15,8 @@ export const FormContext = createContext<FormType<any>>({});
|
|||||||
export type FormState<T> = {
|
export type FormState<T> = {
|
||||||
[field in keyof T]?: T[field] extends AbsoluteTime
|
[field in keyof T]?: T[field] extends AbsoluteTime
|
||||||
? Partial<InputFieldState>
|
? Partial<InputFieldState>
|
||||||
|
: T[field] extends Array<infer P>
|
||||||
|
? Partial<InputArrayFieldState<P>>
|
||||||
: T[field] extends object
|
: T[field] extends object
|
||||||
? FormState<T[field]>
|
? FormState<T[field]>
|
||||||
: Partial<InputFieldState>;
|
: Partial<InputFieldState>;
|
||||||
@ -31,6 +33,10 @@ export interface InputFieldState {
|
|||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InputArrayFieldState<T> extends InputFieldState {
|
||||||
|
elements: FormState<T>[];
|
||||||
|
}
|
||||||
|
|
||||||
export function FormProvider<T>({
|
export function FormProvider<T>({
|
||||||
children,
|
children,
|
||||||
initialValue,
|
initialValue,
|
@ -1,6 +1,6 @@
|
|||||||
import { Fragment, VNode, h } from "preact";
|
import { Fragment, VNode, h } from "preact";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import { FormProvider } from "./FormProvider.js";
|
import { FormProvider, InputArrayFieldState } from "./FormProvider.js";
|
||||||
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
|
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
|
||||||
import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js";
|
import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js";
|
||||||
import { useField } from "./useField.js";
|
import { useField } from "./useField.js";
|
||||||
@ -78,7 +78,9 @@ export function InputArray(
|
|||||||
} & UIFormProps<Array<{}>>,
|
} & UIFormProps<Array<{}>>,
|
||||||
): VNode {
|
): VNode {
|
||||||
const { fields, labelField, name, label, required, tooltip } = props;
|
const { fields, labelField, name, label, required, tooltip } = props;
|
||||||
const { value, onChange } = useField<{ [s: string]: Array<any> }>(name);
|
const { value, onChange, state } = useField<{ [s: string]: Array<any> }>(
|
||||||
|
name,
|
||||||
|
);
|
||||||
const list = value ?? [];
|
const list = value ?? [];
|
||||||
const [selectedIndex, setSelected] = useState<number | undefined>(undefined);
|
const [selectedIndex, setSelected] = useState<number | undefined>(undefined);
|
||||||
const selected =
|
const selected =
|
||||||
@ -125,8 +127,20 @@ export function InputArray(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{selectedIndex !== undefined && (
|
{selectedIndex !== undefined && (
|
||||||
|
/**
|
||||||
|
* This form provider act as a substate of the parent form
|
||||||
|
* Consider creating an InnerFormProvider since not every feature is expected
|
||||||
|
*/
|
||||||
<FormProvider
|
<FormProvider
|
||||||
initialValue={selected}
|
initialValue={selected}
|
||||||
|
computeFormState={(v) => {
|
||||||
|
// current state is ignored
|
||||||
|
// the state is defined by the parent form
|
||||||
|
|
||||||
|
// elements should be present in the state object since this is expected to be an array
|
||||||
|
//@ts-ignore
|
||||||
|
return state.elements[selectedIndex];
|
||||||
|
}}
|
||||||
onUpdate={(v) => {
|
onUpdate={(v) => {
|
||||||
const newValue = [...list];
|
const newValue = [...list];
|
||||||
newValue.splice(selectedIndex, 1, v);
|
newValue.splice(selectedIndex, 1, v);
|
||||||
@ -158,20 +172,6 @@ export function InputArray(
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* <div class="flex-none">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
const newValue = [...list];
|
|
||||||
newValue.splice(selectedIndex, 1, subForm);
|
|
||||||
onChange(newValue);
|
|
||||||
setSelected(undefined);
|
|
||||||
}}
|
|
||||||
class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm text-white shadow-sm hover:bg-indigo-500 "
|
|
||||||
>
|
|
||||||
Confirm
|
|
||||||
</button>
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
@ -3,18 +3,6 @@ import { Fragment, VNode, h } from "preact";
|
|||||||
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
|
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
|
||||||
import { useField } from "./useField.js";
|
import { useField } from "./useField.js";
|
||||||
|
|
||||||
function classNames(...classes: string[]): string {
|
|
||||||
return classes.filter(Boolean).join(" ");
|
|
||||||
}
|
|
||||||
const memoryOptions = [
|
|
||||||
{ name: "4 GB", inStock: true },
|
|
||||||
{ name: "8 GB", inStock: true },
|
|
||||||
{ name: "16 GB", inStock: true },
|
|
||||||
{ name: "32 GB", inStock: true },
|
|
||||||
{ name: "64 GB", inStock: true },
|
|
||||||
{ name: "128 GB", inStock: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
export interface Choice {
|
export interface Choice {
|
||||||
label: TranslatedString;
|
label: TranslatedString;
|
||||||
description?: TranslatedString;
|
description?: TranslatedString;
|
||||||
@ -37,7 +25,9 @@ export function InputChoiceStacked(
|
|||||||
after,
|
after,
|
||||||
converter,
|
converter,
|
||||||
} = props;
|
} = props;
|
||||||
const { value, onChange, state, isDirty } = useField(name);
|
const { value, onChange, state, isDirty } = useField<{
|
||||||
|
[s: string]: undefined | string;
|
||||||
|
}>(name);
|
||||||
if (state.hidden) {
|
if (state.hidden) {
|
||||||
return <Fragment />;
|
return <Fragment />;
|
||||||
}
|
}
|
||||||
@ -50,22 +40,6 @@ export function InputChoiceStacked(
|
|||||||
tooltip={tooltip}
|
tooltip={tooltip}
|
||||||
/>
|
/>
|
||||||
<fieldset class="mt-2">
|
<fieldset class="mt-2">
|
||||||
{value !== undefined && !required && (
|
|
||||||
<div class="flex mb-2 items-center ">
|
|
||||||
<div class="flex-auto">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
onChange(undefined!);
|
|
||||||
}}
|
|
||||||
class="block rounded-md bg-white px-3 py-2 text-center text-sm font-semibold shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
{choices.map((choice) => {
|
{choices.map((choice) => {
|
||||||
let clazz =
|
let clazz =
|
||||||
@ -83,7 +57,7 @@ export function InputChoiceStacked(
|
|||||||
name="server-size"
|
name="server-size"
|
||||||
defaultValue={choice.value}
|
defaultValue={choice.value}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
onChange(choice.value);
|
onChange(value === choice.value ? undefined : choice.value);
|
||||||
}}
|
}}
|
||||||
class="sr-only"
|
class="sr-only"
|
||||||
aria-labelledby="server-size-0-label"
|
aria-labelledby="server-size-0-label"
|
@ -1,4 +1,4 @@
|
|||||||
import { VNode, h } from "preact";
|
import { Fragment, VNode, h } from "preact";
|
||||||
import {
|
import {
|
||||||
InputLine,
|
InputLine,
|
||||||
LabelWithTooltipMaybeRequired,
|
LabelWithTooltipMaybeRequired,
|
||||||
@ -19,8 +19,11 @@ export function InputFile(
|
|||||||
maxBites,
|
maxBites,
|
||||||
accept,
|
accept,
|
||||||
} = props;
|
} = props;
|
||||||
const { value, onChange } = useField<{ [s: string]: string }>(name);
|
const { value, onChange, state } = useField<{ [s: string]: string }>(name);
|
||||||
|
|
||||||
|
if (state.hidden) {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div class="col-span-full">
|
<div class="col-span-full">
|
||||||
<LabelWithTooltipMaybeRequired
|
<LabelWithTooltipMaybeRequired
|
@ -185,7 +185,7 @@ export function InputLine<T>(props: { type: string } & UIFormProps<T>): VNode {
|
|||||||
const { name, placeholder, before, after, converter, type } = props;
|
const { name, placeholder, before, after, converter, type } = props;
|
||||||
const { value, onChange, state, isDirty } = useField(name);
|
const { value, onChange, state, isDirty } = useField(name);
|
||||||
|
|
||||||
if (state.hidden) return <Fragment />;
|
if (state.hidden) return <div />;
|
||||||
|
|
||||||
let clazz =
|
let clazz =
|
||||||
"block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6 disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500 disabled:ring-gray-200";
|
"block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6 disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500 disabled:ring-gray-200";
|
@ -81,11 +81,13 @@ export function RenderAllFieldsByUiConfig({
|
|||||||
return create(
|
return create(
|
||||||
Fragment,
|
Fragment,
|
||||||
{},
|
{},
|
||||||
fields.map((field) => {
|
fields.map((field, i) => {
|
||||||
const Component = UIFormConfiguration[
|
const Component = UIFormConfiguration[
|
||||||
field.type
|
field.type
|
||||||
] as FieldComponentFunction<any>;
|
] as FieldComponentFunction<any>;
|
||||||
return Component(field.props);
|
const c = structuredClone(field.props);
|
||||||
|
c.key = i;
|
||||||
|
return Component(c);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -1,5 +1,9 @@
|
|||||||
import { TargetedEvent, useContext, useState } from "preact/compat";
|
import { TargetedEvent, useContext, useState } from "preact/compat";
|
||||||
import { FormContext, InputFieldState } from "./FormProvider.js";
|
import {
|
||||||
|
FormContext,
|
||||||
|
InputArrayFieldState,
|
||||||
|
InputFieldState,
|
||||||
|
} from "./FormProvider.js";
|
||||||
|
|
||||||
export interface InputFieldHandler<Type> {
|
export interface InputFieldHandler<Type> {
|
||||||
value: Type;
|
value: Type;
|
||||||
@ -20,18 +24,17 @@ export function useField<T>(name: keyof T): InputFieldHandler<T[keyof T]> {
|
|||||||
const formState = computeFormState ? computeFormState(formValue.current) : {};
|
const formState = computeFormState ? computeFormState(formValue.current) : {};
|
||||||
|
|
||||||
const fieldValue = readField(formValue.current, String(name)) as V;
|
const fieldValue = readField(formValue.current, String(name)) as V;
|
||||||
const [currentValue, setCurrentValue] = useState<any | undefined>(undefined);
|
const [currentValue, setCurrentValue] = useState<any | undefined>(fieldValue);
|
||||||
const fieldState = readField<Partial<InputFieldState>>(
|
const fieldState =
|
||||||
formState,
|
readField<Partial<InputFieldState>>(formState, String(name)) ?? {};
|
||||||
String(name),
|
|
||||||
);
|
|
||||||
|
|
||||||
//default state
|
//compute default state
|
||||||
const state: InputFieldState = {
|
const state = {
|
||||||
disabled: fieldState?.disabled ?? false,
|
disabled: fieldState.disabled ?? false,
|
||||||
readonly: fieldState?.readonly ?? false,
|
readonly: fieldState.readonly ?? false,
|
||||||
hidden: fieldState?.hidden ?? false,
|
hidden: fieldState.hidden ?? false,
|
||||||
error: fieldState?.error,
|
error: fieldState.error,
|
||||||
|
elements: "elements" in fieldState ? fieldState.elements ?? [] : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
function onChange(value: V): void {
|
function onChange(value: V): void {
|
Loading…
Reference in New Issue
Block a user