diff options
author | Sebastian <sebasjm@gmail.com> | 2023-06-05 10:04:09 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2023-06-05 10:04:09 -0300 |
commit | c680f5aa71b08e978444df07f93c381f9d47ab82 (patch) | |
tree | 81903fac003bb1e202cf69551e06ba41a6e960a5 /packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx | |
parent | df53866e6b148ea5fd2ab57e906a4aa36b535ed3 (diff) |
rename aml
Diffstat (limited to 'packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx')
-rw-r--r-- | packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx | 447 |
1 files changed, 0 insertions, 447 deletions
diff --git a/packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx b/packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx deleted file mode 100644 index e5fb8eaba..000000000 --- a/packages/exchange-backoffice-ui/src/pages/CaseDetails.tsx +++ /dev/null @@ -1,447 +0,0 @@ -import { Fragment, VNode, h } from "preact"; -import { - AmlDecisionDetail, - AmlDecisionDetails, - AmlState, - KycDetail, -} from "../types.js"; -import { - AbsoluteTime, - AmountJson, - Amounts, - TranslatedString, -} from "@gnu-taler/taler-util"; -import { format } from "date-fns"; -import { ArrowDownCircleIcon, ClockIcon } from "@heroicons/react/20/solid"; -import { useState } from "preact/hooks"; -import { NiceForm } from "../NiceForm.js"; -import { FlexibleForm } from "../forms/index.js"; -import { UIFormField } from "../handlers/forms.js"; -import { Pages } from "../pages.js"; - -const response: AmlDecisionDetails = { - aml_history: [ - { - justification: "Lack of documentation", - decider_pub: "ASDASDASD", - decision_time: { - t_s: Date.now() / 1000, - }, - new_state: 2, - new_threshold: "USD:0", - }, - { - justification: "Doing a transfer of high amount", - decider_pub: "ASDASDASD", - decision_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 6, - }, - new_state: 1, - new_threshold: "USD:2000", - }, - { - justification: "Account is known to the system", - decider_pub: "ASDASDASD", - decision_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 9, - }, - new_state: 0, - new_threshold: "USD:100", - }, - ], - kyc_attributes: [ - { - collection_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 8, - }, - expiration_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 4, - }, - provider_section: "asdasd", - attributes: { - name: "Sebastian", - }, - }, - { - collection_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 5, - }, - expiration_time: { - t_s: Date.now() / 1000 - 60 * 60 * 24 * 30 * 2, - }, - provider_section: "asdasd", - attributes: { - creditCard: "12312312312", - }, - }, - ], -}; -type AmlEvent = AmlFormEvent | KycCollectionEvent | KycExpirationEvent; -type AmlFormEvent = { - type: "aml-form"; - when: AbsoluteTime; - title: TranslatedString; - state: 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 getEventsFromAmlHistory( - aml: AmlDecisionDetail[], - kyc: KycDetail[], -): AmlEvent[] { - const ae: AmlEvent[] = aml.map((a) => { - return { - type: "aml-form", - state: a.new_state, - threshold: Amounts.parseOrThrow(a.new_threshold), - title: a.justification as TranslatedString, - 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: "collection" as TranslatedString, - when: AbsoluteTime.fromProtocolTimestamp(k.collection_time), - values: !k.attributes ? {} : k.attributes, - provider: k.provider_section, - }); - prev.push({ - type: "kyc-expiration", - title: "expired" as TranslatedString, - 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 events = getEventsFromAmlHistory( - response.aml_history, - response.kyc_attributes, - ); - console.log("DETAILS", events, events[events.length - 1 - 2]); - const [selected, setSelected] = useState<AmlEvent>( - events[events.length - 1 - 2], - ); - return ( - <div> - <a - href={Pages.newFormEntry.url({ account })} - class="m-4 block 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" - > - New AML form - </a> - - <header class="flex items-center justify-between border-b border-white/5 px-4 py-4 sm:px-6 sm:py-6 lg:px-8"> - <h1 class="text-base font-semibold leading-7 text-black"> - Case history - </h1> - </header> - <div class="flow-root"> - <ul role="list"> - {events.map((e, idx) => { - const isLast = events.length - 1 === idx; - return ( - <li - class="hover:bg-gray-200 p-2 rounded cursor-pointer" - onClick={() => { - setSelected(e); - }} - > - <div class="relative pb-6"> - {!isLast ? ( - <span - class="absolute left-4 top-4 -ml-px h-full w-1 bg-gray-200" - aria-hidden="true" - ></span> - ) : undefined} - <div class="relative flex space-x-3"> - {(() => { - switch (e.type) { - case "aml-form": { - switch (e.state) { - case AmlState.normal: { - return ( - <div> - <span class="inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20"> - Normal - </span> - <span class="inline-flex items-center px-2 py-1 text-xs font-medium text-gray-700 "> - {e.threshold.currency}{" "} - {Amounts.stringifyValue(e.threshold)} - </span> - </div> - ); - } - case AmlState.pending: { - return ( - <div> - <span class="inline-flex items-center rounded-md bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-700 ring-1 ring-inset ring-green-600/20"> - Pending - </span> - <span class="inline-flex items-center px-2 py-1 text-xs font-medium text-gray-700 "> - {e.threshold.currency}{" "} - {Amounts.stringifyValue(e.threshold)} - </span> - </div> - ); - } - case AmlState.frozen: { - return ( - <div> - <span class="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-green-600/20"> - Frozen - </span> - <span class="inline-flex items-center px-2 py-1 text-xs font-medium text-gray-700 "> - {e.threshold.currency}{" "} - {Amounts.stringifyValue(e.threshold)} - </span> - </div> - ); - } - } - } - case "kyc-collection": { - return ( - <ArrowDownCircleIcon class="h-8 w-8 text-green-700" /> - ); - } - case "kyc-expiration": { - return <ClockIcon class="h-8 w-8 text-gray-700" />; - } - } - })()} - <div class="flex min-w-0 flex-1 justify-between space-x-4 pt-1.5"> - <div> - <p class="text-sm text-gray-900">{e.title}</p> - </div> - <div class="whitespace-nowrap text-right text-sm text-gray-500"> - {e.when.t_ms === "never" ? ( - "never" - ) : ( - <time dateTime={format(e.when.t_ms, "dd MMM yyyy")}> - {format(e.when.t_ms, "dd MMM yyyy")} - </time> - )} - </div> - </div> - </div> - </div> - </li> - ); - })} - </ul> - </div> - {selected && <ShowEventDetails event={selected} />} - {selected && <ShowConsolidated history={events} until={selected} />} - </div> - ); -} - -function ShowEventDetails({ event }: { event: AmlEvent }): VNode { - return <div>type {event.type}</div>; -} - -function ShowConsolidated({ - history, - until, -}: { - history: AmlEvent[]; - until: AmlEvent; -}): VNode { - console.log("UNTIL", until); - const cons = getConsolidated(history, until.when); - - const form: FlexibleForm<Consolidated> = { - versionId: "1", - behavior: (form) => { - return {}; - }, - design: [ - { - title: "AML" as TranslatedString, - fields: [ - { - type: "amount", - props: { - label: "Threshold" as TranslatedString, - name: "aml.threshold", - }, - }, - { - type: "choiceHorizontal", - props: { - label: "State" as TranslatedString, - name: "aml.state", - converter: amlStateConverter, - choices: [ - { - label: "Frozen" as TranslatedString, - value: AmlState.frozen, - }, - { - label: "Pending" as TranslatedString, - value: AmlState.pending, - }, - { - label: "Normal" as TranslatedString, - value: AmlState.normal, - }, - ], - }, - }, - ], - }, - Object.entries(cons.kyc).length > 0 - ? { - title: "KYC" as TranslatedString, - fields: Object.entries(cons.kyc).map(([key, field]) => { - const result: UIFormField = { - type: "text", - props: { - label: key as TranslatedString, - name: `kyc.${key}.value`, - help: `${field.provider} since ${ - field.since.t_ms === "never" - ? "never" - : format(field.since.t_ms, "dd/MM/yyyy") - }` as TranslatedString, - }, - }; - return result; - }), - } - : undefined, - ], - }; - return ( - <Fragment> - <h1 class="text-base font-semibold leading-7 text-black"> - Consolidated information after{" "} - {until.when.t_ms === "never" - ? "never" - : format(until.when.t_ms, "dd MMMM yyyy")} - </h1> - <NiceForm - key={`${String(Date.now())}`} - form={form} - initial={cons} - onUpdate={() => {}} - /> - </Fragment> - ); -} - -interface Consolidated { - aml: { - state?: AmlState; - threshold?: AmountJson; - since: AbsoluteTime; - }; - kyc: { - [field: string]: { - value: any; - provider: string; - since: AbsoluteTime; - }; - }; -} - -function getConsolidated( - history: AmlEvent[], - when: AbsoluteTime, -): Consolidated { - const initial: Consolidated = { - aml: { - since: AbsoluteTime.never(), - }, - kyc: {}, - }; - return history.reduce((prev, cur) => { - if (AbsoluteTime.cmp(when, cur.when) < 0) { - return prev; - } - switch (cur.type) { - case "kyc-expiration": { - cur.fields.forEach((field) => { - delete prev.kyc[field]; - }); - break; - } - case "aml-form": { - prev.aml.threshold = cur.threshold; - prev.aml.state = cur.state; - prev.aml.since = cur.when; - break; - } - case "kyc-collection": { - Object.keys(cur.values).forEach((field) => { - prev.kyc[field] = { - value: (cur.values as any)[field], - provider: cur.provider, - since: cur.when, - }; - }); - break; - } - } - return prev; - }, initial); -} - -export const amlStateConverter = { - toStringUI: stringifyAmlState, - fromStringUI: parseAmlState, -}; - -function stringifyAmlState(s: AmlState | undefined): string { - if (s === undefined) return ""; - switch (s) { - case AmlState.normal: - return "normal"; - case AmlState.pending: - return "pending"; - case AmlState.frozen: - return "frozen"; - } -} - -function parseAmlState(s: string | undefined): AmlState { - switch (s) { - case "normal": - return AmlState.normal; - case "pending": - return AmlState.pending; - case "frozen": - return AmlState.frozen; - default: - throw Error(`unknown AML state: ${s}`); - } -} |