Skip to content

feat(frontend): standardize loading states and extract a base DataTable - #320

Merged
nourshoreibah merged 2 commits into
mainfrom
feat/standardize-loading-states
Aug 12, 2026
Merged

feat(frontend): standardize loading states and extract a base DataTable#320
nourshoreibah merged 2 commits into
mainfrom
feat/standardize-loading-states

Conversation

@nourshoreibah

Copy link
Copy Markdown
Collaborator

Summary

Two related bits of cleanup that both came out of "make loading states nicer":

  • One base table. Expenses, reports, donors and donations each rebuilt the same furniture by hand — green header row, colgroup, empty state — and had drifted (header weight 700 vs 600 vs unset). They now render through DataTable, where columns are data ({ key, header, width, align, cell, skeleton }) and optional selection supplies the checkbox column reports needs for bulk delete. ExpensesTable becomes a thin wrapper that just fixes the expense column set, so its props are unchanged.
  • One set of loading affordances. No more <p>Loading…</p>. Spinner is the primitive, LoadingState is the centred block for a region, and Skeleton / TableSkeletonRows are 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, FullPageSpinner can still render before Chakra is mounted, and both honour prefers-reduced-motion.

Two things that came along for the ride: donors and donations dropped their copy-pasted duplicate of Pagination in favour of the existing component, and rows that act like buttons are now keyboard-reachable (a tr with onClick previously could not be focused).

Conventions are documented in apps/frontend/AGENTS.md, per the repo rule about keeping those current.

Notes for reviewers

  • Header cells now use <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.
  • The remaining loading call sites (StaffPicker, ProjectFormModal, the custom Button) live on an in-flight feature branch and are not on main yet, so they are deliberately out of scope here.

Test plan

  • npm run typecheck
  • npm run lint
  • npm run test — 29 suites / 292 passing, including new DataTable and LoadingState suites covering the skeleton, empty state, selection and keyboard row activation
  • npm run build (static export)
  • Manually verified the table skeleton and page spinner against the local mock API with a throttled fetch

Made with Cursor

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>
@nourshoreibah nourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
…ing-states

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	apps/frontend/src/app/dashboard/page.tsx
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — failed ❌ to update. See the workflow logs.

@nourshoreibah nourshoreibah removed the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

@nourshoreibah nourshoreibah added the test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 ⏳ Creating preview environment… (logs)

@nourshoreibah nourshoreibah added the no-review The PR review bot won't run label Aug 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment — ready ✅

Open: https://d3nmtjoh6ir9ym.cloudfront.net/pr-320/
API: https://rzwd20me73.execute-api.us-east-2.amazonaws.com/prod

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 test-environment label or close the PR to tear it down.

@nourshoreibah
nourshoreibah merged commit 55a42e0 into main Aug 12, 2026
26 of 27 checks passed
@nourshoreibah
nourshoreibah deleted the feat/standardize-loading-states branch August 12, 2026 04:12
@github-actions

Copy link
Copy Markdown
Contributor

🌿 Preview environment torn down 🧹 — the stack for this PR has been destroyed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-review The PR review bot won't run test-environment Creates a temporary (nearly free) test environment. Uses prod DB and cognito

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant