Skip to content

fix(dashboard): resolve P0/P1 issues from full visual audit#201

Open
ajitpratap0 wants to merge 1 commit intomainfrom
fix/dashboard-visual-audit-p0-p1
Open

fix(dashboard): resolve P0/P1 issues from full visual audit#201
ajitpratap0 wants to merge 1 commit intomainfrom
fix/dashboard-visual-audit-p0-p1

Conversation

@ajitpratap0
Copy link
Copy Markdown
Owner

Summary

Comprehensive fix for all P0 and P1 issues identified during a full visual audit of the CryptoFunk trading dashboard. The audit used 5 parallel CDP agents covering Lighthouse scores, performance traces, network analysis, responsive design, and route-by-route screenshots.

Before: Homepage crashed on load, 6 API endpoints returned 401, candlestick chart threw 404, no light mode, borderline CLS score, and slow LCP due to monolithic client component.

After: All 7 routes render successfully, API auth is configurable, light/dark/system theme toggle persists, LCP improved via Server Component extraction, CLS stabilized via skeleton dimension matching.

Changes

P0 — Critical (blocked usage)

Fix File What Changed
SystemStatusIndicator null crash components/ui/StatusDot.tsx Added services ?? {} guard before Object.entries() — prevents crash when API returns undefined services
Missing API authentication lib/api.ts, .env.example Added X-API-Key header to request() method and standalone fetch functions, sourced from NEXT_PUBLIC_API_KEY env var

P1 — High (degraded experience)

Fix File(s) What Changed
Candlestick endpoint 404 hooks/usePerformance.ts, lib/api.ts Replaced raw fetch() with apiClient.getMarketCandlestick() using correct /api/v1/ prefix and auth headers. Mock fallback preserved.
CLS at 0.10 boundary components/ui/StatCard.tsx Added min-h-[120px] to skeleton and rendered card containers, sparkline skeleton placeholder, subtitle skeleton
LCP 1,002ms render delay app/page.tsx, new components/dashboard/DashboardContent.tsx Split monolithic 'use client' page into Server Component (static header = LCP element) + Client Component (data-dependent content via Suspense)
Light mode not implemented app/globals.css, app/layout.tsx, app/providers.tsx, app/settings/page.tsx, package.json Installed next-themes, added ThemeProvider with defaultTheme="dark", defined light CSS variables, replaced manual theme toggle with useTheme() hook (persists via localStorage)

Bonus

Fix File What Changed
Pre-existing type error app/performance/page.tsx, lib/api.ts Typed getMarketCandlestick() return as ApiResponse<CandlestickData[]>, added CandlestickData import — unblocked production builds

Audit Evidence

The full audit ran 5 parallel CDP agents and produced:

Audit Dimension Tool Key Finding
Lighthouse CDP lighthouse_audit 95/100 a11y, 100/100 SEO, 100/100 best practices
Performance CDP performance_start_trace LCP 1,230ms (good), CLS 0.10 (borderline), 81.5% render delay
Network CDP list_network_requests 6x 401 (missing API key), 1x 404 (wrong URL), CORS OK
Routes CDP navigate_page + take_screenshot 5/7 routes OK, 2 crashed (SystemStatusIndicator)
Responsive CDP resize_page + emulate Mobile functional but rough, light mode missing entirely

Screenshots saved in audit-screenshots/ (not committed).

Test Plan

  • TypeScript: npx tsc --noEmit — 0 errors
  • Production build: npm run build — all 7 routes compile
  • Homepage renders (no crash) — verified via CDP screenshot
  • Settings page: Light/Dark/System toggle works
  • Light mode persists across navigation
  • Dark mode visually identical to pre-change state
  • Candlestick chart renders with mock data (no console 404)
  • Set NEXT_PUBLIC_API_KEY in .env.local and verify authenticated endpoints return 200

Breaking Changes

None. Dark mode remains the default theme. All existing functionality preserved.

Remaining (P2/P3 — follow-up)

  • Stop button contrast ratio (4.45:1 vs required 4.5:1)
  • Hydration mismatch on /risk page (time formatting)
  • Mobile header crowding at 375px
  • No tablet-specific sidebar layout at 768px

🤖 Generated with Claude Code

Fix 6 issues identified during a comprehensive CDP-based visual audit
of the trading dashboard (Lighthouse, performance traces, network
analysis, responsive testing, and route-by-route screenshots).

P0 fixes (critical — blocked usage):
- Fix SystemStatusIndicator crash: guard Object.entries(services ?? {})
  when API returns undefined services field (StatusDot.tsx:121)
- Add X-API-Key header to dashboard API client for authenticated
  endpoints (/trades, /performance/*, /risk/*) via NEXT_PUBLIC_API_KEY

P1 fixes (high — degraded experience):
- Fix candlestick 404: route fetch through apiClient with /api/v1/
  prefix instead of raw fetch to non-existent /market/ path
- Fix CLS (0.10 borderline): add min-h-[120px] to StatCard loading
  skeletons and rendered cards to prevent layout shift
- Fix LCP (1,002ms render delay): extract static page header into
  Server Component, push 'use client' boundary to DashboardContent
- Implement light mode: add next-themes with ThemeProvider, define
  light CSS variables, persist theme choice via localStorage

Bonus: fix pre-existing CandlestickData type error blocking prod build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

Label error. Requires at least 1 of: bug, enhancement, documentation, dependencies, performance, security, breaking-change. Found:

@claude
Copy link
Copy Markdown

claude bot commented Mar 29, 2026

PR Review placeholder - testing auth

@claude
Copy link
Copy Markdown

claude bot commented Mar 29, 2026

PR Review - fix(dashboard) P0/P1 visual audit fixes

Good structured approach. The audit-driven methodology is clear and the fixes are well-scoped.


SECURITY: NEXT_PUBLIC_API_KEY is bundled into client JavaScript

Any NEXT_PUBLIC_* variable is inlined into the browser bundle at build time and visible to anyone who opens DevTools. An API key exposed this way is effectively public - it can be scraped and used to hit /admin/resolve-outcomes and trading endpoints directly. For a trading system, consider: (1) a thin Next.js API route that holds the key server-side and proxies requests, or (2) a short-lived JWT issued at login. At minimum, add a warning comment to .env.example noting browser bundle exposure and track the proper fix.


Type assertion masks a real type mismatch (performance/page.tsx:33)

The cast 'as CandlestickData[]' silences the compiler without fixing the underlying mismatch. Since getMarketCandlestick now returns ApiResponse<CandlestickData[]>, candlestickData?.data already has the right type. Remove the cast and prefer ?? over ||:

const candlestickChartData: CandlestickData[] = candlestickData?.data ?? []

Suspense boundary will not stream (app/page.tsx)

DashboardContent is a use client component (it uses hooks). Client components cannot be streamed by Next.js App Router as async Server Components. The Suspense fallback shows during client hydration - still a real LCP win since the static header SSRs - but it is not streaming. The comment 'streams in after hydration' should be updated to avoid confusing future contributors.


API key logic is duplicated in two places (lib/api.ts)

Both ApiClient.request() and standalone buildHeaders() independently read process.env.NEXT_PUBLIC_API_KEY. If the header name or env var changes, two spots need updating. Consolidate by calling buildHeaders() inside ApiClient.request() as well.


triggerOutcomeResolution lost its Content-Type header

After this PR it uses buildHeaders() which only adds X-API-Key. If the backend expects Content-Type: application/json on POST requests this could fail silently. Pass Content-Type: application/json to buildHeaders() for that call.


Minor notes:

  • suppressHydrationWarning on html element (layout.tsx): Required for next-themes, correct. Suppresses all hydration mismatches on that element - acceptable trade-off.
  • DashboardSkeleton height mismatch: Chart skeleton uses h-80 (~320px) but charts render at height=300. Aligning them would further reduce CLS.
  • StatCard sparkline skeleton gap: No sparkline placeholder in loading state. Cards with sparklineData will be taller than their skeleton.

What works well:

  • services ?? {} guard in StatusDot.tsx is the correct minimal fix for the crash.
  • Moving CSS variables to :root / .dark is the right structure for next-themes.
  • URL-encoding symbol and timeRange in getMarketCandlestick is good practice.
  • The inline backend-not-yet-implemented comments are helpful for future contributors.
  • The Server Component split in page.tsx is architecturally sound - the SSR benefit for LCP is real even without true streaming.

Summary: The P0 null crash and 401 fixes are correct and needed. Two things worth addressing before merge: (1) security caveat on NEXT_PUBLIC_API_KEY and (2) the type assertion in performance/page.tsx. The rest are low-risk improvements.

@ajitpratap0 ajitpratap0 added the bug Something isn't working label Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant