[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-wallet-core] branch master updated: show server measures
From: |
gnunet |
Subject: |
[taler-wallet-core] branch master updated: show server measures |
Date: |
Fri, 20 Dec 2024 19:03:33 +0100 |
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 6b291c6dc show server measures
6b291c6dc is described below
commit 6b291c6dcda98c9b7c3561b23d7af91c0376c84a
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Fri Dec 20 15:03:26 2024 -0300
show server measures
---
.../aml-backoffice-ui/src/pages/CaseDetails.tsx | 183 ++++++++++++++++++++-
packages/aml-backoffice-ui/src/pages/Measures.tsx | 57 ++++++-
.../aml-backoffice-ui/src/pages/MeasuresTable.tsx | 123 ++++++++++++++
3 files changed, 353 insertions(+), 10 deletions(-)
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index fd7e562c1..8a2dca1bf 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -61,12 +61,13 @@ import { useState } from "preact/hooks";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
import { useUiFormsContext } from "../context/ui-forms.js";
import { preloadedForms } from "../forms/index.js";
-import { useAccountInformation } from "../hooks/account.js";
+import { useAccountInformation, useServerMeasures } from "../hooks/account.js";
import { useAccountDecisions } from "../hooks/decisions.js";
import { ShowConsolidated } from "./ShowConsolidated.js";
import { useOfficer } from "../hooks/officer.js";
import { getShapeFromFields, useFormState } from "../hooks/form.js";
import { privatePages } from "../Routing.js";
+import { CurrentMeasureTable, MeasureInfo } from "./MeasuresTable.js";
export type AmlEvent =
| AmlFormEvent
@@ -201,6 +202,7 @@ export function CaseDetails({
const [request, setDesicionRequest] = useState<NewDecision | undefined>(
undefined,
);
+ const [selectMeasure, setSelectMeasure] = useState<boolean>();
const { config } = useExchangeApiContext();
const { i18n } = useTranslationContext();
@@ -249,6 +251,37 @@ export function CaseDetails({
const events = getEventsFromAmlHistory(accountDetails, i18n, allForms);
+ if (selectMeasure) {
+ return (
+ <ShowMeasuresToSelect
+ onSelect={(d) => {
+ setSelectMeasure(false);
+ setDesicionRequest({
+ request: {
+ payto_uri: paytoString,
+ decision_time: AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.now(),
+ ),
+ h_payto: account,
+ keep_investigating: false,
+ properties: {},
+ // the custom measure with context
+ new_measures: d.name,
+ new_rules: {
+ // this value is going to be overridden
+ custom_measures: {},
+ expiration_time: AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.never(),
+ ),
+ rules: FREEZE_RULES(config.currency),
+ },
+ },
+ askInformation: false,
+ });
+ }}
+ />
+ );
+ }
if (request) {
return (
<SubmitNewDecision
@@ -391,6 +424,15 @@ export function CaseDetails({
>
<i18n.Translate>Ask for more information</i18n.Translate>
</button>
+
+ <button
+ onClick={async () => {
+ setSelectMeasure(true);
+ }}
+ 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 predefined measure</i18n.Translate>
+ </button>
</div>
{!activeDecision ? (
@@ -575,7 +617,9 @@ function SubmitNewDecision({
};
const submitHandler =
- decisionForm === undefined || !session || customForm === undefined
+ decisionForm === undefined ||
+ !session ||
+ (decision.askInformation && customForm === undefined)
? undefined
: withErrorHandler(
() => {
@@ -598,7 +642,7 @@ function SubmitNewDecision({
// check of type form, it will use the officer defined form
check_name: "askContext",
// after that, mark as investigate to read what the user
sent
- prog_name: "markInvestigate",
+ prog_name: "preserve-investigate",
},
},
},
@@ -673,6 +717,11 @@ function SubmitNewDecision({
<i18n.Translate>New rules to submit</i18n.Translate>
</h1>
+ <ShowMesaureInfo
+ nextMeasures={separateMeasures(decision.request.new_measures)}
+ customMeasure={decision.request.new_rules.custom_measures}
+ />
+
<ShowDecisionLimitInfo
since={AbsoluteTime.fromProtocolTimestamp(
decision.request.decision_time,
@@ -687,6 +736,99 @@ function SubmitNewDecision({
);
}
+function separateMeasures(measureList: string | undefined): string[][] {
+ if (!measureList) return new Array();
+ const orList = measureList.trim().split(" ");
+ const orAndList = orList.map((or) => or.split("+"));
+ return orAndList;
+}
+
+function ShowMesaureInfo({
+ nextMeasures,
+ customMeasure,
+}: {
+ nextMeasures: string[][];
+ customMeasure: { [d: string]: TalerExchangeApi.MeasureInformation };
+}): VNode {
+ const measures = useServerMeasures();
+ const { i18n } = useTranslationContext();
+ if (!measures) {
+ return <Loading />;
+ }
+ if (measures instanceof TalerError) {
+ return <ErrorLoadingWithDebug error={measures} />;
+ }
+ const summary: TalerExchangeApi.AvailableMeasureSummary = measures.body;
+
+ const map: { [d: string]: MeasureInfo } = {};
+
+ function addUpIntoMap([key, value]: [
+ string,
+ TalerExchangeApi.MeasureInformation,
+ ]): void {
+ if (value.check_name !== "SKIP") {
+ map[key] = {
+ name: key,
+ context: value.context,
+ program: summary.programs[value.prog_name],
+ check: summary.checks[value.check_name],
+ };
+ } else {
+ map[key] = {
+ name: key,
+ context: value.context,
+ program: summary.programs[value.prog_name],
+ };
+ }
+ }
+
+ Object.entries(measures.body.roots).forEach(addUpIntoMap);
+ Object.entries(customMeasure).forEach(addUpIntoMap);
+
+ const filteredMeasures = nextMeasures.filter((n) => !!n.length);
+
+ if (!filteredMeasures.length) {
+ return (
+ <div>
+ <i18n.Translate>no new measure</i18n.Translate>
+ </div>
+ );
+ }
+ if (filteredMeasures.length === 1) {
+ const measurePath = filteredMeasures[0];
+ if (measurePath.length === 1) {
+ const m = map[measurePath[0]];
+
+ return (
+ <div>
+ <i18n.Translate>
+ the user needs to complete this measure
+ </i18n.Translate>
+ <CurrentMeasureTable list={[m]} />
+ </div>
+ );
+ }
+ return (
+ <div>
+ <i18n.Translate>
+ the user needs to complete all of these measures
+ </i18n.Translate>
+ <CurrentMeasureTable list={measurePath.map((name) => map[name])} />
+ </div>
+ );
+ }
+
+ return (
+ <div>
+ <i18n.Translate>
+ the user has more than one option with different measures to complete.
+ For any options, if there is more than one measure then the user will
+ need to complete all the measures.
+ </i18n.Translate>
+ </div>
+ );
+}
+
function ShowDecisionLimitInfo({
ruleSet,
since,
@@ -1381,3 +1523,38 @@ const FREEZE_RULES: (currency: string) =>
TalerExchangeApi.KycRule[] = (
is_and_combinator: true,
},
];
+
+function ShowMeasuresToSelect({
+ onSelect,
+}: {
+ onSelect?: (m: MeasureInfo) => void;
+}): VNode {
+ const measures = useServerMeasures();
+
+ if (!measures) {
+ return <Loading />;
+ }
+ if (measures instanceof TalerError) {
+ return <ErrorLoadingWithDebug error={measures} />;
+ }
+
+ const list = Object.entries(measures.body.roots).map(
+ ([key, value]): MeasureInfo => {
+ if (value.check_name !== "SKIP") {
+ return {
+ name: key,
+ context: value.context,
+ program: measures.body.programs[value.prog_name],
+ check: measures.body.checks[value.check_name],
+ };
+ }
+ return {
+ name: key,
+ context: value.context,
+ program: measures.body.programs[value.prog_name],
+ };
+ },
+ );
+
+ return <CurrentMeasureTable list={list} onSelect={onSelect} />;
+}
diff --git a/packages/aml-backoffice-ui/src/pages/Measures.tsx
b/packages/aml-backoffice-ui/src/pages/Measures.tsx
index 1e6a633d5..911fae82e 100644
--- a/packages/aml-backoffice-ui/src/pages/Measures.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Measures.tsx
@@ -14,20 +14,21 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import {
- TalerError
+ AmlProgramRequirement,
+ KycCheckInformation,
+ TalerError,
} from "@gnu-taler/taler-util";
import {
Loading,
useExchangeApiContext,
- useTranslationContext
+ useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { Fragment, h } from "preact";
+import { Fragment, h, VNode } from "preact";
import { ErrorLoadingWithDebug } from "../components/ErrorLoadingWithDebug.js";
import { useServerMeasures } from "../hooks/account.js";
+import { CurrentMeasureTable, MeasureInfo } from "./MeasuresTable.js";
export function Measures({}: {}) {
- const { config } = useExchangeApiContext();
-
const { i18n } = useTranslationContext();
// const { forms } = useUiFormsContext();
@@ -35,16 +36,58 @@ export function Measures({}: {}) {
// const allForms = [...forms, ...preloadedForms(i18n)];
const measures = useServerMeasures();
- if (!measures || !history) {
+ if (!measures) {
return <Loading />;
}
if (measures instanceof TalerError) {
return <ErrorLoadingWithDebug error={measures} />;
}
+ const list = Object.entries(measures.body.roots).map(
+ ([key, value]): MeasureInfo => {
+ if (value.check_name !== "SKIP") {
+ return {
+ name: key,
+ context: value.context,
+ program: measures.body.programs[value.prog_name],
+ check: measures.body.checks[value.check_name],
+ };
+ }
+ return {
+ name: key,
+ context: value.context,
+ program: measures.body.programs[value.prog_name],
+ };
+ },
+ );
+
return (
<div>
- <pre>{JSON.stringify(measures, undefined ,2)}</pre>
+ <div class="px-4 sm:px-6 lg:px-8">
+ <div class="sm:flex sm:items-center">
+ <div class="sm:flex-auto">
+ <h1 class="text-base font-semibold text-gray-900">
+ <i18n.Translate>Measures</i18n.Translate>
+ </h1>
+ <p class="mt-2 text-sm text-gray-700">
+ <i18n.Translate>
+ A list of all the pre-define measures in your that can used
with
+ the user.
+ </i18n.Translate>
+ </p>
+ </div>
+ <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
+ {/* <button
+ type="button"
+ class="block rounded-md bg-indigo-600 px-3 py-2 text-center
text-sm font-semibold text-white shadow-sm hover:bg-indigo-500
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2
focus-visible:outline-indigo-600"
+ >
+ Add user
+ </button> */}
+ </div>
+ </div>
+
+ <CurrentMeasureTable list={list} />
+ </div>
</div>
);
}
diff --git a/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
b/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
new file mode 100644
index 000000000..a9a02c2cb
--- /dev/null
+++ b/packages/aml-backoffice-ui/src/pages/MeasuresTable.tsx
@@ -0,0 +1,123 @@
+/*
+ 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 {
+ AmlProgramRequirement,
+ KycCheckInformation,
+} from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+
+export type MeasureInfo = {
+ name: string;
+ program: AmlProgramRequirement;
+ check?: KycCheckInformation;
+ context?: object;
+};
+export function CurrentMeasureTable({
+ list,
+ onSelect,
+}: {
+ list: MeasureInfo[];
+ onSelect?: (m: MeasureInfo) => void;
+}): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="mt-8 flow-root">
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
+ <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>Check</i18n.Translate>
+ </th>
+ <th
+ scope="col"
+ class="p-2 text-left text-sm font-semibold text-gray-900"
+ >
+ <i18n.Translate>Program</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">
+ {list.map((m) => {
+ if (
+ m.context &&
+ "internal" in m.context &&
+ m.context.internal
+ ) {
+ return <Fragment />;
+ }
+ 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>Select</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">
+ {m.check?.description ?? ""}
+ </td>
+ <td class="whitespace-nowrap p-2 text-sm text-gray-500">
+ {m.program.description}
+ </td>
+ <td class="whitespace-nowrap p-2 text-sm text-gray-500">
+ {Object.keys(m.context ?? {}).join(", ")}
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-wallet-core] branch master updated: show server measures,
gnunet <=