taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit e5a22cf09987829fed2b84309417f94c7f0d2169
parent 0ee19f2c03e0366cb73077e325f97f5b823fa2ae
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Thu, 15 Jan 2026 15:52:13 -0300

fix #10673

Diffstat:
Apackages/aml-backoffice-ui/src/assets/pdf-icon.png | 0
Mpackages/aml-backoffice-ui/src/pages/AccountDetails.tsx | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/packages/aml-backoffice-ui/src/assets/pdf-icon.png b/packages/aml-backoffice-ui/src/assets/pdf-icon.png Binary files differ. diff --git a/packages/aml-backoffice-ui/src/pages/AccountDetails.tsx b/packages/aml-backoffice-ui/src/pages/AccountDetails.tsx @@ -24,10 +24,14 @@ import { } from "@gnu-taler/taler-util"; import { Attention, + ButtonBetter, CopyButton, ErrorLoading, Loading, + LocalNotificationBanner, RouteDefinition, + useExchangeApiContext, + useLocalNotificationBetter, useTranslationContext, } from "@gnu-taler/web-util/browser"; import { format } from "date-fns"; @@ -42,6 +46,11 @@ import { useAccountDecisions } from "../hooks/decisions.js"; import { useCurrentLegitimizations } from "../hooks/legitimizations.js"; import { useServerMeasures } from "../hooks/server-info.js"; import { BANK_RULES, WALLET_RULES } from "./decision/Rules.js"; +import { useState } from "preact/hooks"; +import pdfIcon from "../assets/pdf-icon.png"; +import { useOfficer } from "../hooks/officer.js"; + +const utfDecoder = new TextDecoder("Latin1"); export function ShowProperties(props: { properties?: AccountProperties; @@ -82,6 +91,11 @@ export function AccountDetails({ const details = useAccountInformation(account); const history = useAccountDecisions(account); const legistimizations = useCurrentLegitimizations(account); + const officer = useOfficer(); + const session = officer.state === "ready" ? officer.account : undefined; + const { lib } = useExchangeApiContext(); + const [exported, setExported] = useState<{ content: string; file: string }>(); + const [notification, safeFunctionHandler] = useLocalNotificationBetter(); const measures = useServerMeasures(); @@ -176,13 +190,28 @@ export function AccountDetails({ const filteredRulesByType = !activeDecision ? defaultRules : defaultRules.filter((r) => { - return activeDecision.is_wallet - ? WALLET_RULES.includes(r.operation_type) - : BANK_RULES.includes(r.operation_type); - }); + return activeDecision.is_wallet + ? WALLET_RULES.includes(r.operation_type) + : BANK_RULES.includes(r.operation_type); + }); + + const time = format(new Date(), "yyyyMMdd_HHmmss"); + + const downloadPdf = safeFunctionHandler(lib.exchange.getAmlAttributesForAccountAsPdf.bind(lib.exchange), session ? [session, account] : undefined); + downloadPdf.onSuccess = (result) => { + setExported({ + content: new Uint8Array(result).reduce( + (data, byte) => data + String.fromCharCode(byte), + "", + ), + file: `account_${time}_${account}.pdf`, + }); + }; + return ( <div class="min-w-60"> + <LocalNotificationBanner notification={notification} /> <header class="flex flex-col justify-between border-b border-white/5 px-4 py-4 sm:px-6 sm:py-6 lg:px-8 gap-2"> <h1 class="text-base font-semibold leading-7 text-black"> <i18n.Translate>Case history for selected account</i18n.Translate> @@ -231,11 +260,41 @@ export function AccountDetails({ {collectionEvents.length === 0 ? ( <Attention title={i18n.str`The event list is empty`} type="warning" /> ) : ( - <ShowTimeline - account={account} - history={collectionEvents} - routeToShowCollectedInfo={routeToShowCollectedInfo} - /> + <Fragment> + <div class="flex space-x-2 mb-4"> + <i18n.Translate>Export as PDF</i18n.Translate> + <ButtonBetter onClick={downloadPdf}> + <img class="size-6 w-6" src={pdfIcon} /> + </ButtonBetter> + </div> + {!exported ? ( + <div /> + ) : ( + <a + href={ + "data:application/pdf;base64," + + window.btoa(exported.content) + } + name="save file" + download={exported.file} + > + <Attention + title={i18n.str`Export completed`} + onClose={() => setExported(undefined)} + > + <i18n.Translate> + Click here to save the file in your computer. + </i18n.Translate> + </Attention> + </a> + )} + + <ShowTimeline + account={account} + history={collectionEvents} + routeToShowCollectedInfo={routeToShowCollectedInfo} + /> + </Fragment> )} <h1 class="mb-4 text-base font-semibold leading-6 text-black">