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

Create shared getVehicleLabel/getVehicleSubtitle in core/utils with
VehicleLike interface. Replace all direct year/make/model concatenation
across 17 consumer files to prevent null values in vehicle names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-02-13 19:32:40 -06:00
parent 75e4660c58
commit 325cf08df0
23 changed files with 63 additions and 51 deletions

View File

@@ -13,6 +13,7 @@
- `src/App.tsx`, `src/main.tsx` — app entry. - `src/App.tsx`, `src/main.tsx` — app entry.
- `src/features/*` — feature pages/components/hooks. - `src/features/*` — feature pages/components/hooks.
- `src/core/*` — auth, api, store, hooks, query config, utils. - `src/core/*` — auth, api, store, hooks, query config, utils.
- `src/core/utils/vehicleDisplay.ts` — shared vehicle display helpers: `getVehicleLabel()` (display name with fallback chain) and `getVehicleSubtitle()` (Year Make Model formatting).
- `src/shared-minimal/*` — shared UI components and theme. - `src/shared-minimal/*` — shared UI components and theme.
## Mobile + Desktop (required) ## Mobile + Desktop (required)

View File

@@ -0,0 +1,27 @@
/** Vehicle-like object with minimal fields for display purposes */
export interface VehicleLike {
year?: number | null;
make?: string | null;
model?: string | null;
trimLevel?: string | null;
nickname?: string | null;
vin?: string | null;
id?: string | null;
}
/** Primary display name with fallback chain: nickname -> year/make/model -> VIN -> ID */
export const getVehicleLabel = (vehicle: VehicleLike | 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: VehicleLike | undefined): string => {
if (!vehicle) return '';
const parts = [vehicle.year?.toString(), vehicle.make, vehicle.model].filter(Boolean);
return parts.length >= 2 ? parts.join(' ') : '';
};

View File

@@ -24,6 +24,7 @@ import {
SubscriptionTier, SubscriptionTier,
ListUsersParams, ListUsersParams,
} from '../types/admin.types'; } from '../types/admin.types';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
// Modal component for dialogs // Modal component for dialogs
interface ModalProps { interface ModalProps {
@@ -128,7 +129,7 @@ const UserVehiclesList: React.FC<{ auth0Sub: string; isOpen: boolean }> = ({ aut
<div className="space-y-1"> <div className="space-y-1">
{data.vehicles.map((vehicle, idx) => ( {data.vehicles.map((vehicle, idx) => (
<div key={idx} className="text-sm text-slate-600 dark:text-silverstone bg-slate-50 dark:bg-carbon px-2 py-1 rounded"> <div key={idx} className="text-sm text-slate-600 dark:text-silverstone bg-slate-50 dark:bg-carbon px-2 py-1 rounded">
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</div> </div>
))} ))}
</div> </div>

View File

@@ -9,6 +9,7 @@ import ErrorRoundedIcon from '@mui/icons-material/ErrorRounded';
import WarningAmberRoundedIcon from '@mui/icons-material/WarningAmberRounded'; import WarningAmberRoundedIcon from '@mui/icons-material/WarningAmberRounded';
import ScheduleRoundedIcon from '@mui/icons-material/ScheduleRounded'; import ScheduleRoundedIcon from '@mui/icons-material/ScheduleRounded';
import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard'; import { GlassCard } from '../../../shared-minimal/components/mobile/GlassCard';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
import { VehicleNeedingAttention } from '../types'; import { VehicleNeedingAttention } from '../types';
interface VehicleAttentionProps { interface VehicleAttentionProps {
@@ -104,7 +105,7 @@ export const VehicleAttention: React.FC<VehicleAttentionProps> = ({ vehicles, on
mb: 0.5, mb: 0.5,
}} }}
> >
{vehicle.nickname || `${vehicle.year} ${vehicle.make} ${vehicle.model}`} {getVehicleLabel(vehicle)}
</Box> </Box>
<p className="text-sm text-slate-600 dark:text-titanio"> <p className="text-sm text-slate-600 dark:text-titanio">
{vehicle.reason} {vehicle.reason}

View File

@@ -12,7 +12,6 @@ Document management UI with maintenance manual extraction. Handles file uploads,
| `mobile/` | Mobile-specific document layout | Mobile UI | | `mobile/` | Mobile-specific document layout | Mobile UI |
| `pages/` | DocumentsPage, DocumentDetailPage | Page layout | | `pages/` | DocumentsPage, DocumentDetailPage | Page layout |
| `types/` | TypeScript type definitions | Type changes | | `types/` | TypeScript type definitions | Type changes |
| `utils/` | Utility functions (vehicle label formatting) | Helper logic |
## Key Files ## Key Files

View File

@@ -10,7 +10,7 @@ import { AddDocumentDialog } from '../components/AddDocumentDialog';
import { ExpirationBadge } from '../components/ExpirationBadge'; import { ExpirationBadge } from '../components/ExpirationBadge';
import { DocumentCardMetadata } from '../components/DocumentCardMetadata'; import { DocumentCardMetadata } from '../components/DocumentCardMetadata';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { getVehicleLabel } from '../utils/vehicleLabel'; import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
export const DocumentsMobileScreen: React.FC = () => { export const DocumentsMobileScreen: React.FC = () => {
console.log('[DocumentsMobileScreen] Component initializing'); console.log('[DocumentsMobileScreen] Component initializing');

View File

@@ -12,7 +12,7 @@ import { EditDocumentDialog } from '../components/EditDocumentDialog';
import { ExpirationBadge } from '../components/ExpirationBadge'; import { ExpirationBadge } from '../components/ExpirationBadge';
import { DocumentCardMetadata } from '../components/DocumentCardMetadata'; import { DocumentCardMetadata } from '../components/DocumentCardMetadata';
import { useVehicle, useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicle, useVehicles } from '../../vehicles/hooks/useVehicles';
import { getVehicleLabel } from '../utils/vehicleLabel'; import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
export const DocumentDetailPage: React.FC = () => { export const DocumentDetailPage: React.FC = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();

View File

@@ -21,7 +21,7 @@ import { ExpirationBadge } from '../components/ExpirationBadge';
import type { DocumentRecord } from '../types/documents.types'; import type { DocumentRecord } from '../types/documents.types';
import { DocumentCardMetadata } from '../components/DocumentCardMetadata'; import { DocumentCardMetadata } from '../components/DocumentCardMetadata';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { getVehicleLabel } from '../utils/vehicleLabel'; import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
export const DocumentsPage: React.FC = () => { export const DocumentsPage: React.FC = () => {
const { isAuthenticated, isLoading: authLoading, loginWithRedirect } = useAuth0(); const { isAuthenticated, isLoading: authLoading, loginWithRedirect } = useAuth0();

View File

@@ -1,11 +0,0 @@
import type { Vehicle } from '../../vehicles/types/vehicles.types';
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);
const primary = parts.join(' ').trim();
if (primary.length > 0) return primary;
if (vehicle.vin?.length > 0) return vehicle.vin;
return vehicle.id.slice(0, 8) + '...';
};

View File

@@ -23,6 +23,7 @@ import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { useResolveAssociation } from '../hooks/usePendingAssociations'; import { useResolveAssociation } from '../hooks/usePendingAssociations';
import type { PendingVehicleAssociation } from '../types/email-ingestion.types'; import type { PendingVehicleAssociation } from '../types/email-ingestion.types';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface ResolveAssociationDialogProps { interface ResolveAssociationDialogProps {
open: boolean; open: boolean;
@@ -166,9 +167,7 @@ export const ResolveAssociationDialog: React.FC<ResolveAssociationDialogProps> =
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}> <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{vehicles.map((vehicle) => { {vehicles.map((vehicle) => {
const isSelected = selectedVehicleId === vehicle.id; const isSelected = selectedVehicleId === vehicle.id;
const vehicleName = vehicle.nickname const vehicleName = getVehicleLabel(vehicle);
|| [vehicle.year, vehicle.make, vehicle.model].filter(Boolean).join(' ')
|| 'Unnamed Vehicle';
return ( return (
<Box <Box

View File

@@ -36,6 +36,7 @@ import { SubtypeCheckboxGroup } from './SubtypeCheckboxGroup';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { documentsApi } from '../../documents/api/documents.api'; import { documentsApi } from '../../documents/api/documents.api';
import type { Vehicle } from '../../vehicles/types/vehicles.types'; import type { Vehicle } from '../../vehicles/types/vehicles.types';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface MaintenanceRecordEditDialogProps { interface MaintenanceRecordEditDialogProps {
open: boolean; open: boolean;
@@ -218,10 +219,7 @@ export const MaintenanceRecordEditDialog: React.FC<MaintenanceRecordEditDialogPr
disabled disabled
value={(() => { value={(() => {
const vehicle = vehicles?.find((v: Vehicle) => v.id === record.vehicleId); const vehicle = vehicles?.find((v: Vehicle) => v.id === record.vehicleId);
if (!vehicle) return 'Unknown Vehicle'; return getVehicleLabel(vehicle);
if (vehicle.nickname?.trim()) return vehicle.nickname.trim();
const parts = [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean);
return parts.length > 0 ? parts.join(' ') : 'Vehicle';
})()} })()}
helperText="Vehicle cannot be changed when editing" helperText="Vehicle cannot be changed when editing"
/> />

View File

@@ -46,6 +46,7 @@ import { useTierAccess } from '../../../core/hooks/useTierAccess';
import { UpgradeRequiredDialog } from '../../../shared-minimal/components/UpgradeRequiredDialog'; import { UpgradeRequiredDialog } from '../../../shared-minimal/components/UpgradeRequiredDialog';
import { documentsApi } from '../../documents/api/documents.api'; import { documentsApi } from '../../documents/api/documents.api';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
const schema = z.object({ const schema = z.object({
vehicle_id: z.string().uuid({ message: 'Please select a vehicle' }), vehicle_id: z.string().uuid({ message: 'Please select a vehicle' }),
@@ -279,7 +280,7 @@ export const MaintenanceRecordForm: React.FC = () => {
{vehicles && vehicles.length > 0 ? ( {vehicles && vehicles.length > 0 ? (
vehicles.map((vehicle) => ( vehicles.map((vehicle) => (
<MenuItem key={vehicle.id} value={vehicle.id}> <MenuItem key={vehicle.id} value={vehicle.id}>
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</MenuItem> </MenuItem>
)) ))
) : ( ) : (

View File

@@ -39,6 +39,7 @@ import {
import { SubtypeCheckboxGroup } from './SubtypeCheckboxGroup'; import { SubtypeCheckboxGroup } from './SubtypeCheckboxGroup';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import type { Vehicle } from '../../vehicles/types/vehicles.types'; import type { Vehicle } from '../../vehicles/types/vehicles.types';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface MaintenanceScheduleEditDialogProps { interface MaintenanceScheduleEditDialogProps {
open: boolean; open: boolean;
@@ -206,10 +207,7 @@ export const MaintenanceScheduleEditDialog: React.FC<MaintenanceScheduleEditDial
disabled disabled
value={(() => { value={(() => {
const vehicle = vehicles?.find((v: Vehicle) => v.id === schedule.vehicleId); const vehicle = vehicles?.find((v: Vehicle) => v.id === schedule.vehicleId);
if (!vehicle) return 'Unknown Vehicle'; return getVehicleLabel(vehicle);
if (vehicle.nickname?.trim()) return vehicle.nickname.trim();
const parts = [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean);
return parts.length > 0 ? parts.join(' ') : 'Vehicle';
})()} })()}
helperText="Vehicle cannot be changed when editing" helperText="Vehicle cannot be changed when editing"
/> />

View File

@@ -42,6 +42,7 @@ import {
getCategoryDisplayName, getCategoryDisplayName,
} from '../types/maintenance.types'; } from '../types/maintenance.types';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
const schema = z const schema = z
.object({ .object({
@@ -214,7 +215,7 @@ export const MaintenanceScheduleForm: React.FC = () => {
{vehicles && vehicles.length > 0 ? ( {vehicles && vehicles.length > 0 ? (
vehicles.map((vehicle) => ( vehicles.map((vehicle) => (
<MenuItem key={vehicle.id} value={vehicle.id}> <MenuItem key={vehicle.id} value={vehicle.id}>
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</MenuItem> </MenuItem>
)) ))
) : ( ) : (

View File

@@ -16,6 +16,7 @@ import { MaintenanceScheduleForm } from '../components/MaintenanceScheduleForm';
import { MaintenanceSchedulesList } from '../components/MaintenanceSchedulesList'; import { MaintenanceSchedulesList } from '../components/MaintenanceSchedulesList';
import { MaintenanceScheduleEditDialog } from '../components/MaintenanceScheduleEditDialog'; import { MaintenanceScheduleEditDialog } from '../components/MaintenanceScheduleEditDialog';
import type { MaintenanceRecordResponse, UpdateMaintenanceRecordRequest, MaintenanceScheduleResponse, UpdateScheduleRequest } from '../types/maintenance.types'; import type { MaintenanceRecordResponse, UpdateMaintenanceRecordRequest, MaintenanceScheduleResponse, UpdateScheduleRequest } from '../types/maintenance.types';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
export const MaintenanceMobileScreen: React.FC = () => { export const MaintenanceMobileScreen: React.FC = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -125,7 +126,7 @@ export const MaintenanceMobileScreen: React.FC = () => {
{vehicles && vehicles.length > 0 ? ( {vehicles && vehicles.length > 0 ? (
vehicles.map((vehicle) => ( vehicles.map((vehicle) => (
<MenuItem key={vehicle.id} value={vehicle.id} sx={{ minHeight: 44 }}> <MenuItem key={vehicle.id} value={vehicle.id} sx={{ minHeight: 44 }}>
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</MenuItem> </MenuItem>
)) ))
) : ( ) : (

View File

@@ -16,6 +16,7 @@ import { useMaintenanceRecords } from '../hooks/useMaintenanceRecords';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { FormSuspense } from '../../../components/SuspenseWrappers'; import { FormSuspense } from '../../../components/SuspenseWrappers';
import type { MaintenanceRecordResponse, UpdateMaintenanceRecordRequest, MaintenanceScheduleResponse, UpdateScheduleRequest } from '../types/maintenance.types'; import type { MaintenanceRecordResponse, UpdateMaintenanceRecordRequest, MaintenanceScheduleResponse, UpdateScheduleRequest } from '../types/maintenance.types';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
export const MaintenancePage: React.FC = () => { export const MaintenancePage: React.FC = () => {
const { data: vehicles, isLoading: isLoadingVehicles } = useVehicles(); const { data: vehicles, isLoading: isLoadingVehicles } = useVehicles();
@@ -156,7 +157,7 @@ export const MaintenancePage: React.FC = () => {
{vehicles && vehicles.length > 0 ? ( {vehicles && vehicles.length > 0 ? (
vehicles.map((vehicle) => ( vehicles.map((vehicle) => (
<MenuItem key={vehicle.id} value={vehicle.id}> <MenuItem key={vehicle.id} value={vehicle.id}>
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</MenuItem> </MenuItem>
)) ))
) : ( ) : (

View File

@@ -7,6 +7,7 @@ import { useSettings } from '../hooks/useSettings';
import { useProfile, useUpdateProfile } from '../hooks/useProfile'; import { useProfile, useUpdateProfile } from '../hooks/useProfile';
import { useExportUserData } from '../hooks/useExportUserData'; import { useExportUserData } from '../hooks/useExportUserData';
import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { useVehicles } from '../../vehicles/hooks/useVehicles';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
import { useSubscription } from '../../subscription/hooks/useSubscription'; import { useSubscription } from '../../subscription/hooks/useSubscription';
import { useAdminAccess } from '../../../core/auth/useAdminAccess'; import { useAdminAccess } from '../../../core/auth/useAdminAccess';
import { useNavigationStore } from '../../../core/store'; import { useNavigationStore } from '../../../core/store';
@@ -373,7 +374,7 @@ export const MobileSettingsScreen: React.FC = () => {
className="p-3 bg-slate-50 dark:bg-nero rounded-lg" className="p-3 bg-slate-50 dark:bg-nero rounded-lg"
> >
<p className="font-medium text-slate-800 dark:text-avus"> <p className="font-medium text-slate-800 dark:text-avus">
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
</p> </p>
{vehicle.nickname && ( {vehicle.nickname && (
<p className="text-sm text-slate-500 dark:text-titanio">{vehicle.nickname}</p> <p className="text-sm text-slate-500 dark:text-titanio">{vehicle.nickname}</p>

View File

@@ -13,6 +13,7 @@ import {
Box, Box,
} from '@mui/material'; } from '@mui/material';
import type { SubscriptionTier } from '../types/subscription.types'; import type { SubscriptionTier } from '../types/subscription.types';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface Vehicle { interface Vehicle {
id: string; id: string;
@@ -70,13 +71,6 @@ export const VehicleSelectionDialog = ({
onConfirm(selectedVehicleIds); onConfirm(selectedVehicleIds);
}; };
const getVehicleLabel = (vehicle: Vehicle): string => {
if (vehicle.nickname) {
return vehicle.nickname;
}
const parts = [vehicle.year, vehicle.make, vehicle.model].filter(Boolean);
return parts.join(' ') || 'Unknown Vehicle';
};
const canConfirm = selectedVehicleIds.length > 0 && selectedVehicleIds.length <= maxSelections; const canConfirm = selectedVehicleIds.length > 0 && selectedVehicleIds.length <= maxSelections;

View File

@@ -9,6 +9,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
import { Vehicle } from '../types/vehicles.types'; import { Vehicle } from '../types/vehicles.types';
import { useUnits } from '../../../core/units/UnitsContext'; import { useUnits } from '../../../core/units/UnitsContext';
import { VehicleImage } from './VehicleImage'; import { VehicleImage } from './VehicleImage';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface VehicleCardProps { interface VehicleCardProps {
vehicle: Vehicle; vehicle: Vehicle;
@@ -24,8 +25,7 @@ export const VehicleCard: React.FC<VehicleCardProps> = ({
onSelect, onSelect,
}) => { }) => {
const { formatDistance } = useUnits(); const { formatDistance } = useUnits();
const displayName = vehicle.nickname || const displayName = getVehicleLabel(vehicle);
[vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ');
return ( return (
<Card <Card

View File

@@ -12,6 +12,7 @@ import { FuelLogEditDialog } from '../../fuel-logs/components/FuelLogEditDialog'
import { fuelLogsApi } from '../../fuel-logs/api/fuel-logs.api'; import { fuelLogsApi } from '../../fuel-logs/api/fuel-logs.api';
import { VehicleImage } from '../components/VehicleImage'; import { VehicleImage } from '../components/VehicleImage';
import { OwnershipCostsList } from '../../ownership-costs'; import { OwnershipCostsList } from '../../ownership-costs';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface VehicleDetailMobileProps { interface VehicleDetailMobileProps {
vehicle: Vehicle; vehicle: Vehicle;
@@ -38,8 +39,7 @@ export const VehicleDetailMobile: React.FC<VehicleDetailMobileProps> = ({
onLogFuel, onLogFuel,
onEdit onEdit
}) => { }) => {
const displayName = vehicle.nickname || const displayName = getVehicleLabel(vehicle);
[vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ') || 'Vehicle';
const displayModel = vehicle.model || 'Unknown Model'; const displayModel = vehicle.model || 'Unknown Model';
const [recordFilter, setRecordFilter] = useState<'All' | 'Fuel Logs' | 'Maintenance' | 'Documents'>('All'); const [recordFilter, setRecordFilter] = useState<'All' | 'Fuel Logs' | 'Maintenance' | 'Documents'>('All');

View File

@@ -6,6 +6,7 @@ import React from 'react';
import { Card, CardActionArea, Box, Typography } from '@mui/material'; import { Card, CardActionArea, Box, Typography } from '@mui/material';
import { Vehicle } from '../types/vehicles.types'; import { Vehicle } from '../types/vehicles.types';
import { VehicleImage } from '../components/VehicleImage'; import { VehicleImage } from '../components/VehicleImage';
import { getVehicleLabel } from '@/core/utils/vehicleDisplay';
interface VehicleMobileCardProps { interface VehicleMobileCardProps {
vehicle: Vehicle; vehicle: Vehicle;
@@ -18,8 +19,7 @@ export const VehicleMobileCard: React.FC<VehicleMobileCardProps> = ({
onClick, onClick,
compact = false compact = false
}) => { }) => {
const displayName = vehicle.nickname || const displayName = getVehicleLabel(vehicle);
[vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ') || 'Vehicle';
const displayModel = vehicle.model || 'Unknown Model'; const displayModel = vehicle.model || 'Unknown Model';
return ( return (

View File

@@ -12,6 +12,7 @@ import LocalGasStationIcon from '@mui/icons-material/LocalGasStation';
import BuildIcon from '@mui/icons-material/Build'; import BuildIcon from '@mui/icons-material/Build';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import { Vehicle } from '../types/vehicles.types'; import { Vehicle } from '../types/vehicles.types';
import { getVehicleLabel, getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
import { vehiclesApi } from '../api/vehicles.api'; import { vehiclesApi } from '../api/vehicles.api';
import { Card } from '../../../shared-minimal/components/Card'; import { Card } from '../../../shared-minimal/components/Card';
import { VehicleForm } from '../components/VehicleForm'; import { VehicleForm } from '../components/VehicleForm';
@@ -224,8 +225,7 @@ export const VehicleDetailPage: React.FC = () => {
); );
} }
const displayName = vehicle.nickname || const displayName = getVehicleLabel(vehicle);
[vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ') || 'Vehicle';
const handleRowClick = (recId: string, type: VehicleRecord['type']) => { const handleRowClick = (recId: string, type: VehicleRecord['type']) => {
if (type === 'Fuel Logs') { if (type === 'Fuel Logs') {
@@ -373,8 +373,7 @@ export const VehicleDetailPage: React.FC = () => {
Vehicle Details Vehicle Details
</Typography> </Typography>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
{vehicle.year} {vehicle.make} {vehicle.model} {getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
{vehicle.trimLevel && ` ${vehicle.trimLevel}`}
</Typography> </Typography>
{vehicle.vin && ( {vehicle.vin && (
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}> <Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>

View File

@@ -11,6 +11,7 @@ import { useAdminAccess } from '../core/auth/useAdminAccess';
import { useProfile, useUpdateProfile } from '../features/settings/hooks/useProfile'; import { useProfile, useUpdateProfile } from '../features/settings/hooks/useProfile';
import { useExportUserData } from '../features/settings/hooks/useExportUserData'; import { useExportUserData } from '../features/settings/hooks/useExportUserData';
import { useVehicles } from '../features/vehicles/hooks/useVehicles'; import { useVehicles } from '../features/vehicles/hooks/useVehicles';
import { getVehicleSubtitle } from '@/core/utils/vehicleDisplay';
import { useSubscription } from '../features/subscription/hooks/useSubscription'; import { useSubscription } from '../features/subscription/hooks/useSubscription';
import { useTheme } from '../shared-minimal/theme/ThemeContext'; import { useTheme } from '../shared-minimal/theme/ThemeContext';
import { DeleteAccountDialog } from '../features/settings/components/DeleteAccountDialog'; import { DeleteAccountDialog } from '../features/settings/components/DeleteAccountDialog';
@@ -375,7 +376,7 @@ export const SettingsPage: React.FC = () => {
{index > 0 && <Divider />} {index > 0 && <Divider />}
<ListItem sx={{ py: 1.5 }}> <ListItem sx={{ py: 1.5 }}>
<ListItemText <ListItemText
primary={`${vehicle.year} ${vehicle.make} ${vehicle.model}`} primary={getVehicleSubtitle(vehicle) || 'Unknown Vehicle'}
secondary={vehicle.nickname || undefined} secondary={vehicle.nickname || undefined}
primaryTypographyProps={{ fontWeight: 500 }} primaryTypographyProps={{ fontWeight: 500 }}
/> />