gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (682c7847e -> 94cb65802)


From: gnunet
Subject: [taler-wallet-core] branch master updated (682c7847e -> 94cb65802)
Date: Tue, 27 Aug 2024 23:21:39 +0200

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

sebasjm pushed a change to branch master
in repository wallet-core.

    from 682c7847e bump version to 0.13.0
     new 3e834cd10 fix signature
     new 94cb65802 freeze account

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../aml-backoffice-ui/src/ExchangeAmlFrame.tsx     |   9 +-
 packages/aml-backoffice-ui/src/Routing.tsx         |  17 +-
 packages/aml-backoffice-ui/src/hooks/decisions.ts  |  12 +-
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    | 421 +++++++++++++++++++--
 packages/aml-backoffice-ui/src/pages/Cases.tsx     |  89 ++---
 packages/taler-util/src/http-client/exchange.ts    |  16 +-
 6 files changed, 462 insertions(+), 102 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx 
b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
index 7dc36e7f6..44cd0ce82 100644
--- a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
+++ b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
@@ -33,7 +33,7 @@ import {
   getLabelForPreferences,
   usePreferences,
 } from "./hooks/preferences.js";
-import { HomeIcon } from "./pages/Cases.js";
+import { HomeIcon, PeopleIcon, ToInvestigateIcon } from "./pages/Cases.js";
 
 /**
  * mapping route to view
@@ -208,7 +208,7 @@ export function ExchangeAmlFrame({
       <div class="-mt-32 flex grow ">
         {officer?.state !== "ready" ? undefined : <Navigation />}
         <div class="flex mx-auto my-4">
-          <main class="rounded-lg bg-white px-5 py-6 shadow">{children}</main>
+          <main class="block rounded-lg bg-white px-5 py-6 shadow " 
style={{minWidth: 300}} >{children}</main>
         </div>
       </div>
 
@@ -224,8 +224,9 @@ export function ExchangeAmlFrame({
 function Navigation(): VNode {
   const { i18n } = useTranslationContext();
   const pageList = [
-    { route: privatePages.account, Icon: HomeIcon, label: i18n.str`Account` },
-    { route: privatePages.cases, Icon: HomeIcon, label: i18n.str`Cases` },
+    { route: privatePages.profile, Icon: PeopleIcon, label: i18n.str`Profile` 
},
+    { route: privatePages.investigation, Icon: ToInvestigateIcon, label: 
i18n.str`Investigation` },
+    { route: privatePages.active, Icon: HomeIcon, label: i18n.str`Active` },
   ];
   const { path } = useNavigationContext();
   return (
diff --git a/packages/aml-backoffice-ui/src/Routing.tsx 
b/packages/aml-backoffice-ui/src/Routing.tsx
index 4000cac00..082dc8ab5 100644
--- a/packages/aml-backoffice-ui/src/Routing.tsx
+++ b/packages/aml-backoffice-ui/src/Routing.tsx
@@ -94,8 +94,9 @@ function PublicRounting(): VNode {
 }
 
 export const privatePages = {
-  account: urlPattern(/\/account/, () => "#/account"),
-  cases: urlPattern(/\/cases/, () => "#/cases"),
+  profile: urlPattern(/\/profile/, () => "#/profile"),
+  investigation: urlPattern(/\/investigation/, () => "#/investigation"),
+  active: urlPattern(/\/active/, () => "#/active"),
   caseUpdate: urlPattern<{ cid: string; type: string }>(
     /\/case\/(?<cid>[a-zA-Z0-9]+)\/new\/(?<type>[a-zA-Z0-9_.]+)/,
     ({ cid, type }) => `#/case/${cid}/new/${type}`,
@@ -114,8 +115,9 @@ function PrivateRouting(): VNode {
   const { navigateTo } = useNavigationContext();
   const location = useCurrentLocation(privatePages);
   useEffect(() => {
-    if (location === undefined) {
-      navigateTo(privatePages.account.url({}));
+    if (location.name === undefined) {
+      console.log("asd")
+      navigateTo(privatePages.profile.url({}));
     }
   }, [location]);
 
@@ -123,7 +125,7 @@ function PrivateRouting(): VNode {
     case undefined: {
       return <Fragment />;
     }
-    case "account": {
+    case "profile": {
       return <Officer />;
     }
     case "caseDetails": {
@@ -137,7 +139,10 @@ function PrivateRouting(): VNode {
     case "caseNew": {
       return <SelectForm account={location.values.cid} />;
     }
-    case "cases": {
+    case "investigation": {
+      return <Cases filtered/>;
+    }
+    case "active": {
       return <Cases />;
     }
     default:
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts 
b/packages/aml-backoffice-ui/src/hooks/decisions.ts
index e652f233e..de8f1637e 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decisions.ts
@@ -37,7 +37,7 @@ export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1;
  * @param args
  * @returns
  */
-export function useCurrentDecisions() {
+export function useCurrentDecisions(filtered?: boolean) {
   const officer = useOfficer();
   const session = officer.state === "ready" ? officer.account : undefined;
   const {
@@ -46,13 +46,15 @@ export function useCurrentDecisions() {
 
   const [offset, setOffset] = useState<string>();
 
-  async function fetcher([officer, offset]: [
+  async function fetcher([officer, offset, investigation]: [
     OfficerAccount,
     string | undefined,
+    boolean | undefined
   ]) {
     return await api.getAmlDecisions(officer, {
-      order: "asc",
+      order: "dec",
       offset,
+      investigation,
       active: true,
       limit: PAGINATED_LIST_REQUEST,
     });
@@ -62,7 +64,7 @@ export function useCurrentDecisions() {
     TalerExchangeResultByMethod<"getAmlDecisions">,
     TalerHttpError
   >(
-    !session ? undefined : [session, offset, "getAmlDecisions"],
+    !session ? undefined : [session, offset, filtered ? true : filtered, 
"getAmlDecisions"],
     fetcher,
   );
 
@@ -95,7 +97,7 @@ export function useAccountDecisions(accountStr: string) {
     string | undefined,
   ]) {
     return await api.getAmlDecisions(officer, {
-      order: "asc",
+      order: "dec",
       offset,
       account,
       limit: PAGINATED_LIST_REQUEST,
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index b26e6f430..b421bd8c5 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -51,6 +51,7 @@ import { preloadedForms } from "../forms/index.js";
 import { useAccountInformation } from "../hooks/account.js";
 import { useAccountDecisions } from "../hooks/decisions.js";
 import { ShowConsolidated } from "./ShowConsolidated.js";
+import { useOfficer } from "../hooks/officer.js";
 
 export type AmlEvent =
   | AmlFormEvent
@@ -175,6 +176,10 @@ export function CaseDetails({ account }: { account: string 
}) {
     justification: Justification;
     metadata: FormMetadata;
   }>();
+  const { config, lib } = useExchangeApiContext()
+  const officer = useOfficer();
+  const session = officer.state === "ready" ? officer.account : undefined;
+
 
   const { i18n } = useTranslationContext();
   const details = useAccountInformation(account);
@@ -215,7 +220,8 @@ export function CaseDetails({ account }: { account: string 
}) {
     }
   }
   const { details: accountDetails } = details.body;
-  const activeDesicion = history.body.find(d => d.is_active)
+  const activeDecision = history.body.find(d => d.is_active)
+  const restDecisions = !activeDecision ? history.body : history.body.filter(d 
=> d.rowid !== activeDecision.rowid)
 
   const events = getEventsFromAmlHistory(
     accountDetails,
@@ -243,30 +249,9 @@ export function CaseDetails({ account }: { account: string 
}) {
       </DefaultForm>
     );
   }
-  return (
-    <div>
-      <a
-        // href={privatePages.caseNew.url({ cid: account })}
-        href="#"
-        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"
-      >
-        <i18n.Translate>Freeze account</i18n.Translate>
-      </a>
-      <a
-        // href={privatePages.caseNew.url({ cid: account })}
-        href="#"
-        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"
-      >
-        <i18n.Translate>New threshold</i18n.Translate>
-      </a>
-      <a
-        // href={privatePages.caseNew.url({ cid: account })}
-        href="#"
-        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"
-      >
-        <i18n.Translate>Ask more information</i18n.Translate>
-      </a>
 
+  return (
+    <div class="min-w-60">
       <header class="flex items-center justify-between border-b border-white/5 
px-4 py-4 sm:px-6 sm:py-6 lg:px-8">
         <h1 class="text-base font-semibold leading-7 text-black">
           <i18n.Translate>
@@ -275,10 +260,112 @@ export function CaseDetails({ account }: { account: 
string }) {
           </i18n.Translate>
         </h1>
       </header>
-      {!activeDesicion ? <Attention title={i18n.str`No active decision found`} 
type="warning" /> : <Fragment>
-        {!activeDesicion.to_investigate ? undefined : <Attention 
title={i18n.str`Requires investigation`} type="warning" />}
-        <ShowActiveDecision decision={activeDesicion} />
+
+      {!activeDecision || !activeDecision.to_investigate ? undefined : 
<Attention title={i18n.str`Under investigation`} type="warning" >
+        <i18n.Translate>This account requires a manual review and is waiting 
for a decision to be made.</i18n.Translate>
+      </Attention>}
+
+      <div>
+        <button
+          onClick={async () => {
+            if (!session) return;
+            lib.exchange.makeAmlDesicion(session, {
+              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              h_payto: account,
+              justification: "",
+              keep_investigating: false,
+              properties: {},
+              new_rules: {
+                custom_measures: {},
+                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                rules: FREEZE_RULES(config.currency),
+                successor_measure: "verboten",
+              },
+            })
+          }}
+          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>Freeze account</i18n.Translate>
+        </button>
+        <button
+          onClick={async () => {
+            if (!session) return;
+            lib.exchange.makeAmlDesicion(session, {
+              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              h_payto: account,
+              justification: "",
+              keep_investigating: false,
+              properties: {},
+              new_rules: {
+                custom_measures: {},
+                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                rules: THRESHOLD_100_HOUR(config.currency),
+                successor_measure: "verboten",
+              },
+            })
+          }}
+
+          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>Set threshold to 100 / hour</i18n.Translate>
+        </button>
+        <button
+          onClick={async () => {
+            if (!session) return;
+            lib.exchange.makeAmlDesicion(session, {
+              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              h_payto: account,
+              justification: "",
+              keep_investigating: false,
+              properties: {},
+              new_rules: {
+                custom_measures: {},
+                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                rules: THRESHOLD_2000_WEEK(config.currency),
+                successor_measure: "verboten",
+              },
+            })
+          }}
+
+          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>Set threshold to 2000 / week</i18n.Translate>
+        </button>
+        <button
+          onClick={async () => {
+            if (!session) return;
+            lib.exchange.makeAmlDesicion(session, {
+              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              h_payto: account,
+              justification: "",
+              keep_investigating: false,
+              properties: {},
+              new_measure: "onlyTalers",
+              new_rules: {
+                custom_measures: {},
+                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                rules: FREEZE_RULES(config.currency),
+                successor_measure: "verboten",
+              },
+            })
+          }}
+
+          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>Ask for more information</i18n.Translate>
+        </button>
+      </div>
+
+      {!activeDecision ? <Attention title={i18n.str`No active decision found`} 
type="warning" /> : <Fragment>
+        <h1 class="my-4 text-base font-semibold leading-6 text-black">
+          <i18n.Translate>Current active rules</i18n.Translate>
+        </h1>
+
+        <ShowDecisionInfo decision={activeDecision} />
       </Fragment>}
+      <h1 class="my-4 text-base font-semibold leading-6 text-black">
+        <i18n.Translate>KYC collection events</i18n.Translate>
+      </h1>
       <ShowTimeline
         history={events}
         onSelect={(e) => {
@@ -299,25 +386,55 @@ export function CaseDetails({ account }: { account: 
string }) {
       />
       {/* {selected && <ShowEventDetails event={selected} />} */}
       {selected && <ShowConsolidated history={events} until={selected} />}
+      {restDecisions.length > 0 ?
+        <Fragment>
+          <h1 class="my-4 text-base font-semibold leading-6 text-black">
+            <i18n.Translate>Previous AML decisions</i18n.Translate>
+          </h1>
+          {restDecisions.map(d => {
+            return <ShowDecisionInfo decision={d} />
+          })}
+
+        </Fragment> : <div />}
     </div>
   );
 }
 
-function ShowActiveDecision({ decision }: { decision: 
TalerExchangeApi.AmlDecision }): VNode {
+function ShowDecisionInfo({ decision }: { decision: 
TalerExchangeApi.AmlDecision }): VNode {
   const { i18n } = useTranslationContext();
   const { config } = useExchangeApiContext()
 
   return <Fragment>
-    <h1 class="mt-4 text-base font-semibold leading-6 text-black">
-      <i18n.Translate>Current active rules</i18n.Translate>
-    </h1>
 
     <div class="sm:col-span-5">
       <label
         for="amount"
         class="block text-sm font-medium leading-6 text-gray-900"
       >
-        <b>Expiration time</b>
+        <b><i18n.Translate>Since</i18n.Translate></b>
+        <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset 
ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
+
+          <div
+            class="p-4 disabled:bg-gray-200 rounded-md rounded-l-none 
data-[left=true]:text-left w-full py-1.5 pl-3 text-gray-900  
placeholder:text-gray-400  sm:text-sm sm:leading-6"
+          >
+
+            <Time format="dd/MM/yyyy HH:mm:ss" 
timestamp={AbsoluteTime.fromProtocolTimestamp(decision.decision_time)} />
+          </div>
+        </div>
+
+
+      </label>
+    </div>
+
+    <div class="sm:col-span-5">
+      <label
+        for="amount"
+        class="block text-sm font-medium leading-6 text-gray-900"
+      >
+        
{AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(decision.limits.expiration_time))
 ?
+          <b><i18n.Translate>Expired at</i18n.Translate></b> :
+          <b><i18n.Translate>Expires at</i18n.Translate></b>
+        }
         <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset 
ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
 
           <div
@@ -631,3 +748,243 @@ function parseJustification(
     };
   }
 }
+
+const THRESHOLD_2000_WEEK: (currency: string) => TalerExchangeApi.KycRule[] = 
(currency) => [{
+  "operation_type": "WITHDRAW",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "DEPOSIT",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "AGGREGATE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "MERGE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "BALANCE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "CLOSE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+}]
+
+const THRESHOLD_100_HOUR: (currency: string) => TalerExchangeApi.KycRule[] = 
(currency) => [{
+  "operation_type": "WITHDRAW",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "DEPOSIT",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "AGGREGATE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "MERGE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "BALANCE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "CLOSE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+}]
+
+
+
+const FREEZE_RULES: (currency: string) => TalerExchangeApi.KycRule[] = 
(currency) => [{
+  "operation_type": "WITHDRAW",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "DEPOSIT",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "AGGREGATE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "MERGE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "BALANCE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+},
+{
+  "operation_type": "CLOSE",
+  "threshold": `${currency}:0`,
+  "timeframe": {
+    "d_us": "forever"
+  },
+  "measures": [
+    "verboten"
+  ],
+  "display_priority": 1,
+  "exposed": true,
+  "is_and_combinator": true
+}]
+
diff --git a/packages/aml-backoffice-ui/src/pages/Cases.tsx 
b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index 9a569cd60..d79ce9006 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -36,6 +36,7 @@ import { FormErrors, RecursivePartial, useFormState } from 
"../hooks/form.js";
 import { undefinedIfEmpty } from "./CreateAccount.js";
 import { Officer } from "./Officer.js";
 import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
+import { useState } from "preact/hooks";
 
 type FormType = {
   // state: TalerExchangeApi.AmlState;
@@ -43,15 +44,13 @@ type FormType = {
 
 export function CasesUI({
   records,
-  // filter,
-  // onChangeFilter,
   onFirstPage,
   onNext,
+  filtered,
 }: {
+  filtered: boolean,
   onFirstPage?: () => void;
   onNext?: () => void;
-  // filter: TalerExchangeApi.AmlState;
-  // onChangeFilter: (f: TalerExchangeApi.AmlState) => void;
   records: TalerExchangeApi.AmlDecision[];
 }): VNode {
   const { i18n } = useTranslationContext();
@@ -94,38 +93,27 @@ export function CasesUI({
   return (
     <div>
       <div class="sm:flex sm:items-center">
-        <div class="px-2 sm:flex-auto">
-          <h1 class="text-base font-semibold leading-6 text-gray-900">
-            <i18n.Translate>Cases</i18n.Translate>
-          </h1>
-          <p class="mt-2 text-sm text-gray-700 w-80">
-            <i18n.Translate>
-              A list of all the account with the status
-            </i18n.Translate>
-          </p>
-        </div>
-        <div class="px-2">
-          {/* <InputChoiceHorizontal<FormType, "state">
-            name="state"
-            label={i18n.str`Filter`}
-            handler={form.state}
-            converter={amlStateConverter}
-            choices={[
-              {
-                label: i18n.str`Pending`,
-                value: "pending",
-              },
-              {
-                label: i18n.str`Frozen`,
-                value: "frozen",
-              },
-              {
-                label: i18n.str`Normal`,
-                value: "normal",
-              },
-            ]}
-          /> */}
-        </div>
+        {filtered ?
+          <div class="px-2 sm:flex-auto">
+            <h1 class="text-base font-semibold leading-6 text-gray-900">
+              <i18n.Translate>Cases under investigation</i18n.Translate>
+            </h1>
+            <p class="mt-2 text-sm text-gray-700 w-80">
+              <i18n.Translate>
+                A list of all the accounts which are waiting for a deicison to 
be made.
+              </i18n.Translate>
+            </p>
+          </div> : <div class="px-2 sm:flex-auto">
+            <h1 class="text-base font-semibold leading-6 text-gray-900">
+              <i18n.Translate>Cases</i18n.Translate>
+            </h1>
+            <p class="mt-2 text-sm text-gray-700 w-80">
+              <i18n.Translate>
+                A list of all the known account by the exchange.
+              </i18n.Translate>
+            </p>
+          </div>
+        }
       </div>
       <div class="mt-8 flow-root">
         <div class="overflow-x-auto">
@@ -167,7 +155,7 @@ export function CasesUI({
                           </div>
                         </td>
                         <td class="whitespace-nowrap px-3 py-5 text-sm 
text-gray-900">
-                          {r.to_investigate ? <ToInvestigateIcon /> : 
undefined}
+                          {r.to_investigate ? <span title="require 
investigation"><ToInvestigateIcon /></span> : undefined}
                         </td>
                       </tr>
                     );
@@ -182,19 +170,9 @@ export function CasesUI({
     </div>
   );
 }
-function ToInvestigateIcon(): VNode {
-  return <svg title="requires investigation" 
xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" class="size-6 w-6">
-    <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 
3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 
1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 
15.75h.007v.008H12v-.008Z" />
-  </svg>
-}
 
-
-export function Cases() {
-  // const [stateFilter, setStateFilter] = useState(
-  //   TalerExchangeApi.AmlState.pending,
-  // );
-
-  const list = useCurrentDecisions();
+export function Cases({ filtered }: { filtered?: boolean }) {
+  const list = useCurrentDecisions(filtered);
   const { i18n } = useTranslationContext();
 
   if (!list) {
@@ -251,6 +229,7 @@ export function Cases() {
 
   return (
     <CasesUI
+      filtered={!!filtered}
       records={list.body}
       onFirstPage={list.isFirstPage ? undefined : list.loadFirst}
       onNext={list.isLastPage ? undefined : list.loadNext}
@@ -262,6 +241,18 @@ export function Cases() {
   );
 }
 
+// function ToInvestigateIcon(): VNode {
+//   return <svg title="requires investigation" 
xmlns="http://www.w3.org/2000/svg"; fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" class="size-6 w-6">
+//     <path stroke-linecap="round" stroke-linejoin="round" d="M12 
9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 
1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 
15.75h.007v.008H12v-.008Z" />
+//   </svg>
+// }
+export const ToInvestigateIcon = () => (
+  <svg title="requires investigation" xmlns="http://www.w3.org/2000/svg"; 
fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" 
class="size-6 w-6">
+    <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 
3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 
1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 
15.75h.007v.008H12v-.008Z" />
+  </svg>
+);
+
+
 export const PeopleIcon = () => (
   <svg
     xmlns="http://www.w3.org/2000/svg";
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
index f82cf6608..6ab7706b5 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -22,6 +22,7 @@ import {
 import { Codec, codecForAny } from "../codec.js";
 import {
   TalerSignaturePurpose,
+  bufferForUint64,
   buildSigPS,
   decodeCrock,
   eddsaSign,
@@ -66,6 +67,7 @@ import {
 import { TalerError } from "../errors.js";
 import { TalerErrorCode } from "../taler-error-codes.js";
 import { codecForEmptyObject } from "../types-taler-wallet.js";
+import { canonicalJson } from "../helpers.js";
 
 export type TalerExchangeResultByMethod<
   prop extends keyof TalerExchangeHttpClient,
@@ -777,7 +779,7 @@ export class TalerExchangeHttpClient {
    * 
https://docs.taler.net/core/api-exchange.html#get--kyc-proof-$PROVIDER_NAME?state=$H_PAYTO
    *
    */
-  async completeExternalKycProcess(provider: string, state: string) {}
+  async completeExternalKycProcess(provider: string, state: string) { }
 
   //
   // AML operations
@@ -1021,16 +1023,18 @@ function buildAMLDecisionSignature(
   const zero = new Uint8Array(new ArrayBuffer(64));
 
   const sigBlob = buildSigPS(TalerSignaturePurpose.AML_DECISION)
-    //TODO: new need the null terminator, also in the exchange
-    .put(hash(stringToBytes(decision.justification))) //check null
     .put(timestampRoundedToBuffer(decision.decision_time))
-    // .put(amountToBuffer(decision.new_threshold))
     .put(decodeCrock(decision.h_payto))
-    .put(zero) //kyc_requirement
-    // .put(bufferForUint32(decision.new_state))
+    .put(hash(stringToBytes(decision.justification)))
+    .put(hash(stringToBytes(canonicalJson(decision.properties) + "\0")))
+    .put(hash(stringToBytes(canonicalJson(decision.new_rules) + "\0")))
+    .put(decision.new_measure != null ? 
hash(stringToBytes(decision.new_measure)) : zero)
+    .put(bufferForUint64(decision.keep_investigating ? 1 : 0))
     .build();
 
+
   const officer_sig = encodeCrock(eddsaSign(sigBlob, key));
+
   return {
     ...decision,
     officer_sig,

-- 
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]