one form left

This commit is contained in:
Sebastian 2023-05-16 01:23:44 -03:00
parent 02fb71c0ff
commit 245ab840ba
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
14 changed files with 750 additions and 46 deletions

View File

@ -77,17 +77,17 @@ const navigation = [
impl: form_902_13e_v1, impl: form_902_13e_v1,
}, },
{ {
name: "WIP (902.15e)", name: "Information on life insurance policies (902.15e)",
icon: DocumentDuplicateIcon, icon: DocumentDuplicateIcon,
impl: form_902_15e_v1, impl: form_902_15e_v1,
}, },
{ {
name: "WIP (902.9e)", name: "Declaration of beneficial owner (902.9e)",
icon: DocumentDuplicateIcon, icon: DocumentDuplicateIcon,
impl: form_902_9e_v1, impl: form_902_9e_v1,
}, },
{ {
name: "WIP (902.5e)", name: "Customer profile (902.5e)",
icon: DocumentDuplicateIcon, icon: DocumentDuplicateIcon,
impl: form_902_5e_v1, impl: form_902_5e_v1,
}, },
@ -167,7 +167,7 @@ export function Dashboard({
children?: ComponentChildren; children?: ComponentChildren;
}): VNode { }): VNode {
const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false);
const [selectedForm, setSelectedForm] = useState(3); const [selectedForm, setSelectedForm] = useState(6);
function changeForm(next: number) { function changeForm(next: number) {
setSelectedForm(next); setSelectedForm(next);
} }
@ -177,6 +177,9 @@ export function Dashboard({
when: { when: {
t_ms: new Date().getTime(), t_ms: new Date().getTime(),
}, },
originOfAssets: {
currency: "usd",
},
}; };
function showFormOnSidebar(v: any) { function showFormOnSidebar(v: any) {
if (!logRef.current) return; if (!logRef.current) return;

View File

@ -1,6 +1,6 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { FlexibleForm, languageList } from "./index.js";
import { FormState } from "../handlers/FormProvider.js"; import { FormState } from "../handlers/FormProvider.js";
import { FlexibleForm } from "./index.js";
export const v1: FlexibleForm<Form902_11e.Form> = { export const v1: FlexibleForm<Form902_11e.Form> = {
versionId: "2023-05-15", versionId: "2023-05-15",

View File

@ -97,10 +97,12 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
}, },
}, },
{ {
type: "text", type: "date",
props: { props: {
name: "dateOfBirth", name: "dateOfBirth",
label: "Date of birth" as TranslatedString, label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{ {
@ -111,11 +113,12 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
}, },
}, },
{ {
type: "text", type: "date",
props: { props: {
name: "dateOfDeath", name: "dateOfDeath",
label: "Date of death" as TranslatedString, label: "Date of death" as TranslatedString,
help: "if deceased" as TranslatedString, pattern: "dd/MM/yyyy",
help: "if deceased. format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{ {
@ -172,10 +175,12 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
}, },
}, },
{ {
type: "text", type: "date",
props: { props: {
name: "dateOfBirth", name: "dateOfBirth",
label: "Date of birth" as TranslatedString, label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{ {
@ -186,11 +191,12 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
}, },
}, },
{ {
type: "text", type: "date",
props: { props: {
name: "dateOfDeath", name: "dateOfDeath",
label: "Date of death" as TranslatedString, label: "Date of death" as TranslatedString,
help: "if deceased" as TranslatedString, pattern: "dd/MM/yyyy",
help: "if deceased. format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
], ],
@ -228,10 +234,12 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
}, },
}, },
{ {
type: "text", type: "date",
props: { props: {
name: "dateOfBirth", name: "dateOfBirth",
label: "Date of birth" as TranslatedString, label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{ {
@ -417,8 +425,8 @@ export const v1: FlexibleForm<Form902_13e.Form> = {
type: "date", type: "date",
props: { props: {
name: "when", name: "when",
pattern: "dd/MM/yyyy",
label: "Date" as TranslatedString, label: "Date" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString, help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },

View File

@ -1,14 +1,13 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { FlexibleForm, languageList } from "./index.js";
import { FormState } from "../handlers/FormProvider.js"; import { FormState } from "../handlers/FormProvider.js";
import { FlexibleForm } from "./index.js";
export const v1: FlexibleForm<Form902_12e.Form> = { export const v1: FlexibleForm<Form902_15e.Form> = {
versionId: "2023-05-15", versionId: "2023-05-15",
design: [ design: [
{ {
title: "15" as TranslatedString, title:
description: "Information on life insurance policies with separately managed accounts/securities accounts" as TranslatedString,
"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: [ fields: [
{ {
type: "textArea", type: "textArea",
@ -17,6 +16,123 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
label: "Contracting partner" as TranslatedString, label: "Contracting partner" as TranslatedString,
}, },
}, },
{
type: "text",
props: {
name: "contractualRelationship",
label:
"Name or number of the contractual relationship between the contracting party and the financial intermediary" as TranslatedString,
},
},
{
type: "text",
props: {
name: "insurancePolicy",
label: "Insurance policy" as TranslatedString,
},
},
{
type: "separator",
props: {
label:
"The contracting partner confirms in accordance with Art. 41a SRO Regulations that it is a licensed and state-supervised insurance company and that it has entered into the above-mentioned contractual relationship the assets connected to the life insurance policy also mentioned above." as TranslatedString,
},
},
{
type: "separator",
props: {
label:
"In relation with the above insurance policy, the contracting partner gives the following further details" as TranslatedString,
},
},
{
type: "group",
props: {
before: "Policy holder" as TranslatedString,
fields: [
{
type: "text",
props: {
name: "holder.fullName",
label:
"Last name(s), first name(s)/entity" as TranslatedString,
},
},
{
type: "text",
props: {
name: "holder.address",
label:
"Actual address of domicile/registered office (incl. country)" as TranslatedString,
},
},
{
type: "date",
props: {
name: "holder.dateOfBirth",
label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
},
},
{
type: "text",
props: {
name: "holder.nationality",
label: "Nationality" as TranslatedString,
},
},
],
},
},
{
type: "group",
props: {
before:
"Person actually (not in a fiduciary capacity) paying the premiums (to be filled in if not identical with point 1 above)" as TranslatedString,
fields: [
{
type: "text",
props: {
name: "premiumPayer.fullName",
label:
"Last name(s), first name(s)/entity" as TranslatedString,
},
},
{
type: "text",
props: {
name: "premiumPayer.address",
label:
"Actual address of domicile/registered office (incl. country)" as TranslatedString,
},
},
{
type: "date",
props: {
name: "premiumPayer.dateOfBirth",
label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
},
},
{
type: "text",
props: {
name: "premiumPayer.nationality",
label: "Nationality" as TranslatedString,
},
},
],
},
},
{
type: "separator",
props: {
label:
"The contracting partner hereby undertakes to automatically inform the financial intermediary of any changes. The contracting partner hereby also declares having been given permission by the above individuals and/or entities to transmit their data to the financial intermediary" as TranslatedString,
},
},
{ {
type: "date", type: "date",
props: { props: {
@ -26,12 +142,26 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
help: "format 'dd/MM/yyyy'" as TranslatedString, help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{
type: "text",
props: {
name: "signature",
label: "Signature" as TranslatedString,
},
},
{
type: "separator",
props: {
label:
"It is a criminal offense to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, document forgery)" as TranslatedString,
},
},
], ],
}, },
], ],
behavior: function formBehavior( behavior: function formBehavior(
v: Partial<Form902_12e.Form>, v: Partial<Form902_15e.Form>,
): FormState<Form902_12e.Form> { ): FormState<Form902_15e.Form> {
return { return {
when: { when: {
disabled: true, disabled: true,
@ -40,9 +170,21 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
}, },
}; };
namespace Form902_12e { namespace Form902_15e {
interface Person {
fullName: string;
address: string;
dateOfBirth: AbsoluteTime;
nationality: string;
}
export interface Form { export interface Form {
contractingPartner: string; contractingPartner: string;
contractualRelationship: string;
insurancePolicy: string;
holder: Person;
premiumsPayer: Person;
when: AbsoluteTime; when: AbsoluteTime;
signature: string;
} }
} }

View File

@ -1,6 +1,6 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { FlexibleForm, languageList } from "./index.js";
import { FormState } from "../handlers/FormProvider.js"; import { FormState } from "../handlers/FormProvider.js";
import { FlexibleForm } from "./index.js";
export const v1: FlexibleForm<Form902_12e.Form> = { export const v1: FlexibleForm<Form902_12e.Form> = {
versionId: "2023-05-15", versionId: "2023-05-15",

View File

@ -1,20 +1,32 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import {
import { FlexibleForm, languageList } from "./index.js"; AbsoluteTime,
AmountJson,
TranslatedString,
} from "@gnu-taler/taler-util";
import { FormState } from "../handlers/FormProvider.js"; import { FormState } from "../handlers/FormProvider.js";
import { FlexibleForm, currencyList } from "./index.js";
export const v1: FlexibleForm<Form902_12e.Form> = { export const v1: FlexibleForm<Form902_12e.Form> = {
versionId: "2023-05-15", versionId: "2023-05-15",
design: [ design: [
{ {
title: "5" as TranslatedString, title: "Customer Profile" as TranslatedString,
description: 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, "The information below has to refer to the persons from whom the assets originate ultimately (e.g. beneficial owner of the assets, founder/creator of a trust or foundation). Is the customer an operational legal entity or partnership the information may refer to the entity itself (not to the controlling person), unless the entity holds the assets in trust for a third party." as TranslatedString,
fields: [ fields: [
{ {
type: "textArea", type: "text",
props: { props: {
name: "contractingPartner", name: "customer",
label: "Contracting partner" as TranslatedString, label: "Customer" as TranslatedString,
help: "Pursuant Identification Form (VQF doc. No. 902.1) numeral 1" as TranslatedString,
},
},
{
type: "text",
props: {
name: "fullName",
label: "Full name" as TranslatedString,
}, },
}, },
{ {
@ -28,6 +40,194 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
}, },
], ],
}, },
{
title: "Business activity" as TranslatedString,
fields: [
{
type: "textArea",
props: {
label: "Profession, business activities" as TranslatedString,
name: "businessActivity",
help: "former, current, potentially planned" as TranslatedString,
},
},
],
},
{
title: "Financial circumstances" as TranslatedString,
fields: [
{
type: "textArea",
props: {
label: "Income and assets, liabilities" as TranslatedString,
name: "financial",
help: "estimated" as TranslatedString,
},
},
],
},
{
title: "Origin of the deposited assets involved" as TranslatedString,
fields: [
{
type: "text",
props: {
label: "Nature" as TranslatedString,
name: "originOfAssets.nature",
help: "nature of the involved assets" as TranslatedString,
},
},
{
type: "selectOne",
props: {
name: "originOfAssets.currency",
label: "Currency" as TranslatedString,
choices: currencyList,
},
},
{
type: "integer",
props: {
label: "Amount" as TranslatedString,
name: "originOfAssets.amount",
},
},
{
type: "choiceStacked",
props: {
label: "Category" as TranslatedString,
name: "originOfAssets.category",
choices: [
{
label: "Savings" as TranslatedString,
value: "savings",
},
{
label: "Own business operations" as TranslatedString,
value: "own-business",
},
{
label: "Inheritance" as TranslatedString,
value: "inheritance",
},
{
label: "Other, what?" as TranslatedString,
value: "other",
},
],
},
},
{
type: "text",
props: {
label: "Other category" as TranslatedString,
name: "originOfAssets.categoryOther",
required: true,
},
},
{
type: "textArea",
props: {
label:
"Detailed description of the origins/economical background of the assets involved in the business relationship" as TranslatedString,
name: "originOfAssets.details",
},
},
],
},
{
title:
"Nature and purpose of the business relationship" as TranslatedString,
fields: [
{
type: "textArea",
props: {
label: "Purpose of the business relationship" as TranslatedString,
name: "nature.purpose",
help: "nature of the involved assets" as TranslatedString,
},
},
{
type: "textArea",
props: {
label:
"Information on the planned development of the business relationship and the assets" as TranslatedString,
name: "nature.plan",
},
},
{
type: "textArea",
props: {
label:
"Especially in the case of cash or money and asset transfer transactions with regular customers: Details on usual business volume, Information on the beneficiaries, (Full name, address, bank account)" as TranslatedString,
name: "nature.cashOrMoneyTransfer",
},
},
],
},
{
title: "Relationship with third parties" as TranslatedString,
fields: [
{
type: "textArea",
props: {
label:
"Relation of the customer to the beneficial owner involved in the business relationship" as TranslatedString,
name: "relations.beneficialOwners",
},
},
{
type: "textArea",
props: {
label:
"Relation of the customer to the controlling persons involved in the business relationship" as TranslatedString,
name: "relations.controllingPersons",
},
},
{
type: "textArea",
props: {
label:
"Relation of the customer to the authorized signatories involved in the business relationship" as TranslatedString,
name: "relations.authorizedSignatories",
},
},
{
type: "textArea",
props: {
label:
"Relation of the customer to other persons involved in the business relationship" as TranslatedString,
name: "relations.otherPersons",
},
},
{
type: "textArea",
props: {
label: "Relation to other AMLA-Files" as TranslatedString,
name: "relations.withOtherAmlaFiles",
},
},
{
type: "textArea",
props: {
label: "Introducer / agents / references" as TranslatedString,
name: "relations.references",
},
},
],
},
{
title: "Further information" as TranslatedString,
fields: [
{
type: "textArea",
props: {
label: "Other relevant information" as TranslatedString,
name: "furtherInformation",
},
},
],
},
], ],
behavior: function formBehavior( behavior: function formBehavior(
v: Partial<Form902_12e.Form>, v: Partial<Form902_12e.Form>,
@ -36,13 +236,43 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
when: { when: {
disabled: true, disabled: true,
}, },
originOfAssets: {
categoryOther: {
hidden: v.originOfAssets?.category !== "other",
},
},
}; };
}, },
}; };
namespace Form902_12e { namespace Form902_12e {
export interface Form { export interface Form {
contractingPartner: string; customer: string;
fullName: string;
when: AbsoluteTime; when: AbsoluteTime;
businessActivity: string;
financial: string;
originOfAssets: {
nature: string;
currency: string;
amount: number;
category: "savings" | "own-business" | "inheritance" | "other";
categoryOther: string;
details: string;
};
nature: {
purpose: string;
plan: string;
cashOrMoneyTransfer: string;
};
relations: {
beneficialOwners: string;
controllingPersons: string;
authorizedSignatories: string;
otherPersons: string;
withOtherAmlaFiles: string;
references: string;
};
furtherInformation: string;
} }
} }

View File

@ -1,14 +1,13 @@
import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util";
import { FlexibleForm, languageList } from "./index.js";
import { FormState } from "../handlers/FormProvider.js"; import { FormState } from "../handlers/FormProvider.js";
import { FlexibleForm } from "./index.js";
export const v1: FlexibleForm<Form902_12e.Form> = { export const v1: FlexibleForm<Form902_9e.Form> = {
versionId: "2023-05-15", versionId: "2023-05-15",
design: [ design: [
{ {
title: "9" as TranslatedString, title:
description: "Declaration of identity of the beneficial owner" as TranslatedString,
"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: [ fields: [
{ {
type: "textArea", type: "textArea",
@ -26,12 +25,87 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
help: "format 'dd/MM/yyyy'" as TranslatedString, help: "format 'dd/MM/yyyy'" as TranslatedString,
}, },
}, },
{
type: "separator",
props: {
label:
"The contracting partner hereby declares that the person(s) listed below is/are the beneficial owner(s) of the assets involved in the business relationship. If the contracting partner is also the sole beneficial owner of the assets, the contracting partner's detail must be set out below" as TranslatedString,
},
},
{
type: "array",
props: {
label: "Persons" as TranslatedString,
labelField: "surname",
name: "persons",
fields: [
{
type: "text",
props: {
name: "surname",
label: "Surname(s)" as TranslatedString,
},
},
{
type: "text",
props: {
name: "firstName",
label: "First name(s)" as TranslatedString,
},
},
{
type: "date",
props: {
name: "dateOfBirth",
label: "Date of birth" as TranslatedString,
pattern: "dd/MM/yyyy",
help: "format 'dd/MM/yyyy'" as TranslatedString,
},
},
{
type: "text",
props: {
name: "nationality",
label: "Nationality" as TranslatedString,
},
},
{
type: "text",
props: {
name: "address",
label: "Actual address of domicile" as TranslatedString,
},
},
],
},
},
{
type: "separator",
props: {
label:
"The contracting partner hereby undertakes to inform automatically of any changes to the information contained herein" as TranslatedString,
},
},
{
type: "text",
props: {
name: "signature",
label: "Signature" as TranslatedString,
},
},
{
type: "separator",
props: {
label:
"It is a criminal offense to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, document forgery)" as TranslatedString,
},
},
], ],
}, },
], ],
behavior: function formBehavior( behavior: function formBehavior(
v: Partial<Form902_12e.Form>, v: Partial<Form902_9e.Form>,
): FormState<Form902_12e.Form> { ): FormState<Form902_9e.Form> {
return { return {
when: { when: {
disabled: true, disabled: true,
@ -40,9 +114,18 @@ export const v1: FlexibleForm<Form902_12e.Form> = {
}, },
}; };
namespace Form902_12e { namespace Form902_9e {
interface Person {
surname: string;
firstName: string;
dateOfBirth: AbsoluteTime;
nationality: string;
address: string;
}
export interface Form { export interface Form {
contractingPartner: string; contractingPartner: string;
persons: Person;
when: AbsoluteTime; when: AbsoluteTime;
signature: string;
} }
} }

View File

@ -118,3 +118,29 @@ export const languageList = [
value: "hau", value: "hau",
}, },
]; ];
export const currencyList = [
{
label: "United States dollar" as TranslatedString,
value: "usd",
},
{
label: "Euro" as TranslatedString,
value: "eur",
},
{
label: "Swiss franc" as TranslatedString,
value: "chf",
},
{
label: "Argentine peso" as TranslatedString,
value: "ars",
},
{
label: "Mexican peso" as TranslatedString,
value: "mxn",
},
{
label: "Brazilian real" as TranslatedString,
value: "brl",
},
];

View File

@ -0,0 +1,41 @@
import { TranslatedString } from "@gnu-taler/taler-util";
import { VNode, h } from "preact";
import { LabelWithTooltipMaybeRequired } from "./InputLine.js";
import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js";
interface Props {
before?: TranslatedString;
after?: TranslatedString;
tooltipBefore?: TranslatedString;
tooltipAfter?: TranslatedString;
fields: UIFormField[];
}
export function Group({
before,
after,
tooltipAfter,
tooltipBefore,
fields,
}: Props): VNode {
return (
<div class="sm:col-span-6 p-4 rounded-lg border-r-2 border-2 bg-gray-50">
<div class="pb-4">
{before && (
<LabelWithTooltipMaybeRequired
label={before}
tooltip={tooltipBefore}
/>
)}
</div>
<div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
<RenderAllFieldsByUiConfig fields={fields} />
</div>
<div class="pt-4">
{after && (
<LabelWithTooltipMaybeRequired label={after} tooltip={tooltipAfter} />
)}
</div>
</div>
);
}

View File

@ -1,5 +1,5 @@
import { Fragment, VNode, h } from "preact"; import { Fragment, VNode, h } from "preact";
import { Choice } from "./InputChoice.js"; import { Choice } from "./InputChoiceStacked.js";
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
import { useField } from "./useField.js"; import { useField } from "./useField.js";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -8,9 +8,10 @@ export function InputSelectMultiple(
props: { props: {
choices: Choice[]; choices: Choice[];
unique?: boolean; unique?: boolean;
max?: number;
} & UIFormProps<Array<string>>, } & UIFormProps<Array<string>>,
): VNode { ): VNode {
const { name, label, choices, placeholder, tooltip, required, unique } = const { name, label, choices, placeholder, tooltip, required, unique, max } =
props; props;
const { value, onChange } = useField<{ [s: string]: Array<string> }>(name); const { value, onChange } = useField<{ [s: string]: Array<string> }>(name);
@ -113,6 +114,9 @@ export function InputSelectMultiple(
if (unique && list.indexOf(v.value) !== -1) { if (unique && list.indexOf(v.value) !== -1) {
return; return;
} }
if (max !== undefined && list.length >= max) {
return;
}
const newValue = [...list]; const newValue = [...list];
newValue.splice(0, 0, v.value); newValue.splice(0, 0, v.value);
onChange(newValue); onChange(newValue);

View File

@ -0,0 +1,136 @@
import { Fragment, VNode, h } from "preact";
import { Choice } from "./InputChoiceStacked.js";
import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js";
import { useField } from "./useField.js";
import { useState } from "preact/hooks";
export function InputSelectOne(
props: {
choices: Choice[];
} & UIFormProps<Array<string>>,
): VNode {
const { name, label, choices, placeholder, tooltip, required } = props;
const { value, onChange } = useField<{ [s: string]: string | undefined }>(
name,
);
const [filter, setFilter] = useState<string | undefined>(undefined);
const regex = new RegExp(`.*${filter}.*`, "i");
const choiceMap = choices.reduce((prev, curr) => {
return { ...prev, [curr.value]: curr.label };
}, {} as Record<string, string>);
const filteredChoices =
filter === undefined
? undefined
: choices.filter((v) => {
return regex.test(v.label);
});
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
label={label}
required={required}
tooltip={tooltip}
/>
{value ? (
<span class="inline-flex items-center gap-x-0.5 rounded-md bg-gray-100 p-1 mr-2 font-medium text-gray-600">
{choiceMap[value]}
<button
type="button"
onClick={() => {
onChange(undefined);
}}
class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
>
<span class="sr-only">Remove</span>
<svg
viewBox="0 0 14 14"
class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
>
<path d="M4 4l6 6m0-6l-6 6" />
</svg>
<span class="absolute -inset-1"></span>
</button>
</span>
) : (
<div class="relative mt-2">
<input
id="combobox"
type="text"
value={filter ?? ""}
onChange={(e) => {
setFilter(e.currentTarget.value);
}}
placeholder={placeholder}
class="w-full rounded-md border-0 bg-white py-1.5 pl-3 pr-12 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
role="combobox"
aria-controls="options"
aria-expanded="false"
/>
<button
type="button"
onClick={() => {
setFilter(filter === undefined ? "" : undefined);
}}
class="absolute inset-y-0 right-0 flex items-center rounded-r-md px-2 focus:outline-none"
>
<svg
class="h-5 w-5 text-gray-400"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
clip-rule="evenodd"
/>
</svg>
</button>
{filteredChoices !== undefined && (
<ul
class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
id="options"
role="listbox"
>
{filteredChoices.map((v, idx) => {
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);
onChange(v.value);
}}
// tabindex="-1"
>
{/* <!-- Selected: "font-semibold" --> */}
<span class="block truncate">{v.label}</span>
{/* <!--
Checkmark, only display for selected option.
Active: "text-white", Not Active: "text-indigo-600"
--> */}
</li>
);
})}
{/* <!--
Combobox option, manage highlight styles based on mouseenter/mouseleave and keyboard navigation.
Active: "text-white bg-indigo-600", Not Active: "text-gray-900"
--> */}
{/* <!-- More items... --> */}
</ul>
)}
</div>
)}
</div>
);
}

View File

@ -0,0 +1,26 @@
import { VNode, h } from "preact";
import {
InputLine,
LabelWithTooltipMaybeRequired,
UIFormProps,
} from "./InputLine.js";
import { TranslatedString } from "@gnu-taler/taler-util";
interface Props {
label: TranslatedString;
tooltip?: TranslatedString;
help?: TranslatedString;
}
export function Separator({ label, tooltip, help }: Props): VNode {
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired label={label} tooltip={tooltip} />
{help && (
<p class="mt-2 text-sm text-gray-500" id="email-description">
{help}
</p>
)}
</div>
);
}

View File

@ -3,11 +3,14 @@ import { InputText } from "./InputText.js";
import { InputDate } from "./InputDate.js"; import { InputDate } from "./InputDate.js";
import { InputInteger } from "./InputInteger.js"; import { InputInteger } from "./InputInteger.js";
import { h as create, Fragment, VNode } from "preact"; import { h as create, Fragment, VNode } from "preact";
import { InputChoiceStacked } from "./InputChoice.js"; import { InputChoiceStacked } from "./InputChoiceStacked.js";
import { InputArray } from "./InputArray.js"; import { InputArray } from "./InputArray.js";
import { InputSelectMultiple } from "./InputSelectMultiple.js"; import { InputSelectMultiple } from "./InputSelectMultiple.js";
import { InputTextArea } from "./InputTextArea.js"; import { InputTextArea } from "./InputTextArea.js";
import { InputFile } from "./InputFile.js"; import { InputFile } from "./InputFile.js";
import { Separator } from "./Separator.js";
import { Group } from "./Group.js";
import { InputSelectOne } from "./InputSelectOne.js";
export type DoubleColumnForm = DoubleColumnFormSection[]; export type DoubleColumnForm = DoubleColumnFormSection[];
@ -21,9 +24,11 @@ type DoubleColumnFormSection = {
* Constrain the type with the ui props * Constrain the type with the ui props
*/ */
type FieldType = { type FieldType = {
separator: {}; group: Parameters<typeof Group>[0];
separator: Parameters<typeof Separator>[0];
array: Parameters<typeof InputArray>[0]; array: Parameters<typeof InputArray>[0];
file: Parameters<typeof InputFile>[0]; file: Parameters<typeof InputFile>[0];
selectOne: Parameters<typeof InputSelectOne>[0];
selectMultiple: Parameters<typeof InputSelectMultiple>[0]; selectMultiple: Parameters<typeof InputSelectMultiple>[0];
text: Parameters<typeof InputText>[0]; text: Parameters<typeof InputText>[0];
textArea: Parameters<typeof InputTextArea>[0]; textArea: Parameters<typeof InputTextArea>[0];
@ -36,9 +41,11 @@ type FieldType = {
* List all the form fields so typescript can type-check the form instance * List all the form fields so typescript can type-check the form instance
*/ */
export type UIFormField = export type UIFormField =
| { type: "group"; props: FieldType["group"] }
| { type: "separator"; props: FieldType["separator"] } | { type: "separator"; props: FieldType["separator"] }
| { type: "array"; props: FieldType["array"] } | { type: "array"; props: FieldType["array"] }
| { type: "file"; props: FieldType["file"] } | { type: "file"; props: FieldType["file"] }
| { type: "selectOne"; props: FieldType["selectOne"] }
| { type: "selectMultiple"; props: FieldType["selectMultiple"] } | { type: "selectMultiple"; props: FieldType["selectMultiple"] }
| { type: "text"; props: FieldType["text"] } | { type: "text"; props: FieldType["text"] }
| { type: "textArea"; props: FieldType["textArea"] } | { type: "textArea"; props: FieldType["textArea"] }
@ -54,14 +61,11 @@ type UIFormFieldMap = {
[key in keyof FieldType]: FieldComponentFunction<key>; [key in keyof FieldType]: FieldComponentFunction<key>;
}; };
function Separator(): VNode {
return create("div", {});
}
/** /**
* Maps input type with component implementation * Maps input type with component implementation
*/ */
const UIFormConfiguration: UIFormFieldMap = { const UIFormConfiguration: UIFormFieldMap = {
group: Group,
separator: Separator, separator: Separator,
array: InputArray, array: InputArray,
text: InputText, text: InputText,
@ -70,6 +74,7 @@ const UIFormConfiguration: UIFormFieldMap = {
date: InputDate, date: InputDate,
choiceStacked: InputChoiceStacked, choiceStacked: InputChoiceStacked,
integer: InputInteger, integer: InputInteger,
selectOne: InputSelectOne,
selectMultiple: InputSelectMultiple, selectMultiple: InputSelectMultiple,
}; };