fix(web): make every table readable on mobile#39
Conversation
Tables across the admin app and contractor portal dropped columns below the sm breakpoint via hidden sm:table-cell, leaving only two columns visible on a phone with no way to reach the rest; a few detail-tab tables instead overflowed and clipped their right-most columns. Add shared responsive-table primitives (MobileCard / MobileCardList / MobileCardRow / TableScroll). Below sm, entity-list tables now render as a stack of cards showing every field; the desktop table is unchanged and simply hidden below sm. Invoice line-item tables keep a real table but scroll horizontally with a right-edge fade affordance. Covers contractors, invoices, documents, 1099 readiness, offboarding, audit log, the contractor-detail engagement/invoice/document/time-entry tabs, and the portal invoice/payment/document/time-entry/dashboard views.
There was a problem hiding this comment.
Code Review
This pull request introduces a set of responsive table components—MobileCardList, MobileCard, MobileCardRow, and TableScroll—to enhance the mobile user interface across multiple pages. Existing tables are now conditionally hidden on small screens in favor of a card-based layout or wrapped in a scrolling container. Review feedback suggests several improvements: restricting hover effects to interactive cards, using the formatCurrency utility for consistent formatting, replacing inline font styles with Tailwind's font-mono class, and correcting the indentation of table elements within the new scroll wrapper.
| children?: ReactNode; | ||
| }) { | ||
| const inner = ( | ||
| <div className="rounded-xl border border-slate-200 bg-white p-4 transition-colors hover:bg-slate-50"> |
There was a problem hiding this comment.
The hover background color should only be applied if the card is interactive (i.e., when href or onClick is provided). Currently, it shows a hover state even for static cards, which can be misleading for users.
<div className={`rounded-xl border border-slate-200 bg-white p-4 transition-colors ${href || onClick ? "hover:bg-slate-50" : ""}`}>
| $ | ||
| {inv.totalAmount.toLocaleString('en-US', { | ||
| minimumFractionDigits: 2, | ||
| })} |
There was a problem hiding this comment.
| className="text-sm font-medium text-brand-600" | ||
| style={{ fontFamily: 'JetBrains Mono, monospace' }} |
| <TableScroll> | ||
| <table className="w-full border-separate border-spacing-0"> |
Problem
On a phone (~375px wide), almost every table in the app was unusable. Tables hid columns below the
smbreakpoint withhidden sm:table-cell, leaving only the first ~2 columns visible and no way to scroll to the rest — e.g. the admin Invoices list showed only the invoice number and status, with the amount completely gone. A few contractor‑detail tabs (Invoices/Engagements) instead overflowed and clipped their right‑most columns.The desktop layout was fine; this is a mobile‑only rendering bug.
Fix
Added shared responsive‑table primitives in
components/ui/responsive-table.tsx(MobileCard,MobileCardList,MobileCardRow,TableScroll) and applied them consistently. The desktop<table>markup is untouched — it is simply hidden belowsm, where a mobile rendering takes over.Pattern chosen per table
Card‑stack (below
sm) — for every entity‑list table. Each row becomes a card: the most important field is the title, the status badge sits opposite it, and the remaining fields are label/value lines so nothing is lost. Applied to:Horizontal scroll with a right‑edge fade affordance — for the invoice line‑item tables (admin + portal). These are dense, equal‑weight numeric columns (Description / Qty / Unit Price / Amount) that read better as a real table than as cards, so they keep the table and scroll sideways with a visible fade hinting there is more.
No data, copy, or behaviour changed. Desktop (≥
sm) is byte‑for‑byte the same table as before.Verification
Built, type‑checked, linted, unit tests (34) green. Verified against a real seeded DB at 375×812 (cards / scroll), 1024×768 (table, unchanged) and 1440×900 (table, unchanged) — desktop and tablet do not regress; the card stack only appears below 640px.
Before → After @ 375px
No desktop/tablet regression