Transactions
A compact 'Transactions' tray panel — merchant avatar, type, signed amount and relative time.
Preview
@props([
'title' => null,
'transactions' => null,
'actionLabel' => null,
'actionHref' => null,
'limit' => null,
])
{{--
Transactions List — the compact "Transactions" dashboard panel: a tray
frame (swap icon, title, "View all" action) around a divided list of
merchant rows. This is the at-a-glance companion to the full
data-tables-transactions table, not a replacement for it.
Rows are composed directly (avatar + two-line text + two-line trailing
stack) rather than nesting <x-ui.list-panel> inside the tray's well:
list-panel wraps its rows in its own <x-ui.card>, and the tray well is
already a bg-card/border/shadow surface, so the two would stack into a
visible double frame (border-on-border, shadow-on-shadow). Composing the
<ul>/<li> here — the same shape tray's own default preview uses for this
exact panel — keeps a single frame, matching the reference layout.
'amount' is an unsigned number; 'direction' ('in'|'out') supplies the
sign, so every row formats identically ("-$220.36", "+$3,500.00") no
matter what the caller passes — money-value doesn't have a "force a
leading +" mode, so the sign is composed here instead. The sign always
stays in the text; colour is never the only signal.
--}}
@php
$title ??= __('Transactions');
$actionLabel ??= __('View all');
$actionHref ??= '#';
// Fictional merchants only — no real brand names or logo marks in sample
// data. Initials-fallback avatars are used here; the `logo` row key still
// accepts a consumer-supplied image URL (see the loop below).
$transactions ??= [
['name' => 'Northwind', 'type' => __('Subscription'), 'amount' => 220.36, 'direction' => 'out', 'time' => now()->subMinutes(2)->toIso8601String()],
['name' => 'Acme Cloud', 'type' => __('Subscription'), 'amount' => 110.00, 'direction' => 'out', 'time' => now()->subMinutes(10)->toIso8601String()],
['name' => 'Lumen Studio', 'type' => __('Subscription'), 'amount' => 49.00, 'direction' => 'out', 'time' => now()->subHours(2)->toIso8601String()],
['name' => 'Alex Dorsey', 'type' => __('Payment'), 'amount' => 220.36, 'direction' => 'out', 'time' => now()->subDay()->toIso8601String()],
['name' => 'Harbor Payroll', 'type' => __('Payment'), 'amount' => 3500.00, 'direction' => 'in', 'time' => now()->subMonths(2)->toIso8601String()],
];
$rows = array_values((array) $transactions);
if (is_numeric($limit)) {
$rows = array_slice($rows, 0, max(0, (int) $limit));
}
@endphp
<div data-slot="transactions-list" {{ $attributes->merge(['class' => 'min-w-0']) }}>
<x-ui.tray :title="$title" :action-label="$actionLabel" action-href="{{ $actionHref }}" padding="none">
<x-slot:icon>
{{-- Swap / arrows — the same icon tray's own "Transactions" preview uses. --}}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 3 4 4-4 4" /><path d="M21 7H9" /><path d="m7 21-4-4 4-4" /><path d="M3 17h12" /></svg>
</x-slot:icon>
@if (count($rows) > 0)
<ul role="list" class="divide-y divide-border">
@foreach ($rows as $tx)
@php
$name = (string) ($tx['name'] ?? '');
$type = (string) ($tx['type'] ?? '');
$direction = ($tx['direction'] ?? 'out') === 'in' ? 'in' : 'out';
$amount = is_numeric($tx['amount'] ?? null) ? (float) $tx['amount'] : 0.0;
$amountText = ($direction === 'in' ? '+' : '-').'$'.number_format($amount, 2);
$time = $tx['time'] ?? null;
$logo = $tx['logo'] ?? null;
$href = $tx['href'] ?? null;
$tag = $href ? 'a' : 'div';
// Rows under a day old read as a relative label ("2 min ago");
// older rows read as an absolute date — the same split the
// reference panel uses between recent and historical rows.
$timeMode = 'relative';
if ($time) {
try {
$timeMode = \Illuminate\Support\Carbon::parse($time)->diffInHours(now()) < 24 ? 'relative' : 'date';
} catch (\Throwable) {
$timeMode = 'relative';
}
}
@endphp
<li data-slot="transactions-list-item" class="border-b border-border last:border-b-0">
<{{ $tag }}
@if ($href) href="{{ $href }}" @endif
@class([
'flex items-center gap-4 px-4 py-2',
'transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-inset focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none' => $href,
])
>
@if ($logo)
<x-ui.avatar size="sm" :src="$logo" :alt="$name" class="shrink-0" />
@else
<x-ui.avatar size="sm" :alt="$name" :fallback="mb_strtoupper(mb_substr($name, 0, 1))" class="shrink-0" />
@endif
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium text-foreground">{{ $name }}</span>
@if ($type !== '')
<span class="block truncate text-xs text-muted-foreground">{{ $type }}</span>
@endif
</span>
<span class="ms-auto flex shrink-0 flex-col items-end gap-0.5">
<span class="whitespace-nowrap text-sm font-medium tabular-nums text-foreground">{{ $amountText }}</span>
@if ($time)
<x-ui.relative-time :datetime="$time" :mode="$timeMode" class="whitespace-nowrap text-2xs text-muted-foreground" />
@endif
</span>
</{{ $tag }}>
</li>
@endforeach
</ul>
@else
<div class="p-6">
<x-ui.empty variant="plain" size="sm">
<x-ui.empty.media variant="icon" size="sm">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 3 4 4-4 4" /><path d="M21 7H9" /><path d="m7 21-4-4 4-4" /><path d="M3 17h12" /></svg>
</x-ui.empty.media>
<x-ui.empty.title as="p">{{ __('No transactions yet') }}</x-ui.empty.title>
<x-ui.empty.description>{{ __('Transactions will show up here once they happen.') }}</x-ui.empty.description>
</x-ui.empty>
</div>
@endif
</x-ui.tray>
</div>
Installation
php artisan ui:add blocks/transactions-list
Registry contract
Install confidence
php artisan ui:add blocks/transactions-list
writes only the generated targets below. The CLI validates each file hash before writing and
prompts before replacing local changes unless --force is used.
- Version
- 1.0.1
- License
- open
- Stability
- stable
- Contract
- v1
- Foundation
- ≥ 1.0.0
| Type | Generated target |
|---|---|
| blade | resources/views/blocks/transactions-list.blade.php |
Registry dependencies
Package dependencies
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.
# Brok UI block: Transactions List (`transactions-list`)
A compact 'Transactions' dashboard panel: a tray frame with a swap icon and 'View all' action around a divided list of merchant rows — avatar, name, type, signed amount and relative time. The at-a-glance companion to the full data-tables-transactions table.
Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.
## Install
```bash
php artisan ui:add blocks/transactions-list
```
## Render it
```blade
<x-blocks.transactions-list />
```
## Props
- `title` (string|null, default `Transactions`) — Tray header title.
- `transactions` (array|null, default `null`) — Array of {name, type, amount, direction, time, logo?, href?} rows; defaults to a 5-row sample. amount is an unsigned number; direction ('in'|'out') supplies the sign so every row formats consistently. time is any Carbon-parseable instant; rows under 24h old render as a relative label, older rows render as an absolute date. logo is an optional avatar image URL (falls back to initials). href turns the row into a link.
- `action-label` (string|null, default `View all`) — Tray header end-action link text.
- `action-href` (string|null, default `#`) — Href for the actionLabel link.
- `limit` (int|null, default `null`) — Caps the rendered rows to the first N transactions.
## Use when
- Showing a short, glanceable 'Transactions' panel beside other dashboard tiles — merchant, type, signed amount, and a relative time.
- Pairing with a chart or spending-breakdown tray on an overview dashboard, where the full data-tables-transactions table would be too heavy.
## Avoid when
- The list needs sorting, filtering, pagination, or a status column; use data-tables-transactions instead.
## Rules
- Render the installed block with `<x-blocks.transactions-list />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.
## Links
- Docs: https://brokui.dev/blocks/transactions-list
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/transactions-list.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Guidance
Use when
- Showing a short, glanceable 'Transactions' panel beside other dashboard tiles — merchant, type, signed amount, and a relative time.
- Pairing with a chart or spending-breakdown tray on an overview dashboard, where the full data-tables-transactions table would be too heavy.
Avoid when
- The list needs sorting, filtering, pagination, or a status column; use data-tables-transactions instead.
Anti-patterns
- Do not replace semantic props with conflicting utility classes.
- Do not remove labels, focus styles, or state attributes.
- Anatomy
Usage
Render the block as a component, passing data where useful:
<x-blocks.transactions-list />
Built from primitives
This block composes installed <brok:*> primitives and semantic design
tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by
editing CSS variables; flip the preview to RTL to confirm it mirrors.
Source
The exact, editable file ui:add writes
into your app. The preview above renders this same source — there are no preview-only blocks.
@props([
'title' => null,
'transactions' => null,
'actionLabel' => null,
'actionHref' => null,
'limit' => null,
])
{{--
Transactions List — the compact "Transactions" dashboard panel: a tray
frame (swap icon, title, "View all" action) around a divided list of
merchant rows. This is the at-a-glance companion to the full
data-tables-transactions table, not a replacement for it.
Rows are composed directly (avatar + two-line text + two-line trailing
stack) rather than nesting <x-ui.list-panel> inside the tray's well:
list-panel wraps its rows in its own <x-ui.card>, and the tray well is
already a bg-card/border/shadow surface, so the two would stack into a
visible double frame (border-on-border, shadow-on-shadow). Composing the
<ul>/<li> here — the same shape tray's own default preview uses for this
exact panel — keeps a single frame, matching the reference layout.
'amount' is an unsigned number; 'direction' ('in'|'out') supplies the
sign, so every row formats identically ("-$220.36", "+$3,500.00") no
matter what the caller passes — money-value doesn't have a "force a
leading +" mode, so the sign is composed here instead. The sign always
stays in the text; colour is never the only signal.
--}}
@php
$title ??= __('Transactions');
$actionLabel ??= __('View all');
$actionHref ??= '#';
// Fictional merchants only — no real brand names or logo marks in sample
// data. Initials-fallback avatars are used here; the `logo` row key still
// accepts a consumer-supplied image URL (see the loop below).
$transactions ??= [
['name' => 'Northwind', 'type' => __('Subscription'), 'amount' => 220.36, 'direction' => 'out', 'time' => now()->subMinutes(2)->toIso8601String()],
['name' => 'Acme Cloud', 'type' => __('Subscription'), 'amount' => 110.00, 'direction' => 'out', 'time' => now()->subMinutes(10)->toIso8601String()],
['name' => 'Lumen Studio', 'type' => __('Subscription'), 'amount' => 49.00, 'direction' => 'out', 'time' => now()->subHours(2)->toIso8601String()],
['name' => 'Alex Dorsey', 'type' => __('Payment'), 'amount' => 220.36, 'direction' => 'out', 'time' => now()->subDay()->toIso8601String()],
['name' => 'Harbor Payroll', 'type' => __('Payment'), 'amount' => 3500.00, 'direction' => 'in', 'time' => now()->subMonths(2)->toIso8601String()],
];
$rows = array_values((array) $transactions);
if (is_numeric($limit)) {
$rows = array_slice($rows, 0, max(0, (int) $limit));
}
@endphp
<div data-slot="transactions-list" {{ $attributes->merge(['class' => 'min-w-0']) }}>
<x-ui.tray :title="$title" :action-label="$actionLabel" action-href="{{ $actionHref }}" padding="none">
<x-slot:icon>
{{-- Swap / arrows — the same icon tray's own "Transactions" preview uses. --}}
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 3 4 4-4 4" /><path d="M21 7H9" /><path d="m7 21-4-4 4-4" /><path d="M3 17h12" /></svg>
</x-slot:icon>
@if (count($rows) > 0)
<ul role="list" class="divide-y divide-border">
@foreach ($rows as $tx)
@php
$name = (string) ($tx['name'] ?? '');
$type = (string) ($tx['type'] ?? '');
$direction = ($tx['direction'] ?? 'out') === 'in' ? 'in' : 'out';
$amount = is_numeric($tx['amount'] ?? null) ? (float) $tx['amount'] : 0.0;
$amountText = ($direction === 'in' ? '+' : '-').'$'.number_format($amount, 2);
$time = $tx['time'] ?? null;
$logo = $tx['logo'] ?? null;
$href = $tx['href'] ?? null;
$tag = $href ? 'a' : 'div';
// Rows under a day old read as a relative label ("2 min ago");
// older rows read as an absolute date — the same split the
// reference panel uses between recent and historical rows.
$timeMode = 'relative';
if ($time) {
try {
$timeMode = \Illuminate\Support\Carbon::parse($time)->diffInHours(now()) < 24 ? 'relative' : 'date';
} catch (\Throwable) {
$timeMode = 'relative';
}
}
@endphp
<li data-slot="transactions-list-item" class="border-b border-border last:border-b-0">
<{{ $tag }}
@if ($href) href="{{ $href }}" @endif
@class([
'flex items-center gap-4 px-4 py-2',
'transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-inset focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none' => $href,
])
>
@if ($logo)
<x-ui.avatar size="sm" :src="$logo" :alt="$name" class="shrink-0" />
@else
<x-ui.avatar size="sm" :alt="$name" :fallback="mb_strtoupper(mb_substr($name, 0, 1))" class="shrink-0" />
@endif
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium text-foreground">{{ $name }}</span>
@if ($type !== '')
<span class="block truncate text-xs text-muted-foreground">{{ $type }}</span>
@endif
</span>
<span class="ms-auto flex shrink-0 flex-col items-end gap-0.5">
<span class="whitespace-nowrap text-sm font-medium tabular-nums text-foreground">{{ $amountText }}</span>
@if ($time)
<x-ui.relative-time :datetime="$time" :mode="$timeMode" class="whitespace-nowrap text-2xs text-muted-foreground" />
@endif
</span>
</{{ $tag }}>
</li>
@endforeach
</ul>
@else
<div class="p-6">
<x-ui.empty variant="plain" size="sm">
<x-ui.empty.media variant="icon" size="sm">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m17 3 4 4-4 4" /><path d="M21 7H9" /><path d="m7 21-4-4 4-4" /><path d="M3 17h12" /></svg>
</x-ui.empty.media>
<x-ui.empty.title as="p">{{ __('No transactions yet') }}</x-ui.empty.title>
<x-ui.empty.description>{{ __('Transactions will show up here once they happen.') }}</x-ui.empty.description>
</x-ui.empty>
</div>
@endif
</x-ui.tray>
</div>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.0.1 - Status
- Stable
- License
-
open - Deprecation
- Not deprecated
- Contract
-
v1 - Foundation
-
≥ 1.0.0