taler-typescript-core

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

commit 543ce88c6a2dcfba439b4b3fd09cdf6834b58db3
parent e8cc8a123606a7127af446d147e4f429e131b3f5
Author: Sebastian <sebasjm@gmail.com>
Date:   Mon,  7 Apr 2025 13:09:54 -0300

fix #9533

Diffstat:
Mpackages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx | 388+++++++++++++++++++++++++++++++++----------------------------------------------
Mpackages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx | 48++++++++++++++++++++++++------------------------
2 files changed, 188 insertions(+), 248 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx @@ -24,14 +24,12 @@ import { PaytoType, PaytoUri, PaytoUriBitcoin, - PaytoUriIBAN, - PaytoUriTalerBank, - PaytoUriUnknown, - TalerMerchantApi, + TalerMerchantApi } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, h, VNode } from "preact"; import { StateUpdater, useState } from "preact/hooks"; +import { paytoDisplayAccountName } from "../update/UpdatePage.js"; type Entity = TalerMerchantApi.BankAccountEntry; @@ -108,6 +106,7 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { PaytoType | "unknown", { parsed: PaytoUri; acc: Entity }[] > = { bitcoin: [], "x-taler-bank": [], iban: [], taler: [], unknown: [] }; + const accountsByType = accounts.reduce((prev, acc) => { const parsed = parsePaytoUri(acc.payto_uri); if (!parsed) return prev; //skip @@ -131,226 +130,167 @@ function Table({ accounts, onDelete, onSelect }: TableProps): VNode { return ( <Fragment> - {bitcoinAccounts.length > 0 && ( - <div class="table-container"> - <p class="card-header-title"> - <i18n.Translate>Wire method: Bitcoin</i18n.Translate> - </p> - <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> - <thead> - <tr> - <th> - <i18n.Translate>Address</i18n.Translate> - </th> - <th> - <i18n.Translate>SegWit 1</i18n.Translate> - </th> - <th> - <i18n.Translate>SegWit 2</i18n.Translate> - </th> - <th /> - </tr> - </thead> - <tbody> - {bitcoinAccounts.map(({ parsed, acc }, idx) => { - const ac = parsed as PaytoUriBitcoin; - return ( - <tr key={idx}> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.targetPath} - </td> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.segwitAddrs[0]} - </td> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.segwitAddrs[1]} - </td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} - > - <i18n.Translate>Delete</i18n.Translate> - </button> - </div> - </td> - </tr> - ); - })} - </tbody> - </table> - </div> - )} - - {talerbankAccounts.length > 0 && ( - <div class="table-container"> - <p class="card-header-title"> - <i18n.Translate>Wire method: x-taler-bank</i18n.Translate> - </p> - <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> - <thead> - <tr> - <th> - <i18n.Translate>Host</i18n.Translate> - </th> - <th> - <i18n.Translate>Account name</i18n.Translate> - </th> - <th /> - </tr> - </thead> - <tbody> - {talerbankAccounts.map(({ parsed, acc }, idx) => { - const ac = parsed as PaytoUriTalerBank; - return ( - <tr key={idx}> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.host} - </td> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.account} - </td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} - > - <i18n.Translate>Delete</i18n.Translate> - </button> - </div> - </td> - </tr> - ); - })} - </tbody> - </table> - </div> - )} - - {ibanAccounts.length > 0 && ( - <div class="table-container"> - <p class="card-header-title"> - <i18n.Translate>Wire method: IBAN</i18n.Translate> - </p> - <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> - <thead> - <tr> - <th> - <i18n.Translate>Account name</i18n.Translate> - </th> - <th> - <i18n.Translate>IBAN</i18n.Translate> - </th> - <th /> - </tr> - </thead> - <tbody> - {ibanAccounts.map(({ parsed, acc }, idx) => { - const ac = parsed as PaytoUriIBAN; - return ( - <tr key={idx}> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.params["receiver-name"]} - </td> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.iban} - </td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} - > - <i18n.Translate>Delete</i18n.Translate> - </button> - </div> - </td> - </tr> - ); - })} - </tbody> - </table> - </div> - )} - - {unknownAccounts.length > 0 && ( - <div class="table-container"> - <p class="card-header-title"> - <i18n.Translate>Other accounts</i18n.Translate> - </p> - <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> - <thead> - <tr> - <th> - <i18n.Translate>Type</i18n.Translate> - </th> - <th> - <i18n.Translate>Path</i18n.Translate> - </th> - <th /> - </tr> - </thead> - <tbody> - {unknownAccounts.map(({ parsed, acc }, idx) => { - const ac = parsed as PaytoUriUnknown; - return ( - <tr key={idx}> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.targetType} - </td> - <td - onClick={(): void => onSelect(acc)} - style={{ cursor: "pointer" }} - > - {ac.targetPath} - </td> - <td class="is-actions-cell right-sticky"> - <div class="buttons is-right"> - <button - class="button is-danger is-small has-tooltip-left" - data-tooltip={i18n.str`Delete selected accounts from the database`} - onClick={() => onDelete(acc)} - > - <i18n.Translate>Delete</i18n.Translate> - </button> - </div> - </td> - </tr> - ); - })} - </tbody> - </table> - </div> - )} + <div class="table-container"> + <table class="table is-fullwidth is-striped is-hoverable is-fullwidth"> + <thead> + <tr> + <th> + <i18n.Translate>Wire method</i18n.Translate> + </th> + <th> + <i18n.Translate>Account</i18n.Translate> + </th> + <th> + <i18n.Translate>Owner's name</i18n.Translate> + </th> + <th /> + </tr> + </thead> + <tbody> + {bitcoinAccounts.map(({ parsed, acc }, idx) => { + return ( + <tr key={idx}> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + Bitcoin + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {paytoDisplayAccountName(acc.payto_uri)} + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {parsed.params["receiver-name"]} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n.str`Delete selected accounts from the database`} + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </div> + </td> + </tr> + ); + })} + {talerbankAccounts.map(({ parsed, acc }, idx) => { + return ( + <tr key={idx}> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + x-taler-bank + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {paytoDisplayAccountName(acc.payto_uri)} + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {parsed.params["receiver-name"]} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n.str`Delete selected accounts from the database`} + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </div> + </td> + </tr> + ); + })} + {ibanAccounts.map(({ parsed, acc }, idx) => { + return ( + <tr key={idx}> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + IBAN + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {paytoDisplayAccountName(acc.payto_uri)} + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {parsed.params["receiver-name"]} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n.str`Delete selected accounts from the database`} + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </div> + </td> + </tr> + ); + })} + {unknownAccounts.map(({ parsed, acc }, idx) => { + return ( + <tr key={idx}> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {parsed.targetType} + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {paytoDisplayAccountName(acc.payto_uri)} + </td> + <td + onClick={(): void => onSelect(acc)} + style={{ cursor: "pointer" }} + > + {parsed.params["receiver-name"]} + </td> + <td class="is-actions-cell right-sticky"> + <div class="buttons is-right"> + <button + class="button is-danger is-small has-tooltip-left" + data-tooltip={i18n.str`Delete selected accounts from the database`} + onClick={() => onDelete(acc)} + > + <i18n.Translate>Delete</i18n.Translate> + </button> + </div> + </td> + </tr> + ); + })} + </tbody> + </table> + </div> + </Fragment> ); } diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx @@ -259,29 +259,6 @@ export function UpdatePage({ } } - function stringifyPayto(account: PaytoString): string { - const p = parsePaytoUri(account); - if (!p) return ""; - if (!p.isKnown) return account; - switch (p.targetType) { - case "iban": { - return p.iban; - } - case "taler": { - return p.reservePub; - } - case "x-taler-bank": { - return p.account; - } - case "bitcoin": { - return p.address; - } - default: { - assertUnreachable(p); - } - } - } - return ( <Fragment> <section class="section"> @@ -292,7 +269,7 @@ export function UpdatePage({ <div class="level-item"> <span class="is-size-4"> <i18n.Translate>Account:</i18n.Translate>{" "} - <b>{stringifyPayto(account.payto_uri)}</b> + <b>{paytoDisplayAccountName(account.payto_uri)}</b> </span> </div> </div> @@ -478,3 +455,26 @@ export function safeConvertURL(s?: string): URL | undefined { return undefined; } } + +export function paytoDisplayAccountName(account: PaytoString): string { + const p = parsePaytoUri(account); + if (!p) return ""; + if (!p.isKnown) return account; + switch (p.targetType) { + case "iban": { + return p.iban; + } + case "taler": { + return p.reservePub; + } + case "x-taler-bank": { + return `${p.account}@${p.host}`; + } + case "bitcoin": { + return p.address; + } + default: { + assertUnreachable(p); + } + } +}