[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">
<{i18n.str`Invalid value`}>
</span>
)}
@@ -299,7 +299,7 @@ export function Transfers({
spec={config.config.currency_specification}
/>
) : (
- <span style={{ color: "grey" }}>
+ <span class="text-grey-500">
<
{i18n.str`Invalid value`}>
</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.