- Home
- Skills
- Frontend & Web Apps
- Dashboard & Data Table Design That Survives Real Data
Dashboard & Data Table Design That Survives Real Data
Dashboards and tables that hold up at 4,000 messy rows: density, columns, filters, bulk actions, and every state.
$9
Works with the AI Agents you already use
Dashboard & Data Table Design That Survives Real Data
Example session with this skill installed
Design the refunds queue for our payments admin tool. Support agents live in this all day — they work through pending refund requests and approve or reject them.
Data shape: ~2,000 pending at any time, 40k total. Columns available: refund id, customer email (some are 50+ chars), order id, amount, currency, reason (free text, often empty), status (pending / approved / rejected / failed), requested date, assigned agent (nullable — about 60% unassigned).
Agents need to filter by status and assignee, search by email or order id, and approve/reject in bulk.
- Read your context and instructions
- Compiled the dashboard & data
Refunds Queue
Answers the question: what do I work on next, and can I clear a batch of it at once?
Users: support agents, all day, every day. Power users — density and keyboard matter more than polish.
Data reality: 2k pending / 40k total · email up to 50+ chars · reason often empty · assigned_agent null ~60% · status cardinality 4
Next action: approve or reject, individually or in bulk.
This is an operational worklist, not an analytics dashboard. That decides almost everything below: density over beauty, the queue is the page, and metrics at the top are navigation rather than decoration.
Layout
┌──────────────────────────────────────────────────────┐
│ Refunds [Export] [⚙] │
├──────────────────────────────────────────────────────┤
│ [Unassigned 1,204] [Mine 87] [Pending 2,041] [Failed 12]│ ← filter tabs, not stat tiles
├──────────────────────────────────────────────────────┤
│ 🔍 search Status ▾ Assignee ▾ Date ▾ ⚙ 25▾ │
│ Status: Pending × Assignee: Unassigned × Clear all │
├──────────────────────────────────────────────────────┤
│ ☐ │ Requested │ Customer │ Order │ Amount │ Status │⋯│
│ ... │
├──────────────────────────────────────────────────────┤
│ Showing 1–50 of 1,204 ‹ 1 2 3 … 25 › │
└──────────────────────────────────────────────────────┘
Focal point: the table. It gets everything below the filter bar.
The counts are tabs, not tiles. An agent doesn't want to know there are 1,204 unassigned — they want to work on them. Making the count clickable collapses "read a number" and "apply a filter" into one action, and removes a whole row of decorative tiles from a screen where vertical space is the scarcest resource.
Failed gets a red dot when > 0. It's the only one that's an alarm rather than a queue.
Columns
7 visible. Everything else behind the column picker.
| # | Column | Type | Align | Width | Sortable | Truncation | Notes |
|---|---|---|---|---|---|---|---|
| — | Select | checkbox | — | 44px fixed | — | — | Header selects page |
| 1 | Requested | date | left | 110px fixed | ✅ default desc | — | Relative < 7d ("2d ago"), absolute after; exact on hover |
| 2 | Customer | left | flex, max 260px | ✅ | Middle-ellipsis + tooltip | The scanning column | |
| 3 | Order | id | left | 120px fixed | ✅ | — | Monospace, links to order |
| 4 | Amount | currency | right | 110px fixed | ✅ | — | tabular-nums, 2dp, symbol per row (mixed currency) |
| 5 | Status | enum | left | 110px fixed | ✅ | — | Badge + label |
| 6 | Assignee | user | left | 140px fixed | ✅ | Avatar + first name | — when null, not "Unassigned" |
| 7 | Actions | — | right | 96px fixed | — | — | Approve / Reject inline |
Hidden by default: Refund ID, Reason, Currency code, Payment method, Order total, Customer since
Middle-ellipsis on email, not end-ellipsis. jonathan.mcallister@… tells you nothing; jonathan.m…@acmecorp.com keeps the domain, which is what agents actually scan for. This is the single highest-value detail in the spec for this particular table.
Mixed currency means no column-level unit. Symbol goes in the cell (€42.00), and sorting by Amount must sort by a normalised value or it's nonsense — flag to engineering.
reason stays hidden. Free text that's usually empty is the worst possible default column: it's mostly whitespace and occasionally 400 characters. It belongs in the row expansion.
Default sort: Requested, descending. Tiebreak: refund id, ascending.
Density
Default: compact, 36px rows. Agents live here; 50 rows on screen beats 25 comfortable ones. Offer the density control in the toolbar and persist per user.
| Density | Row height |
|---|---|
| Compact (default) | 36px |
| Default | 44px |
| Comfortable | 56px |
Cell padding-x 12px, first/last 16px. 1px bottom border, no zebra striping — 7 columns doesn't need it.
Filters & search
| Control | Type | Behaviour |
|---|---|---|
| Search | text, debounce 300ms | Matches email and order id. Placeholder: Search email or order ID |
| Status | multi-select | Default: Pending only |
| Assignee | select | Me · Unassigned · any agent · Anyone |
| Date | preset + custom | 7d / 30d / all. Default: all |
- Active filters as removable chips, always visible.
Clear allat 2+.
Filter state in the URL. Agents paste these links to each other constantly — "can you take this batch". Non-negotiable in a support tool.
- Count:
Showing 1–50 of 1,204 - Spinner goes in the search field, never over the table.
Pagination
Pagination, not infinite scroll. Agents process systematically and need to know where they are; infinite scroll also breaks the bulk-action bar. Page sizes 25 / 50 / 100, default 50. First/last jumps.
Selection & bulk actions
- Header checkbox selects the page.
- Full page selected + more matching →
All 50 on this page selected. [Select all 1,204 matching] - Bulk bar pins to the bottom on selection:
3 selected · Approve · Reject · Assign to me · Clear
Confirmation names the count and the money: Approve 1,204 refunds totalling €48,210.55? — the amount is the number that stops a mistake.
- Bulk reject requires a reason (select + optional note). Approve doesn't.
- Progress past 2s:
Approving 340 of 1,204… - Partial failure:
1,198 approved, 6 failed. [View failures]— never report "Done" when some failed. - Selection survives sort. Clears on filter change, with a toast saying so.
Row actions
Approve and Reject as inline buttons — they're 95% of the work, so they don't go in an overflow menu. Overflow (⋯) holds View order · Assign · Copy refund ID · View customer.
Row click expands inline (reason, payment method, customer history), rather than navigating away. Agents lose their place otherwise.
States
| State | Design |
|---|---|
| Initial load | 8 skeleton rows at 36px, real column widths |
| Refetch | Rows stay at 60% opacity, 2px progress bar on the table top edge — never blank |
| Slow > 5s | Still loading — searching 40,000 refunds + [Cancel] |
| Empty — first use | n/a (queue is never empty on first use) |
| Empty — filtered | No refunds match / Status: Pending · Assignee: me / [Clear filters] |
| Empty — all clear | Queue clear / Nothing pending. New requests appear here. |
| Error — whole | Couldn't load refunds / [Try again] — filter bar and chrome preserved |
| Error — partial | Counts fail independently of the table; each retries alone |
| Error — row | Inline on the row: ⚠ Approval failed — card issuer declined · [Retry], row stays |
| Stale | Updated 2 min ago · [Refresh] — poll every 60s, never auto-refresh mid-selection |
| Capped | Not applicable — pagination handles 40k |
The "all clear" empty state matters more than it looks. It's the only moment of the day the agent gets to feel finished. Acknowledge it.
Responsive
Agents are on desktop, so optimise there — but support tablet for on-call.
| Width | Behaviour |
|---|---|
| < 768px | Card per row: customer + amount on line 1, status + date line 2, actions full-width. Bulk actions disabled. |
| 768–1280px | Priority columns (Requested, Customer, Amount, Status, Actions), rest behind row expansion |
| > 1280px | Full 7 columns |
Keyboard
Daily-use tool, so this earns its place:
| Key | Action |
|---|---|
/ | Focus search |
j / k | Next / previous row |
x | Toggle selection |
a | Approve focused row |
r | Reject focused row (opens reason) |
Enter | Expand row |
Esc | Clear selection / collapse |
? | Shortcut help |
a and r need a 3-second undo toast, since they're single-keypress and destructive-ish.
Performance
- Server-side sort, filter and paginate — 40k rows, non-negotiable.
- No virtualisation needed at 50 rows/page.
- Poll 60s, paused while rows are selected or a row is expanded.
- Target p95 < 400ms for the filtered query; the search index needs to cover email and order id.
Accessibility
- Real
<table>,<th scope="col">,<caption class="sr-only">Refund requests</caption>✅ aria-sorton the sorted header; sort control is a<button>inside the<th>✅- Sticky header with an explicit background ✅
Showing 1–50 of 1,204in anaria-live="polite"region ✅- Approve/Reject reachable by keyboard, never hover-only ✅
- Status badge = colour + label (failed vs rejected must not rely on red vs grey) ✅
- Bulk bar announced on appearance; count updates announced politely ✅
Open decisions
DECISION NEEDED:does approving assign the refund to the acting agent automatically? Recommend
yes — it's an audit trail agents shouldn't have to maintain by hand.
DECISION NEEDED:shouldfailedrefunds sit in this queue or their own? Recommend
own view — they need investigation, not approval, and they pollute the working count.
Connects securely to your tools. The creator never sees your data.
About this skill
The problem
Dashboards look fine with six tidy rows and fall apart with four thousand messy ones. The email that's 60 characters long pushes the actions column off screen. Every filter change blanks the table. Nobody designed the empty state, the loading state, or what happens when one query times out — so the whole page fails.
What it does
- Designs the layout from the question the screen answers, not from a 12-column grid: context bar, headline metrics with real comparisons, one focal view, supporting detail.
- Specifies the table properly — column order and alignment (numbers right, always), how many are visible by default, fixed vs flex widths, truncation per column, sorting with a sensible default and a tiebreak.
- Covers filters and search as one system: chips for active filters, state in the URL, result counts, debounce timing, and when pagination beats infinite scroll.
- Enumerates the nine states that get skipped — initial load, refetch, slow query, three kinds of empty, whole-table error, partial failure, row-level error — with the copy for each.
- Handles responsive honestly: a table doesn't become a smaller table. Card per row, priority columns, or frozen-column scroll — with the tradeoffs.
- Includes real measurements: row heights by density, cell padding, sticky header CSS, tabular-nums, badge sizing, breakpoint behaviour.
Why this beats prompting it yourself
It designs against your actual data shape — row counts, longest values, nullable fields, status cardinality — rather than against the six perfect rows in the example. And it forces the states and the bulk-selection escalation ("select all 1,247 matching") that cause real incidents when they're left unspecified.
Use cases
- Building an admin panel or internal tool
- A SaaS dashboard that needs to be acted on, not just read
- Fixing a table that works in dev and breaks on production data
- Speccing a CRUD interface before writing it
Known limitations
Chart selection and palette are out of scope — pair it with a data-viz skill. Assumes a web interface; doesn't cover native mobile table patterns.
Does not provide visual assets or code snippets. Focuses on functional specs and layout logic.
How to use
- 1
Connect your agent to the Agensi MCP
Copy the prompt and paste it into Claude, ChatGPT, Cursor or any agent that supports MCP. It sets up the connection for you. Manual setup
- 2
Sign in to Agensi
Your agent shows a sign-in link. Sign in and click Allow.
- 3
Ask for what you need
Your agent loads this skill and uses it on your task. It always asks before any payment.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill goes through the trust checks below.
Security scanned
Checked by our 8-point scan before listing
Fresh listing
Recently published to Agensi
Money-back guarantee
Doesn't do what its page promises? You get your money back.
Trust & safety
Security scan
Security scan passed 1 month ago
Permissions
No special permissions declared or detected