Dashral Pro is on the wayGet 30% off at launch

Dashral

Payment flowPro

Payments from checkout through the processor, a declined card going round a smart retry before it goes through, and each paid one landing on the invoice, receipt and ledger at its own pace; live counts on every card and the day's total climbing. Pauses, and stacks on phones.

When to use

  • Payments from checkout through the processor, a declined card going round a retry, money landing.
  • Payments and billing products; a live view of your own payments.

When not to use

  • Shops that simply take payments: Orders table shows what matters to them.
  • Reconciling money: an invoice or a ledger table.

Usage

Payment flow with your own data: every prop has the sample as its default, so pass only what you have.

example.tsx
import { useEffect, useRef } from "react"
import { PaymentFlow, type PaymentsHandle } from "@/components/dashral/blocks/payment-flow"

const payments = useRef<PaymentsHandle>(null)
useEffect(() => webhooks.on("payment", (p) => payments.current?.send({ label: p.customer, amount: p.amount, declined: p.declined })), [])

<PaymentFlow ref={payments} mode="live" outputs={payouts} />

API reference

PaymentFlow

The section: a heading, the flow in a dotted panel with a Live badge and Pause, and a row of figures. Every part comes from props, with a sample flow by default.

PropTypeDefault
eyebrowReact.ReactNode"Payments"
titleReact.ReactNodesample
descriptionReact.ReactNodesample
checkoutFlowCard

Where payments start, the card a declined one waits on, and what each paid one goes to.

sample
retryFlowCardsample
outputsFlowCard[]sample
processor{ label: string; mark: React.ReactNode; color: string }

The processor in the middle; under its name, the day's total, from today.

sample
todaynumber

Where the day's total starts; every payment that goes through adds its amount.

128_430
stats{ value: string; label: string }[]

The figures under the diagram; none hides the row.

sample
modePaymentsProps["mode"]

With your own charges instead of sample payments (see Payments' mode="live").

—
refReact.Ref<PaymentsHandle>—

Payments

Payments from checkout through a processor to what follows them. Each payment leaves checkout as a hollow dot and is authorized in the processor (its outline pulses); about one card in five is declined at first: the dot turns amber, goes down to the retry card, waits, and comes back to go through. A paid payment becomes a solid square that goes to the ledger and, most of the time, the invoice and the receipt, each copy at its own pace. Cards light up and keep live counts. Side by side when there's room, stacked on a narrow container. One requestAnimationFrame loop writes the SVG and the counts directly; React only hears about payments going through. Sample payments by default; with mode="live", only the ones you send (see PaymentsHandle). It stops off screen, in a hidden tab, when paused and (live) with nothing on its way; with reduced motion it's a still picture.

PropTypeDefault
checkoutFlowCard

Required.

—
retryFlowCard

Required.

—
outputsFlowCard[]

Required.

—
processor{ label: string; mark: React.ReactNode; color: string; description: React.ReactNode }

Required.

—
speednumber

How fast payments flow: 1 is the usual pace, 2 twice as fast, 0 holds them still.

1
pausedboolean

Brings the flow to rest where it is (easing down, not stopping in one frame).

false
onPaid(amount: number) => void

Called as each payment goes through, with its amount, e.g. to add up the day.

—
mode"demo" | "live"

"demo" plays sample payments on its own; "live" moves one only when you send it (see PaymentsHandle).

"demo"
refReact.Ref<PaymentsHandle>—
classNamestring—

type FlowCard

PropTypeDefault
idstring—
labelstring—
descriptionstring—
detailstring

More about it, in a tooltip on hover and keyboard focus (touch screens don't show it, so nothing essential).

—
iconLucideIcon—
metric{ unit: string; value?: number; base?: number }

A count on the card, then unit. value is your own number, shown as it is. Without it the card counts payments itself: base plus one for every payment it handles.

—

type PaymentsHandle

Moves payments through a live flow (mode="live").

PropTypeDefault
send(payment: { label: string; amount: number; declined?: boolean; to?: string[] }) => void

Sends one payment from checkout: label and amount show on the checkout card. With declined the processor declines it first and it goes through on the retry. It goes to the outputs in to (every one without it). onPaid hears its amount as it goes through. Off screen, in a hidden tab or with reduced motion it isn't drawn, but it's counted at once.

—