feat(frontend): standardize loading states and extract a base DataTable - #320
Merged
Conversation
Every list view rebuilt the same table furniture by hand -- green header row, colgroup, empty state -- and they had drifted apart, while each page signalled loading with its own `<p>Loading...</p>`. Adds a DataTable that takes columns as data and owns the header, widths, empty state, row-click behaviour and loading skeleton, plus Spinner / LoadingState / Skeleton / TableSkeletonRows for loading affordances. Tables now keep their header and column widths while loading instead of blanking; everything else gets a centred spinner whose label stays as the accessible name. Animations live in globals.css so there is one timing curve and both honour prefers-reduced-motion. Donors and donations also drop their copy-pasted pagination in favour of the existing Pagination component. Co-authored-by: Cursor <cursoragent@cursor.com>
…ing-states Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # apps/frontend/src/app/dashboard/page.tsx
nourshoreibah
had a problem deploying
to
preview
August 12, 2026 04:04 — with
GitHub Actions
Failure
Contributor
|
🌿 Preview environment — failed ❌ to update. See the workflow logs. |
Contributor
|
🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed. |
Contributor
|
🌿 ⏳ Creating preview environment… (logs) |
Contributor
🌿 Preview environment — ready ✅Open: https://d3nmtjoh6ir9ym.cloudfront.net/pr-320/ Shared RDS + Cognito (prod data); DB migrations are not applied here — if this PR adds a migration, endpoints using the new columns will fail until it merges. New commits update this environment in place — a note is posted here on each update. Remove the |
Contributor
|
🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two related bits of cleanup that both came out of "make loading states nicer":
colgroup, empty state — and had drifted (header weight 700 vs 600 vs unset). They now render throughDataTable, where columns are data ({ key, header, width, align, cell, skeleton }) and optionalselectionsupplies the checkbox column reports needs for bulk delete.ExpensesTablebecomes a thin wrapper that just fixes the expense column set, so its props are unchanged.<p>Loading…</p>.Spinneris the primitive,LoadingStateis the centred block for a region, andSkeleton/TableSkeletonRowsare the table treatment.Tables keep their header, column widths and page height while loading, with jittered bar widths, a staggered shimmer and a pill-shaped placeholder where the status badge goes. Everything else gets a centred spinner; the label is kept as the accessible name rather than visible text, so
getByRole('status', { name: … })still identifies it.The animations live in
globals.css(.branch-spinner,.branch-skeleton) rather than in the components, so there is a single timing curve,FullPageSpinnercan still render before Chakra is mounted, and both honourprefers-reduced-motion.Two things that came along for the ride: donors and donations dropped their copy-pasted duplicate of
Paginationin favour of the existing component, and rows that act like buttons are now keyboard-reachable (atrwithonClickpreviously could not be focused).Conventions are documented in
apps/frontend/AGENTS.md, per the repo rule about keeping those current.Notes for reviewers
<h5>everywhere, matching the design system's heading scale. Reports, donors and donations headers get slightly bolder as a result — that is the intended standardization, not an accident.StaffPicker,ProjectFormModal, the customButton) live on an in-flight feature branch and are not onmainyet, so they are deliberately out of scope here.Test plan
npm run typechecknpm run lintnpm run test— 29 suites / 292 passing, including newDataTableandLoadingStatesuites covering the skeleton, empty state, selection and keyboard row activationnpm run build(static export)fetchMade with Cursor