gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: refresh decisions history whe


From: gnunet
Subject: [taler-wallet-core] branch master updated: refresh decisions history when the auditor makes decision
Date: Wed, 28 Aug 2024 00:02:04 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new ca4bccc5d refresh decisions history when the auditor makes decision
ca4bccc5d is described below

commit ca4bccc5de2d92333267ced69535d4400c394fb9
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Tue Aug 27 19:01:57 2024 -0300

    refresh decisions history when the auditor makes decision
---
 packages/aml-backoffice-ui/src/App.tsx             |  31 +-
 packages/aml-backoffice-ui/src/hooks/account.ts    |  32 +-
 packages/aml-backoffice-ui/src/hooks/decisions.ts  |  17 +-
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    | 702 +++++++++++----------
 .../aml-backoffice-ui/src/pages/CaseUpdate.tsx     |  36 +-
 .../src/pages/ShowConsolidated.tsx                 |  22 +-
 .../bank-ui/src/pages/PaytoWireTransferForm.tsx    |   5 +
 packages/kyc-ui/src/app.tsx                        |  12 +-
 packages/taler-util/src/http-client/exchange.ts    |  16 +-
 9 files changed, 493 insertions(+), 380 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/App.tsx 
b/packages/aml-backoffice-ui/src/App.tsx
index 0b66e0d26..d2cb2fd62 100644
--- a/packages/aml-backoffice-ui/src/App.tsx
+++ b/packages/aml-backoffice-ui/src/App.tsx
@@ -13,7 +13,12 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { canonicalizeBaseUrl } from "@gnu-taler/taler-util";
+import {
+  CacheEvictor,
+  TalerExchangeCacheEviction,
+  assertUnreachable,
+  canonicalizeBaseUrl,
+} from "@gnu-taler/taler-util";
 import {
   BrowserHashNavigationProvider,
   ExchangeApiProvider,
@@ -31,6 +36,8 @@ import { strings } from "./i18n/strings.js";
 import "./scss/main.css";
 import { UiSettings, fetchUiSettings } from "./context/ui-settings.js";
 import { UiFormsProvider, fetchUiForms } from "./context/ui-forms.js";
+import { revalidateAccountDecisions } from "./hooks/decisions.js";
+import { revalidateAccountInformation } from "./hooks/account.js";
 
 const WITH_LOCAL_STORAGE_CACHE = false;
 
@@ -56,6 +63,9 @@ export function App(): VNode {
         <ExchangeApiProvider
           baseUrl={new URL("/", baseUrl)}
           frameOnError={ExchangeAmlFrame}
+          evictors={{
+            exchange: evictExchangeSwrCache,
+          }}
         >
           <SWRConfig
             value={{
@@ -136,3 +146,22 @@ function getInitialBackendBaseURL(
     return canonicalizeBaseUrl(window.origin);
   }
 }
+
+const evictExchangeSwrCache: CacheEvictor<TalerExchangeCacheEviction> = {
+  async notifySuccess(op) {
+    switch (op) {
+      case TalerExchangeCacheEviction.MAKE_AML_DECISION: {
+        await revalidateAccountDecisions();
+        await revalidateAccountInformation();
+        return;
+      }
+      case TalerExchangeCacheEviction.CREATE_DESCISION:
+      case TalerExchangeCacheEviction.UPLOAD_KYC_FORM: {
+        return;
+      }
+      default: {
+        assertUnreachable(op);
+      }
+    }
+  },
+};
diff --git a/packages/aml-backoffice-ui/src/hooks/account.ts 
b/packages/aml-backoffice-ui/src/hooks/account.ts
index 1c5f013a7..9889c906d 100644
--- a/packages/aml-backoffice-ui/src/hooks/account.ts
+++ b/packages/aml-backoffice-ui/src/hooks/account.ts
@@ -13,25 +13,43 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { OfficerAccount, PaytoString, TalerExchangeResultByMethod, 
TalerHttpError } from "@gnu-taler/taler-util";
+import {
+  OfficerAccount,
+  PaytoString,
+  TalerExchangeResultByMethod,
+  TalerHttpError,
+} from "@gnu-taler/taler-util";
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import _useSWR, { SWRHook } from "swr";
+import _useSWR, { SWRHook, mutate } from "swr";
 import { useOfficer } from "./officer.js";
 import { useExchangeApiContext } from "@gnu-taler/web-util/browser";
 const useSWR = _useSWR as unknown as SWRHook;
 
+export function revalidateAccountInformation() {
+  return mutate(
+    (key) =>
+      Array.isArray(key) &&
+      key[key.length - 1] === "getAmlAttributesForAccount",
+    undefined,
+    { revalidate: true },
+  );
+}
 export function useAccountInformation(paytoHash: string) {
   const officer = useOfficer();
   const session = officer.state === "ready" ? officer.account : undefined;
 
-  const { lib: {exchange: api} } = useExchangeApiContext();
+  const {
+    lib: { exchange: api },
+  } = useExchangeApiContext();
 
   async function fetcher([officer, account]: [OfficerAccount, PaytoString]) {
-    return await api.getAmlAttributesForAccount(officer, account)
+    return await api.getAmlAttributesForAccount(officer, account);
   }
 
-  const { data, error } = 
useSWR<TalerExchangeResultByMethod<"getAmlAttributesForAccount">, 
TalerHttpError>(
-    !session ? undefined : [session, paytoHash], fetcher, {
+  const { data, error } = useSWR<
+    TalerExchangeResultByMethod<"getAmlAttributesForAccount">,
+    TalerHttpError
+  >(!session ? undefined : [session, paytoHash], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -47,5 +65,3 @@ export function useAccountInformation(paytoHash: string) {
   if (error) return error;
   return undefined;
 }
-
-
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts 
b/packages/aml-backoffice-ui/src/hooks/decisions.ts
index de8f1637e..eb861ae14 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decisions.ts
@@ -20,10 +20,10 @@ import {
   OfficerAccount,
   OperationOk,
   TalerExchangeResultByMethod,
-  TalerHttpError
+  TalerHttpError,
 } from "@gnu-taler/taler-util";
 import { useExchangeApiContext } from "@gnu-taler/web-util/browser";
-import _useSWR, { SWRHook } from "swr";
+import _useSWR, { SWRHook, mutate } from "swr";
 import { useOfficer } from "./officer.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
@@ -49,7 +49,7 @@ export function useCurrentDecisions(filtered?: boolean) {
   async function fetcher([officer, offset, investigation]: [
     OfficerAccount,
     string | undefined,
-    boolean | undefined
+    boolean | undefined,
   ]) {
     return await api.getAmlDecisions(officer, {
       order: "dec",
@@ -64,7 +64,9 @@ export function useCurrentDecisions(filtered?: boolean) {
     TalerExchangeResultByMethod<"getAmlDecisions">,
     TalerHttpError
   >(
-    !session ? undefined : [session, offset, filtered ? true : filtered, 
"getAmlDecisions"],
+    !session
+      ? undefined
+      : [session, offset, filtered ? true : filtered, "getAmlDecisions"],
     fetcher,
   );
 
@@ -77,6 +79,13 @@ export function useCurrentDecisions(filtered?: boolean) {
   );
 }
 
+export function revalidateAccountDecisions() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getAmlDecisions",
+    undefined,
+    { revalidate: true },
+  );
+}
 /**
  * @param account
  * @param args
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index b421bd8c5..a1c09025b 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -18,6 +18,7 @@ import {
   AmountJson,
   Amounts,
   Codec,
+  CurrencySpecification,
   HttpStatusCode,
   OperationFail,
   OperationOk,
@@ -40,7 +41,7 @@ import {
   ShowInputErrorLabel,
   Time,
   useExchangeApiContext,
-  useTranslationContext
+  useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { format, formatDuration, intervalToDuration } from "date-fns";
 import { Fragment, Ref, VNode, h } from "preact";
@@ -165,7 +166,7 @@ export function getEventsFromAmlHistory(
       when: AbsoluteTime.fromProtocolTimestamp(event.collection_time),
       values: !event.attributes ? {} : event.attributes,
       provider: event.provider_name,
-    } as AmlEvent
+    } as AmlEvent;
   });
   return ke.sort(selectSooner);
 }
@@ -176,11 +177,10 @@ export function CaseDetails({ account }: { account: 
string }) {
     justification: Justification;
     metadata: FormMetadata;
   }>();
-  const { config, lib } = useExchangeApiContext()
+  const { config, lib } = useExchangeApiContext();
   const officer = useOfficer();
   const session = officer.state === "ready" ? officer.account : undefined;
 
-
   const { i18n } = useTranslationContext();
   const details = useAccountInformation(account);
   const history = useAccountDecisions(account);
@@ -220,14 +220,12 @@ export function CaseDetails({ account }: { account: 
string }) {
     }
   }
   const { details: accountDetails } = details.body;
-  const activeDecision = history.body.find(d => d.is_active)
-  const restDecisions = !activeDecision ? history.body : history.body.filter(d 
=> d.rowid !== activeDecision.rowid)
+  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,
-    i18n,
-    allForms,
-  );
+  const events = getEventsFromAmlHistory(accountDetails, i18n, allForms);
 
   if (showForm !== undefined) {
     return (
@@ -261,27 +259,36 @@ export function CaseDetails({ account }: { account: 
string }) {
         </h1>
       </header>
 
-      {!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>}
+      {!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()),
+              decision_time: AbsoluteTime.toProtocolTimestamp(
+                AbsoluteTime.now(),
+              ),
               h_payto: account,
               justification: "",
               keep_investigating: false,
               properties: {},
               new_rules: {
                 custom_measures: {},
-                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                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"
         >
@@ -291,20 +298,23 @@ export function CaseDetails({ account }: { account: 
string }) {
           onClick={async () => {
             if (!session) return;
             lib.exchange.makeAmlDesicion(session, {
-              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              decision_time: AbsoluteTime.toProtocolTimestamp(
+                AbsoluteTime.now(),
+              ),
               h_payto: account,
               justification: "",
               keep_investigating: false,
               properties: {},
               new_rules: {
                 custom_measures: {},
-                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                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>
@@ -313,20 +323,23 @@ export function CaseDetails({ account }: { account: 
string }) {
           onClick={async () => {
             if (!session) return;
             lib.exchange.makeAmlDesicion(session, {
-              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              decision_time: AbsoluteTime.toProtocolTimestamp(
+                AbsoluteTime.now(),
+              ),
               h_payto: account,
               justification: "",
               keep_investigating: false,
               properties: {},
               new_rules: {
                 custom_measures: {},
-                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                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>
@@ -335,7 +348,9 @@ export function CaseDetails({ account }: { account: string 
}) {
           onClick={async () => {
             if (!session) return;
             lib.exchange.makeAmlDesicion(session, {
-              decision_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+              decision_time: AbsoluteTime.toProtocolTimestamp(
+                AbsoluteTime.now(),
+              ),
               h_payto: account,
               justification: "",
               keep_investigating: false,
@@ -343,26 +358,30 @@ export function CaseDetails({ account }: { account: 
string }) {
               new_measure: "onlyTalers",
               new_rules: {
                 custom_measures: {},
-                expiration_time: 
AbsoluteTime.toProtocolTimestamp(AbsoluteTime.never()),
+                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>}
+      {!activeDecision ? (
+        <Attention title={i18n.str`No active rules 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>
@@ -386,91 +405,143 @@ export function CaseDetails({ account }: { account: 
string }) {
       />
       {/* {selected && <ShowEventDetails event={selected} />} */}
       {selected && <ShowConsolidated history={events} until={selected} />}
-      {restDecisions.length > 0 ?
+      {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} />
+          {restDecisions.map((d) => {
+            return <ShowDecisionInfo decision={d} />;
           })}
-
-        </Fragment> : <div />}
+        </Fragment>
+      ) : (
+        <div />
+      )}
     </div>
   );
 }
 
-function ShowDecisionInfo({ decision }: { decision: 
TalerExchangeApi.AmlDecision }): VNode {
+function ShowDecisionInfo({
+  decision,
+}: {
+  decision: TalerExchangeApi.AmlDecision;
+}): VNode {
   const { i18n } = useTranslationContext();
-  const { config } = useExchangeApiContext()
-
-  return <Fragment>
-
-    <div class="sm:col-span-5">
-      <label
-        for="amount"
-        class="block text-sm font-medium leading-6 text-gray-900"
-      >
-        <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"
-          >
+  const { config } = useExchangeApiContext();
 
-            <Time format="dd/MM/yyyy HH:mm:ss" 
timestamp={AbsoluteTime.fromProtocolTimestamp(decision.decision_time)} />
+  return (
+    <Fragment>
+      <textarea>asdasdlaksdj</textarea>
+      <div class="flex col justify-between">
+        <div class="flex mt-2 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="pointer-events-none bg-gray-200 inset-y-0 flex 
items-center px-3">
+            <i18n.Translate>Since</i18n.Translate>
+          </div>
+          <div class="p-2  disabled:bg-gray-200 text-right 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
-            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.limits.expiration_time)} 
/>
+        <div class="flex mt-2 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="pointer-events-none bg-gray-200 inset-y-0 flex 
items-center px-3">
+            {AbsoluteTime.isExpired(
+              AbsoluteTime.fromProtocolTimestamp(
+                decision.limits.expiration_time,
+              ),
+            ) ? (
+              <i18n.Translate>Expired</i18n.Translate>
+            ) : (
+              <i18n.Translate>Expires</i18n.Translate>
+            )}
+          </div>
+          <div class="p-2  disabled:bg-gray-200 text-right 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>
+      </div>
 
+      <table class="w-full text-center m-4">
+        <thead>
+          <tr>
+            <th>operation</th>
+            <th>timeframe</th>
+            <th>amount</th>
+          </tr>
+        </thead>
+        <tbody>
+          {decision.limits.rules.map((r) => {
+            const bySpec = Amounts.stringifyValueWithSpec(
+              Amounts.parseOrThrow(r.threshold),
+              config.currency_specification,
+            );
+            return (
+              <tr>
+                <td class="text-left">{r.operation_type}</td>
+                <td>
+                  {r.timeframe.d_us === "forever"
+                    ? ""
+                    : formatDuration(
+                        intervalToDuration({
+                          start: 0,
+                          end: r.timeframe.d_us / 1000,
+                        }),
+                      )}
+                </td>
+                <td class="text-right">
+                  <RenderAmount
+                    value={Amounts.parseOrThrow(r.threshold)}
+                    spec={config.currency_specification}
+                  />
+                </td>
+              </tr>
+            );
+          })}
+        </tbody>
+      </table>
+    </Fragment>
+  );
+}
 
-      </label>
-    </div>
-    {decision.limits.rules.map(r => {
-      const bySpec = 
Amounts.stringifyValueWithSpec(Amounts.parseOrThrow(r.threshold), 
config.currency_specification)
-      return <div class="sm:col-span-5">
-        <label
-          for="amount"
-          class="block text-sm font-medium leading-6 text-gray-900"
-        >
-          {r.operation_type}
-          <InputAmount
-            name="minCashout"
-            left
-            currency={bySpec.currency}
-            value={bySpec.normal}
-            onChange={undefined}
-          />
-        </label>
-        <p class="mt-2 text-sm text-gray-500">
-          over {r.timeframe.d_us === "forever" ? "" : 
formatDuration(intervalToDuration({ start: 0, end: r.timeframe.d_us / 1000 }))}
-        </p>
-      </div>
-    })}
+export function RenderAmount({
+  value,
+  spec,
+  negative,
+  withColor,
+  hideSmall,
+}: {
+  spec: CurrencySpecification;
+  value: AmountJson;
+  hideSmall?: boolean;
+  negative?: boolean;
+  withColor?: boolean;
+}): VNode {
+  const neg = !!negative; // convert to true or false
 
-  </Fragment>
+  const { currency, normal, small } = Amounts.stringifyValueWithSpec(
+    value,
+    spec,
+  );
+
+  return (
+    <span
+      data-negative={withColor ? neg : undefined}
+      class="whitespace-nowrap data-[negative=false]:text-green-600 
data-[negative=true]:text-red-600"
+    >
+      {negative ? "- " : undefined}
+      {currency} {normal}{" "}
+      {!hideSmall && small && <sup class="-ml-1">{small}</sup>}
+    </span>
+  );
 }
 
 function AmlStateBadge({ state }: { state: TalerExchangeApi.AmlState }): VNode 
{
@@ -631,7 +702,7 @@ function InputAmount(
   },
   ref: Ref<HTMLInputElement>,
 ): VNode {
-  const FRAC_SEPARATOR = ","
+  const FRAC_SEPARATOR = ",";
   const { config } = useExchangeApiContext();
   return (
     <div class="mt-2">
@@ -658,13 +729,13 @@ function InputAmount(
             if (
               sep_pos !== -1 &&
               l - sep_pos - 1 >
-              config.currency_specification.num_fractional_input_digits
+                config.currency_specification.num_fractional_input_digits
             ) {
               e.currentTarget.value = e.currentTarget.value.substring(
                 0,
                 sep_pos +
-                config.currency_specification.num_fractional_input_digits +
-                1,
+                  config.currency_specification.num_fractional_input_digits +
+                  1,
               );
             }
             onChange(e.currentTarget.value);
@@ -702,9 +773,9 @@ function parseJustification(
   listOfAllKnownForms: FormMetadata[],
 ):
   | OperationOk<{
-    justification: Justification;
-    metadata: FormMetadata;
-  }>
+      justification: Justification;
+      metadata: FormMetadata;
+    }>
   | OperationFail<ParseJustificationFail> {
   try {
     const justification = JSON.parse(s);
@@ -749,242 +820,215 @@ function parseJustification(
   }
 }
 
-const THRESHOLD_2000_WEEK: (currency: string) => TalerExchangeApi.KycRule[] = 
(currency) => [{
-  "operation_type": "WITHDRAW",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+const THRESHOLD_2000_WEEK: (currency: string) => TalerExchangeApi.KycRule[] = (
+  currency,
+) => [
+  {
+    operation_type: "WITHDRAW",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "DEPOSIT",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "DEPOSIT",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "AGGREGATE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "AGGREGATE",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "MERGE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "MERGE",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "BALANCE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "BALANCE",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "CLOSE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "CLOSE",
+    threshold: `${currency}:2000`,
+    timeframe: {
+      d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "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"
+];
+
+const THRESHOLD_100_HOUR: (currency: string) => TalerExchangeApi.KycRule[] = (
+  currency,
+) => [
+  {
+    operation_type: "WITHDRAW",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "DEPOSIT",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "DEPOSIT",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "AGGREGATE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "AGGREGATE",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "MERGE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "MERGE",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "BALANCE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "BALANCE",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "CLOSE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "CLOSE",
+    threshold: `${currency}:100`,
+    timeframe: {
+      d_us: 1 * 60 * 60 * 1000 * 1000,
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "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"
+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,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "DEPOSIT",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "DEPOSIT",
+    threshold: `${currency}:0`,
+    timeframe: {
+      d_us: "forever",
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "AGGREGATE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "AGGREGATE",
+    threshold: `${currency}:0`,
+    timeframe: {
+      d_us: "forever",
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "MERGE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "MERGE",
+    threshold: `${currency}:0`,
+    timeframe: {
+      d_us: "forever",
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "BALANCE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "BALANCE",
+    threshold: `${currency}:0`,
+    timeframe: {
+      d_us: "forever",
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-},
-{
-  "operation_type": "CLOSE",
-  "threshold": `${currency}:0`,
-  "timeframe": {
-    "d_us": "forever"
+  {
+    operation_type: "CLOSE",
+    threshold: `${currency}:0`,
+    timeframe: {
+      d_us: "forever",
+    },
+    measures: ["verboten"],
+    display_priority: 1,
+    exposed: true,
+    is_and_combinator: true,
   },
-  "measures": [
-    "verboten"
-  ],
-  "display_priority": 1,
-  "exposed": true,
-  "is_and_combinator": true
-}]
-
+];
diff --git a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
index d1257c8fa..b79746107 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseUpdate.tsx
@@ -157,28 +157,30 @@ export function CaseUpdate({
               value: validatedForm,
             };
 
-            const decision: Omit<TalerExchangeApi.AmlDecisionRequest, 
"officer_sig"> =
-              {
-                justification: JSON.stringify(justification),
-                decision_time: TalerProtocolTimestamp.now(),
-                h_payto: account,
-                keep_investigating: false,
-                new_rules: {
-                  custom_measures: {},
-                  expiration_time: {
-                    t_s: "never"
-                  },
-                  rules: [],
-                  successor_measure: undefined
+            const decision: Omit<
+              TalerExchangeApi.AmlDecisionRequest,
+              "officer_sig"
+            > = {
+              justification: JSON.stringify(justification),
+              decision_time: TalerProtocolTimestamp.now(),
+              h_payto: account,
+              keep_investigating: false,
+              new_rules: {
+                custom_measures: {},
+                expiration_time: {
+                  t_s: "never",
                 },
-                properties: {},
-                new_measure: undefined,
-              };
+                rules: [],
+                successor_measure: undefined,
+              },
+              properties: {},
+              new_measure: undefined,
+            };
 
             return api.makeAmlDesicion(officer.account, decision);
           },
           () => {
-            window.location.href = privatePages.cases.url({});
+            window.location.href = privatePages.profile.url({});
           },
           (fail) => {
             switch (fail.case) {
diff --git a/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx 
b/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
index cea8157b0..e53c74aa5 100644
--- a/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
+++ b/packages/aml-backoffice-ui/src/pages/ShowConsolidated.tsx
@@ -24,7 +24,7 @@ import {
   FormConfiguration,
   UIFormElementConfig,
   UIHandlerId,
-  useTranslationContext
+  useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { format } from "date-fns";
 import { Fragment, VNode, h } from "preact";
@@ -33,21 +33,21 @@ import { AmlEvent } from "./CaseDetails.js";
 /**
  * the exchange doesn't hava a consistent api
  * https://bugs.gnunet.org/view.php?id=9142
- * 
- * @param data 
- * @returns 
+ *
+ * @param data
+ * @returns
  */
 function fixProvidedInfo(data: object): object {
-  return Object.entries(data).reduce((prev, [key,value]) => {
-    prev[key] = value
+  return Object.entries(data).reduce((prev, [key, value]) => {
+    prev[key] = value;
     if (typeof value === "object" && value["value"]) {
-      const v = value["value"]
+      const v = value["value"];
       if (typeof v === "object" && v["text"]) {
-        prev[key].value = v["text"]
+        prev[key].value = v["text"];
       }
     }
-    return prev
-  }, {} as any)
+    return prev;
+  }, {} as any);
 }
 
 export function ShowConsolidated({
@@ -62,7 +62,7 @@ export function ShowConsolidated({
   const cons = getConsolidated(history, until);
 
   const fixed = fixProvidedInfo(cons.kyc);
-  console.log("fixed", fixed)
+
   const form: FormConfiguration = {
     type: "double-column",
     design: [
diff --git a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
index 852cfe816..743a1092b 100644
--- a/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/bank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -779,6 +779,11 @@ export function InputAmount(
   );
 }
 
+/**
+ * send to web-utils
+ * @param param0
+ * @returns
+ */
 export function RenderAmount({
   value,
   spec,
diff --git a/packages/kyc-ui/src/app.tsx b/packages/kyc-ui/src/app.tsx
index fb63771d7..0c7cd71bf 100644
--- a/packages/kyc-ui/src/app.tsx
+++ b/packages/kyc-ui/src/app.tsx
@@ -20,14 +20,14 @@ import {
   assertUnreachable,
   canonicalizeBaseUrl,
   getGlobalLogLevel,
-  setGlobalLogLevelFromString
+  setGlobalLogLevelFromString,
 } from "@gnu-taler/taler-util";
 import {
   BrowserHashNavigationProvider,
   ExchangeApiProvider,
   Loading,
   TalerWalletIntegrationBrowserProvider,
-  TranslationProvider
+  TranslationProvider,
 } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
@@ -63,7 +63,7 @@ export function App(): VNode {
           baseUrl={new URL("/", baseUrl)}
           frameOnError={Frame}
           evictors={{
-            exchange: evictExchangeSwrCache
+            exchange: evictExchangeSwrCache,
           }}
         >
           <SWRConfig
@@ -154,10 +154,11 @@ function getInitialBackendBaseURL(
 const evictExchangeSwrCache: CacheEvictor<TalerExchangeCacheEviction> = {
   async notifySuccess(op) {
     switch (op) {
-      case TalerExchangeCacheEviction.CREATE_DESCISION:{ 
-        await revalidateKycInfo()
+      case TalerExchangeCacheEviction.CREATE_DESCISION: {
+        await revalidateKycInfo();
         return;
       }
+      case TalerExchangeCacheEviction.MAKE_AML_DECISION:
       case TalerExchangeCacheEviction.UPLOAD_KYC_FORM: {
         return;
       }
@@ -167,4 +168,3 @@ const evictExchangeSwrCache: 
CacheEvictor<TalerExchangeCacheEviction> = {
     }
   },
 };
-
diff --git a/packages/taler-util/src/http-client/exchange.ts 
b/packages/taler-util/src/http-client/exchange.ts
index 6ab7706b5..4bc888cc4 100644
--- a/packages/taler-util/src/http-client/exchange.ts
+++ b/packages/taler-util/src/http-client/exchange.ts
@@ -79,6 +79,7 @@ export type TalerExchangeErrorsByMethod<
 export enum TalerExchangeCacheEviction {
   CREATE_DESCISION,
   UPLOAD_KYC_FORM,
+  MAKE_AML_DECISION,
 }
 
 declare const __pubId: unique symbol;
@@ -779,7 +780,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
@@ -948,8 +949,12 @@ export class TalerExchangeHttpClient {
     });
 
     switch (resp.status) {
-      case HttpStatusCode.NoContent:
+      case HttpStatusCode.NoContent: {
+        this.cacheEvictor.notifySuccess(
+          TalerExchangeCacheEviction.MAKE_AML_DECISION,
+        );
         return opEmptySuccess(resp);
+      }
       case HttpStatusCode.Forbidden:
         return opKnownHttpFailure(resp.status, resp);
       case HttpStatusCode.NotFound:
@@ -1028,11 +1033,14 @@ function buildAMLDecisionSignature(
     .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(
+      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 {

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