taler-typescript-core

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

commit 43330f13b92dcf8778c50b43560c2b79caa06d71
parent 206e532eb6b0de40c8c517f5978eb247217e5e46
Author: Florian Dold <dold@taler.net>
Date:   Tue, 25 Aug 2026 23:57:02 +0200

bank web UI: show debt limits on transfer forms

Diffstat:
Mpackages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.stories.tsx | 10++++++++++
Mpackages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx | 28++++++++++++++++++++++++----
Mpackages/libeufin-bank-webui/src/pages/WalletWithdrawForm.tsx | 31+++++++++++++++++++++++++++----
Mpackages/libeufin-bank-webui/src/pages/WalletWithdrawal.stories.tsx | 9+++++++++
Mpackages/libeufin-bank-webui/src/pages/WalletWithdrawal.tsx | 6+++---
Mpackages/libeufin-bank-webui/src/pages/WireTransfer.tsx | 1+
Mpackages/taler-harness/src/integrationtests/test-libeufin-bank-webui.ts | 1+
7 files changed, 75 insertions(+), 11 deletions(-)

diff --git a/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.stories.tsx b/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.stories.tsx @@ -27,6 +27,11 @@ export default { }; export const USD = tests.createExample(PaytoWireTransferForm, { + debtLimit: { + currency: "ASR", + fraction: 0, + value: 0, + }, limit: { currency: "ASR", fraction: 0, @@ -37,6 +42,11 @@ export const USD = tests.createExample(PaytoWireTransferForm, { }); export const WithTransferDetails = tests.createExample(PaytoWireTransferForm, { + debtLimit: { + currency: "ASR", + fraction: 0, + value: 50, + }, limit: { currency: "ASR", fraction: 0, diff --git a/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx b/packages/libeufin-bank-webui/src/pages/PaytoWireTransferForm.tsx @@ -56,6 +56,7 @@ export interface Props { routeCancel?: RouteDefinition; routeCashout?: RouteDefinition; limit: IntAmountJson; + debtLimit: AmountJson; } export function PaytoWireTransferForm({ @@ -67,6 +68,7 @@ export function PaytoWireTransferForm({ routeCancel, routeCashout, limit, + debtLimit, }: Props): VNode { const [inputType, setInputType] = useState<"form" | "payto">("form"); const isRawPayto = inputType === "payto"; @@ -339,11 +341,29 @@ export function PaytoWireTransferForm({ spec={config.currency_specification} /> </div> + <div class="mt-3 flex items-baseline justify-between gap-4 border-t border-gray-200 pt-3 text-xs text-gray-500"> + <span class="font-medium"> + <i18n.Translate>Debt limit</i18n.Translate> + </span> + <span class="font-semibold text-secondary"> + <RenderAmount + value={debtLimit} + spec={config.currency_specification} + /> + </span> + </div> <p class="mt-1 max-w-xs text-xs text-gray-500"> - <i18n.Translate> - This is not your account balance. It includes any amount you - may borrow up to your debt limit. - </i18n.Translate> + {Amounts.isZero(debtLimit) ? ( + <i18n.Translate> + This is not your account balance. This account cannot go into + debt. + </i18n.Translate> + ) : ( + <i18n.Translate> + This is not your account balance. It includes borrowing up to + the debt limit shown here. + </i18n.Translate> + )} </p> </div> </div> diff --git a/packages/libeufin-bank-webui/src/pages/WalletWithdrawForm.tsx b/packages/libeufin-bank-webui/src/pages/WalletWithdrawForm.tsx @@ -69,9 +69,11 @@ function parseConfiguredAmount( export function WithdrawalAmountForm({ onOperationCreated, limit, + debtLimit, focus, }: { limit: IntAmountJson; + debtLimit: AmountJson; focus?: boolean; onOperationCreated: (wopid: string) => void; }): VNode { @@ -259,11 +261,29 @@ export function WithdrawalAmountForm({ spec={config.currency_specification} /> </div> + <div class="mt-3 flex items-baseline justify-between gap-4 border-t border-secondary/20 pt-3 text-sm text-secondary"> + <span class="font-medium"> + <i18n.Translate>Debt limit</i18n.Translate> + </span> + <span class="font-semibold"> + <RenderAmount + value={debtLimit} + spec={config.currency_specification} + /> + </span> + </div> <p class="mt-1 text-xs text-secondary"> - <i18n.Translate> - This is not your account balance. It includes any amount you - may borrow up to your debt limit. - </i18n.Translate> + {Amounts.isZero(debtLimit) ? ( + <i18n.Translate> + This is not your account balance. This account cannot go + into debt. + </i18n.Translate> + ) : ( + <i18n.Translate> + This is not your account balance. It includes borrowing up + to the debt limit shown here. + </i18n.Translate> + )} </p> </div> @@ -434,11 +454,13 @@ export function WithdrawalAmountForm({ export function WalletWithdrawForm({ focus, limit, + debtLimit, routeCancel, onOperationCreated, onOperationAborted, }: { limit: IntAmountJson; + debtLimit: AmountJson; focus?: boolean; onOperationCreated: (wopid: string) => void; onOperationAborted: () => void; @@ -472,6 +494,7 @@ export function WalletWithdrawForm({ <WithdrawalAmountForm focus={focus} limit={limit} + debtLimit={debtLimit} onOperationCreated={onOperationCreated} /> ); diff --git a/packages/libeufin-bank-webui/src/pages/WalletWithdrawal.stories.tsx b/packages/libeufin-bank-webui/src/pages/WalletWithdrawal.stories.tsx @@ -15,6 +15,11 @@ export default { }; const defaultProps = { + debtLimit: { + currency: "ASR", + value: 20, + fraction: 0, + }, limit: { currency: "ASR", value: 95, @@ -51,6 +56,10 @@ export const FeeConsumesAvailableBalance = tests.createExample( WithdrawalAmountForm, { ...defaultProps, + debtLimit: { + ...defaultProps.debtLimit, + value: 0, + }, limit: { ...defaultProps.limit, value: 0, diff --git a/packages/libeufin-bank-webui/src/pages/WalletWithdrawal.tsx b/packages/libeufin-bank-webui/src/pages/WalletWithdrawal.tsx @@ -68,9 +68,8 @@ export function WalletWithdrawal({ const amount = Amounts.parseOrThrow(result.body.balance.amount); const isDebit = result.body.balance.credit_debit_indicator === "debit"; const signed = IntAmounts.toIntAmount(amount, isDebit); - const limit = signed.increment( - Amounts.parseOrThrow(result.body.debit_threshold), - ).result; + const debtLimit = Amounts.parseOrThrow(result.body.debit_threshold); + const limit = signed.increment(debtLimit).result; return ( <section class="mt-2"> @@ -85,6 +84,7 @@ export function WalletWithdrawal({ </a> <WalletWithdrawForm limit={limit} + debtLimit={debtLimit} onOperationCreated={onOperationCreated} onOperationAborted={() => undefined} routeCancel={routeCancel} diff --git a/packages/libeufin-bank-webui/src/pages/WireTransfer.tsx b/packages/libeufin-bank-webui/src/pages/WireTransfer.tsx @@ -113,6 +113,7 @@ export function WireTransfer({ withAmount={withAmount} withSubject={withSubject} limit={limit} + debtLimit={debitThreshold} onSuccess={onSuccess} routeCancel={routeCancel} /> diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-bank-webui.ts b/packages/taler-harness/src/integrationtests/test-libeufin-bank-webui.ts @@ -1152,6 +1152,7 @@ export async function runLibeufinBankWebuiMoneyFlowsTest(t: GlobalTestState) { const withdrawalAmount = page.locator('input[name="withdraw-amount"]'); await withdrawalAmount.fill("10"); await page.getByText("Maximum withdrawal", { exact: true }).waitFor(); + await page.getByText("Debt limit", { exact: true }).waitFor(); const amountFeeRow = page .getByText("Bank fee", { exact: true }) .locator("..");