gnunet-svn
[Top][All Lists]
Advanced

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

[taler-typescript-core] 01/02: fix #9810


From: Admin
Subject: [taler-typescript-core] 01/02: fix #9810
Date: Mon, 09 Jun 2025 17:12:53 +0200

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 07d0ae31b43db5afdd2f3997c284e6594342bb84
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Mon Jun 9 11:48:48 2025 -0300

    fix #9810
---
 .../aml-backoffice-ui/src/ExchangeAmlFrame.tsx     |  10 +-
 .../src/hooks/decision-request.ts                  |   2 +-
 .../aml-backoffice-ui/src/pages/CaseDetails.tsx    |  83 ++++------
 .../aml-backoffice-ui/src/pages/MeasuresTable.tsx  |  87 +++++++++--
 .../aml-backoffice-ui/src/pages/NewMeasure.tsx     |  11 +-
 packages/aml-backoffice-ui/src/pages/Transfers.tsx |   4 +-
 .../src/pages/decision/Measures.tsx                | 170 +++++++++++++++++----
 .../src/pages/decision/Summary.tsx                 |  47 ++++--
 packages/aml-backoffice-ui/src/utils/QR.tsx        |  54 -------
 packages/taler-util/src/aml/events.ts              |   3 +-
 packages/taler-util/src/aml/properties.ts          |   5 +-
 packages/taler-util/src/types-taler-exchange.ts    |   2 +-
 packages/taler-util/src/types-taler-kyc-aml.ts     | 148 +-----------------
 packages/web-util/src/components/Attention.tsx     |   4 +-
 packages/web-util/src/components/Button.tsx        |   4 +-
 packages/web-util/src/components/Header.tsx        |   3 +-
 packages/web-util/src/components/Loading.tsx       |   2 +-
 .../src/components/ShowInputErrorLabel.tsx         |   2 +-
 18 files changed, 313 insertions(+), 328 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx 
b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
index 6e2cce6fb..d8d7b8e8f 100644
--- a/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
+++ b/packages/aml-backoffice-ui/src/ExchangeAmlFrame.tsx
@@ -148,10 +148,7 @@ export function ExchangeAmlFrame({
   const settings = useUiSettingsContext();
 
   return (
-    <div
-      class="min-h-full flex flex-col m-0 bg-slate-200"
-      style="min-height: 100vh;"
-    >
+    <div class="min-h-full flex flex-col m-0 bg-slate-200 min-h-screen">
       <div class="bg-indigo-600 pb-32">
         <Header
           title="Exchange"
@@ -219,10 +216,7 @@ export function ExchangeAmlFrame({
       <div class="-mt-32 flex grow ">
         {officer?.state !== "ready" ? undefined : <Navigation />}
         <div class="flex mx-auto my-4 min-w-80">
-          <main
-            class="block rounded-lg bg-white px-5 py-6 shadow "
-            style={{ minWidth: 600 }}
-          >
+          <main class="block rounded-lg bg-white px-5 py-6 shadow min-w-xl">
             {children}
           </main>
         </div>
diff --git a/packages/aml-backoffice-ui/src/hooks/decision-request.ts 
b/packages/aml-backoffice-ui/src/hooks/decision-request.ts
index ba07863ef..c3753a1fc 100644
--- a/packages/aml-backoffice-ui/src/hooks/decision-request.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decision-request.ts
@@ -115,7 +115,7 @@ export interface DecisionRequest {
 export const codecForMeasure = (): Codec<MeasureInformation> =>
   buildCodecForObject<MeasureInformation>()
     .property("check_name", codecOptionalDefault(codecForString(), "SKIP"))
-    .property("prog_name", codecForString())
+    .property("prog_name", codecOptional(codecForString()))
     .property("context", codecOptional(codecForMap(codecForAny())))
     .build("MeasureInformation");
 
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx 
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index 990da4fe0..02b0da523 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -1255,10 +1255,7 @@ export function ShowMeasuresToSelect({
 
   return (
     <CurrentMeasureTable
-      measures={computeAvailableMesaures(
-        measures.body,
-        // , cm
-      )}
+      measures={computeAvailableMesaures(measures.body)}
       onSelect={onSelect}
     />
   );
@@ -1269,26 +1266,45 @@ export function computeAvailableMesaures(
   // customMeasures?: Readonly<CustomMeasures>,
   skpiFilter?: (m: MeasureInfo) => boolean,
 ): Mesaures {
-  const init: Mesaures = { forms: [], procedures: [] };
+  const init: Mesaures = { forms: [], procedures: [], info: [] };
   if (!serverMeasures) {
     return init;
   }
   const server = Object.entries(serverMeasures.roots).reduce(
     (prev, [key, value]) => {
       if (value.check_name !== "SKIP") {
-        const r: MeasureInfo = {
-          type: "form",
-          name: key,
-          context: value.context,
-          programName: value.prog_name,
-          program: serverMeasures.programs[value.prog_name],
-          checkName: value.check_name,
-          check: serverMeasures.checks[value.check_name],
-          custom: false,
-        };
-        if (skpiFilter && skpiFilter(r)) return prev; // skip
-        prev.forms.push(r);
+        if (!value.prog_name) {
+          const r: MeasureInfo = {
+            type: "info",
+            name: key,
+            context: value.context,
+            checkName: value.check_name,
+            check: serverMeasures.checks[value.check_name],
+            custom: true,
+          };
+          if (skpiFilter && skpiFilter(r)) return prev; // skip
+          prev.info.push(r);
+        } else {
+          const r: MeasureInfo = {
+            type: "form",
+            name: key,
+            context: value.context,
+            programName: value.prog_name,
+            program: serverMeasures.programs[value.prog_name],
+            checkName: value.check_name,
+            check: serverMeasures.checks[value.check_name],
+            custom: false,
+          };
+          if (skpiFilter && skpiFilter(r)) return prev; // skip
+          prev.forms.push(r);
+        }
       } else {
+        if (!value.prog_name) {
+          console.error(
+            `ERROR: program name can't be empty for measure "${key}"`,
+          );
+          return prev;
+        }
         const r: MeasureInfo = {
           type: "procedure",
           name: key,
@@ -1306,37 +1322,4 @@ export function computeAvailableMesaures(
   );
 
   return server;
-  // if (!customMeasures) {
-  // }
-
-  // const serverAndCustom = customMeasures.measures.reduce((prev, value) => {
-  //   if (value.check_name !== "SKIP") {
-  //     const r: MeasureInfo = {
-  //       type: "form",
-  //       name: value.name,
-  //       context: value.context,
-  //       programName: value.program,
-  //       program: serverMeasures.programs[value.program],
-  //       checkName: value.check_name,
-  //       check: serverMeasures.checks[value.check_name],
-  //       custom: true,
-  //     };
-  //     if (skpiFilter && skpiFilter(r)) return prev; // skip
-  //     prev.forms.push(r);
-  //   } else {
-  //     const r: MeasureInfo = {
-  //       type: "procedure",
-  //       name: value.name,
-  //       context: value.context,
-  //       programName: value.program,
-  //       program: serverMeasures.programs[value.program],
-  //       custom: true,
-  //     };
-  //     if (skpiFilter && skpiFilter(r)) return prev; // skip
-  //     prev.procedures.push(r);
-  //   }
-  //   return prev;
-  // }, server);
-
-  // return serverAndCustom;
 }
diff --git a/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx 
b/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
index fcd6ab603..c062a31bb 100644
--- a/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
+++ b/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
@@ -33,9 +33,9 @@ export type ProcedureMeasure = {
 export type FormMeasure = {
   type: "form";
   name: string;
-  programName: string;
+  programName?: string;
+  program?: AmlProgramRequirement;
   checkName: string;
-  program: AmlProgramRequirement;
   check: KycCheckInformation;
   context?: object;
   custom: boolean;
@@ -43,12 +43,15 @@ export type FormMeasure = {
 export type InfoMeasure = {
   type: "info";
   name: string;
+  checkName: string;
+  check: KycCheckInformation;
   context?: object;
   custom: boolean;
 };
 export type Mesaures = {
   procedures: ProcedureMeasure[];
   forms: FormMeasure[];
+  info: InfoMeasure[];
 };
 export function CurrentMeasureTable({
   measures,
@@ -149,6 +152,79 @@ export function CurrentMeasureTable({
         </div>
       )}
 
+      {!measures.info.length ? undefined : (
+        <div class="mt-4 flow-root">
+          <div class="sm:flex sm:items-center">
+            <div class="sm:flex-auto">
+              <h1 class="text-base font-semibold text-gray-900">
+                <i18n.Translate>Info</i18n.Translate>
+              </h1>
+              <p class="mt-2 text-sm text-gray-700">
+                <i18n.Translate>
+                  Will show a label or information to the user until a new 
state.
+                </i18n.Translate>
+              </p>
+            </div>
+          </div>
+
+          <div class="inline-block min-w-full py-2 align-middle sm:px-6 
lg:px-8">
+            <div class="overflow-hidden shadow ring-1 ring-black/5 
sm:rounded-lg">
+              <table class="min-w-full divide-y divide-gray-300">
+                <thead class="bg-gray-50">
+                  <tr>
+                    {onSelect ? (
+                      <th scope="col" class="relative p-2 ">
+                        <span class="sr-only">Select</span>
+                      </th>
+                    ) : (
+                      <Fragment />
+                    )}
+                    <th
+                      scope="col"
+                      class="p-2 text-left text-sm font-semibold text-gray-900 
sm:pl-6"
+                    >
+                      <i18n.Translate>Name</i18n.Translate>
+                    </th>
+                    <th
+                      scope="col"
+                      class="p-2 text-left text-sm font-semibold text-gray-900"
+                    >
+                      <i18n.Translate>Context</i18n.Translate>
+                    </th>
+                  </tr>
+                </thead>
+                <tbody class="divide-y divide-gray-200 bg-white">
+                  {measures.info.map((m) => {
+                    return (
+                      <tr>
+                        {onSelect ? (
+                          <td class="relative whitespace-nowrap p-2 text-right 
text-sm font-medium ">
+                            <button
+                              onClick={() => onSelect(m)}
+                              class="rounded-md w-fit border-0 p-2 text-center 
text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700"
+                            >
+                              <i18n.Translate>Modify</i18n.Translate>
+                            </button>
+                          </td>
+                        ) : (
+                          <Fragment />
+                        )}
+                        <td class="whitespace-nowrap p-2 text-sm font-medium 
text-gray-900 sm:pl-6">
+                          {m.name}
+                        </td>
+                        <td class="whitespace-nowrap p-2 text-sm 
text-gray-500">
+                          {Object.keys(m.context ?? {}).join(", ")}
+                        </td>
+                      </tr>
+                    );
+                  })}
+                </tbody>
+              </table>
+            </div>
+          </div>
+        </div>
+      )}
+
       {!measures.procedures.length ? undefined : (
         <div class="mt-4 flow-root">
           <div class="sm:flex sm:items-center">
@@ -204,13 +280,6 @@ export function CurrentMeasureTable({
                 </thead>
                 <tbody class="divide-y divide-gray-200 bg-white">
                   {measures.procedures.map((m) => {
-                    // if (
-                    //   m.context &&
-                    //   "internal" in m.context &&
-                    //   m.context.internal
-                    // ) {
-                    //   return <Fragment />;
-                    // }
                     return (
                       <tr>
                         {onSelect ? (
diff --git a/packages/aml-backoffice-ui/src/pages/NewMeasure.tsx 
b/packages/aml-backoffice-ui/src/pages/NewMeasure.tsx
index 0fe5140eb..edc8688c3 100644
--- a/packages/aml-backoffice-ui/src/pages/NewMeasure.tsx
+++ b/packages/aml-backoffice-ui/src/pages/NewMeasure.tsx
@@ -7,6 +7,7 @@ import {
   TranslatedString,
 } from "@gnu-taler/taler-util";
 import {
+  ErrorsSummary,
   FormDesign,
   FormUI,
   InternationalizationAPI,
@@ -217,7 +218,7 @@ export function MeasureForm({
                 custom_measures: CURRENT_MEASURES,
               });
               if (onChanged) {
-                onChanged(newMeasure.name)
+                onChanged(newMeasure.name);
               }
             }}
             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 
disabled:bg-gray-600"
@@ -232,7 +233,7 @@ export function MeasureForm({
                 custom_measures: currentMeasures,
               });
               if (onRemoved) {
-                onRemoved(name!)
+                onRemoved(name!);
               }
             }}
             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 
disabled:bg-gray-600"
@@ -387,7 +388,6 @@ const formDesign = (
     {
       type: "selectOne",
       id: "program",
-      required: true,
       label: i18n.str`Program`,
       choices: programs.map((m) => {
         return {
@@ -395,9 +395,12 @@ const formDesign = (
           label: m.key,
         };
       }),
+      help: i18n.str`Only required when no check is specified`,
       validator(value, form) {
         return !value
-          ? i18n.str`required`
+          ? !form.check
+            ? i18n.str`Missing check or program`
+            : undefined
           : programAndCheckMatch(i18n, summary, value, form.check) ??
               programAndContextMatch(i18n, summary, value, form.context);
       },
diff --git a/packages/aml-backoffice-ui/src/pages/Transfers.tsx 
b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
index 7215f9312..a7770e364 100644
--- a/packages/aml-backoffice-ui/src/pages/Transfers.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Transfers.tsx
@@ -260,7 +260,7 @@ export function Transfers({
                                   />
                                 </span>
                               ) : (
-                                <span style={{ color: "grey" }}>
+                                <span class="text-grey-500">
                                   &lt;{i18n.str`Invalid value`}&gt;
                                 </span>
                               )}
@@ -299,7 +299,7 @@ export function Transfers({
                               spec={config.config.currency_specification}
                             />
                           ) : (
-                            <span style={{ color: "grey" }}>
+                            <span class="text-grey-500">
                               &lt;
                               {i18n.str`Invalid value`}&gt;
                             </span>
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx 
b/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx
index e52f01192..3d503ee55 100644
--- a/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx
+++ b/packages/aml-backoffice-ui/src/pages/decision/Measures.tsx
@@ -1,4 +1,5 @@
 import {
+  assertUnreachable,
   MeasureInformation,
   TalerError,
   TalerExchangeApi,
@@ -95,13 +96,51 @@ function ActiveMeasureForm(): VNode {
       ? undefined
       : measures.body;
 
-  const measureList = !measureBody ? [] : Object.keys(measureBody.roots);
-  const design = formDesign(i18n, [
+  const measureList = (!measureBody ? [] : Object.keys(measureBody.roots)).map(
+    (m) =>
+      ({
+        type: "normal",
+        name: m,
+      }) satisfies NormalMeasure,
+  );
+
+  const requestCustomMeasures = request?.custom_measures ?? {}
+  const customMeasures = Object.keys(requestCustomMeasures).map(
+    (m) =>
+      ({
+        type: "normal",
+        name: m,
+      }) satisfies NormalMeasure,
+  );
+
+  const checkList = !measureBody ? [] : Object.entries(measureBody.checks);
+  const simpleChecks = checkList
+    .filter(
+      ([, check]) => check.outputs.length > 0 && check.requires.length > 0,
+    )
+    .map(
+      ([key]) =>
+        ({
+          type: "simple-check-form",
+          checkName: key,
+        }) satisfies SimpleCheckMeasure,
+    );
+
+  const allMeasures: MeasureType[] = [
     ...measureList,
-    ...Object.keys(request?.custom_measures ?? {}),
-  ]);
+    ...customMeasures,
+    ...simpleChecks,
+  ];
 
-  const nm = !request.new_measures ? [] : request.new_measures;
+  const design = formDesign(i18n, allMeasures);
+
+  const nm = (!request.new_measures ? [] : request.new_measures).map(
+    (m) =>
+      ({
+        type: "normal",
+        name: m,
+      }) satisfies NormalMeasure,
+  );
 
   const initValue = useMemo<FormType>(
     () => ({ measures: nm }),
@@ -110,8 +149,30 @@ function ActiveMeasureForm(): VNode {
 
   const form = useForm<FormType>(design, initValue);
   onComponentUnload(() => {
+    const newMeasures: string[] = [];
+    const formMeasures = form.status.result.measures ?? [];
+    for (const m of formMeasures) {
+      switch (m.type) {
+        case "normal": {
+          newMeasures.push(m.name)
+          break;
+        }
+        case "simple-check-form": {
+          const generatedId = `check-${m.checkName}`
+          requestCustomMeasures[generatedId] = {
+            check_name: m.checkName,
+          }
+          newMeasures.push(generatedId)
+          break;
+        }
+        default: {
+          assertUnreachable(m);
+        }
+      }
+    }
     updateRequest("unload active measure", {
-      new_measures: (form.status.result.measures ?? []) as string[],
+      new_measures: newMeasures,
+      custom_measures: requestCustomMeasures,
     });
   });
 
@@ -208,13 +269,35 @@ function ShowAllMeasures({
   );
 }
 
+type MeasureType = NormalMeasure | SimpleCheckMeasure;
+
+/**
+ * Normal measures are custom measures or server defined measure.
+ * The name reference some measure already defined
+ */
+type NormalMeasure = {
+  type: "normal";
+  name: string;
+};
+
+/**
+ * Simple check form measure are not yet defined but it should be automatically
+ * added on submission.
+ * This checks requires no context and output no information so it doesn't need
+ * any program.
+ */
+type SimpleCheckMeasure = {
+  type: "simple-check-form";
+  checkName: string;
+};
+
 type FormType = {
-  measures: string[];
+  measures: MeasureType[];
 };
 
 function formDesign(
   i18n: InternationalizationAPI,
-  measureNames: string[],
+  measureNames: MeasureType[],
 ): FormDesign<FormType> {
   return {
     type: "single-column",
@@ -222,12 +305,24 @@ function formDesign(
       {
         type: "selectMultiple",
         unique: true,
-        choices: measureNames.map((name) => {
-          return {
-            value: name,
-            label: name,
-          };
-        }),
+        choices: measureNames.map((me) => {
+          switch (me.type) {
+            case "normal": {
+              return {
+                label: me.name,
+                value: me,
+              };
+            }
+            case "simple-check-form": {
+              return {
+                label: `CHECK: ${me.checkName}`,
+                value: me,
+              };
+            }
+          }
+          // FIXME: choises should allow value to be any type 
+          // check: why do we require value to be string?
+        }) as any,
         id: "measures",
         label: i18n.str`Active measures`,
         help: i18n.str`Measures that the customer will need to satisfy while 
the rules are active.`,
@@ -241,7 +336,7 @@ function computeAvailableMesauresCustom(
   serverMeasures: TalerExchangeApi.AvailableMeasureSummary | undefined,
   skpiFilter?: (m: MeasureInfo) => boolean,
 ): Mesaures {
-  const init: Mesaures = { forms: [], procedures: [] };
+  const init: Mesaures = { forms: [], procedures: [], info: [] };
 
   if (!customMeasures || !serverMeasures) {
     return init;
@@ -249,19 +344,40 @@ function computeAvailableMesauresCustom(
 
   const custom = Object.entries(customMeasures).reduce((prev, [key, value]) => 
{
     if (value.check_name !== "SKIP") {
-      const r: MeasureInfo = {
-        type: "form",
-        name: key,
-        context: value.context,
-        programName: value.prog_name,
-        program: serverMeasures.programs[value.prog_name],
-        checkName: value.check_name,
-        check: serverMeasures.checks[value.check_name],
-        custom: true,
-      };
-      if (skpiFilter && skpiFilter(r)) return prev; // skip
-      prev.forms.push(r);
+      if (!value.prog_name) {
+        const r: MeasureInfo = {
+          type: "info",
+          name: key,
+          context: value.context,
+          checkName: value.check_name,
+          check: serverMeasures.checks[value.check_name],
+          custom: true,
+        };
+        if (skpiFilter && skpiFilter(r)) return prev; // skip
+        prev.info.push(r);
+      } else {
+        const r: MeasureInfo = {
+          type: "form",
+          name: key,
+          context: value.context,
+          programName: value.prog_name,
+          program: value.prog_name
+            ? serverMeasures.programs[value.prog_name]
+            : undefined,
+          checkName: value.check_name,
+          check: serverMeasures.checks[value.check_name],
+          custom: true,
+        };
+        if (skpiFilter && skpiFilter(r)) return prev; // skip
+        prev.forms.push(r);
+      }
     } else {
+      if (!value.prog_name) {
+        console.error(
+          `ERROR: program name can't be empty for measure "${key}"`,
+        );
+        return prev;
+      }
       const r: MeasureInfo = {
         type: "procedure",
         name: key,
diff --git a/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx 
b/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx
index 96a170615..139da5055 100644
--- a/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx
+++ b/packages/aml-backoffice-ui/src/pages/decision/Summary.tsx
@@ -3,12 +3,13 @@ import {
   AmlDecisionRequest,
   assertUnreachable,
   HttpStatusCode,
+  MeasureInformation,
   opFixedSuccess,
   parsePaytoUri,
   PaytoString,
   stringifyPaytoUri,
   TalerError,
-  TOPS_AmlEventsName
+  TOPS_AmlEventsName,
 } from "@gnu-taler/taler-util";
 import {
   Attention,
@@ -20,13 +21,9 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
-import {
-  useCurrentDecisionRequest
-} from "../../hooks/decision-request.js";
+import { useCurrentDecisionRequest } from "../../hooks/decision-request.js";
 import { useOfficer } from "../../hooks/officer.js";
-import {
-  useServerMeasures
-} from "../../hooks/server-info.js";
+import { useServerMeasures } from "../../hooks/server-info.js";
 import {
   computeAvailableMesaures,
   ShowDecisionLimitInfo,
@@ -73,7 +70,9 @@ export function Summary({
   const activeMeasureInfo: Mesaures = {
     forms: allMeasures.forms.filter((m) => d.indexOf(m.name) !== -1),
     procedures: allMeasures.procedures.filter((m) => d.indexOf(m.name) !== -1),
+    info: allMeasures.info.filter((m) => d.indexOf(m.name) !== -1),
   };
+  // preserve-investigate
 
   const { lib } = useExchangeApiContext();
 
@@ -135,7 +134,9 @@ export function Summary({
                 ),
                 rules: decision.rules!,
                 successor_measure: decision.onExpire_measure,
-                custom_measures: decision.custom_measures ?? {},
+                custom_measures: workaround_defaultProgramName(
+                  decision.custom_measures ?? {},
+                ),
               },
               attributes_expiration: decision.attributes?.expiration
                 ? AbsoluteTime.toProtocolTimestamp(
@@ -192,10 +193,7 @@ export function Summary({
   if (submitConfirmation) {
     return (
       <div class="flex">
-        <div
-          class="overflow-hidden rounded-lg bg-white shadow-lg"
-          style={{ width: 600, marign: "auto" }}
-        >
+        <div class="overflow-hidden rounded-lg bg-white shadow-lg w-500 w-64 
m-auto">
           <div class="px-4 py-5 sm:p-6">
             <Attention type="warning" title={i18n.str`Confirmation required`}>
               <i18n.Translate>
@@ -230,7 +228,7 @@ export function Summary({
 
   return (
     <Fragment>
-      <LocalNotificationBanner notification={notification} />
+      {/* <LocalNotificationBanner notification={notification} /> */}
 
       {INVALID_RULES ? (
         <Fragment>
@@ -367,3 +365,26 @@ export function Summary({
     </Fragment>
   );
 }
+/**
+ * FIXME: this should be removed when the server allows null programs
+ * or we define a no-operation program
+ *
+ * https://bugs.gnunet.org/view.php?id=9810
+ * https://bugs.gnunet.org/view.php?id=9874
+ *
+ * @param measures
+ * @returns
+ */
+export function workaround_defaultProgramName(
+  measures: Record<string, MeasureInformation>,
+) {
+  const ms = Object.keys(measures);
+  for (const name of ms) {
+    const m = measures[name];
+    if (!m.prog_name) {
+      measures[name].prog_name = "preserve-investigate";
+    }
+  }
+  return measures;
+}
+
diff --git a/packages/aml-backoffice-ui/src/utils/QR.tsx 
b/packages/aml-backoffice-ui/src/utils/QR.tsx
deleted file mode 100644
index b382348a3..000000000
--- a/packages/aml-backoffice-ui/src/utils/QR.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022-2024 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-import { h, VNode } from "preact";
-import { useEffect, useRef } from "preact/hooks";
-// import qrcode from "qrcode-generator";
-
-export function QR({ text }: { text: string }): VNode {
-  const divRef = useRef<HTMLDivElement>(null);
-  useEffect(() => {
-    // const qr = qrcode(0, "L");
-    // qr.addData(text);
-    // qr.make();
-    // if (divRef.current)
-    //   divRef.current.innerHTML = qr.createSvgTag({
-    //     scalable: true,
-    //   });
-  });
-
-  return (
-    <div
-      style={{
-        width: "100%",
-        display: "flex",
-        flexDirection: "column",
-        alignItems: "left",
-      }}
-    >
-      <div
-        style={{
-          width: "50%",
-          minWidth: 200,
-          maxWidth: 300,
-          marginRight: "auto",
-          marginLeft: "auto",
-        }}
-        ref={divRef}
-      />
-    </div>
-  );
-}
diff --git a/packages/taler-util/src/aml/events.ts 
b/packages/taler-util/src/aml/events.ts
index 06725556e..67745c521 100644
--- a/packages/taler-util/src/aml/events.ts
+++ b/packages/taler-util/src/aml/events.ts
@@ -1,6 +1,5 @@
 import { Amounts } from "../amounts.js";
-import { LimitOperationType } from "../types-taler-exchange.js";
-import { AccountProperties, KycRule } from "../types-taler-kyc-aml.js";
+import { AccountProperties, KycRule, LimitOperationType } from 
"../types-taler-exchange.js";
 import { TalerAmlProperties } from "../taler-account-properties.js";
 import { isOneOf } from "./properties.js";
 
diff --git a/packages/taler-util/src/aml/properties.ts 
b/packages/taler-util/src/aml/properties.ts
index f6de13e6e..53572977d 100644
--- a/packages/taler-util/src/aml/properties.ts
+++ b/packages/taler-util/src/aml/properties.ts
@@ -1,9 +1,6 @@
 import { TalerAmlProperties } from "../taler-account-properties.js";
 import { TalerFormAttributes } from "../taler-form-attributes.js";
-import {
-  AccountProperties,
-  LegitimizationRuleSet,
-} from "../types-taler-kyc-aml.js";
+import { AccountProperties, LegitimizationRuleSet } from 
"../types-taler-exchange.js";
 
 /**
  * List of account properties required by TOPS
diff --git a/packages/taler-util/src/types-taler-exchange.ts 
b/packages/taler-util/src/types-taler-exchange.ts
index 03c6d89c5..ef24fac63 100644
--- a/packages/taler-util/src/types-taler-exchange.ts
+++ b/packages/taler-util/src/types-taler-exchange.ts
@@ -1688,7 +1688,7 @@ export interface MeasureInformation {
   check_name: string;
 
   // Name of an AML program.
-  prog_name: string;
+  prog_name?: string;
 
   // Context for the check. Optional.
   context?: Object;
diff --git a/packages/taler-util/src/types-taler-kyc-aml.ts 
b/packages/taler-util/src/types-taler-kyc-aml.ts
index 22d024f5e..9ec1ff1c8 100644
--- a/packages/taler-util/src/types-taler-kyc-aml.ts
+++ b/packages/taler-util/src/types-taler-kyc-aml.ts
@@ -15,19 +15,17 @@
  */
 
 import {
+  AccountProperties,
   buildCodecForObject,
   Codec,
   codecForAccountProperties,
   codecForAny,
   codecForList,
   codecOptional,
-  LimitOperationType,
+  LegitimizationRuleSet
 } from "./index.js";
 import {
-  AmountString,
-  Integer,
-  RelativeTime,
-  Timestamp,
+  Timestamp
 } from "./types-taler-common.js";
 
 // 
https://docs.taler.net/taler-kyc-manual.html#implementing-your-own-aml-programs
@@ -215,146 +213,6 @@ export interface AmlOutcome {
   new_measures?: string;
 }
 
-// All fields in this object are optional. The actual
-// properties collected depend fully on the discretion
-// of the exchange operator;
-// however, some common fields are standardized
-// and thus described here.
-export interface AccountProperties {
-  // True if this is a politically exposed account.
-  // Rules for classifying accounts as politically
-  // exposed are country-dependent.
-  pep?: boolean;
-
-  // True if this is a sanctioned account.
-  // Rules for classifying accounts as sanctioned
-  // are country-dependent.
-  sanctioned?: boolean;
-
-  // True if this is a high-risk account.
-  // Rules for classifying accounts as at-risk
-  // are exchange operator-dependent.
-  high_risk?: boolean;
-
-  // Business domain of the account owner.
-  // The list of possible business domains is
-  // operator- or country-dependent.
-  business_domain?: string;
-
-  // Is the client's account currently frozen?
-  is_frozen?: boolean;
-
-  // Was the client's account reported to the authorities?
-  was_reported?: boolean;
-
-  [key: string]: string | boolean | number | undefined;
-}
-
-export interface LegitimizationRuleSet {
-  // When does this set of rules expire and
-  // we automatically transition to the successor
-  // measure?
-  expiration_time: Timestamp;
-
-  // Name of the measure to apply when the expiration time is
-  // reached.  If not set, we refer to the default
-  // set of rules (and the default account state).
-  successor_measure?: string;
-
-  // Legitimization rules that are to be applied
-  // to this account.
-  rules: KycRule[];
-
-  // Custom measures that KYC rules and the
-  // successor_measure may refer to.
-  custom_measures: { [measure: string]: MeasureInformation };
-}
-
-export interface KycRule {
-  // Type of operation to which the rule applies.
-  //
-  // Must be one of "WITHDRAW", "DEPOSIT",
-  // (p2p) "MERGE", (wallet) "BALANCE",
-  // (reserve) "CLOSE", "AGGREGATE",
-  // "TRANSACTION" or "REFUND".
-  operation_type: LimitOperationType;
-
-  // Name of the configuration section this rule
-  // originates from. Not available for all rules.
-  // Primarily informational, but also useful to
-  // explicitly manipulate rules by-name in AML programs.
-  rule_name?: string;
-
-  // The measures will be taken if the given
-  // threshold is crossed over the given timeframe.
-  threshold: AmountString;
-
-  // Over which duration should the threshold be
-  // computed.  All amounts of the respective
-  // operation_type will be added up for this
-  // duration and the sum compared to the threshold.
-  timeframe: RelativeTime;
-
-  // Array of names of measures to apply.
-  // Names listed can be original measures or
-  // custom measures from the AmlOutcome.
-  // A special measure "verboten" is used if the
-  // threshold may never be crossed.
-  measures: string[];
-
-  // If multiple rules apply to the same account
-  // at the same time, the number with the highest
-  // rule determines which set of measures will
-  // be activated and thus become visible for the
-  // user.
-  display_priority: Integer;
-
-  // True if the rule (specifically, operation_type,
-  // threshold, timeframe) and the general nature of
-  // the measures (verboten or approval required)
-  // should be exposed to the client.
-  // Defaults to "false" if not set.
-  exposed?: boolean;
-
-  // True if all the measures will eventually need to
-  // be satisfied, false if any of the measures should
-  // do.  Primarily used by the SPA to indicate how
-  // the measures apply when showing them to the user;
-  // in the end, AML programs will decide after each
-  // measure what to do next.
-  // Default (if missing) is false.
-  is_and_combinator?: boolean;
-}
-
-export interface MeasureInformation {
-  // Name of a KYC check.
-  check_name: string;
-
-  // Name of an AML program.
-  prog_name: string;
-
-  // Context for the check. Optional.
-  context?: Object;
-
-  // Operation that this measure relates to.
-  // NULL if unknown. Useful as a hint to the
-  // user if there are many (voluntary) measures
-  // and some related to unlocking certain operations.
-  // (and due to zero-amount thresholds, no measure
-  // was actually specifically triggered).
-  //
-  // Must be one of "WITHDRAW", "DEPOSIT",
-  // (p2p) "MERGE", (wallet) "BALANCE",
-  // (reserve) "CLOSE", "AGGREGATE",
-  // "TRANSACTION" or "REFUND".
-  // New in protocol **v21**.
-  operation_type?: LimitOperationType;
-
-  // Can this measure be undertaken voluntarily?
-  // Optional, default is false.
-  // Since protocol **vATTEST**.
-  voluntary?: boolean;
-}
 
 export const codecForAmlProgramInput = (): Codec<AmlProgramInput> =>
   buildCodecForObject<AmlProgramInput>()
diff --git a/packages/web-util/src/components/Attention.tsx 
b/packages/web-util/src/components/Attention.tsx
index 4172c0c9b..7b2310c63 100644
--- a/packages/web-util/src/components/Attention.tsx
+++ b/packages/web-util/src/components/Attention.tsx
@@ -11,7 +11,7 @@ interface Props {
 export function Attention({ type = "info", title, children, onClose, timeout = 
Duration.getForever() }: Props): VNode {
 
   return <div class={`group attention-${type} mt-2 shadow-lg`}>
-    {timeout.d_ms === "forever" ? undefined : <style>{`
+    {/* {timeout.d_ms === "forever" ? undefined : <style>{`
     .progress {
         animation: notificationTimeoutBar ${Math.round(timeout.d_ms / 1000)}s 
ease-in-out;
         animation-fill-mode:both; 
@@ -22,7 +22,7 @@ export function Attention({ type = "info", title, children, 
onClose, timeout = D
       100% { width: 100%; }
     }
   `}</style>
-    }
+    } */}
 
     <div data-timed={timeout.d_ms !== "forever"} class="rounded-md 
data-[timed=true]:rounded-b-none group-[.attention-info]:bg-blue-50 
group-[.attention-low]:bg-gray-100 group-[.attention-warning]:bg-yellow-50 
group-[.attention-danger]:bg-red-50 group-[.attention-success]:bg-green-50 p-4 
shadow">
       <div class="flex">
diff --git a/packages/web-util/src/components/Button.tsx 
b/packages/web-util/src/components/Button.tsx
index 18047418e..0d5605699 100644
--- a/packages/web-util/src/components/Button.tsx
+++ b/packages/web-util/src/components/Button.tsx
@@ -88,7 +88,7 @@ export function Button<T extends OperationResult<A, B>, A, 
B>({
 function Wait(): VNode {
   return (
     <Fragment>
-      <style>
+      {/* <style>
         {`
       #l1 {          width: 120px;
         height: 20px;
@@ -99,7 +99,7 @@ function Wait(): VNode {
       @keyframes l17 {
           100% {background-size:120% 100%}
 `}
-      </style>
+      </style> */}
       <div id="l1" />
     </Fragment>
   );
diff --git a/packages/web-util/src/components/Header.tsx 
b/packages/web-util/src/components/Header.tsx
index 23ac62383..4a0180afc 100644
--- a/packages/web-util/src/components/Header.tsx
+++ b/packages/web-util/src/components/Header.tsx
@@ -38,10 +38,9 @@ export function Header({
             <div class="flex-shrink-0 bg-white rounded-lg">
               <a href={iconLinkURL ?? "#"} name="logo">
                 <img
-                  class="h-8 w-auto"
+                  class="h-8 w-auto m-1"
                   src={logo}
                   alt="GNU Taler"
-                  style={{ height: "1.5rem", margin: ".5rem" }}
                 />
               </a>
             </div>
diff --git a/packages/web-util/src/components/Loading.tsx 
b/packages/web-util/src/components/Loading.tsx
index c5dcd90c1..e8b9274fa 100644
--- a/packages/web-util/src/components/Loading.tsx
+++ b/packages/web-util/src/components/Loading.tsx
@@ -35,7 +35,7 @@ export function Loading(): VNode {
 
 function Spinner(): VNode {
   return (
-    <div class="lds-ring" style={{ margin: "auto" }}>
+    <div class="lds-ring m-auto" >
       <div />
       <div />
       <div />
diff --git a/packages/web-util/src/components/ShowInputErrorLabel.tsx 
b/packages/web-util/src/components/ShowInputErrorLabel.tsx
index c5840cad9..8ba46af40 100644
--- a/packages/web-util/src/components/ShowInputErrorLabel.tsx
+++ b/packages/web-util/src/components/ShowInputErrorLabel.tsx
@@ -25,5 +25,5 @@ export function ShowInputErrorLabel({
 }): VNode {
   if (message && isDirty)
     return <div class="text-base" style={{ color: "red" }}>{message}</div>;
-  return <div class="text-base" style={{ }}> </div>;
+  return <div class="text-base" > </div>;
 }

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