import SwiftUI import CoreData struct FuelLogListView: View { @Environment(\.managedObjectContext) private var viewContext // All fuel logs (sorted by date descending) @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \FuelLog.date, ascending: false)], animation: .default) private var fuelLogs: FetchedResults // All vehicles (sorted by make) @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Vehicle.make, ascending: true)], animation: .default) private var vehicles: FetchedResults @State private var showingAddFuelLog = false @State private var selectedVehicleID: UUID? = nil @State private var showAddVehicleSheet = false // For tracking the previous odometer reading (used for validation) @State private var previousOdometer: Double? = nil @State private var showOdometerAlert = false @State private var isUpdatingCalculation = false @State private var showingImporter = false // Computed property for formatted previous odometer value private var previousOdometerString: String { previousOdometer.map { String(format: "%.0f", $0) } ?? "N/A" } // Filter fuel logs by the selected vehicle. private var filteredFuelLogs: [FuelLog] { if let vehicleID = selectedVehicleID { return fuelLogs.filter { log in if let logVehicleID = log.vehicle?.id { return logVehicleID == vehicleID } return false } } else { return Array(fuelLogs) } } // Computed property to calculate average MPG for full-tank logs. // MPG for an individual log is computed as: // (current.odometer - next.odometer) / current.fuelVolume private var averageMPG: Double? { let logs = filteredFuelLogs guard logs.count > 1 else { return nil } var mpgValues: [Double] = [] // Iterate over logs except the oldest. for i in 0.. 0, current.odometer > next.odometer { let mpg = (current.odometer - next.odometer) / current.fuelVolume mpgValues.append(mpg) } } guard !mpgValues.isEmpty else { return nil } let total = mpgValues.reduce(0, +) return total / Double(mpgValues.count) } var body: some View { NavigationView { if vehicles.isEmpty { // Empty state: no vehicles exist. VStack(spacing: 20) { Text("No vehicles found.") .font(.headline) Text("Please add a vehicle before logging fuel records.") .multilineTextAlignment(.center) .padding(.horizontal) Button("Add Vehicle") { showAddVehicleSheet = true } .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } .navigationTitle("Fuel Logs") .sheet(isPresented: $showAddVehicleSheet) { AddVehicleView().environment(\.managedObjectContext, viewContext) } } else { List { // Vehicle Picker Section Section { Picker("Vehicle:", selection: $selectedVehicleID) { ForEach(vehicles, id: \.id) { vehicle in Text("\(vehicle.year ?? "") \(vehicle.make ?? "") \(vehicle.model ?? "")") .tag(vehicle.id) } } .pickerStyle(MenuPickerStyle()) } // Vehicle Photo Section Section { if let selectedVehicle = vehicles.first(where: { $0.id == selectedVehicleID }) { if let photoData = selectedVehicle.photo, let uiImage = UIImage(data: photoData) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity, maxHeight: 200) .clipShape(Circle()) // .cornerRadius(8) } else { // Fallback image if no photo is available Image(systemName: "car.fill") .resizable() .aspectRatio(contentMode: .fit) .frame(maxWidth: .infinity, maxHeight: 200) .foregroundColor(.gray) .padding() } } } .listRowBackground(Color.black) // Average MPG Section Section { HStack { Text("Average MPG:") Spacer() if let avg = averageMPG { Text("\(avg, specifier: "%.1f")") .bold() } else { Text("N/A") .foregroundColor(.secondary) } } } // Fuel Logs Section (filtered by selected vehicle) ForEach(filteredFuelLogs.indices, id: \.self) { index in let log = filteredFuelLogs[index] let distance: Double? = { guard index < filteredFuelLogs.count - 1 else { return nil } let previousLog = filteredFuelLogs[index + 1] let milesDriven = log.odometer - previousLog.odometer return milesDriven > 0 ? milesDriven : nil }() let mpg: Double? = { // Only calculate MPG if fullTank is true. guard log.fullTank else { return nil } guard !log.missedPrevious else { return nil } guard index < filteredFuelLogs.count - 1 else { return nil } let previousLog = filteredFuelLogs[index + 1] let milesDriven = log.odometer - previousLog.odometer guard log.fuelVolume > 0, milesDriven > 0 else { return nil } return milesDriven / log.fuelVolume }() NavigationLink(destination: FuelLogDetailView(fuelLog: log)) { FuelLogSummaryView(log: log, mpg: mpg, distanceSincePrevious: distance) } } .onDelete(perform: deleteFuelLogs) } .listStyle(InsetGroupedListStyle()) .navigationTitle("Fuel Logs") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { showingAddFuelLog = true } label: { Label("Add Fuel Log", systemImage: "plus") } } ToolbarItem(placement: .navigationBarLeading) { EditButton() } ToolbarItem(placement: .navigationBarLeading) { Button("Import CSV") { showingImporter = true } } } .sheet(isPresented: $showingAddFuelLog) { AddFuelLogView().environment(\.managedObjectContext, viewContext) } .sheet(isPresented: $showingImporter) { // Retrieve the selected vehicle from the vehicles fetched results. if let selectedVehicle = vehicles.first(where: { $0.id == selectedVehicleID }) { FuelLogImporterView(selectedVehicle: selectedVehicle) .environment(\.managedObjectContext, viewContext) } else { // Optionally, provide a fallback, e.g. use the first vehicle. FuelLogImporterView(selectedVehicle: vehicles.first) .environment(\.managedObjectContext, viewContext) } // FuelLogImporterView() // .environment(\.managedObjectContext, PersistenceController.shared.container.viewContext) } .onAppear { print("Total fuel logs: \(fuelLogs.count)") setDefaultVehicle() } } } .alert(isPresented: $showOdometerAlert) { Alert(title: Text("Odometer Reading Error"), message: Text("Odometer reading must be greater than the previous record (\(previousOdometerString))."), dismissButton: .default(Text("OK"))) } } // Set default vehicle selection on appear. private func setDefaultVehicle() { if selectedVehicleID == nil { if let lastFuelLog = fuelLogs.first, let vehicle = lastFuelLog.vehicle { selectedVehicleID = vehicle.id } else { selectedVehicleID = vehicles.first?.id } } } private func deleteFuelLogs(offsets: IndexSet) { withAnimation { let logsToDelete = offsets.map { filteredFuelLogs[$0] } logsToDelete.forEach { viewContext.delete($0) } do { try viewContext.save() } catch { let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } } } }