Wallet
A balance card with top-up and withdraw dialogs and a grouped transaction list, all amounts through Intl.
Code
import { Wallet } from "@fadymondy/nasaq/web";export function MyWallet() { return ( <Wallet balance={1250.5} pending={100} currency="USD" sources={[{ id: "visa", label: "Visa ending 4242" }]} destinations={[{ id: "bank", label: "Al Rajhi Bank", description: "SA03 **** 1234" }]} transactions={[{ id: "t1", type: "topup", amount: 200, status: "completed", date: new Date(), description: "Top-up" }]} onTopUp={async ({ amount, sourceId }) => api.topUp(amount, sourceId)} onPayout={async ({ amount, destinationId }) => api.payout(amount, destinationId)} /> );}Billing · beta
Live examples and controls: Wallet in the lab.
Install
npx shadcn@latest add https://docs.nasaqui.com/r/wallet.jsonA prepaid balance in one piece: the balance with a 30-day trend, Add funds and Withdraw dialogs that validate the amount against limits, and a transaction list grouped by day with a money in / money out filter. It never moves money itself: your async callbacks do, and the dialogs show their busy and error states.
When to use
- Prepaid credit, marketplace balances, payouts to a bank.
When not to use
- Recurring subscription payment: use
CheckoutSteps. - Invoice history: use
InvoiceList.
Import
import { Wallet, type WalletTransaction } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"Quick start
import { Wallet } from "@fadymondy/nasaq/web";
export function MyWallet() {
return (
<Wallet
balance={1250.5}
pending={100}
currency="USD"
sources={[{ id: "visa", label: "Visa ending 4242" }]}
destinations={[{ id: "bank", label: "Al Rajhi Bank", description: "SA03 **** 1234" }]}
transactions={[{ id: "t1", type: "topup", amount: 200, status: "completed", date: new Date(), description: "Top-up" }]}
onTopUp={async ({ amount, sourceId }) => api.topUp(amount, sourceId)}
onPayout={async ({ amount, destinationId }) => api.payout(amount, destinationId)}
/>
);
}Anatomy
Wallet data-slot="wallet"
├─ WalletBalance balance (hide/show), pending, Sparkline trend, Add funds, Withdraw
├─ WalletTransactions filter ToggleGroup, day groups, rows with status
├─ TopUpDialog presets, amount field, source RadioCard list
└─ PayoutDialog amount, "Withdraw all", destination listAPI
Wallet
WalletProps extends Omit<ComponentProps<"div">, "children">.
| Prop | Type | Default | Description |
|---|---|---|---|
balance | number | required | Available balance. |
pending? | number | none | Money on its way in or out. |
currency | string | required | ISO 4217 code. |
trend? | readonly number[] | none | Balance for the last days, oldest first. |
transactions | readonly WalletTransaction[] | required | Activity. |
sources? | readonly WalletAccount[] | none | Top-up sources. |
destinations? | readonly WalletAccount[] | none | Withdrawal accounts. |
onTopUp? | (input: { amount: number; sourceId: string }) => Promise<Result> | none | Omitted: Add funds is hidden. Resolve { error } or reject to keep the dialog open. |
onPayout? | (input: { amount: number; destinationId: string }) => Promise<Result> | none | Omitted: Withdraw is hidden. |
topUpMin? | number | 0.01 | Smallest top-up. |
topUpPresets? | readonly number[] | [50, 100, 250, 500] | One-tap amounts. |
payoutMin? | number | 0.01 | Smallest withdrawal. The maximum is the balance. |
feeNote? | ReactNode | none | Fee note under the top-up amount. |
loading? | boolean | false | Skeletons. |
labels? | WalletLabels | built-in en/ar | String overrides. |
Result is void | { error?: string }.
WalletBalance
WalletBalanceProps extends Omit<ComponentProps<typeof Card>, "children">: balance, pending?, currency, trend?,
onTopUp?, onPayout? (buttons with no arguments), loading?, labels?.
WalletTransactions
WalletTransactionsProps extends Omit<ComponentProps<"section">, "children">: transactions, currency, loading?, labels?.
WalletTransaction: id, type ("topup" | "payout" | "payment" | "refund" | "fee"), signed amount, status
("completed" | "pending" | "failed"), date, description, reference?.
TopUpDialog / PayoutDialog
| Prop | Type | Description |
|---|---|---|
open, onOpenChange | boolean, (open: boolean) => void | Controlled state. |
currency | string | ISO 4217 code. |
sources / destinations | readonly WalletAccount[] | Accounts (id, label, description?). |
presets?, min?, max?, feeNote? | top-up only | One-tap amounts, limits and a fee note. |
available | number | Payout only: the maximum withdrawal. |
onTopUp / onPayout | async callbacks | As above. |
labels? | WalletLabels | String overrides. |
Examples
Balance only
<WalletBalance balance={80} currency="USD" onTopUp={() => setOpen(true)} />Failing top-up
<Wallet balance={0} currency="USD" transactions={[]} sources={sources} onTopUp={async () => ({ error: "Card declined." })} />Arabic
<NasaqProvider locale="ar">
<Wallet balance={1250.5} currency="SAR" transactions={[]} />
</NasaqProvider>Accessibility
| Key | Action |
|---|---|
| Tab | Move through buttons, filter, amount and accounts. |
| Arrow keys | Change the filter and pick an account. |
| Enter | Submit the open dialog. |
| Escape | Close the dialog (not while busy). |
- The balance can be hidden; the hidden state is announced by label.
- Amount errors use
FieldErrorand are announced. - Direction is shown with an icon and a sign, not colour alone.
RTL & i18n
Built-in English and Arabic. Amounts use Intl with currency, Latin digits by default, isolated left to right. Typed amounts
accept Arabic-Indic digits and the Arabic decimal mark. References stay left to right.
Styling & tokens
Uses card, border and text tokens plus the success and danger text tokens for direction. Target [data-slot="wallet"]; extend with
className. No raw hex.
Do / Don't
- Do validate again on your server; the dialog checks are for feedback.
- Do send the signed
amountin transactions (negative for money out). - Don't show the balance to people who should not see it.
Related
Lab
https://docs.nasaqui.com/?path=/docs/components-billing-wallet--docs
Rates and Subscriptions
Effective-dated bill and cost rates, per-project recurring subscriptions with month-end clamping, and an organisation billing overview.
AvailabilityEditor
Weekly hours per day with breaks, copy to other days, and vacations picked as date ranges, with overlap and range checks that block saving.