diff --git a/frontend/src/features/fuel-logs/components/FuelLogEditDialog.tsx b/frontend/src/features/fuel-logs/components/FuelLogEditDialog.tsx index edd48e8..5e6b888 100644 --- a/frontend/src/features/fuel-logs/components/FuelLogEditDialog.tsx +++ b/frontend/src/features/fuel-logs/components/FuelLogEditDialog.tsx @@ -35,15 +35,11 @@ export const FuelLogEditDialog: React.FC = ({ const [isSaving, setIsSaving] = useState(false); const [hookError, setHookError] = useState(null); - // Defensive hook usage with error handling - let fuelGrades: any[] = []; - try { - const hookResult = useFuelGrades(formData.fuelType || log?.fuelType || FuelType.GASOLINE); - fuelGrades = hookResult.fuelGrades || []; - } catch (error) { - console.error('[FuelLogEditDialog] Hook error:', error); - setHookError(error as Error); - } + // Always call hooks at the top level to maintain order across renders + const { fuelGrades } = useFuelGrades( + (formData.fuelType as any) || (log?.fuelType as any) || FuelType.GASOLINE + ); + const isSmallScreen = useMediaQuery('(max-width:600px)'); // Reset form when log changes with defensive checks useEffect(() => { @@ -111,9 +107,10 @@ export const FuelLogEditDialog: React.FC = ({ onClose(); }; - // Early returns for error states - if (!log) return null; + // Early bailout if dialog not open or no log to edit + if (!open || !log) return null; + // Early returns for error states if (hookError) { return ( @@ -150,7 +147,7 @@ export const FuelLogEditDialog: React.FC = ({ onClose={handleCancel} maxWidth="sm" fullWidth - fullScreen={useMediaQuery('(max-width:600px)')} + fullScreen={isSmallScreen} PaperProps={{ sx: { maxHeight: '90vh' } }} @@ -288,4 +285,4 @@ export const FuelLogEditDialog: React.FC = ({ ); -}; \ No newline at end of file +}; diff --git a/frontend/src/features/vehicles/mobile/VehicleDetailMobile.tsx b/frontend/src/features/vehicles/mobile/VehicleDetailMobile.tsx index 5ea6536..e616d45 100644 --- a/frontend/src/features/vehicles/mobile/VehicleDetailMobile.tsx +++ b/frontend/src/features/vehicles/mobile/VehicleDetailMobile.tsx @@ -2,9 +2,14 @@ * @ai-summary Mobile vehicle detail screen with Material Design 3 */ -import React from 'react'; -import { Box, Typography, Button, Card, CardContent, Divider } from '@mui/material'; +import React, { useMemo, useState } from 'react'; +import { Box, Typography, Button, Card, CardContent, Divider, FormControl, InputLabel, Select, MenuItem, List, ListItem, ListItemText } from '@mui/material'; +import { useQueryClient } from '@tanstack/react-query'; import { Vehicle } from '../types/vehicles.types'; +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 { fuelLogsApi } from '../../fuel-logs/api/fuel-logs.api'; // Theme colors now defined in Tailwind config @@ -48,6 +53,55 @@ export const VehicleDetailMobile: React.FC = ({ [vehicle.year, vehicle.make, vehicle.model, vehicle.trimLevel].filter(Boolean).join(' ') || 'Vehicle'; const displayModel = vehicle.model || 'Unknown Model'; + const [recordFilter, setRecordFilter] = useState<'All' | 'Fuel Logs' | 'Maintenance' | 'Documents'>('All'); + const { fuelLogs, isLoading: isFuelLoading } = useFuelLogs(vehicle.id); + const queryClient = useQueryClient(); + const [editingLog, setEditingLog] = useState(null); + + type VehicleRecord = { + id: string; + type: 'Fuel Logs' | 'Maintenance' | 'Documents'; + date: string; // ISO + summary: string; + amount?: string; + }; + + const records: VehicleRecord[] = useMemo(() => { + const list: VehicleRecord[] = []; + if (fuelLogs && Array.isArray(fuelLogs)) { + for (const log of fuelLogs as FuelLogResponse[]) { + const parts: string[] = []; + if (log.fuelUnits) parts.push(`${Number(log.fuelUnits).toFixed(3)} units`); + if (log.fuelType) parts.push(`${log.fuelType}${log.fuelGrade ? ' ' + log.fuelGrade : ''}`); + if (log.efficiencyLabel) parts.push(log.efficiencyLabel); + 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]); + + const openEditLog = (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', vehicle.id] }); + await queryClient.invalidateQueries({ queryKey: ['fuelLogs'] }); + setEditingLog(null); + }; + return (