/** * @ai-summary Vehicle detail page matching VehicleForm styling */ import React, { useMemo, useState, useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { Box, Typography, Button as MuiButton, Divider, FormControl, InputLabel, Select, MenuItem, Table, TableHead, TableRow, TableCell, TableBody, Dialog, DialogTitle, DialogContent, useMediaQuery } from '@mui/material'; import { useQueryClient } from '@tanstack/react-query'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import EditIcon from '@mui/icons-material/Edit'; import LocalGasStationIcon from '@mui/icons-material/LocalGasStation'; import BuildIcon from '@mui/icons-material/Build'; import { Vehicle } from '../types/vehicles.types'; import { vehiclesApi } from '../api/vehicles.api'; import { Card } from '../../../shared-minimal/components/Card'; import { VehicleForm } from '../components/VehicleForm'; import { useFuelLogs } from '../../fuel-logs/hooks/useFuelLogs'; import { FuelLogResponse, UpdateFuelLogRequest } from '../../fuel-logs/types/fuel-logs.types'; import { FuelLogEditDialog } from '../../fuel-logs/components/FuelLogEditDialog'; import { FuelLogForm } from '../../fuel-logs/components/FuelLogForm'; // Unit conversions now handled by backend import { fuelLogsApi } from '../../fuel-logs/api/fuel-logs.api'; const DetailField: React.FC<{ label: string; value?: string | number; isRequired?: boolean; className?: string; }> = ({ label, value, isRequired, className = "" }) => (
{value || Not provided}
); export const VehicleDetailPage: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const [vehicle, setVehicle] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isEditing, setIsEditing] = useState(false); const [error, setError] = useState(null); const [recordFilter, setRecordFilter] = useState<'All' | 'Fuel Logs' | 'Maintenance' | 'Documents'>('All'); const { fuelLogs, isLoading: isFuelLoading } = useFuelLogs(id); const queryClient = useQueryClient(); const [editingLog, setEditingLog] = useState(null); const [showAddDialog, setShowAddDialog] = useState(false); const isSmallScreen = useMediaQuery('(max-width:600px)'); // Unit conversions now handled by backend // Define records list hooks BEFORE any early returns to keep hooks order stable type VehicleRecord = { id: string; type: 'Fuel Logs' | 'Maintenance' | 'Documents'; date: string; // ISO summary: string; amount?: string; // formatted }; const records: VehicleRecord[] = useMemo(() => { const list: VehicleRecord[] = []; if (fuelLogs && Array.isArray(fuelLogs)) { // Build a map of prior odometer readings to compute trip distance when missing const logsAsc = [...(fuelLogs as FuelLogResponse[])].sort( (a, b) => new Date(a.dateTime).getTime() - new Date(b.dateTime).getTime() ); const prevOdoById = new Map(); let lastOdo: number | undefined = undefined; for (const l of logsAsc) { prevOdoById.set(l.id, lastOdo); if (typeof l.odometerReading === 'number' && !isNaN(l.odometerReading)) { lastOdo = l.odometerReading; } } for (const log of fuelLogs as FuelLogResponse[]) { const parts: string[] = []; // Efficiency: Use backend calculation (primary display) if (typeof log.efficiency === 'number' && log.efficiency > 0) { parts.push(`${log.efficiencyLabel || 'MPG'}: ${log.efficiency.toFixed(3)}`); } // Grade label (secondary display) if (log.fuelGrade) { parts.push(`Grade: ${log.fuelGrade}`); } else if (log.fuelType) { const ft = String(log.fuelType); parts.push(ft.charAt(0).toUpperCase() + ft.slice(1)); } const summary = parts.join(' • '); const amount = (typeof log.totalCost === 'number') ? `$${log.totalCost.toFixed(2)}` : undefined; list.push({ id: log.id, type: 'Fuel Logs', date: log.dateTime, summary, amount }); } } return list.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }, [fuelLogs]); const filteredRecords = useMemo(() => { if (recordFilter === 'All') return records; return records.filter(r => r.type === recordFilter); }, [records, recordFilter]); useEffect(() => { const loadVehicle = async () => { if (!id) return; try { setIsLoading(true); const vehicleData = await vehiclesApi.getById(id); setVehicle(vehicleData); } catch (err) { setError('Failed to load vehicle details'); console.error('Error loading vehicle:', err); } finally { setIsLoading(false); } }; loadVehicle(); }, [id]); const handleBack = () => { navigate('/vehicles'); }; const handleEdit = () => { setIsEditing(true); }; const handleUpdateVehicle = async (data: any) => { if (!vehicle) return; try { const updatedVehicle = await vehiclesApi.update(vehicle.id, data); setVehicle(updatedVehicle); setIsEditing(false); } catch (err) { console.error('Error updating vehicle:', err); } }; const handleCancelEdit = () => { setIsEditing(false); }; if (isLoading) { return ( Loading vehicle details... ); } if (error || !vehicle) { return ( {error || 'Vehicle not found'} } > Back to Vehicles ); } const displayName = vehicle.nickname || [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ') || 'Vehicle'; const handleRowClick = (recId: string, type: VehicleRecord['type']) => { if (type === 'Fuel Logs') { const log = (fuelLogs as FuelLogResponse[] | undefined)?.find(l => l.id === recId) || null; setEditingLog(log); } }; const handleCloseEdit = () => setEditingLog(null); const handleSaveEdit = async (id: string, data: UpdateFuelLogRequest) => { await fuelLogsApi.update(id, data); await queryClient.invalidateQueries({ queryKey: ['fuelLogs', id] }); await queryClient.invalidateQueries({ queryKey: ['fuelLogs', vehicle?.id] }); await queryClient.invalidateQueries({ queryKey: ['fuelLogs'] }); setEditingLog(null); }; if (isEditing) { return ( } onClick={handleCancelEdit} sx={{ mr: 2 }} > Cancel Edit {displayName} ); } return ( } onClick={handleBack} sx={{ mr: 2 }} > Back {displayName} } onClick={handleEdit} sx={{ borderRadius: '999px' }} > Edit Vehicle } sx={{ borderRadius: '999px' }} onClick={() => setShowAddDialog(true)} > Add Fuel Log } sx={{ borderRadius: '999px' }} > Schedule Maintenance Vehicle Details
{/* Vehicle Specification Section */}
Vehicle Records Filter Date Type Summary Amount {isFuelLoading && ( Loading records… )} {!isFuelLoading && filteredRecords.length === 0 && ( No records found for this filter. )} {!isFuelLoading && filteredRecords.map((rec) => ( handleRowClick(rec.id, rec.type)}> {new Date(rec.date).toLocaleDateString()} {rec.type} {rec.summary} {rec.amount || '—'} ))}
{/* Edit Dialog for Fuel Logs */} {/* Add Fuel Log Dialog */} setShowAddDialog(false)} maxWidth="md" fullWidth fullScreen={isSmallScreen} PaperProps={{ sx: { maxHeight: '90vh' } }} > Add Fuel Log { setShowAddDialog(false); // Refresh fuel logs data queryClient.invalidateQueries({ queryKey: ['fuelLogs', vehicle?.id] }); queryClient.invalidateQueries({ queryKey: ['fuelLogs'] }); }} />
); };