aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-08-28 10:34:39 -0300
committerSebastian <sebasjm@gmail.com>2024-08-28 10:34:39 -0300
commitf81151d4ecaa610578bd465461b478b0afc66e5b (patch)
treef8bfbe4852b1ba17d9f4be4c3ba6e4c10a66e9c9
parentca4bccc5de2d92333267ced69535d4400c394fb9 (diff)
ask for more info set meaure to m2
-rw-r--r--packages/aml-backoffice-ui/src/Routing.tsx4
-rw-r--r--packages/aml-backoffice-ui/src/hooks/decisions.ts52
-rw-r--r--packages/aml-backoffice-ui/src/pages/CaseDetails.tsx566
-rw-r--r--packages/aml-backoffice-ui/src/pages/Cases.tsx77
4 files changed, 433 insertions, 266 deletions
diff --git a/packages/aml-backoffice-ui/src/Routing.tsx b/packages/aml-backoffice-ui/src/Routing.tsx
index 082dc8ab5..b99314889 100644
--- a/packages/aml-backoffice-ui/src/Routing.tsx
+++ b/packages/aml-backoffice-ui/src/Routing.tsx
@@ -26,7 +26,7 @@ import { assertUnreachable } from "@gnu-taler/taler-util";
import { useEffect } from "preact/hooks";
import { ExchangeAmlFrame } from "./ExchangeAmlFrame.js";
import { useOfficer } from "./hooks/officer.js";
-import { Cases } from "./pages/Cases.js";
+import { Cases, CasesUnderInvestigation } from "./pages/Cases.js";
import { Officer } from "./pages/Officer.js";
import { CaseDetails } from "./pages/CaseDetails.js";
import { CaseUpdate, SelectForm } from "./pages/CaseUpdate.js";
@@ -140,7 +140,7 @@ function PrivateRouting(): VNode {
return <SelectForm account={location.values.cid} />;
}
case "investigation": {
- return <Cases filtered/>;
+ return <CasesUnderInvestigation />;
}
case "active": {
return <Cases />;
diff --git a/packages/aml-backoffice-ui/src/hooks/decisions.ts b/packages/aml-backoffice-ui/src/hooks/decisions.ts
index eb861ae14..24941b29e 100644
--- a/packages/aml-backoffice-ui/src/hooks/decisions.ts
+++ b/packages/aml-backoffice-ui/src/hooks/decisions.ts
@@ -37,7 +37,7 @@ export const PAGINATED_LIST_REQUEST = PAGINATED_LIST_SIZE + 1;
* @param args
* @returns
*/
-export function useCurrentDecisions(filtered?: boolean) {
+export function useCurrentDecisionsUnderInvestigation() {
const officer = useOfficer();
const session = officer.state === "ready" ? officer.account : undefined;
const {
@@ -54,7 +54,7 @@ export function useCurrentDecisions(filtered?: boolean) {
return await api.getAmlDecisions(officer, {
order: "dec",
offset,
- investigation,
+ investigation: true,
active: true,
limit: PAGINATED_LIST_REQUEST,
});
@@ -66,7 +66,53 @@ export function useCurrentDecisions(filtered?: boolean) {
>(
!session
? undefined
- : [session, offset, filtered ? true : filtered, "getAmlDecisions"],
+ : [session, offset, "getAmlDecisions"],
+ fetcher,
+ );
+
+ if (error) return error;
+ if (data === undefined) return undefined;
+ if (data.type !== "ok") return data;
+
+ return buildPaginatedResult(data.body.records, offset, setOffset, (d) =>
+ String(d.rowid),
+ );
+}
+
+/**
+ * @param account
+ * @param args
+ * @returns
+ */
+export function useCurrentDecisions() {
+ const officer = useOfficer();
+ const session = officer.state === "ready" ? officer.account : undefined;
+ const {
+ lib: { exchange: api },
+ } = useExchangeApiContext();
+
+ const [offset, setOffset] = useState<string>();
+
+ async function fetcher([officer, offset]: [
+ OfficerAccount,
+ string | undefined,
+ boolean | undefined,
+ ]) {
+ return await api.getAmlDecisions(officer, {
+ order: "dec",
+ offset,
+ active: true,
+ limit: PAGINATED_LIST_REQUEST,
+ });
+ }
+
+ const { data, error } = useSWR<
+ TalerExchangeResultByMethod<"getAmlDecisions">,
+ TalerHttpError
+ >(
+ !session
+ ? undefined
+ : [session, offset, "getAmlDecisions"],
fetcher,
);
diff --git a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
index a1c09025b..8b54c1861 100644
--- a/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
+++ b/packages/aml-backoffice-ui/src/pages/CaseDetails.tsx
@@ -355,7 +355,7 @@ export function CaseDetails({ account }: { account: string }) {
justification: "",
keep_investigating: false,
properties: {},
- new_measure: "onlyTalers",
+ new_measure: "m2",
new_rules: {
custom_measures: {},
expiration_time: AbsoluteTime.toProtocolTimestamp(
@@ -379,7 +379,7 @@ export function CaseDetails({ account }: { account: string }) {
<h1 class="my-4 text-base font-semibold leading-6 text-black">
<i18n.Translate>Current active rules</i18n.Translate>
</h1>
- <ShowDecisionInfo decision={activeDecision} />
+ <ShowDecisionInfo decision={activeDecision} startOpen />
</Fragment>
)}
<h1 class="my-4 text-base font-semibold leading-6 text-black">
@@ -423,92 +423,144 @@ export function CaseDetails({ account }: { account: string }) {
function ShowDecisionInfo({
decision,
+ startOpen
}: {
decision: TalerExchangeApi.AmlDecision;
+ startOpen?: boolean,
}): VNode {
const { i18n } = useTranslationContext();
const { config } = useExchangeApiContext();
+ const [opened, setOpened] = useState(startOpen ?? false)
- 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>
+
+ function Header() {
+ return <ul role="list" class="divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl">
+ <li class="relative flex justify-between gap-x-6 px-4 py-5 hover:bg-gray-50 sm:px-6">
+ <div class="flex min-w-0 gap-x-4">
+ <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>
- <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 class="flex shrink-0 items-center gap-x-4">
+ <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.limits.expiration_time,
+ )}
+ />
+ </div>
</div>
</div>
+ </li>
+ </ul>
+ }
- <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>
- )}
+ if (!opened) {
+ return <div class="mt-4 cursor-pointer" onClick={() => setOpened(true)}>
+ <Header />
+ </div>
+ }
+ const balanceLimit = decision.limits.rules.find(r => r.operation_type === "BALANCE")
+
+ return (
+ <div>
+
+ <div class="mt-4 cursor-pointer" onClick={() => setOpened(false)}>
+ <Header />
+ </div>
+
+ {!decision.justification ? undefined :
+ <div>
+ <label for="comment" class="block text-sm font-medium leading-6 text-gray-900">Description</label>
+ <div class="mt-2">
+ <textarea rows={2} name="comment" id="comment" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
+ {decision.justification}
+ </textarea>
</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>
+ }
+
+ {!balanceLimit ? undefined :
+ <div class="px-4">
+ <div class="flex mt-2 rounded-md w-fit shadow-sm border-0 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
+ <div class="whitespace-nowrap pointer-events-none bg-gray-200 inset-y-0 items-center px-3 flex">
+ <i18n.Translate>Max balance</i18n.Translate>
+ </div>
+ <div class="p-2 disabled:bg-gray-200 text-right rounded-md rounded-l-none data-[left=true]:text-left py-1.5 pl-3 text-gray-900 placeholder:text-gray-400 sm:text-sm sm:leading-6">
+ <RenderAmount
+ value={Amounts.parseOrThrow(balanceLimit.threshold)}
+ spec={config.currency_specification}
+ />
+
+ </div>
</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(
+ }
+
+ <div class="w-full px-4 pt-4">
+
+ <table class="min-w-full divide-y divide-gray-300">
+ <thead class="bg-gray-50">
+ <tr>
+ <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"><i18n.Translate>Operation</i18n.Translate></th>
+ <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900"><i18n.Translate>Timeframe</i18n.Translate></th>
+ <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6"><i18n.Translate>Amount</i18n.Translate></th>
+ </tr>
+ </thead>
+ <tbody class="divide-y divide-gray-200">
+ {decision.limits.rules.map((r) => {
+ if (r.operation_type === "BALANCE") return;
+ return (
+ <tr>
+ <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 text-left">{r.operation_type}</td>
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500">
+ {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>
+ </td>
+ <td class=" relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 text-right">
+ <RenderAmount
+ value={Amounts.parseOrThrow(r.threshold)}
+ spec={config.currency_specification}
+ />
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+
+ </div>
);
}
@@ -729,13 +781,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);
@@ -773,9 +825,9 @@ function parseJustification(
listOfAllKnownForms: FormMetadata[],
):
| OperationOk<{
- justification: Justification;
- metadata: FormMetadata;
- }>
+ justification: Justification;
+ metadata: FormMetadata;
+ }>
| OperationFail<ParseJustificationFail> {
try {
const justification = JSON.parse(s);
@@ -823,212 +875,212 @@ function parseJustification(
const THRESHOLD_2000_WEEK: (currency: string) => TalerExchangeApi.KycRule[] = (
currency,
) => [
- {
- operation_type: "WITHDRAW",
- threshold: `${currency}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:2000`,
- timeframe: {
- d_us: 7 * 24 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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}:100`,
- timeframe: {
- d_us: 1 * 60 * 60 * 1000 * 1000,
+ {
+ 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",
+ {
+ 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/Cases.tsx b/packages/aml-backoffice-ui/src/pages/Cases.tsx
index d79ce9006..c229850b1 100644
--- a/packages/aml-backoffice-ui/src/pages/Cases.tsx
+++ b/packages/aml-backoffice-ui/src/pages/Cases.tsx
@@ -29,7 +29,7 @@ import {
useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
-import { useCurrentDecisions } from "../hooks/decisions.js";
+import { useCurrentDecisions, useCurrentDecisionsUnderInvestigation } from "../hooks/decisions.js";
import { privatePages } from "../Routing.js";
import { FormErrors, RecursivePartial, useFormState } from "../hooks/form.js";
@@ -171,8 +171,8 @@ export function CasesUI({
);
}
-export function Cases({ filtered }: { filtered?: boolean }) {
- const list = useCurrentDecisions(filtered);
+export function Cases() {
+ const list = useCurrentDecisions();
const { i18n } = useTranslationContext();
if (!list) {
@@ -229,7 +229,76 @@ export function Cases({ filtered }: { filtered?: boolean }) {
return (
<CasesUI
- filtered={!!filtered}
+ filtered={true}
+ records={list.body}
+ onFirstPage={list.isFirstPage ? undefined : list.loadFirst}
+ onNext={list.isLastPage ? undefined : list.loadNext}
+ // filter={stateFilter}
+ // onChangeFilter={(d) => {
+ // setStateFilter(d);
+ // }}
+ />
+ );
+}
+export function CasesUnderInvestigation() {
+ const list = useCurrentDecisionsUnderInvestigation();
+ const { i18n } = useTranslationContext();
+
+ if (!list) {
+ return <Loading />;
+ }
+ if (list instanceof TalerError) {
+ return <ErrorLoadingWithDebug error={list} />;
+ }
+
+ if (list.type === "fail") {
+ switch (list.case) {
+ case HttpStatusCode.Forbidden: {
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account signature is wrong, contact administrator or create a new one.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ }
+ case HttpStatusCode.NotFound: {
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account is not known.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ }
+ case HttpStatusCode.Conflict: {
+ return (
+ <Fragment>
+ <Attention type="danger" title={i18n.str`Operation denied`}>
+ <i18n.Translate>
+ This account doesn't have access. Request account activation
+ sending your public key.
+ </i18n.Translate>
+ </Attention>
+ <Officer />
+ </Fragment>
+ );
+ }
+ return <Officer />;
+ default:
+ assertUnreachable(list);
+ }
+ }
+
+ return (
+ <CasesUI
+ filtered={false}
records={list.body}
onFirstPage={list.isFirstPage ? undefined : list.loadFirst}
onNext={list.isLastPage ? undefined : list.loadNext}