fix(web): repair portal earnings chart, dead header search, bare 404#32
Conversation
Three customer-facing UI issues found during a full walkthrough of the live app: - Portal "Monthly Earnings" rendered as a single full-width bar that swallowed the whole plot area whenever a contractor's paid invoices fell in one month — it read as a broken solid block. Switched it to the same gradient AreaChart the admin "Monthly Revenue" card already uses, so it degrades gracefully with sparse data and the two dashboards now share one chart treatment. - The header magnifying-glass was a button with no handler and no accessible name — clicking it did nothing and screen readers announced only "button". Made it a real, context-aware link to the searchable directory (contractors for admins, invoices in the portal) and gave the mobile nav toggle an accessible name. - The 404 page was unbranded bare text and pointed logged-out users at an auth-gated route. Rebranded it to match the auth shell and pointed the primary action at "/", which resolves correctly in any auth state. Adds regression tests for the header search/menu affordances and the not-found pages.
There was a problem hiding this comment.
Code Review
This pull request enhances the application's UI and accessibility, specifically redesigning the 404 error pages and updating the dashboard's earnings visualization from a bar chart to an area chart. It also improves the Header component by adding dynamic search links for the contractor portal and better accessibility attributes. New tests were introduced for the 404 pages and the Header. Feedback focused on improving the precision of currency formatting in the dashboard chart to avoid misleading rounding and ensuring consistent shadow styling across components.
| tickLine={false} | ||
| tickFormatter={(v: number) => v >= 1000 ? `$${(v / 1000).toFixed(0)}k` : `$${v}`} | ||
| width={48} | ||
| tickFormatter={(v: number) => (v >= 1000 ? `$${(v / 1000).toFixed(0)}k` : `$${v}`)} |
There was a problem hiding this comment.
The use of toFixed(0) rounds currency values to the nearest thousand, which can be misleading for financial data (e.g., $1,500 would be displayed as $2k). Consider using toFixed(1) and removing the trailing zero to provide better precision while keeping the labels concise.
| tickFormatter={(v: number) => (v >= 1000 ? `$${(v / 1000).toFixed(0)}k` : `$${v}`)} | |
| tickFormatter={(v: number) => (v >= 1000 ? `$${(v / 1000).toFixed(1).replace(/\.0$/, '')}k` : `$${v}`)} |
| Unified contractor lifecycle platform | ||
| </p> | ||
| </Link> | ||
| <div className="rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-card"> |
There was a problem hiding this comment.
Inconsistent shadow utility. The rest of the application, including the admin 404 card in apps/web/src/app/(admin)/not-found.tsx, uses shadow-xs. Unless shadow-card is a specific custom utility intended for this branded page, consider using shadow-xs for visual consistency.
| <div className="rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-card"> | |
| <div className="rounded-2xl border border-slate-200 bg-white p-8 text-center shadow-xs"> |
|
Deployed to production (deploy run 26064392129, success) and verified on the live site at https://contractoros.jeffgicharu.com:
Admin dashboard, contractors list, and portal pages re-checked post-deploy — no regressions from the shared Header change. |
Context
Full click-through of the live app surfaced three customer-facing issues that undercut an otherwise polished product. All three are on high-traffic surfaces (the contractor's landing screen, the global header, and the not-found path).
1. Portal "Monthly Earnings" chart rendered as a broken solid block — HIGH
Before: On
/portal/dashboardthe "Monthly Earnings" card used a RechartsBarChartwith nomaxBarSize. When a contractor's paid invoices land in a single month (the common case for the demo contractor), the lone bar expands to fill the entire category band — a giant full-width indigo rectangle that reads as a rendering failure, not a chart. This is the first screen the contractor demo account sees after login.After: The card now uses the exact same gradient
AreaChartthe admin "Monthly Revenue" card already uses. An area series degrades gracefully with one or two data points (a small line/area instead of a wall of color), and the two dashboards now share a single, consistent chart treatment.Why this fix: The admin dashboard already had a chart that handled sparse data well. Rather than patch the bar chart with width caps and a hand-tuned Y domain, aligning the portal to the existing, proven treatment fixes the bug and removes a design inconsistency between the two dashboards.
2. Header search control was dead and unlabelled — MED
Before: The magnifying-glass in the top bar was a
<button>with noonClickand no accessible name. Clicking it did nothing; assistive tech announced only "button". The mobile nav toggle was also unnamed.After: The search control is now a real, context-aware
Link— to the contractor directory for admins, to invoices inside the portal — both of which have working search. It has anaria-label/title, and the mobile nav toggle now has an accessible name too.Why this fix: A visible control that does nothing is worse than no control. Routing to the existing searchable list is an honest, complete behaviour (no half-built search modal) and closes the accessibility gap.
3. 404 page was unbranded and mis-routed — LOW/MED
Before: Root
not-found.tsxwas bare centered text on a flat background with no logo, and its only action pointed at/dashboard— an auth-gated route that bounces logged-out visitors to login.After: Rebranded to match the auth shell (logo mark + product name + gradient) and the primary action now points at
/, which resolves correctly whether the visitor is logged in or out. The in-shell admin 404 got a matching card treatment.Tests
src/components/layout/header.test.tsx— search link target per context (admin vs portal) + accessible mobile nav toggle.src/app/not-found.test.tsx— branded content and a safe/primary action on both not-found pages.vitest run(32 tests) green,tsc --noEmitclean,next buildsucceeds. No new lint findings (the two remaining warnings are pre-existing and on untouched lines).Final visual verification is performed against the live site after deploy.
🤖 Generated with Claude Code