- 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 tools 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
- Generated the UI component
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.
dashboard-data-table-design-that-survive.tsx
TSX · React component
Example file from a real run - the skill writes it into your workspace.
Connects securely to your tools. The creator never sees your data.
What you get
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.
How to install
Works the same in every agent - Claude, Cursor, Codex, Copilot and 20+ more.
- 1
Download the ZIP
Free skills download straight away. Paid skills unlock right after purchase.
- 2
Unzip into your skills folder
Every agent reads skills from one folder on your machine. Drop the unzipped folder in there.
- 3
Ask your agent to use it
Restart the agent if it was already running. It picks the skill up automatically - no config needed.
Skills folder by agent
Click the path to copy it. Create the folder if it does not exist yet.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill passes our trust checks below.
Security scanned
Passed our 8-point scan before listing
Fresh listing
Recently published to Agensi
30-day refund
Not a fit? Get your money back
Trust & safety
Security scanned
Verified clean 7 days ago
- Passed all security checks, Safe to install