feat: Redesign dashboard with vehicle-centric layout #196
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace the current dashboard (summary stat cards + empty activity feed + redundant quick actions) with a vehicle-centric roster layout that shows actionable health data per vehicle. The current dashboard tells users almost nothing -- three numbers (vehicle count, maintenance count, fuel log count), an empty activity section, and navigation buttons that duplicate the sidebar.
Problem Statement
The current dashboard has five UX problems identified during audit:
cursor: auto, no click-through, CTAs only appear when value is 0Choosing a vehicle-centric layout eliminates problems 1, 2, and 4 entirely, and addresses 3 and 5 as part of the redesign.
Design Specification
Layout Structure
Vehicle Card Design
Each card displays:
maintenance_schedules.nextDueDate < now)documents.expirationDatefor typesinsurance,registration)vehicles.odometerReading/garage/vehicles/{id}Compact Action Bar
Slim horizontal row below the heading with two buttons:
Replaces the current Quick Actions section entirely. On mobile, buttons may use icon-only or compact layout.
Heading
Replace generic "Dashboard" with "Your Fleet" (or similar). Remove redundant "Welcome back, email" from header area since email already shows in sidebar/profile.
Empty State (0 vehicles)
Keep existing empty state behavior (already handled in
DashboardScreen.tsx:84-108) -- shows welcome message with "Add Your First Vehicle" CTA.Data Requirements
Current Data (already fetched in
useDashboardData)New Data Needed
documentstable filtered byvehicle_idanddocumentType IN ('insurance', 'registration')whereexpirationDate IS NOT NULL, to surface upcoming renewalsAPI Considerations
documentsApi) may need a method to fetch documents by vehicle with expiry filtering, or the dashboard hook can fetch all user documents and filter client-sideFiles Affected
New Components
frontend/src/features/dashboard/components/VehicleRosterCard.tsx-- per-vehicle cardfrontend/src/features/dashboard/components/ActionBar.tsx-- compact action barModified
frontend/src/features/dashboard/components/DashboardScreen.tsx-- new layout compositionfrontend/src/features/dashboard/hooks/useDashboardData.ts-- extend to fetch document expiry datafrontend/src/features/dashboard/types/index.ts(or similar) -- new types for vehicle healthRemoved/Deprecated
frontend/src/features/dashboard/components/SummaryCards.tsx-- replaced by vehicle cardsfrontend/src/features/dashboard/components/QuickActions.tsx-- replaced by action barfrontend/src/features/dashboard/components/RecentActivity.tsx-- activity context embedded in vehicle cardsAcceptance Criteria
Technical Notes
documentstable withexpirationDatefield anddocumentTypeenum (insurance,registration)useDashboardData.ts:84-113vehicles.imageUrl/imageStorageBucketGlassCardcomponent should be reused for card styling consistencyPlan: Dashboard Redesign with Vehicle-Centric Layout
Phase: Planning | Agent: Planner | Status: AWAITING_REVIEW
Codebase Analysis Summary
Current state: Dashboard has 8 files (0 tests). Layout is flat: SummaryCards (3 metric counters), VehicleAttention (overdue list), RecentActivity (merged feed), QuickActions (4 nav buttons). DashboardScreen is shared between mobile and desktop via callback props.
Key findings:
listByVehicle(vehicleId)withuseDocumentsByVehiclehook -- no backend work neededVehicleImagecomponent exists with 3-tier fallback (uploaded photo, make logo, color placeholder)GlassCardcomponent atshared-minimal/components/mobile/GlassCard.tsx-- click behavior adds hover elevation + cursor pointeruseDashboardData.ts:84-118-- fetches schedules per vehiclegetVehicleLabel(),getVehicleSubtitle()incore/utils/vehicleDisplay.tsgrid-cols-1 sm:grid-cols-2for 2-column desktop gridsborderRadius: 20for cards, 44px min touch targets, Ferrari-inspired color paletteNo backend changes required. All data sources already exist. This is a frontend-only feature.
Architecture Decisions
useDashboardDatahookdocumentsApi.list()with client-side filteringlistByVehicleper vehicle. Filter to insurance/registration with non-null expirationDate client-side.computeVehicleHealth(schedules, documents)as an exportable utility enables unit testing without React Query wrappers.grid-cols-1 sm:grid-cols-2Sub-Issues (milestones map 1:1)
types/index.ts,hooks/useDashboardData.tscomponents/VehicleRosterCard.tsx(new)components/ActionBar.tsx(new)components/DashboardScreen.tsx,index.ts, delete 3 filesBranch:
issue-196-redesign-dashboard-vehicle-centricPR: One PR targeting main, body lists
Fixes #196, Fixes #197, Fixes #198, Fixes #199, Fixes #200, Fixes #201Milestone 1: Types and Vehicle Health Data Layer (#197)
Agent: Feature Agent (frontend data layer)
Files modified:
frontend/src/features/dashboard/types/index.tsfrontend/src/features/dashboard/hooks/useDashboardData.tsType definitions to add:
Hook changes:
documentsApi.list()call to the parallel fetch inuseDashboardData(alongside vehicles and fuelLogs). Filter client-side todocumentType in ['insurance', 'registration']with non-nullexpirationDate.fuelLogsApi.getUserFuelLogs()from the fetch (no longer displayed on dashboard).vehicleIdusing a Map.computeVehicleHealth(schedules: MaintenanceScheduleResponse[], documents: DocumentResponse[]): { health: VehicleHealth; attentionItems: AttentionItem[] }pure function.nextDueDate < now)expirationDate < now)useVehicleRoster()derived hook returningVehicleRosterData[].useDashboardSummaryanduseVehiclesNeedingAttentionfor now (they may still be imported elsewhere). Mark with@deprecatedcomment if no longer needed after M4.Commit:
feat: add vehicle health types and roster data hook (refs #197)Milestone 2: VehicleRosterCard Component (#198)
Agent: Frontend Agent
Files created:
frontend/src/features/dashboard/components/VehicleRosterCard.tsxComponent design:
Layout (inside GlassCard):
Implementation details:
GlassCardwithonClick={() => onClick(data.vehicle.id)}VehicleImage(height 48, compact), vehicle label viagetVehicleLabel(), health badgebg-emerald-500(light) /bg-emerald-400(dark)bg-amber-500/bg-amber-400bg-red-500/bg-red-400text-red-500with warning icontext-amber-600with clock icontext-gray-500(secondary text)text-sm text-secondary, formatted withtoLocaleString()Commit:
feat: add VehicleRosterCard component (refs #198)Milestone 3: ActionBar Component (#199)
Agent: Frontend Agent
Files created:
frontend/src/features/dashboard/components/ActionBar.tsxComponent design:
Implementation:
flexrow withgap-2,justify-endvariant="contained",size="small",startIcon={<Add />}variant="outlined",size="small",startIcon={<LocalGasStation />}smbreakpoint): Buttons remain text+icon but usesize="small"for compactness@mui/icons-materialicons (already in project dependencies)Commit:
feat: add dashboard ActionBar component (refs #199)Milestone 4: DashboardScreen Composition and Cleanup (#200)
Agent: Frontend Agent
Files modified:
frontend/src/features/dashboard/components/DashboardScreen.tsxfrontend/src/features/dashboard/index.tsFiles deleted:
frontend/src/features/dashboard/components/SummaryCards.tsxfrontend/src/features/dashboard/components/QuickActions.tsxfrontend/src/features/dashboard/components/RecentActivity.tsxDashboardScreen new layout:
Implementation steps:
Typography variant="h4")SummaryCards+QuickActions+RecentActivitywith:ActionBarnext to heading (flex row: heading left, action bar right)div.grid.grid-cols-1.sm:grid-cols-2.gap-4useVehicleRoster()data toVehicleRosterCardcomponentsPendingAssociationBannerandPendingReceiptsdialog unchangedonClickto existing navigation: desktop usesnavigate('/garage/vehicles/${id}'), mobile usesonVehicleClick?.(vehicle)index.tsbarrel exports: RemoveSummaryCards,QuickActions,RecentActivityand their skeletons. AddVehicleRosterCard,ActionBar.DashboardPage.tsxandApp.tsx)Commit:
feat: rewire DashboardScreen with vehicle roster layout (refs #200)Milestone 5: Tests (#201)
Agent: Frontend Agent
Files created:
frontend/src/features/dashboard/hooks/__tests__/useDashboardData.test.tsfrontend/src/features/dashboard/components/__tests__/VehicleRosterCard.test.tsxfrontend/src/features/dashboard/components/__tests__/ActionBar.test.tsxfrontend/src/features/dashboard/components/__tests__/DashboardScreen.test.tsxTest plan:
useDashboardData.test.ts-- Unit test the purecomputeVehicleHealthfunction:VehicleRosterCard.test.tsx:ActionBar.test.tsx:DashboardScreen.test.tsx:Testing approach: Mock
useDashboardData/useVehicleRosterhooks. Use@testing-library/reactwithrender,screen,fireEvent. Pure functions tested directly without React wrappers.Commit:
test: add dashboard redesign tests (refs #201)Final Validation
After all milestones complete:
npm run lint-- zero errorsnpm run type-check-- zero errorsnpm test-- all passVerdict: AWAITING_REVIEW | Next: QR plan-completeness review
QR Review: Plan Completeness
Phase: Plan-Review | Agent: Quality Reviewer | Status: FAIL
Checklist Results
1. Coverage: Does every acceptance criterion have a corresponding milestone/implementation step?
FAIL - Missing coverage for loading skeletons acceptance criterion. Plan mentions loading skeletons in M4 but doesn't detail the skeleton design for the new vehicle roster layout.
2. File inventory: Are all files to create, modify, and delete explicitly listed?
PASS - All files are accounted for:
3. Dependency ordering: Are milestone dependencies correct?
PASS - M1 (data layer) -> M2/M3 (components) -> M4 (composition) -> M5 (tests) is sound. M2 and M3 can proceed in parallel.
4. Type safety: Are proposed type definitions complete?
PASS - AttentionItem and VehicleRosterData types look complete. VehicleHealth union type is appropriate.
5. Data flow: Is the data fetching strategy sound?
FAIL - See RULE 1 findings below. The plan proposes calling
documentsApi.list()without filtering, but the API supports filter params that would reduce payload size.6. Mobile + Desktop: Does the plan address both viewports?
PASS - Explicitly states
grid-cols-1 sm:grid-cols-2for responsive grid. M2 and M3 mention mobile behavior.7. Existing behavior preservation: Does the plan preserve empty state, PendingAssociationBanner, etc?
PASS - M4 explicitly preserves empty state (lines 84-108), PendingAssociationBanner, and PendingReceipts dialog.
8. Deletion safety: Are files being deleted safe to remove?
PASS - Verified that SummaryCards, QuickActions, and RecentActivity are only imported by DashboardScreen.tsx. The plan correctly updates index.ts barrel exports.
9. Test coverage: Does the test plan cover key behaviors?
PASS - M5 test plan covers pure function (computeVehicleHealth), component behaviors, and DashboardScreen integration. Uses proper testing patterns.
10. RULE 0 (Critical): Any production reliability risks?
PASS - No unhandled errors, security issues, or resource exhaustion risks identified.
11. RULE 1 (High): Does the plan follow project standards?
FAIL - See findings below.
12. RULE 2 (Should-fix): Any structural quality concerns?
PASS - No god objects or duplication. Pure function pattern for health computation is good architectural choice.
Findings
RULE 1 (HIGH): Inefficient document fetching strategy
useDashboardDatahook changesdocumentsApi.list()filter params to request ONLYdocumentType in ['insurance', 'registration']with non-nullexpirationDate.documentsApi.list({ type: 'insurance' })anddocumentsApi.list({ type: 'registration' })in parallel, then filter client-side for non-null expirationDate. Alternatively, check if the backend supports multiple types in a single filter (e.g.,type=insurance,registration) and use that. The API signature atdocuments.api.ts:16showsparams?: { vehicleId?: string; type?: string; expiresBefore?: string }- thetypeparam can be used.RULE 1 (HIGH): Missing acceptance criteria coverage - loading skeletons
RULE 2 (SHOULD_FIX): Missing error handling detail for document fetch
useDashboardDatahook changesMissing Items
typefilter param.Verdict: FAIL | Next: Planner revision required
Required changes:
typefilter param (RULE 1 - blocking)Plan Revision: Addressing QR Plan-Completeness Findings
Phase: Planning | Agent: Planner | Status: REVISED
Revision 1: Document Fetching Strategy (RULE 1 fix)
Before:
documentsApi.list()fetching all documents, filter client-sideAfter: Two parallel calls with server-side type filtering:
This uses the existing
typequery parameter onGET /documentsto avoid fetching manuals and other irrelevant document types. Two small filtered queries instead of one large unfiltered query.Revision 2: Loading Skeleton Specification (RULE 1 fix)
Added to Milestone 4 (DashboardScreen composition):
Skeleton design: When
isLoadingis true, render 4 GlassCard skeletons in the samegrid-cols-1 sm:grid-cols-2 gap-4grid. Each skeleton card contains:Skeleton variant="circular" width={48} height={48}(image placeholder) +Skeleton variant="text" width="60%"(vehicle name) +Skeleton variant="circular" width={12} height={12}(health dot)Skeleton variant="text" width="80%"(attention item placeholders)Skeleton variant="text" width="30%"(odometer placeholder)Uses MUI
Skeletoncomponent for consistency with existing skeleton patterns in the codebase.Revision 3: VehicleAttention Component Disposition
VehicleAttention.tsxwill be deleted in Milestone 4. The vehicle roster cards embed attention items inline per vehicle, making the separate aggregated attention list redundant.Updated M4 files deleted list:
frontend/src/features/dashboard/components/SummaryCards.tsxfrontend/src/features/dashboard/components/QuickActions.tsxfrontend/src/features/dashboard/components/RecentActivity.tsxfrontend/src/features/dashboard/components/VehicleAttention.tsx(added)Barrel exports in
index.tswill also removeVehicleAttentionandVehicleAttentionSkeleton.Revision 4: Error Handling Strategy (Graceful Degradation)
Added to Milestone 1 (data layer):
If the documents API calls fail, the dashboard gracefully degrades:
Implementation: Wrap the document fetch calls in try/catch within the
useDashboardDataquery function. On failure, setexpiryDocs = []and proceed. This matches the pattern used elsewhere in the app where supplementary data failures don't block primary content.Verdict: REVISED | Next: QR plan-completeness re-review
QR Review: Plan Completeness (Re-review)
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS
Revision Verification
Finding 1: Inefficient document fetching strategy
STATUS: ADDRESSED
The revision correctly changes from
documentsApi.list()(unfiltered) to two parallel filtered calls:This leverages the existing
typequery parameter on the documents API to avoid fetching irrelevant document types (manuals, receipts, etc.). Server-side filtering reduces payload size and network overhead. The approach is sound.Finding 2: Missing acceptance criteria coverage - loading skeletons
STATUS: ADDRESSED
Revision 2 adds explicit skeleton design specification to M4:
grid-cols-1 sm:grid-cols-2 gap-4gridSkeletoncomponent for consistencyThis provides sufficient implementation detail for the developer executing M4. No design decisions left open.
Finding 3: VehicleAttention component fate
STATUS: ADDRESSED
Revision 3 explicitly adds
VehicleAttention.tsxto the deletion list in M4 with clear rationale: "vehicle roster cards embed attention items inline per vehicle, making the separate aggregated attention list redundant."Also updates barrel exports to remove
VehicleAttentionandVehicleAttentionSkeleton. This completes the file inventory.Finding 4: Error handling strategy (RULE 2 - graceful degradation)
STATUS: ADDRESSED
Revision 4 adds graceful degradation strategy to M1:
expiryDocs = []and continueThis is appropriate for supplementary data. Prevents dashboard failure due to documents permission issues or API errors while maintaining core functionality (vehicle + maintenance display).
New Issues
None identified.
The revisions are internally consistent and don't introduce contradictions or new gaps. The sub-issue decomposition, milestone dependencies, type definitions, and test plan remain sound as originally proposed.
Verdict: PASS | Next: TW plan-scrub
TW Review: Plan Scrub
Phase: Plan-Review | Agent: Technical Writer | Status: PASS
Scrub Results
1. Clarity
PASS -- Each milestone is unambiguous and self-contained. M1 specifies exact type definitions, hook changes with numbered steps, and health logic rules. M2 provides component props interface, layout diagram, and per-element implementation details (colors, icons, text styling). M3 gives complete component spec with MUI component names and variants. M4 lists 10 explicit implementation steps. M5 enumerates test cases per file. A developer can implement from the plan alone without clarifying questions.
2. Terminology Consistency
PASS with one minor note -- The plan uses "health indicator" (issue body), "health badge" (M2 layout), and "health dot" (revision 2 skeleton spec) to refer to the same UI element. However, the type system is consistent:
VehicleHealthtype,healthproperty onVehicleRosterData, and "Health badge" in M2 implementation details. The "health dot" reference in the skeleton spec (revision 2) is contextually appropriate since the skeleton usesSkeleton variant="circular" width={12} height={12}-- it IS a dot placeholder. The parent issue body uses "health indicator" as a general concept and "color-coded badge" as the design spec. No ambiguity that would cause implementation confusion.Suggestion (non-blocking): Standardize on "health badge" throughout the plan to match M2's implementation section. The skeleton spec could say "health badge placeholder" instead of "health dot".
3. Code Examples
PASS -- Type definitions (
VehicleHealth,AttentionItem,VehicleRosterData) are complete and internally consistent. TheVehicleRosterCardPropsinterface correctly referencesVehicleRosterData. TheActionBarPropsinterface is clean. The revised document fetching code snippet (revision 1) is syntactically correct and uses the verified API signaturedocumentsApi.list({ type: 'insurance' })which matches the actual API atdocuments.api.ts:16(params?: { vehicleId?: string; type?: string; expiresBefore?: string }).One observation: The
computeVehicleHealthfunction signature in M1 step 4 takes(schedules: MaintenanceScheduleResponse[], documents: DocumentResponse[])-- the actual type in the codebase isMaintenanceSchedule(frommaintenance.types.ts), notMaintenanceScheduleResponse. This is a naming discrepancy but the plan calls it a "pure function" with a proposed signature, so the developer will use the correct import. Non-blocking.4. File Paths
PASS -- All file paths verified against the actual codebase:
frontend/src/features/dashboard/types/index.ts-- EXISTSfrontend/src/features/dashboard/hooks/useDashboardData.ts-- EXISTSfrontend/src/features/dashboard/components/DashboardScreen.tsx-- EXISTSfrontend/src/features/dashboard/components/SummaryCards.tsx-- EXISTS (to delete)frontend/src/features/dashboard/components/QuickActions.tsx-- EXISTS (to delete)frontend/src/features/dashboard/components/RecentActivity.tsx-- EXISTS (to delete)frontend/src/features/dashboard/components/VehicleAttention.tsx-- EXISTS (to delete, per revision 3)frontend/src/features/dashboard/index.ts-- EXISTSshared-minimal/components/mobile/GlassCard.tsx-- EXISTScore/utils/vehicleDisplay.ts-- EXISTS (containsgetVehicleLabelandgetVehicleSubtitle)The plan references "VehicleImage" component -- verified at
frontend/src/features/vehicles/components/VehicleImage.tsx. Plan doesn't specify the import path explicitly in M2, but the component name is unambiguous.5. Formatting
PASS -- The plan uses consistent structure across milestones: Agent assignment, Files section, Type/Interface definitions (code blocks), Implementation details (numbered lists or bullet points), Commit message. Tables are well-formed. Heading levels are consistent (H3 for milestones, H4 for sub-sections within QR review). The revision comment uses clear H3 headings per revision with Before/After pattern.
6. Missing Context
PASS -- All referenced components and utilities are explained or exist in the codebase:
GlassCard-- behavior described (click adds hover elevation + cursor pointer), verified in codegetVehicleLabel()-- referenced with file path, verifiedVehicleImage-- described as "3-tier fallback (uploaded photo, make logo, color placeholder)"PendingAssociationBannerandPendingReceipts-- explicitly noted as preserveddocumentsApi.list()-- API signature referenced, verifiedgrid-cols-1 sm:grid-cols-2) -- explained as matching existing patternsOne minor gap: M4 step 7 mentions "desktop uses
navigate('/garage/vehicles/${id}'), mobile usesonVehicleClick?.(vehicle)" but doesn't reference where thenavigateimport comes from. However, this is standard React Router usage and any React developer would know the import. Non-blocking.7. Commit Messages
PASS -- All commit messages follow the
{type}: {summary} (refs #{index})convention:feat: add vehicle health types and roster data hook (refs #197)feat: add VehicleRosterCard component (refs #198)feat: add dashboard ActionBar component (refs #199)feat: rewire DashboardScreen with vehicle roster layout (refs #200)test: add dashboard redesign tests (refs #201)Each references the correct sub-issue index. Types are appropriate (
featfor features,testfor tests).8. Acceptance Criteria Alignment
PASS -- Mapping each acceptance criterion to plan coverage:
computeVehicleHealthwith explicit rulesgrid-cols-1 sm:grid-cols-2All acceptance criteria are covered. The revision addressed the skeleton gap identified by QR.
Suggestions
(Terminology) Consider standardizing on "health badge" throughout the plan. The skeleton spec (revision 2) uses "health dot" while M2 uses "health badge". Both are understandable, but consistency reduces cognitive load.
(Type name) M1 step 4 references
MaintenanceScheduleResponse[]but the existing codebase type isMaintenanceSchedule(imported frommaintenance.types.tsin useDashboardData.ts line 12). The implementing developer should useMaintenanceSchedule. Similarly,DocumentResponseshould be checked against the actual document type name. Non-blocking since the developer will follow IDE imports.(Derived hooks deprecation) M1 step 7 says "keep existing
useDashboardSummaryanduseVehiclesNeedingAttentionfor now" and mark with@deprecated. Since verified that onlyDashboardScreen.tsximports these hooks (and DashboardScreen is being rewritten in M4), these hooks will have zero consumers after M4 completes. Consider deleting them in M4 instead of deprecating, to avoid dead code. TheuseRecentActivityhook is also only used by DashboardScreen. All three derived hooks could be removed in M4 alongside the component cleanup.(GlassCard hover description) The codebase analysis says "click behavior adds hover elevation + cursor pointer." Verified in
GlassCard.tsx:33: the actual behavior ishover:shadow-xl hover:-translate-y-0.5 transition-- this is shadow increase + slight upward translation, not MUI elevation. The plan's description in M2 says "elevation increase + subtle border highlight" which is slightly inaccurate. The actual behavior is shadow increase + vertical translate. Non-blocking since the developer will use GlassCard as-is.Verdict: PASS | Next: QR plan-code
QR Review: Plan Docs
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS
Documentation Impact
Files checked:
frontend/src/features/dashboard/README.md-- Does NOT exist. No dashboard-specific README to update.frontend/README.md-- Checked. No references to SummaryCards, QuickActions, RecentActivity, or VehicleAttention components. Generic feature structure description only.docs/README.md-- Checked. No dashboard-specific documentation. Lists backend feature READMEs only (application features)..ai/context.json-- Checked. No references to dashboard components in feature registry. Frontend work section points tofrontend/README.mdwhich is generic.CLAUDE.md(root) -- Checked. No references to dashboard-specific files.Findings: No existing documentation references the dashboard components being deleted (SummaryCards, QuickActions, RecentActivity, VehicleAttention). The codebase documentation is at the feature capsule level for backend features, not frontend components.
Missing Documentation Steps
NONE IDENTIFIED.
The plan is a frontend-only redesign with no backend changes. The project's documentation strategy focuses on:
backend/src/features/{feature}/README.md)docs/PLATFORM-SERVICES.md,docs/DATABASE-SCHEMA.md, etc.)frontend/README.md)There are no feature-specific frontend component READMEs in the codebase pattern. The dashboard feature does not have a README.md file.
New components are self-documenting:
VehicleRosterCard.tsx-- Component props and layout described in M2ActionBar.tsx-- Component props described in M3VehicleHealth,AttentionItem,VehicleRosterData) in M1 serve as inline documentationCode quality ensures discoverability:
index.ts(updated in M4) make components discoverableReview Criteria Results
Does the plan include a documentation update step?
Will any existing README files become stale after the changes?
Are the new components/types documented or self-documenting?
Does the plan mention running doc-sync after completion?
Are the sub-issue descriptions clear enough to serve as future reference?
Verdict: PASS | Next: Plan APPROVED
Rationale: The dashboard redesign is a self-contained frontend component change with no existing documentation to update. The plan provides sufficient inline documentation through type definitions, component specs, and test coverage. No README files reference the components being deleted. The project's documentation strategy does not require feature-specific READMEs for frontend components.
QR Review: Plan Code
Phase: Plan-Review | Agent: Quality Reviewer | Status: FAIL
RULE 0 Findings
None
RULE 1 Findings
RULE 1 (HIGH): Type name mismatch in M1 specification
MaintenanceScheduleResponse[]as the parameter type forcomputeVehicleHealth(), but the actual codebase type isMaintenanceSchedule(frommaintenance.types.ts, imported inuseDashboardData.ts:12). The plan also referencesDocumentResponse[]but needs verification that this matches the actual document type name.computeVehicleHealth(schedules: MaintenanceSchedule[], documents: DocumentRecord[]): { health: VehicleHealth; attentionItems: AttentionItem[] }. The document type isDocumentRecordperdocuments.types.ts:3-25.RULE 1 (HIGH): Documents API call pattern incompatible with API signature
documentsApi.list({ type: 'insurance' })anddocumentsApi.list({ type: 'registration' })as two parallel calls. However, the actual API signature atdocuments.api.ts:16showsasync list(params?: { vehicleId?: string; type?: string; expiresBefore?: string })- thetypeparameter is a single string, not an array. The plan assumes each call fetches ONLY that document type, but the API will return all documents of that type for ALL vehicles the user owns (not scoped to the dashboard's vehicle list).GET /documentsendpoint correctly handlestype=insuranceas a single-value filter (not expecting comma-separated values). If correct, the plan is sound but should clarify that the API returns all user documents of that type (not vehicle-scoped). If the API needs vehicle filtering, the plan should usedocumentsApi.list({ vehicleId })in a loop or accept the overhead of fetching all user documents and filtering client-side by the dashboard's vehicle list.RULE 2 Findings
RULE 2 (SHOULD_FIX): Deprecated hooks should be deleted, not marked @deprecated
useDashboardSummaryanduseVehiclesNeedingAttentionfor now" and mark with@deprecatedcomment. However, verified that onlyDashboardScreen.tsximports these hooks (lines 14, 39-41). Since DashboardScreen is being completely rewritten in M4 to use the newuseVehicleRoster()hook, these three derived hooks (useDashboardSummary,useVehiclesNeedingAttention,useRecentActivity) will have ZERO consumers after M4 completes.useDashboardSummary,useVehiclesNeedingAttention,useRecentActivity) in M4 alongside the component cleanup. Update barrel exports inindex.tsto remove these hook exports. This aligns with the project standard "Delete old code when replacing it" (CLAUDE.md).TW Suggestion Evaluation
TW Suggestion 1: Standardize on "health badge" terminology
Status: Keep non-blocking
Rationale: The terminology variance ("health indicator", "health badge", "health dot") is contextually appropriate. "Health dot" in the skeleton spec accurately describes the
Skeleton variant="circular" width={12} height={12}placeholder. No implementation confusion risk.TW Suggestion 2: Use actual type names (MaintenanceSchedule vs MaintenanceScheduleResponse)
Status: PROMOTE TO BLOCKING (see RULE 1 finding above)
Rationale: This is a RULE 1 violation - type names must match the actual codebase to avoid compilation errors. This is not a minor suggestion but a correctness issue.
TW Suggestion 3: Delete deprecated hooks instead of marking @deprecated
Status: PROMOTE TO BLOCKING (see RULE 2 finding above)
Rationale: While categorized as RULE 2 (structural quality), this aligns with the explicit project standard "Delete old code when replacing it". Given zero consumers after M4, keeping deprecated code violates project conventions.
TW Suggestion 4: GlassCard hover behavior description accuracy
Status: Keep non-blocking
Rationale: The plan says "elevation increase + subtle border highlight" but actual GlassCard behavior is
hover:shadow-xl hover:-translate-y-0.5. The discrepancy is minor since the developer will use GlassCard as-is (not reimplement hover behavior). The component provides the correct hover effect regardless of description accuracy.Verification Against Actual Source Files
Documents API signature (
documents.api.ts:16-18):Confirmed:
typeparameter is a single string. The plan's proposed usagedocumentsApi.list({ type: 'insurance' })is syntactically correct.DocumentRecord type (
documents.types.ts:3-25):Confirmed: Type name is
DocumentRecord(notDocumentResponse). FieldexpirationDate?: string | null;exists at line 18.MaintenanceSchedule type (
useDashboardData.ts:12):Confirmed: Type name is
MaintenanceSchedule(notMaintenanceScheduleResponse).GlassCard onClick behavior (
GlassCard.tsx:33):Confirmed: Hover behavior is shadow + translate (not MUI elevation). Non-blocking since developer uses component as-is.
DashboardScreen imports (
DashboardScreen.tsx:10-14):Confirmed: Only DashboardScreen imports these components and hooks. Safe to delete in M4.
App.tsx imports (line 92):
Confirmed: App.tsx only imports
DashboardScreenfrom the dashboard feature barrel export. Does not directly import SummaryCards, QuickActions, RecentActivity, or VehicleAttention. Safe to delete.Barrel exports (
index.ts:5-11):All four components (SummaryCards, VehicleAttention, QuickActions, RecentActivity) and their skeletons are exported, plus the three derived hooks. Plan correctly updates barrel exports in M4.
Verdict: FAIL | Next: Planner revision required
Required changes:
MaintenanceSchedulenotMaintenanceScheduleResponse,DocumentRecordnotDocumentResponse(RULE 1 - blocking)type=insurancereturn ALL user insurance documents across all vehicles, or only for dashboard vehicles? If all vehicles, is this acceptable overhead? (RULE 1 - blocking clarification needed)Plan Revision 2: Addressing QR Plan-Code Findings
Phase: Planning | Agent: Planner | Status: REVISED
Revision 5: Type Name Corrections (RULE 1 fix)
The plan incorrectly referenced types that don't exist. Corrections:
MaintenanceScheduleResponse[]MaintenanceSchedulefrontend/src/features/maintenance/types/maintenance.types.tsDocumentResponse[]DocumentRecordfrontend/src/features/documents/types/documents.types.tsUpdated
computeVehicleHealthsignature:The
useDashboardData.tshook already importsMaintenanceSchedule(notMaintenanceScheduleResponse). TheDocumentRecordtype has fieldsexpirationDate: string | nullanddocumentType: DocumentType.Revision 6: Documents API Clarification (RULE 1 fix)
documentsApi.list({ type: 'insurance' })callsGET /documents?type=insurancewhich is user-scoped (filtered by authenticated user'suser_idon the backend). It returns ALL insurance documents across ALL of the user's vehicles, which is the desired behavior -- we need per-user, cross-vehicle document data to compute health per vehicle.The returned
DocumentRecord[]includesvehicleIdfield, which we group by to map documents to their respective vehicles. This is exactly the same user-scoping pattern asvehiclesApi.getAll()and is correctly efficient (2 API calls for all document types vs N calls per vehicle).Revision 7: Delete Deprecated Hooks (RULE 2 fix, per project standard)
Before: M1 step 7 said "Keep existing derived hooks, mark with
@deprecated"After: In M4, when rewiring DashboardScreen, delete these hooks from
useDashboardData.ts:useDashboardSummary()-- no consumers after M4useVehiclesNeedingAttention()-- replaced byuseVehicleRoster()useRecentActivity()-- no consumers after M4Also delete the associated types from
types/index.tsif they have no consumers:DashboardSummary-- delete (was only used byuseDashboardSummary)RecentActivityItem-- delete (was only used byuseRecentActivity)VehicleNeedingAttention-- delete (was only used byuseVehiclesNeedingAttention)The existing
useDashboardData()hook is retained as the core fetching mechanism, now returning vehicle roster data instead.Per CLAUDE.md: "Delete old code when replacing it."
TW Non-Blocking Suggestions Accepted
hover:shadow-xl hover:-translate-y-0.5).Verdict: REVISED | Next: QR plan-code re-review
QR Review: Plan Code (Re-review)
Phase: Plan-Review | Agent: Quality Reviewer | Status: PASS
Revision Verification
Finding 1: RULE 1 (HIGH) - Type name mismatch in M1 specification
STATUS: ADDRESSED
The revision correctly updates the type names:
MaintenanceScheduleResponse[]toMaintenanceSchedule(actual type frommaintenance.types.ts)DocumentResponse[]toDocumentRecord(actual type fromdocuments.types.ts)The corrected function signature now matches the actual codebase types:
This eliminates the TypeScript compilation error risk. The revision also provides a verification table showing the corrections and confirms the imports and field names.
Finding 2: RULE 1 (HIGH) - Documents API call pattern semantics clarity
STATUS: ADDRESSED
Revision 6 clarifies the API semantics:
GET /documents?type=insuranceis user-scoped (filters by authenticated user'suser_idon backend) and returns ALL documents of that type across ALL the user's vehicles. The returnedDocumentRecord[]includes avehicleIdfield for client-side grouping.This clarification confirms the approach is correct:
vehiclesApi.getAll()(user-scoped)The revision addresses the blocking concern about API semantics and overhead. The approach is sound for single-tenant architecture.
Finding 3: RULE 2 (SHOULD_FIX) - Delete deprecated hooks instead of marking @deprecated
STATUS: ADDRESSED
Revision 7 changes the plan from marking hooks
@deprecatedto deleting them in M4. Specifically:Hooks to delete (from
useDashboardData.ts):useDashboardSummary()useVehiclesNeedingAttention()useRecentActivity()Types to delete (from
types/index.ts):DashboardSummaryRecentActivityItemVehicleNeedingAttentionThe revision explicitly cites the project standard: "Delete old code when replacing it" (CLAUDE.md). Since these hooks have zero consumers after DashboardScreen is rewritten in M4, deletion is the correct approach.
New Issues
None identified.
The revisions are complete and internally consistent. Additional improvements noted:
The type corrections, API semantics clarification, and hook deletion strategy fully address all three blocking findings. The plan is now ready for implementation.
Verdict: PASS | Next: Plan APPROVED
Milestone: Execution Complete
Phase: Execution | Agent: Developer | Status: PASS
Milestone Summary
b57b835505ab82767df9e654a7f0f6684e7Files Changed
New files (7):
frontend/src/features/dashboard/components/VehicleRosterCard.tsxfrontend/src/features/dashboard/components/ActionBar.tsxfrontend/src/features/dashboard/utils/computeVehicleHealth.tsfrontend/src/features/dashboard/hooks/__tests__/useDashboardData.test.tsfrontend/src/features/dashboard/components/__tests__/VehicleRosterCard.test.tsxfrontend/src/features/dashboard/components/__tests__/ActionBar.test.tsxfrontend/src/features/dashboard/components/__tests__/DashboardScreen.test.tsxModified files (6):
frontend/src/features/dashboard/types/index.ts-- new health typesfrontend/src/features/dashboard/hooks/useDashboardData.ts-- roster data hookfrontend/src/features/dashboard/components/DashboardScreen.tsx-- new layoutfrontend/src/features/dashboard/pages/DashboardPage.tsx-- remove duplicate headingfrontend/src/features/dashboard/index.ts-- updated barrel exportsfrontend/jest.config.ts-- API client mock mapperfrontend/setupTests.ts-- TextEncoder polyfillfrontend/src/core/api/__mocks__/client.ts-- API client manual mockDeleted files (4):
frontend/src/features/dashboard/components/SummaryCards.tsxfrontend/src/features/dashboard/components/QuickActions.tsxfrontend/src/features/dashboard/components/RecentActivity.tsxfrontend/src/features/dashboard/components/VehicleAttention.tsxValidation Results
Implementation Notes
computeVehicleHealthtoutils/computeVehicleHealth.tsfor testability (avoids transitiveimport.meta.envissue in Jest)moduleNameMapperforcore/api/clientin jest config to resolve pre-existingimport.meta.envincompatibility with jsdomtype=insurance,type=registration) with graceful degradation on failureVerdict: PASS | Next: QR post-implementation review
QR Review: Post-Implementation
Phase: Review | Agent: Quality Reviewer | Status: CONDITIONAL_PASS
Code Quality Assessment
RULE 0 (CRITICAL) - Production Reliability
Status: PASS
No RULE 0 violations detected:
DashboardScreen.tsx:68-94with proper error UIuseDashboardData.ts:59-61)RULE 1 (HIGH) - Project Conformance
Status: CONDITIONAL_PASS (requires manual verification)
BLOCKING - Mobile+Desktop Manual Testing Required:
The implementation includes responsive design patterns:
grid-cols-1 sm:grid-cols-2(DashboardScreen.tsx:29, 153)useMediaQuery(theme.breakpoints.down('sm'))(DashboardScreen.tsx:55)However, no manual verification provided for actual viewport testing:
ACTION REQUIRED: Test in browser DevTools at all three breakpoints and confirm layout quality before merging.
Other RULE 1 Checks:
RULE 2 (SHOULD_FIX) - Structural Quality
Status: PASS with recommendations
Recommendations (non-blocking):
Positive observations:
Acceptance Criteria Verification
Test Results
Coverage: Health computation logic fully tested (green/yellow/red states, sorting, edge cases).
Files Reviewed
New files (8):
frontend/src/features/dashboard/components/VehicleRosterCard.tsxfrontend/src/features/dashboard/components/ActionBar.tsxfrontend/src/features/dashboard/utils/computeVehicleHealth.tsfrontend/src/features/dashboard/hooks/__tests__/useDashboardData.test.tsfrontend/src/features/dashboard/components/__tests__/VehicleRosterCard.test.tsxfrontend/src/features/dashboard/components/__tests__/ActionBar.test.tsxfrontend/src/features/dashboard/components/__tests__/DashboardScreen.test.tsxfrontend/src/core/api/__mocks__/client.tsModified files (5):
frontend/src/features/dashboard/types/index.tsfrontend/src/features/dashboard/hooks/useDashboardData.tsfrontend/src/features/dashboard/components/DashboardScreen.tsxfrontend/src/features/dashboard/pages/DashboardPage.tsxfrontend/src/features/dashboard/index.tsDeleted files (4):
frontend/src/features/dashboard/components/SummaryCards.tsxfrontend/src/features/dashboard/components/QuickActions.tsxfrontend/src/features/dashboard/components/RecentActivity.tsxfrontend/src/features/dashboard/components/VehicleAttention.tsxVerdict
CONDITIONAL_PASS - Implementation is high quality with RULE 0 compliance and comprehensive test coverage. Code patterns for responsive design are correct.
Before merging: Complete manual verification of mobile (320px, 768px) and desktop (1920px) layouts in browser DevTools. Confirm touch targets, grid behavior, and visual quality at all breakpoints.
Next: Manual responsive testing -> PR approval -> Merge to main