gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-typescript-core] 05/08: update measures


From: gnunet
Subject: [taler-typescript-core] 05/08: update measures
Date: Wed, 15 Jan 2025 22:32:51 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository taler-typescript-core.

commit 140bef3747521b9ab4a92e24dceedfaf1ecc15f9
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Jan 15 12:01:32 2025 -0300

    update measures
---
 packages/aml-backoffice-ui/src/Routing.tsx         |   3 +
 packages/aml-backoffice-ui/src/forms.json          | 528 +--------------------
 packages/aml-backoffice-ui/src/hooks/account.ts    |   8 +-
 .../src/hooks/decision-request.ts                  |  83 +++-
 packages/aml-backoffice-ui/src/hooks/decisions.ts  |  63 ++-
 .../src/pages/AmlDecisionRequestWizard.tsx         | 306 ++++++++++--
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    |   4 +-
 .../aml-backoffice-ui/src/pages/CreateAccount.tsx  |   4 +-
 8 files changed, 400 insertions(+), 599 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/Routing.tsx 
b/packages/aml-backoffice-ui/src/Routing.tsx
index ae9d14991..148ece344 100644
--- a/packages/aml-backoffice-ui/src/Routing.tsx
+++ b/packages/aml-backoffice-ui/src/Routing.tsx
@@ -164,6 +164,7 @@ function PrivateRouting(): VNode {
     case "decideNew": {
       return (
         <AmlDecisionRequestWizard
+          account={location.values.cid}
           onMove={(step) => {
             if (!step) {
               navigateTo(privatePages.profile.url({}));
@@ -182,6 +183,7 @@ function PrivateRouting(): VNode {
     case "decide": {
       return (
         <AmlDecisionRequestWizard
+          account={location.values.cid}
           onMove={(step) => {
             if (!step) {
               navigateTo(privatePages.profile.url({}));
@@ -200,6 +202,7 @@ function PrivateRouting(): VNode {
     case "decideWithStep": {
       return (
         <AmlDecisionRequestWizard
+          account={location.values.cid}
           step={location.values.step as WizardSteps}
           onMove={(step) => {
             if (!step) {
diff --git a/packages/aml-backoffice-ui/src/forms.json 
b/packages/aml-backoffice-ui/src/forms.json
index 94dcda317..6aff29275 100644
--- a/packages/aml-backoffice-ui/src/forms.json
+++ b/packages/aml-backoffice-ui/src/forms.json
@@ -1,529 +1,3 @@
 {
-  "forms": [
-    {
-      "label": "Information on customer",
-      "id": "902_1e",
-      "version": 1,
-      "config": {
-        "type": "double-column",
-        "design": [
-          {
-            "title": "Information on customer",
-            "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.",
-            "fields": [
-              {
-                "type": "choiceStacked",
-
-                "name": "customerType",
-                "id": ".customerType",
-                "label": "Type of customer",
-                "help": "Select one and complete the next form",
-                "required": true,
-                "choices": [
-                  {
-                    "label": "Natural person",
-                    "value": "natural"
-                  },
-                  {
-                    "label": "Legal entity",
-                    "value": "legal"
-                  }
-                ]
-              },
-              {
-                "type": "group",
-
-                "label": "Natural customer form",
-                "name": "algo",
-                "id": "algo",
-                "before": "a) Country risk (nationality)",
-                "after": "a) Country risk (nationality)",
-                "fields": [
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.fullName",
-                    "id": ".naturalCustomer.fullName",
-                    "label": "Full name",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.address",
-                    "id": ".naturalCustomer.address",
-                    "label": "Residential address",
-                    "required": true
-                  },
-                  {
-                    "type": "integer",
-
-                    "name": "naturalCustomer.telephone",
-                    "id": ".naturalCustomer.telephone",
-                    "label": "Telephone"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.email",
-                    "id": ".naturalCustomer.email",
-                    "label": "E-mail"
-                  },
-                  {
-                    "type": "absoluteTimeText",
-
-                    "pattern": "dd/MM/yyyy",
-                    "name": "naturalCustomer.dateOfBirth",
-                    "id": ".naturalCustomer.dateOfBirth",
-                    "label": "Date of birth",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.nationality",
-                    "id": ".naturalCustomer.nationality",
-                    "label": "Nationality",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.document",
-                    "id": ".naturalCustomer.document",
-                    "label": "Identification document",
-                    "required": true
-                  },
-                  {
-                    "type": "file",
-
-                    "name": "naturalCustomer.documentAttachment",
-                    "id": ".naturalCustomer.documentAttachment",
-                    "label": "Document attachment",
-                    "required": true,
-                    "maxBites": 2097152,
-                    "accept": ".pdf",
-                    "help": "PDF file with max size of 2 mega bytes"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.companyName",
-                    "id": ".naturalCustomer.companyName",
-                    "label": "Company name"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.office",
-                    "id": ".naturalCustomer.office",
-                    "label": "Registered office"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "naturalCustomer.companyDocument",
-                    "id": ".naturalCustomer.companyDocument",
-                    "label": "Company identification document"
-                  },
-                  {
-                    "type": "file",
-
-                    "name": "naturalCustomer.companyDocumentAttachment",
-                    "id": ".naturalCustomer.companyDocumentAttachment",
-                    "label": "Document attachment",
-                    "required": true,
-                    "maxBites": 2097152,
-                    "accept": ".png",
-                    "help": "PNG file with max size of 2 mega bytes"
-                  }
-                ]
-              },
-
-              {
-                "type": "group",
-
-                "label": "Natural customer form",
-                "name": "algo",
-                "id": "algo",
-                "before": "a) Country risk (nationality)",
-                "after": "a) Country risk (nationality)",
-                "fields": [
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.companyName",
-                    "id": ".legalCustomer.companyName",
-                    "label": "Company name",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.domicile",
-                    "id": ".legalCustomer.domicile",
-                    "label": "Domicile",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.contactPerson",
-                    "id": ".legalCustomer.contactPerson",
-                    "label": "Contact person"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.telephone",
-                    "id": ".legalCustomer.telephone",
-                    "label": "Telephone"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.email",
-                    "id": ".legalCustomer.email",
-                    "label": "E-mail"
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "legalCustomer.document",
-                    "id": ".legalCustomer.document",
-                    "label": "Identification document",
-                    "help": "Not older than 12 month"
-                  },
-                  {
-                    "type": "file",
-
-                    "name": "legalCustomer.documentAttachment",
-                    "id": ".legalCustomer.documentAttachment",
-                    "label": "Document attachment",
-                    "required": true,
-                    "maxBites": 2097152,
-                    "accept": ".png",
-                    "help": "PNG file with max size of 2 mega bytes"
-                  }
-                ]
-              }
-            ]
-          },
-          {
-            "title": "Information on the natural persons who establish the 
business relationship for legal entities and partnerships",
-            "description": "For legal entities and partnerships the identity 
of the natural persons who establish the business relationship must be 
verified.",
-            "fields": [
-              {
-                "type": "array",
-
-                "name": "businessEstablisher",
-                "id": ".businessEstablisher",
-                "label": "Persons",
-                "required": true,
-                "labelFieldId": "fullName",
-                "placeholder": "this is the placeholder",
-                "fields": [
-                  {
-                    "type": "text",
-
-                    "name": "fullName",
-                    "id": ".fullName",
-                    "label": "Full name",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "address",
-                    "id": ".address",
-                    "label": "Residential address",
-                    "required": true
-                  },
-                  {
-                    "type": "absoluteTimeText",
-
-                    "pattern": "dd/MM/yyyy",
-                    "name": "dateOfBirth",
-                    "id": ".dateOfBirth",
-                    "label": "Date of birth",
-                    "required": true
-                  },
-
-                  {
-                    "type": "text",
-
-                    "name": "nationality",
-                    "id": ".nationality",
-                    "label": "Nationality",
-                    "required": true
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "typeOfAuthorization",
-                    "id": ".typeOfAuthorization",
-                    "label": "Type of authorization (signatory of 
representation)",
-                    "required": true
-                  },
-                  {
-                    "type": "file",
-
-                    "name": "documentAttachment",
-                    "id": ".documentAttachment",
-                    "label": "Identification document attachment",
-                    "required": true,
-                    "maxBites": 2097152,
-                    "accept": ".pdf",
-                    "help": "PDF file with max size of 2 mega bytes"
-                  },
-                  {
-                    "type": "choiceStacked",
-
-                    "name": "powerOfAttorneyArrangements",
-                    "id": ".powerOfAttorneyArrangements",
-                    "label": "Power of attorney arrangements",
-                    "required": true,
-                    "choices": [
-                      {
-                        "label": "CR extract",
-                        "value": "cr"
-                      },
-                      {
-                        "label": "Mandate",
-                        "value": "mandate"
-                      },
-                      {
-                        "label": "Other",
-                        "value": "other"
-                      }
-                    ]
-                  },
-                  {
-                    "type": "text",
-
-                    "name": "powerOfAttorneyArrangementsOther",
-                    "id": ".powerOfAttorneyArrangementsOther",
-                    "label": "Power of attorney arrangements",
-                    "required": true
-                  }
-                ],
-                "labelField": "fullName"
-              }
-            ]
-          },
-          {
-            "title": "Acceptance of business relationship",
-            "fields": [
-              {
-                "type": "absoluteTimeText",
-
-                "name": "acceptance.when",
-                "id": ".acceptance.when",
-                "pattern": "dd/MM/yyyy",
-                "converterId": "Taler.AbsoluteTime",
-                "label": "Date (conclusion of contract)"
-              },
-              {
-                "type": "choiceStacked",
-
-                "name": "acceptance.acceptedBy",
-                "id": ".acceptance.acceptedBy",
-                "label": "Accepted by",
-                "required": true,
-                "choices": [
-                  {
-                    "label": "Face-to-face meeting with customer",
-                    "value": "face-to-face"
-                  },
-                  {
-                    "label": "Correspondence: authenticated copy of 
identification document obtained",
-                    "value": "correspondence-document"
-                  },
-                  {
-                    "label": "Correspondence: residential address validated",
-                    "value": "correspondence-address"
-                  }
-                ]
-              },
-              {
-                "type": "choiceStacked",
-
-                "name": "acceptance.typeOfCorrespondence",
-                "id": ".acceptance.typeOfCorrespondence",
-                "label": "Type of correspondence service",
-                "choices": [
-                  {
-                    "label": "to the customer",
-                    "value": "customer"
-                  },
-                  {
-                    "label": "hold at bank",
-                    "value": "bank"
-                  },
-                  {
-                    "label": "to the member",
-                    "value": "member"
-                  },
-                  {
-                    "label": "to a third party",
-                    "value": "third-party"
-                  }
-                ]
-              },
-              {
-                "type": "text",
-
-                "name": "acceptance.thirdPartyFullName",
-                "id": ".acceptance.thirdPartyFullName",
-                "label": "Third party full name",
-                "required": true
-              },
-              {
-                "type": "text",
-
-                "name": "acceptance.thirdPartyAddress",
-                "id": ".acceptance.thirdPartyAddress",
-                "label": "Third party  address",
-                "required": true
-              },
-              {
-                "type": "selectMultiple",
-
-                "name": "acceptance.language",
-                "id": ".acceptance.language",
-                "label": "Languages",
-                "choices": [
-                  {
-                    "label": "Espanol",
-                    "value": "es"
-                  }
-                ],
-                "unique": true
-              },
-              {
-                "type": "textArea",
-
-                "name": "acceptance.furtherInformation",
-                "id": ".acceptance.furtherInformation",
-                "label": "Further information"
-              }
-            ]
-          },
-          {
-            "title": "Information on the beneficial owner of the assets and/or 
controlling person",
-            "description": "Establishment of the beneficial owner of the 
assets and/or controlling person",
-            "fields": [
-              {
-                "type": "choiceStacked",
-
-                "name": "establishment",
-                "id": ".establishment",
-                "label": "The customer is",
-                "required": true,
-                "choices": [
-                  {
-                    "label": "a natural person and there are no doubts that 
this person is the sole beneficial owner of the assets",
-                    "value": "natural"
-                  },
-                  {
-                    "label": "a foundation (or a similar construct; incl. 
underlying companies)",
-                    "value": "foundation"
-                  },
-                  {
-                    "label": "a trust (incl. underlying companies)",
-                    "value": "trust"
-                  },
-                  {
-                    "label": "a life insurance policy with separately managed 
accounts/securities accounts",
-                    "value": "insurance-wrapper"
-                  },
-                  {
-                    "label": "all other cases",
-                    "value": "other"
-                  }
-                ]
-              }
-            ]
-          },
-          {
-            "title": "Evaluation with regard to embargo procedures/terrorism 
lists on establishing the business relationship",
-            "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)",
-            "fields": [
-              {
-                "type": "textArea",
-
-                "name": "embargoEvaluation",
-                "id": ".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.",
-                "label": "Evaluation"
-              }
-            ]
-          },
-          {
-            "title": "In the case of cash transactions/occasional customers: 
Information on type and purpose of business relationship",
-            "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",
-            "fields": [
-              {
-                "type": "choiceStacked",
-
-                "name": "cashTransactions.typeOfBusiness",
-                "id": ".cashTransactions.typeOfBusiness",
-                "label": "Type of business relationship",
-                "choices": [
-                  {
-                    "label": "Money exchange",
-                    "value": "money-exchange"
-                  },
-                  {
-                    "label": "Money and asset transfer",
-                    "value": "money-and-asset-transfer"
-                  },
-                  {
-                    "label": "Other cash transactions. Specify below",
-                    "value": "other"
-                  }
-                ]
-              },
-              {
-                "type": "text",
-
-                "name": "cashTransactions.otherTypeOfBusiness",
-                "id": ".cashTransactions.otherTypeOfBusiness",
-                "required": true,
-                "label": "Specify other cash transactions:"
-              },
-              {
-                "type": "textArea",
-                "name": "cashTransactions.purpose",
-                "id": ".cashTransactions.purpose",
-                "label": "Purpose of the business relationship (purpose of 
service requested)"
-              }
-            ]
-          }
-        ]
-      }
-    },
-    {
-      "label": "Example form",
-      "id": "example",
-      "version": 1,
-      "config": {
-        "type": "double-column",
-        "design": [
-          {
-            "title": "Boolean inputs",
-            "fields": [
-              {
-                "type": "toggle",
-                "name": "yes",
-                "id": ".yes",
-                "label": "Yes or no?"
-              }
-            ]
-          }
-        ]
-      }
-    }
-  ],
-  "not_yet_supported": []
+  "forms": []
 }
diff --git a/packages/aml-backoffice-ui/src/hooks/account.ts 
b/packages/aml-backoffice-ui/src/hooks/account.ts
index 9c2049129..a4fbcb803 100644
--- a/packages/aml-backoffice-ui/src/hooks/account.ts
+++ b/packages/aml-backoffice-ui/src/hooks/account.ts
@@ -34,9 +34,12 @@ export function revalidateAccountInformation() {
     { revalidate: true },
   );
 }
-export function useAccountInformation(paytoHash: string) {
+export function useAccountInformation(paytoHash?: string) {
   const officer = useOfficer();
-  const session = officer.state === "ready" ? officer.account : undefined;
+  const session =
+    officer.state === "ready" && paytoHash !== undefined
+      ? officer.account
+      : undefined;
 
   const {
     lib: { exchange: api },
@@ -97,4 +100,3 @@ export function useServerMeasures() {
   if (error) return error;
   return undefined;
 }
-
diff --git a/packages/aml-backoffice-ui/src/hooks/decision-request.ts 
b/packages/aml-backoffice-ui/src/hooks/decision-request.ts
index 4298ad6f3..0990be3b1 100644
--- a/packages/aml-backoffice-ui/src/hooks/decision-request.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decision-request.ts
@@ -16,36 +16,52 @@
 
 import {
   AbsoluteTime,
+  AmountJson,
   Codec,
-  KycRule,
+  Duration,
   MeasureInformation,
-  PaytoHash,
-  Timestamp,
-  TranslatedString,
   buildCodecForObject,
   codecForAbsoluteTime,
+  codecForAmountJson,
   codecForAny,
   codecForBoolean,
-  codecForKycRules,
+  codecForDurationMs,
   codecForList,
   codecForMap,
   codecForMeasureInformation,
   codecForString,
   codecOptional,
+  codecOptionalDefault,
 } from "@gnu-taler/taler-util";
-import {
-  buildStorageKey,
-  useLocalStorage,
-  useTranslationContext,
-} from "@gnu-taler/web-util/browser";
+import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
+
+export type BalanceForm = {
+  balance: AmountJson | undefined;
+  balanceUnlimited: boolean;
+  withdrawalAmount: AmountJson | undefined;
+  withdrawalTimeframe: Duration | undefined;
+  withdrawalUnlimited: boolean;
+  depositAmount: AmountJson | undefined;
+  depositTimeframe: Duration | undefined;
+  depositUnlimited: boolean;
+  closeAmount: AmountJson | undefined;
+  closeTimeframe: Duration | undefined;
+  closeUnlimited: boolean;
+  mergeAmount: AmountJson | undefined;
+  mergeTimeframe: Duration | undefined;
+  mergeUnlimited: boolean;
+  aggregateAmount: AmountJson | undefined;
+  aggregateTimeframe: Duration | undefined;
+  aggregateUnlimited: boolean;
+};
 
 export interface DecisionRequest {
-  rules: KycRule[] | undefined;
+  rules: BalanceForm | undefined;
   deadline: AbsoluteTime | undefined;
   properties: object | undefined;
   custom_events: string[] | undefined;
   inhibit_events: string[] | undefined;
-  keep_investigating: boolean | undefined;
+  keep_investigating: boolean;
   justification: string | undefined;
   next_measure: string[][] | undefined;
   custom_measures:
@@ -55,15 +71,51 @@ export interface DecisionRequest {
     | undefined;
 }
 
+export const codecForBalanceForm = (): Codec<BalanceForm> =>
+  buildCodecForObject<BalanceForm>()
+    .property("balance", codecOptional(codecForAmountJson()))
+    .property(
+      "balanceUnlimited",
+      codecOptionalDefault(codecForBoolean(), false),
+    )
+    .property("withdrawalAmount", codecOptional(codecForAmountJson()))
+    .property("withdrawalTimeframe", codecOptional(codecForDurationMs))
+    .property(
+      "withdrawalUnlimited",
+      codecOptionalDefault(codecForBoolean(), false),
+    )
+    .property("depositAmount", codecOptional(codecForAmountJson()))
+    .property("depositTimeframe", codecOptional(codecForDurationMs))
+    .property(
+      "depositUnlimited",
+      codecOptionalDefault(codecForBoolean(), false),
+    )
+    .property("closeAmount", codecOptional(codecForAmountJson()))
+    .property("closeTimeframe", codecOptional(codecForDurationMs))
+    .property("closeUnlimited", codecOptionalDefault(codecForBoolean(), false))
+    .property("mergeAmount", codecOptional(codecForAmountJson()))
+    .property("mergeTimeframe", codecOptional(codecForDurationMs))
+    .property("mergeUnlimited", codecOptionalDefault(codecForBoolean(), false))
+    .property("aggregateAmount", codecOptional(codecForAmountJson()))
+    .property("aggregateTimeframe", codecOptional(codecForDurationMs))
+    .property(
+      "aggregateUnlimited",
+      codecOptionalDefault(codecForBoolean(), false),
+    )
+    .build("BalanceForm");
+
 export const codecForDecisionRequest = (): Codec<DecisionRequest> =>
   buildCodecForObject<DecisionRequest>()
-    .property("rules", codecOptional(codecForList(codecForKycRules())))
+    .property("rules", codecOptional(codecForBalanceForm()))
     .property("deadline", codecOptional(codecForAbsoluteTime))
     .property("properties", codecForAny())
     .property("justification", codecOptional(codecForString()))
     .property("custom_events", codecOptional(codecForList(codecForString())))
     .property("inhibit_events", codecOptional(codecForList(codecForString())))
-    .property("keep_investigating", codecOptional(codecForBoolean()))
+    .property(
+      "keep_investigating",
+      codecOptionalDefault(codecForBoolean(), false),
+    )
     .property(
       "next_measure",
       codecOptional(codecForList(codecForList(codecForString()))),
@@ -80,7 +132,7 @@ const defaultDecisionRequest: DecisionRequest = {
   custom_events: undefined,
   inhibit_events: undefined,
   justification: undefined,
-  keep_investigating: undefined,
+  keep_investigating: false,
   next_measure: undefined,
   properties: undefined,
   rules: undefined,
@@ -110,6 +162,7 @@ export function useCurrentDecisionRequest(): [
     v: DecisionRequest[T],
   ) {
     const newValue = { ...value, [k]: v };
+    console.log("===", v, k);
     update(newValue);
   }
   return [value, updateField, update];
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts 
b/packages/aml-backoffice-ui/src/hooks/decisions.ts
index 24941b29e..a6c95098d 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decisions.ts
@@ -19,6 +19,8 @@ import { useState } from "preact/hooks";
 import {
   OfficerAccount,
   OperationOk,
+  opFixedSuccess,
+  opSuccessFromHttp,
   TalerExchangeResultByMethod,
   TalerHttpError,
 } from "@gnu-taler/taler-util";
@@ -63,12 +65,7 @@ export function useCurrentDecisionsUnderInvestigation() {
   const { data, error } = useSWR<
     TalerExchangeResultByMethod<"getAmlDecisions">,
     TalerHttpError
-  >(
-    !session
-      ? undefined
-      : [session, offset, "getAmlDecisions"],
-    fetcher,
-  );
+  >(!session ? undefined : [session, offset, "getAmlDecisions"], fetcher);
 
   if (error) return error;
   if (data === undefined) return undefined;
@@ -109,12 +106,7 @@ export function useCurrentDecisions() {
   const { data, error } = useSWR<
     TalerExchangeResultByMethod<"getAmlDecisions">,
     TalerHttpError
-  >(
-    !session
-      ? undefined
-      : [session, offset, "getAmlDecisions"],
-    fetcher,
-  );
+  >(!session ? undefined : [session, offset, "getAmlDecisions"], fetcher);
 
   if (error) return error;
   if (data === undefined) return undefined;
@@ -176,6 +168,53 @@ export function useAccountDecisions(accountStr: string) {
   );
 }
 
+/**
+ * @param account
+ * @param args
+ * @returns
+ */
+export function useAccountActiveDecision(accountStr?: string) {
+  const officer = useOfficer();
+  const session =
+    accountStr !== undefined && officer.state === "ready"
+      ? officer.account
+      : undefined;
+  const {
+    lib: { exchange: api },
+  } = useExchangeApiContext();
+
+  const [offset, setOffset] = useState<string>();
+
+  async function fetcher([officer, account, offset]: [
+    OfficerAccount,
+    string,
+    string | undefined,
+  ]) {
+    return await api.getAmlDecisions(officer, {
+      order: "dec",
+      offset,
+      account,
+      active: true,
+      limit: PAGINATED_LIST_REQUEST,
+    });
+  }
+
+  const { data, error } = useSWR<
+    TalerExchangeResultByMethod<"getAmlDecisions">,
+    TalerHttpError
+  >(
+    !session ? undefined : [session, accountStr, offset, "getAmlDecisions"],
+    fetcher,
+  );
+
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  if (!data.body.records.length) return opFixedSuccess(undefined);
+  return opFixedSuccess(data.body.records[0]);
+}
+
 type PaginatedResult<T> = OperationOk<T> & {
   isLastPage: boolean;
   isFirstPage: boolean;
diff --git a/packages/aml-backoffice-ui/src/pages/AmlDecisionRequestWizard.tsx 
b/packages/aml-backoffice-ui/src/pages/AmlDecisionRequestWizard.tsx
index 8491abe0b..322d6d886 100644
--- a/packages/aml-backoffice-ui/src/pages/AmlDecisionRequestWizard.tsx
+++ b/packages/aml-backoffice-ui/src/pages/AmlDecisionRequestWizard.tsx
@@ -16,15 +16,29 @@
 import {
   AbsoluteTime,
   assertUnreachable,
+  MeasureInformation,
+  TalerError,
   TranslatedString,
 } from "@gnu-taler/taler-util";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
 import {
+  FormDesign,
+  FormUI,
+  InternationalizationAPI,
+  UIHandlerId,
+  useExchangeApiContext,
+  useForm,
+  useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import {
+  BalanceForm,
   DecisionRequest,
   useCurrentDecisionRequest,
 } from "../hooks/decision-request.js";
-import { ShowDecisionLimitInfo } from "./CaseDetails.js";
+import { useAccountActiveDecision } from "../hooks/decisions.js";
+import { ShowDecisionLimitInfo, ShowMeasuresToSelect } from "./CaseDetails.js";
+import { useEffect, useRef } from "preact/hooks";
+import { useServerMeasures } from "../hooks/account.js";
 
 export type WizardSteps =
   | "rules" // define the limits
@@ -58,15 +72,7 @@ const STEPS_ORDER_MAP = STEPS_ORDER.reduce(
 );
 
 function isRulesCompleted(request: DecisionRequest): boolean {
-  return (
-    request.rules !== undefined &&
-    request.rules.findIndex((r) => r.operation_type === "AGGREGATE") !== -1 &&
-    request.rules.findIndex((r) => r.operation_type === "BALANCE") !== -1 &&
-    request.rules.findIndex((r) => r.operation_type === "CLOSE") !== -1 &&
-    request.rules.findIndex((r) => r.operation_type === "DEPOSIT") !== -1 &&
-    request.rules.findIndex((r) => r.operation_type === "MERGE") !== -1 &&
-    request.rules.findIndex((r) => r.operation_type === "WITHDRAW") !== -1
-  );
+  return request.rules !== undefined;
 }
 function isPropertiesCompleted(request: DecisionRequest): boolean {
   return request.properties !== undefined;
@@ -87,9 +93,11 @@ function isJustificationCompleted(request: DecisionRequest): 
boolean {
 }
 
 export function AmlDecisionRequestWizard({
+  account,
   step,
   onMove,
 }: {
+  account?: string;
   step?: WizardSteps;
   onMove: (n: WizardSteps | undefined) => void;
 }): VNode {
@@ -98,7 +106,7 @@ export function AmlDecisionRequestWizard({
   const content = (function () {
     switch (stepOrDefault) {
       case "rules":
-        return <Rules />;
+        return <Rules account={account} />;
       case "properties":
         return <Properties />;
       case "events":
@@ -132,39 +140,199 @@ export function AmlDecisionRequestWizard({
       >
         <i18n.Translate>Next</i18n.Translate>
       </button>
-      {/* <button
-        onClick={() => {
-          onMove(undefined);
-        }}
-        class="m-4  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"
-      >
-        <i18n.Translate>Exit</i18n.Translate>
-      </button> */}
       {content}
     </div>
   );
 }
 
+const rulesForm = (
+  i18n: InternationalizationAPI,
+  currency: string,
+): FormDesign<BalanceForm> => ({
+  type: "double-column",
+  sections: [
+    {
+      title: i18n.str`Wallet`,
+      description: i18n.str`Limit the state of the wallet.`,
+      fields: [
+        {
+          id: "balance" as UIHandlerId,
+          type: "amount",
+          label: i18n.str`Balance`,
+          currency,
+        },
+        {
+          id: "balanceUnlimited" as UIHandlerId,
+          type: "toggle",
+          label: i18n.str`Unlimited`,
+        },
+      ],
+    },
+    {
+      title: i18n.str`Operations`,
+      description: i18n.str`Limit the operation rate.`,
+      fields: [
+        {
+          type: "group",
+          label: i18n.str`Withdrawal`,
+          fields: [
+            {
+              id: "withdrawalAmount" as UIHandlerId,
+              type: "amount",
+              label: i18n.str`Amount`,
+              currency,
+            },
+            {
+              id: "withdrawalTimeframe" as UIHandlerId,
+              type: "duration",
+              label: i18n.str`Timeframe`,
+            },
+            {
+              id: "withdrawalUnlimited" as UIHandlerId,
+              type: "toggle",
+              label: i18n.str`Unlimited`,
+            },
+          ],
+        },
+        {
+          type: "group",
+          label: i18n.str`Deposit`,
+          fields: [
+            {
+              id: "depositAmount" as UIHandlerId,
+              type: "amount",
+              label: i18n.str`Amount`,
+
+              currency,
+            },
+            {
+              id: "depositTimeframe" as UIHandlerId,
+              type: "duration",
+              label: i18n.str`Timeframe`,
+            },
+          ],
+        },
+        {
+          type: "group",
+          label: i18n.str`Aggregate`,
+          fields: [
+            {
+              id: "aggregateAmount" as UIHandlerId,
+              type: "amount",
+              label: i18n.str`Amount`,
+
+              currency,
+            },
+            {
+              id: "aggregateTimeframe" as UIHandlerId,
+              type: "duration",
+              label: i18n.str`Timeframe`,
+            },
+          ],
+        },
+        {
+          type: "group",
+          label: i18n.str`Merge`,
+          fields: [
+            {
+              id: "mergeAmount" as UIHandlerId,
+              type: "amount",
+              label: i18n.str`Amount`,
+
+              currency,
+            },
+            {
+              id: "mergeTimeframe" as UIHandlerId,
+              type: "duration",
+              label: i18n.str`Timeframe`,
+            },
+          ],
+        },
+        {
+          type: "group",
+          label: i18n.str`Close`,
+          fields: [
+            {
+              id: "closeAmount" as UIHandlerId,
+              type: "amount",
+              label: i18n.str`Amount`,
+
+              currency,
+            },
+            {
+              id: "closeTimeframe" as UIHandlerId,
+              type: "duration",
+              label: i18n.str`Timeframe`,
+            },
+          ],
+        },
+      ],
+    },
+  ],
+});
+
+function onComponentUnload(callback: () => void) {
+  /**
+   * we use a ref to avoid evaluating the effect function
+   * on every render and so the unload is called only once
+   */
+  const ref = useRef<typeof callback>();
+  ref.current = callback;
+
+  useEffect(() => {
+    return () => {
+      ref.current!();
+    };
+  }, []);
+}
+
 /**
  * Defined new limits for the account
  * @param param0
  * @returns
  */
-function Rules({}: {}): VNode {
-  const [request] = useCurrentDecisionRequest();
-  if (request.rules) {
-    return (
-      <ShowDecisionLimitInfo
-        fixed
-        since={AbsoluteTime.now()}
-        until={request.deadline ?? AbsoluteTime.never()}
-        rules={request.rules}
-        startOpen
-      />
-    );
-  }
-
-  return <div></div>;
+function Rules({ account }: { account?: string }): VNode {
+  const activeDecision = useAccountActiveDecision(account);
+
+  const { i18n } = useTranslationContext();
+  const { config } = useExchangeApiContext();
+  const [request, updateRequest] = useCurrentDecisionRequest();
+  const currency = config.config.currency;
+  const design = rulesForm(i18n, currency);
+  const form = useForm<BalanceForm>(design, request.rules ?? {});
+
+  onComponentUnload(() => {
+    updateRequest("rules", form.status.result as any);
+  });
+
+  const info =
+    !activeDecision ||
+    activeDecision instanceof TalerError ||
+    activeDecision.type === "fail"
+      ? undefined
+      : activeDecision.body;
+
+  return (
+    <div>
+      <FormUI design={design} handler={form.handler} />
+      {!info ? undefined : (
+        <div>
+          <h2 class="mt-4 mb-2">
+            <i18n.Translate>Current limits</i18n.Translate>
+          </h2>
+          <ShowDecisionLimitInfo
+            fixed
+            since={AbsoluteTime.fromProtocolTimestamp(info.decision_time)}
+            until={AbsoluteTime.fromProtocolTimestamp(
+              info.limits.expiration_time,
+            )}
+            rules={info.limits.rules}
+            startOpen
+          />
+        </div>
+      )}
+    </div>
+  );
 }
 
 /**
@@ -189,6 +357,40 @@ function Events({}: {}): VNode {
   return <div> not yet impltemented: events</div>;
 }
 
+type MeasureForm = {
+  paths: { steps: Array<string> }[];
+};
+
+const measureForm = (
+  i18n: InternationalizationAPI,
+  mi: (MeasureInformation & { id: string })[],
+): FormDesign<MeasureForm> => ({
+  type: "single-column",
+  fields: [
+    {
+      type: "array",
+      id: "paths" as UIHandlerId,
+      label: i18n.str`Paths`,
+      help: i18n.str`For every entry the customer will have a different path 
to satify checks.`,
+      labelFieldId: "steps" as UIHandlerId,
+      fields: [
+        {
+          type: "selectMultiple",
+          choices: mi.map((m) => {
+            return {
+              value: m.id,
+              label: m.id,
+            };
+          }),
+          id: "steps" as UIHandlerId,
+          label: i18n.str`Steps`,
+          help: i18n.str`The checks that the customer will need to satisfy for 
this path.`,
+        },
+      ],
+    },
+  ],
+});
+
 /**
  * Ask for more information, define new paths to proceed
  * @param param0
@@ -196,8 +398,36 @@ function Events({}: {}): VNode {
  */
 function Measures({}: {}): VNode {
   const { i18n } = useTranslationContext();
-  const [request] = useCurrentDecisionRequest();
-  return <div> not yet impltemented: measures</div>;
+  const [request, updateRequest] = useCurrentDecisionRequest();
+  const measures = useServerMeasures();
+  const measureList =
+    !measures || measures instanceof TalerError || measures.type === "fail"
+      ? []
+      : Object.entries(measures.body.roots).map(([id, mi]) => ({ id, ...mi }));
+
+  const initValue: MeasureForm = !request.next_measure
+    ? { paths: [] }
+    : { paths: request.next_measure.map((steps) => ({ steps })) };
+
+  const design = measureForm(i18n, measureList);
+  const form = useForm<MeasureForm>(design, initValue);
+
+  onComponentUnload(() => {
+    const r = !form.status.result.paths
+      ? []
+      : (form.status.result.paths.map(
+          (path) => path?.steps ?? [],
+        ) as string[][]);
+    updateRequest("next_measure", r);
+    updateRequest("custom_measures", {});
+  });
+
+  return (
+    <div>
+      <FormUI design={design} handler={form.handler} />
+      <ShowMeasuresToSelect />
+    </div>
+  );
 }
 
 /**
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index 48e9a1e82..8cde09808 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -256,7 +256,7 @@ export function CaseDetails({
               custom_events: undefined,
               inhibit_events: undefined,
               justification: undefined,
-              keep_investigating: undefined,
+              keep_investigating: false,
               next_measure: undefined,
               properties: undefined,
               rules: undefined,
@@ -1554,7 +1554,7 @@ const FREEZE_RULES: (currency: string) => 
TalerExchangeApi.KycRule[] = (
   },
 ];
 
-function ShowMeasuresToSelect({
+export function ShowMeasuresToSelect({
   onSelect,
 }: {
   onSelect?: (m: MeasureInfo) => void;
diff --git a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx 
b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
index d1bafb0a4..bf4beb62a 100644
--- a/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CreateAccount.tsx
@@ -72,13 +72,13 @@ const createAccountForm = (i18n: InternationalizationAPI): 
FormDesign => ({
   fields: [
     {
       id: "password" as UIHandlerId,
-      type: "text",
+      type: "secret",
       label: i18n.str`Password`,
       required: true,
     },
     {
       id: "repeat" as UIHandlerId,
-      type: "text",
+      type: "secret",
       label: i18n.str`Repeat password`,
       required: true,
     },

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]