From 860e4216cdaf3e3bfb50ec1d9d285d34b6af5470 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 16 Nov 2023 15:38:27 -0300 Subject: some fixes and examples --- .../src/pages/AntiMoneyLaunderingForm.stories.tsx | 94 +++++++++ .../src/pages/AntiMoneyLaunderingForm.tsx | 72 +++++-- .../aml-backoffice-ui/src/pages/CaseDetails.tsx | 226 ++------------------- packages/aml-backoffice-ui/src/pages/Cases.tsx | 26 ++- .../src/pages/HandleAccountNotReady.tsx | 10 +- .../aml-backoffice-ui/src/pages/NewFormEntry.tsx | 51 +---- .../src/pages/ShowConsolidated.stories.tsx | 60 ++++++ .../src/pages/ShowConsolidated.tsx | 204 +++++++++++++++++++ .../aml-backoffice-ui/src/pages/UnlockAccount.tsx | 7 +- .../aml-backoffice-ui/src/pages/index.stories.ts | 2 + 10 files changed, 476 insertions(+), 276 deletions(-) create mode 100644 packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.stories.tsx create mode 100644 packages/aml-backoffice-ui/src/pages/ShowConsolidated.stories.tsx create mode 100644 packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx create mode 100644 packages/aml-backoffice-ui/src/pages/index.stories.ts (limited to 'packages/aml-backoffice-ui/src/pages') diff --git a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.stories.tsx b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.stories.tsx new file mode 100644 index 000000000..a14966cc0 --- /dev/null +++ b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.stories.tsx @@ -0,0 +1,94 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import * as tests from "@gnu-taler/web-util/testing"; +import { + AntiMoneyLaunderingForm as TestedComponent, +} from "./AntiMoneyLaunderingForm.js"; + +export default { + title: "aml form", +}; + +export const SimpleComment = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 0, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const Identification = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 1, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const OperationalLegalEntity = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 2, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const Foundations = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 3, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const DelcarationOfTrusts = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 4, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const InformationOnLifeInsurance = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 5, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const DeclarationOfBeneficialOwner = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 6, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const CustomerProfile = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 7, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); +export const RiskProfile = tests.createExample(TestedComponent, { + account: "the_account", + selectedForm: 8, + onSubmit: async (justification, newState, newThreshold) => { + alert(JSON.stringify({justification, newState, newThreshold}, undefined, 2)) + } +}); + diff --git a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx index c3fb7dafe..5d2a3dffe 100644 --- a/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx +++ b/packages/aml-backoffice-ui/src/pages/AntiMoneyLaunderingForm.tsx @@ -1,3 +1,5 @@ +import { AbsoluteTime, AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { h } from "preact"; import { NiceForm } from "../NiceForm.js"; import { v1 as form_902_11e_v1 } from "../forms/902_11e.js"; @@ -9,30 +11,63 @@ import { v1 as form_902_4e_v1 } from "../forms/902_4e.js"; import { v1 as form_902_5e_v1 } from "../forms/902_5e.js"; import { v1 as form_902_9e_v1 } from "../forms/902_9e.js"; import { v1 as simplest } from "../forms/simplest.js"; -import { DocumentDuplicateIcon } from "@heroicons/react/24/solid"; -import { AbsoluteTime } from "@gnu-taler/taler-util"; -import { AmountJson, Amounts } from "@gnu-taler/taler-util"; +import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../types.js"; -export function AntiMoneyLaunderingForm({ number }: { number?: string }) { - const selectedForm = Number.parseInt(number ?? "0", 10); - if (Number.isNaN(selectedForm)) { - return
WHAT! {number}
; - } +export type Justification = { + // form index in the list of forms + index: number; + // form name + name: string; + // form values + value: any; +} + +export function AntiMoneyLaunderingForm({ account, selectedForm, onSubmit }: { account: string, selectedForm: number, onSubmit: (justification: Justification, state: AmlExchangeBackend.AmlState, threshold: AmountJson) => Promise; }) { + const { i18n } = useTranslationContext() const showingFrom = allForms[selectedForm].impl; - const storedValue = { + const formName = allForms[selectedForm].name + const initial = { fullName: "loggedIn_user_fullname", when: AbsoluteTime.now(), + state: AmlExchangeBackend.AmlState.pending, + threshold: Amounts.parseOrThrow("KUDOS:1000"), }; return ( {}} - /> + initial={initial} + form={showingFrom(initial)} + onUpdate={() => { }} + onSubmit={(formValue) => { + if (formValue.state === undefined || formValue.threshold === undefined) return; + const st = formValue.state; + const amount = formValue.threshold; + + const justification = { + index: selectedForm, + name: formName, + value: formValue + } + + onSubmit(justification, st, amount); + }} + > +
+ + Cancel + + +
+
); } @@ -41,6 +76,11 @@ export interface State { threshold: AmountJson; } +const DocumentDuplicateIcon = + + + + export const allForms = [ { name: "Simple comment", diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx index f618a3592..1f8d6ac5e 100644 --- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx +++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx @@ -2,24 +2,20 @@ import { AbsoluteTime, AmountJson, Amounts, - PaytoString, TalerError, TranslatedString, - assertUnreachable, + assertUnreachable } from "@gnu-taler/taler-util"; import { ErrorLoading, Loading, useTranslationContext } from "@gnu-taler/web-util/browser"; -import { ArrowDownCircleIcon, ClockIcon } from "@heroicons/react/20/solid"; import { format } from "date-fns"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { NiceForm } from "../NiceForm.js"; -import { FlexibleForm } from "../forms/index.js"; -import { UIFormField } from "../handlers/forms.js"; import { useCaseDetails } from "../hooks/useCaseDetails.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../types.js"; +import { ShowConsolidated } from "./ShowConsolidated.js"; -type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent; +export type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent; type AmlFormEvent = { type: "aml-form"; when: AbsoluteTime; @@ -47,7 +43,7 @@ function selectSooner(a: WithTime, b: WithTime) { return AbsoluteTime.cmp(a.when, b.when); } -function getEventsFromAmlHistory( +export function getEventsFromAmlHistory( aml: AmlExchangeBackend.AmlDecisionDetail[], kyc: AmlExchangeBackend.KycDetail[], ): AmlEvent[] { @@ -113,12 +109,16 @@ export function CaseDetails({ account }: { account: string }) { href={Pages.newFormEntry.url({ account })} class="m-4 block rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700" > - New AML form + + New AML form +

- Case history + + Case history +

@@ -187,11 +187,18 @@ export function CaseDetails({ account }: { account: string }) { } case "kyc-collection": { return ( - + // + + + ); } case "kyc-expiration": { - return ; + // return ; + return + + + } } })()} @@ -217,7 +224,7 @@ export function CaseDetails({ account }: { account: string }) {
{selected && } - {selected && } + {selected && } ); } @@ -226,197 +233,4 @@ function ShowEventDetails({ event }: { event: AmlEvent }): VNode { return
type {event.type}
; } -function ShowConsolidated({ - history, - until, -}: { - history: AmlEvent[]; - until: AmlEvent; -}): VNode { - const cons = getConsolidated(history, until.when); - - const form: FlexibleForm = { - versionId: "1", - behavior: (form) => { - return { - aml: { - threshold: { - hidden: !form.aml - }, - since: { - hidden: !form.aml - }, - state: { - hidden: !form.aml - } - } - }; - }, - design: [ - { - title: "AML" as TranslatedString, - fields: [ - { - type: "amount", - props: { - label: "Threshold" as TranslatedString, - name: "aml.threshold", - }, - }, - { - type: "choiceHorizontal", - props: { - label: "State" as TranslatedString, - name: "aml.state", - converter: amlStateConverter, - choices: [ - { - label: "Frozen" as TranslatedString, - value: AmlExchangeBackend.AmlState.frozen, - }, - { - label: "Pending" as TranslatedString, - value: AmlExchangeBackend.AmlState.pending, - }, - { - label: "Normal" as TranslatedString, - value: AmlExchangeBackend.AmlState.normal, - }, - ], - }, - }, - ], - }, - Object.entries(cons.kyc).length > 0 - ? { - title: "KYC" as TranslatedString, - fields: Object.entries(cons.kyc).map(([key, field]) => { - const result: UIFormField = { - type: "text", - props: { - label: key as TranslatedString, - name: `kyc.${key}.value`, - help: `${field.provider} since ${field.since.t_ms === "never" - ? "never" - : format(field.since.t_ms, "dd/MM/yyyy") - }` as TranslatedString, - }, - }; - return result; - }), - } - : undefined, - ], - }; - return ( - -

- Consolidated information after{" "} - {until.when.t_ms === "never" - ? "never" - : format(until.when.t_ms, "dd MMMM yyyy")} -

- { }} - /> -
- ); -} -interface Consolidated { - aml: { - state: AmlExchangeBackend.AmlState; - threshold: AmountJson; - since: AbsoluteTime; - }; - kyc: { - [field: string]: { - value: any; - provider: string; - since: AbsoluteTime; - }; - }; -} - -function getConsolidated( - history: AmlEvent[], - when: AbsoluteTime, -): Consolidated { - const initial: Consolidated = { - aml: { - state: AmlExchangeBackend.AmlState.normal, - threshold: { - currency: "ARS", - value: 1000, - fraction: 0, - }, - since: AbsoluteTime.never() - }, - kyc: {}, - }; - return history.reduce((prev, cur) => { - if (AbsoluteTime.cmp(when, cur.when) < 0) { - return prev; - } - switch (cur.type) { - case "kyc-expiration": { - cur.fields.forEach((field) => { - delete prev.kyc[field]; - }); - break; - } - case "aml-form": { - prev.aml = { - since: cur.when, - state: cur.state, - threshold: cur.threshold - } - break; - } - case "kyc-collection": { - Object.keys(cur.values).forEach((field) => { - prev.kyc[field] = { - value: (cur.values as any)[field], - provider: cur.provider, - since: cur.when, - }; - }); - break; - } - } - return prev; - }, initial); -} - -export const amlStateConverter = { - toStringUI: stringifyAmlState, - fromStringUI: parseAmlState, -}; - -function stringifyAmlState(s: AmlExchangeBackend.AmlState | undefined): string { - if (s === undefined) return ""; - switch (s) { - case AmlExchangeBackend.AmlState.normal: - return "normal"; - case AmlExchangeBackend.AmlState.pending: - return "pending"; - case AmlExchangeBackend.AmlState.frozen: - return "frozen"; - } -} - -function parseAmlState(s: string | undefined): AmlExchangeBackend.AmlState { - switch (s) { - case "normal": - return AmlExchangeBackend.AmlState.normal; - case "pending": - return AmlExchangeBackend.AmlState.pending; - case "frozen": - return AmlExchangeBackend.AmlState.frozen; - default: - throw Error(`unknown AML state: ${s}`); - } -} diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx b/packages/aml-backoffice-ui/src/pages/Cases.tsx index 624f2c985..64cacf68c 100644 --- a/packages/aml-backoffice-ui/src/pages/Cases.tsx +++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx @@ -6,8 +6,9 @@ import { createNewForm } from "../handlers/forms.js"; import { useCases } from "../hooks/useCases.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../types.js"; -import { amlStateConverter } from "./CaseDetails.js"; + import { Officer } from "./Officer.js"; +import { amlStateConverter } from "./ShowConsolidated.js"; export function Cases() { const { i18n } = useTranslationContext(); @@ -43,10 +44,14 @@ export function Cases() {

+ Cases +

+ A list of all the account with the status +

+ + + +export const HomeIcon = () => + + + + +export const ChevronRightIcon = () => + + + + +export const ArrowRightIcon = () => + + + + function Pagination() { return (