fix: null model display on Settings vehicle list (#162) #165

Closed
opened 2026-02-13 22:22:17 +00:00 by egullickson · 2 comments
Owner

Relates to #162

Severity: Critical

Problem

On the Settings page vehicle list, one vehicle shows "2022 Volkswagen null". The null should be replaced with a fallback like "Unknown Model" or hidden entirely. The mobile Vehicles page already shows "Unknown Model" for the same vehicle, so the fallback is inconsistent across views.

Acceptance Criteria

  • No null values displayed in vehicle names anywhere in the app
  • Consistent fallback text (e.g. "Unknown Model") used across Settings vehicle list, mobile Vehicles page, and any other vehicle name displays
  • Tested on mobile (320px, 768px) and desktop (1920px)
Relates to #162 ## Severity: Critical ## Problem On the Settings page vehicle list, one vehicle shows "2022 Volkswagen null". The `null` should be replaced with a fallback like "Unknown Model" or hidden entirely. The mobile Vehicles page already shows "Unknown Model" for the same vehicle, so the fallback is inconsistent across views. ## Acceptance Criteria - No `null` values displayed in vehicle names anywhere in the app - Consistent fallback text (e.g. "Unknown Model") used across Settings vehicle list, mobile Vehicles page, and any other vehicle name displays - Tested on mobile (320px, 768px) and desktop (1920px)
egullickson added the
status
backlog
type
chore
labels 2026-02-13 22:22:27 +00:00
egullickson added this to the Sprint 2026-02-02 milestone 2026-02-13 22:22:36 +00:00
Author
Owner

Implementation Plan (from #162 -- Milestone 1)

Phase: 1 (Foundation) | Priority: Critical | Depends on: None | Blocks: #167, #171

Context

Multiple locations across the frontend concatenate ${year} ${make} ${model} directly, resulting in "null" text when fields are missing. A shared utility exists in frontend/src/features/documents/utils/vehicleLabel.ts but is only used by the documents feature. This milestone promotes and extends that utility for project-wide use.

Implementation

1. Create frontend/src/core/utils/ directory (does not currently exist)

2. Create frontend/src/core/utils/vehicleDisplay.ts:

import { Vehicle } from '@/features/vehicles/types';

/** Primary display name with fallback chain: nickname -> year/make/model -> VIN -> ID */
export const getVehicleLabel = (vehicle: Vehicle | undefined): string => {
  if (!vehicle) return 'Unknown Vehicle';
  if (vehicle.nickname?.trim()) return vehicle.nickname.trim();
  const parts = [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean);
  if (parts.length > 0) return parts.join(' ');
  if (vehicle.vin) return vehicle.vin;
  return vehicle.id ? `${vehicle.id.substring(0, 8)}...` : 'Unknown Vehicle';
};

/** Subtitle line: "Year Make Model" with null safety. Returns empty string if insufficient data. */
export const getVehicleSubtitle = (vehicle: Vehicle | undefined): string => {
  if (!vehicle) return '';
  const parts = [vehicle.year?.toString(), vehicle.make, vehicle.model].filter(Boolean);
  return parts.length >= 2 ? parts.join(' ') : '';
};

3. Delete frontend/src/features/documents/utils/vehicleLabel.ts and update all imports to use the new location.

4. Replace direct concatenation in these files:

File Line(s) Current Pattern Change
frontend/src/pages/SettingsPage.tsx ~378 ${year} ${make} ${model} concat Use getVehicleSubtitle()
frontend/src/features/settings/mobile/MobileSettingsScreen.tsx ~376 ${year} ${make} ${model} concat Use getVehicleSubtitle()
frontend/src/features/dashboard/components/VehicleAttention.tsx ~107 Direct concat Use getVehicleLabel()
frontend/src/features/vehicles/pages/VehicleDetailPage.tsx ~228 filter(Boolean) pattern in displayName Use getVehicleLabel()
frontend/src/features/vehicles/pages/VehicleDetailPage.tsx ~376 Direct concat in subtitle Use getVehicleSubtitle()

5. Documentation: After completion, add to frontend/README.md Structure section:

src/core/utils/vehicleDisplay.ts -- Shared vehicle display helpers: getVehicleLabel() (display name with fallback chain) and getVehicleSubtitle() (Year Make Model formatting). Used across vehicles, documents, dashboard, settings, and maintenance features.

Commit Convention

fix: promote vehicle display utils to core with null safety (refs #165)

Test Criteria

  • No null text visible anywhere in the UI
  • Consistent "Unknown Vehicle" or "Unknown Model" fallback when data is missing
  • Settings vehicle list, dashboard attention cards, and vehicle detail pages all display correctly
  • Verify on mobile (320px, 768px) and desktop (1920px) viewports
  • All existing document feature vehicle labels still work after import migration

Branch

Work on branch issue-162-ux-design-audit-cleanup (shared with all #162 sub-issues)

## Implementation Plan (from #162 -- Milestone 1) **Phase**: 1 (Foundation) | **Priority**: Critical | **Depends on**: None | **Blocks**: #167, #171 ### Context Multiple locations across the frontend concatenate `${year} ${make} ${model}` directly, resulting in "null" text when fields are missing. A shared utility exists in `frontend/src/features/documents/utils/vehicleLabel.ts` but is only used by the documents feature. This milestone promotes and extends that utility for project-wide use. ### Implementation **1. Create `frontend/src/core/utils/` directory** (does not currently exist) **2. Create `frontend/src/core/utils/vehicleDisplay.ts`:** ```typescript import { Vehicle } from '@/features/vehicles/types'; /** Primary display name with fallback chain: nickname -> year/make/model -> VIN -> ID */ export const getVehicleLabel = (vehicle: Vehicle | undefined): string => { if (!vehicle) return 'Unknown Vehicle'; if (vehicle.nickname?.trim()) return vehicle.nickname.trim(); const parts = [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean); if (parts.length > 0) return parts.join(' '); if (vehicle.vin) return vehicle.vin; return vehicle.id ? `${vehicle.id.substring(0, 8)}...` : 'Unknown Vehicle'; }; /** Subtitle line: "Year Make Model" with null safety. Returns empty string if insufficient data. */ export const getVehicleSubtitle = (vehicle: Vehicle | undefined): string => { if (!vehicle) return ''; const parts = [vehicle.year?.toString(), vehicle.make, vehicle.model].filter(Boolean); return parts.length >= 2 ? parts.join(' ') : ''; }; ``` **3. Delete** `frontend/src/features/documents/utils/vehicleLabel.ts` and update all imports to use the new location. **4. Replace direct concatenation in these files:** | File | Line(s) | Current Pattern | Change | |------|---------|-----------------|--------| | `frontend/src/pages/SettingsPage.tsx` | ~378 | `${year} ${make} ${model}` concat | Use `getVehicleSubtitle()` | | `frontend/src/features/settings/mobile/MobileSettingsScreen.tsx` | ~376 | `${year} ${make} ${model}` concat | Use `getVehicleSubtitle()` | | `frontend/src/features/dashboard/components/VehicleAttention.tsx` | ~107 | Direct concat | Use `getVehicleLabel()` | | `frontend/src/features/vehicles/pages/VehicleDetailPage.tsx` | ~228 | `filter(Boolean)` pattern in displayName | Use `getVehicleLabel()` | | `frontend/src/features/vehicles/pages/VehicleDetailPage.tsx` | ~376 | Direct concat in subtitle | Use `getVehicleSubtitle()` | **5. Documentation**: After completion, add to `frontend/README.md` Structure section: > `src/core/utils/vehicleDisplay.ts` -- Shared vehicle display helpers: `getVehicleLabel()` (display name with fallback chain) and `getVehicleSubtitle()` (Year Make Model formatting). Used across vehicles, documents, dashboard, settings, and maintenance features. ### Commit Convention ``` fix: promote vehicle display utils to core with null safety (refs #165) ``` ### Test Criteria - No `null` text visible anywhere in the UI - Consistent "Unknown Vehicle" or "Unknown Model" fallback when data is missing - Settings vehicle list, dashboard attention cards, and vehicle detail pages all display correctly - Verify on mobile (320px, 768px) and desktop (1920px) viewports - All existing document feature vehicle labels still work after import migration ### Branch Work on branch `issue-162-ux-design-audit-cleanup` (shared with all #162 sub-issues)
egullickson added
status
in-progress
and removed
status
backlog
labels 2026-02-14 01:25:07 +00:00
Author
Owner

Milestone: Execution Complete

Phase: Execution | Agent: Developer | Status: PASS

Changes Summary

Created: frontend/src/core/utils/vehicleDisplay.ts

  • VehicleLike interface for flexible type compatibility
  • getVehicleLabel() -- primary display name with fallback chain: nickname -> year/make/model/trim -> VIN -> ID
  • getVehicleSubtitle() -- "Year Make Model" line with null safety

Deleted: frontend/src/features/documents/utils/vehicleLabel.ts (promoted to core)

Updated 17 consumer files replacing direct ${vehicle.year} ${vehicle.make} ${vehicle.model} concatenation:

  • SettingsPage.tsx, MobileSettingsScreen.tsx -- Settings vehicle list
  • VehicleAttention.tsx -- Dashboard attention cards
  • VehicleDetailPage.tsx -- Vehicle detail name + subtitle
  • VehicleDetailMobile.tsx, VehicleMobileCard.tsx, VehicleCard.tsx -- Vehicle list/cards
  • MaintenancePage.tsx, MaintenanceMobileScreen.tsx -- Maintenance vehicle display
  • MaintenanceRecordForm.tsx, MaintenanceScheduleForm.tsx -- Form vehicle selectors
  • MaintenanceRecordEditDialog.tsx, MaintenanceScheduleEditDialog.tsx -- Edit dialogs
  • ResolveAssociationDialog.tsx -- Email ingestion vehicle association
  • VehicleSelectionDialog.tsx -- Subscription vehicle selection
  • AdminUsersMobileScreen.tsx -- Admin user vehicle list
  • DocumentDetailPage.tsx, DocumentsMobileScreen.tsx, DocumentsPage.tsx -- Document imports migrated

Documentation: Updated frontend/README.md and frontend/src/features/documents/CLAUDE.md

Verification

  • TypeScript type-check: 0 errors
  • ESLint: 0 errors (226 pre-existing warnings)
  • No remaining direct vehicle field concatenation outside vehicleDisplay.ts

Commit

325cf08 -- fix: promote vehicle display utils to core with null safety (refs #165)

Verdict: PASS | Next: Visual verification on mobile/desktop viewports

## Milestone: Execution Complete **Phase**: Execution | **Agent**: Developer | **Status**: PASS ### Changes Summary **Created**: `frontend/src/core/utils/vehicleDisplay.ts` - `VehicleLike` interface for flexible type compatibility - `getVehicleLabel()` -- primary display name with fallback chain: nickname -> year/make/model/trim -> VIN -> ID - `getVehicleSubtitle()` -- "Year Make Model" line with null safety **Deleted**: `frontend/src/features/documents/utils/vehicleLabel.ts` (promoted to core) **Updated 17 consumer files** replacing direct `${vehicle.year} ${vehicle.make} ${vehicle.model}` concatenation: - `SettingsPage.tsx`, `MobileSettingsScreen.tsx` -- Settings vehicle list - `VehicleAttention.tsx` -- Dashboard attention cards - `VehicleDetailPage.tsx` -- Vehicle detail name + subtitle - `VehicleDetailMobile.tsx`, `VehicleMobileCard.tsx`, `VehicleCard.tsx` -- Vehicle list/cards - `MaintenancePage.tsx`, `MaintenanceMobileScreen.tsx` -- Maintenance vehicle display - `MaintenanceRecordForm.tsx`, `MaintenanceScheduleForm.tsx` -- Form vehicle selectors - `MaintenanceRecordEditDialog.tsx`, `MaintenanceScheduleEditDialog.tsx` -- Edit dialogs - `ResolveAssociationDialog.tsx` -- Email ingestion vehicle association - `VehicleSelectionDialog.tsx` -- Subscription vehicle selection - `AdminUsersMobileScreen.tsx` -- Admin user vehicle list - `DocumentDetailPage.tsx`, `DocumentsMobileScreen.tsx`, `DocumentsPage.tsx` -- Document imports migrated **Documentation**: Updated `frontend/README.md` and `frontend/src/features/documents/CLAUDE.md` ### Verification - TypeScript type-check: 0 errors - ESLint: 0 errors (226 pre-existing warnings) - No remaining direct vehicle field concatenation outside `vehicleDisplay.ts` ### Commit `325cf08` -- `fix: promote vehicle display utils to core with null safety (refs #165)` *Verdict*: PASS | *Next*: Visual verification on mobile/desktop viewports
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#165