/* This file is part of GNU Taler (C) 2022 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 */ import { AbsoluteTime, AmountJson, Amounts, HttpStatusCode, TalerError, TranslatedString, assertUnreachable, } from "@gnu-taler/taler-util"; import { DefaultForm, ErrorLoading, InternationalizationAPI, Loading, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { BaseForm, FormMetadata, uiForms } from "../forms/declaration.js"; import { useCaseDetails } from "../hooks/useCaseDetails.js"; import { Pages } from "../pages.js"; import { AmlExchangeBackend } from "../utils/types.js"; import { Justification, parseJustification, } from "./AntiMoneyLaunderingForm.js"; import { ShowConsolidated } from "./ShowConsolidated.js"; export type AmlEvent = | AmlFormEvent | AmlFormEventError | KycCollectionEvent | KycExpirationEvent; type AmlFormEvent = { type: "aml-form"; when: AbsoluteTime; title: TranslatedString; justification: Justification; metadata: FormMetadata; state: AmlExchangeBackend.AmlState; threshold: AmountJson; }; type AmlFormEventError = { type: "aml-form-error"; when: AbsoluteTime; title: TranslatedString; justification: undefined; metadata: undefined; state: AmlExchangeBackend.AmlState; threshold: AmountJson; }; type KycCollectionEvent = { type: "kyc-collection"; when: AbsoluteTime; title: TranslatedString; values: object; provider: string; }; type KycExpirationEvent = { type: "kyc-expiration"; when: AbsoluteTime; title: TranslatedString; fields: string[]; }; type WithTime = { when: AbsoluteTime }; function selectSooner(a: WithTime, b: WithTime) { return AbsoluteTime.cmp(a.when, b.when); } function titleForJustification( op: ReturnType, i18n: InternationalizationAPI, ): TranslatedString { if (op.type === "ok") { return op.body.justification.label as TranslatedString; } switch (op.case) { case "not-json": return i18n.str`error: the justification is not a form`; case "id-not-found": return i18n.str`error: justification form's id not found`; case "version-not-found": return i18n.str`error: justification form's version not found`; case "form-not-found": return i18n.str`error: justification form not found`; default: { assertUnreachable(op.case); } } } export function getEventsFromAmlHistory( aml: AmlExchangeBackend.AmlDecisionDetail[], kyc: AmlExchangeBackend.KycDetail[], i18n: InternationalizationAPI, ): AmlEvent[] { const ae: AmlEvent[] = aml.map((a) => { const just = parseJustification(a.justification, uiForms.forms(i18n)); return { type: just.type === "ok" ? "aml-form" : "aml-form-error", state: a.new_state, threshold: Amounts.parseOrThrow(a.new_threshold), title: titleForJustification(just, i18n), metadata: just.type === "ok" ? just.body.metadata : undefined, justification: just.type === "ok" ? just.body.justification : undefined, when: { t_ms: a.decision_time.t_s === "never" ? "never" : a.decision_time.t_s * 1000, }, } as AmlEvent; }); const ke = kyc.reduce((prev, k) => { prev.push({ type: "kyc-collection", title: i18n.str`collection`, when: AbsoluteTime.fromProtocolTimestamp(k.collection_time), values: !k.attributes ? {} : k.attributes, provider: k.provider_section, }); prev.push({ type: "kyc-expiration", title: i18n.str`expiration`, when: AbsoluteTime.fromProtocolTimestamp(k.expiration_time), fields: !k.attributes ? [] : Object.keys(k.attributes), }); return prev; }, [] as AmlEvent[]); return ae.concat(ke).sort(selectSooner); } export function CaseDetails({ account }: { account: string }) { const [selected, setSelected] = useState(AbsoluteTime.now()); const [showForm, setShowForm] = useState<{ justification: Justification; metadata: FormMetadata; }>(); const { i18n } = useTranslationContext(); const details = useCaseDetails(account); if (!details) { return ; } if (details instanceof TalerError) { return ; } if (details.type === "fail") { switch (details.case) { case HttpStatusCode.Unauthorized: case HttpStatusCode.Forbidden: case HttpStatusCode.NotFound: case HttpStatusCode.Conflict: return
; default: assertUnreachable(details); } } const { aml_history, kyc_attributes } = details.body; const events = getEventsFromAmlHistory(aml_history, kyc_attributes, i18n); if (showForm !== undefined) { return (
); } return (
New AML form

Case history for account{" "} {account.substring(0, 16)}...

{ switch (e.type) { case "aml-form": { const { justification, metadata } = e; setShowForm({ justification, metadata }); break; } case "kyc-collection": case "kyc-expiration": { setSelected(e.when); break; } case "aml-form-error": } }} /> {/* {selected && } */} {selected && }
); } function AmlStateBadge({ state, }: { state: AmlExchangeBackend.AmlState; }): VNode { switch (state) { case AmlExchangeBackend.AmlState.normal: { return ( Normal ); } case AmlExchangeBackend.AmlState.pending: { return ( Pending ); } case AmlExchangeBackend.AmlState.frozen: { return ( Frozen ); } } assertUnreachable(state); } function ShowTimeline({ history, onSelect, }: { onSelect: (e: AmlEvent) => void; history: AmlEvent[]; }): VNode { return (
    {history.map((e, idx) => { const isLast = history.length - 1 === idx; return (
  • { onSelect(e); }} >
    {!isLast ? ( ) : undefined}
    {(() => { switch (e.type) { case "aml-form-error": case "aml-form": { return (
    {e.threshold.currency}{" "} {Amounts.stringifyValue(e.threshold)}
    ); } case "kyc-collection": { return ( // ); } case "kyc-expiration": { // return ; return ( ); } } assertUnreachable(e); })()}
    {e.type === "aml-form" ? ( {e.title} ) : (

    {e.title}

    )}
    {e.when.t_ms === "never" ? ( "never" ) : ( )}
  • ); })}
); }