From db49cf17cdef6b9d626ba217e1ed12eed21cd96b Mon Sep 17 00:00:00 2001 From: Kameron Kenny Date: Wed, 19 Mar 2025 13:25:34 -0400 Subject: [PATCH] add trend for avg mpg --- Gas Man/Stats/MPGTrendChartView.swift | 63 +++++++++++++++++++++++++++ Gas Man/Stats/StatsView.swift | 48 +++++++++++++------- 2 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 Gas Man/Stats/MPGTrendChartView.swift diff --git a/Gas Man/Stats/MPGTrendChartView.swift b/Gas Man/Stats/MPGTrendChartView.swift new file mode 100644 index 0000000..b03d2ad --- /dev/null +++ b/Gas Man/Stats/MPGTrendChartView.swift @@ -0,0 +1,63 @@ +// +// MPGTrendChartView.swift +// Gas Man +// +// Created by Kameron Kenny on 3/19/25. +// + +import SwiftUI +import Charts +import CoreData + +struct MPGTrendChartView: View { + let fuelLogs: [FuelLog] + + // Compute trend data by sorting logs by date ascending + // and calculating MPG for each interval where fullTank is true. + var trendData: [(date: Date, mpg: Double)] { + let sortedLogs = fuelLogs.sorted { ($0.date ?? Date()) < ($1.date ?? Date()) } + var data: [(Date, Double)] = [] + // Start from index 1 because we need a previous log to compute the difference. + for i in 1.. previous.odometer, + current.fuelVolume > 0 { + let mpg = (current.odometer - previous.odometer) / current.fuelVolume + data.append((date, mpg)) + } + } + return data + } + + var body: some View { + VStack { + if trendData.isEmpty { + Text("No MPG trend data available.") + .foregroundColor(.secondary) + } else { + Chart { + ForEach(trendData, id: \.date) { point in + LineMark( + x: .value("Date", point.date), + y: .value("MPG", point.mpg) + ) + PointMark( + x: .value("Date", point.date), + y: .value("MPG", point.mpg) + ) + } + } + .chartXAxis { + AxisMarks(values: .automatic(desiredCount: 4)) + } + .chartYAxis { + AxisMarks(values: .automatic(desiredCount: 5)) + } + } + } + .padding() + .navigationTitle("MPG Trend") + } +} diff --git a/Gas Man/Stats/StatsView.swift b/Gas Man/Stats/StatsView.swift index 1631d9e..9453758 100644 --- a/Gas Man/Stats/StatsView.swift +++ b/Gas Man/Stats/StatsView.swift @@ -5,7 +5,6 @@ // Created by Kameron Kenny on 3/19/25. // - import SwiftUI import CoreData @@ -18,7 +17,7 @@ struct StatsView: View { animation: .default) private var fuelLogs: FetchedResults - // Fetch all vehicles sorted by make (or any preferred order) + // Fetch all vehicles sorted by make @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \Vehicle.make, ascending: true)], animation: .default) @@ -60,7 +59,7 @@ struct StatsView: View { return sum / Double(mpgValues.count) } - // Price per gallon stats: lowest, highest, average. + // Price per gallon stats. private var pricePerGallonStats: (min: Double?, max: Double?, avg: Double?) { let prices = filteredFuelLogs.map { $0.pricePerGalon } guard !prices.isEmpty else { return (nil, nil, nil) } @@ -70,7 +69,7 @@ struct StatsView: View { return (minPrice, maxPrice, avgPrice) } - // Distance between fill-ups stats: lowest, highest, average. + // Distance between fill-ups stats. private var distanceStats: (min: Double?, max: Double?, avg: Double?) { let logs = filteredFuelLogs guard logs.count > 1 else { return (nil, nil, nil) } @@ -103,7 +102,7 @@ struct StatsView: View { var body: some View { NavigationView { Form { - // Vehicle Selector + // Vehicle Selector Section Section(header: Text("Select Vehicle")) { Picker("Vehicle", selection: $selectedVehicleID) { ForEach(vehicles, id: \.id) { vehicle in @@ -114,20 +113,36 @@ struct StatsView: View { .pickerStyle(MenuPickerStyle()) } - // Stats for Fuel Logs - Section(header: Text("Fill-Up Count")) { - Text("\(fillUpCount)") - } - - Section(header: Text("Average MPG")) { - if let avgMPG = averageMPG { - Text("\(avgMPG, specifier: "%.1f") MPG") - } else { - Text("N/A") - .foregroundColor(.secondary) + // Fill-Up Count Section + Section { + HStack { + Text("Fill-Up Count:") + Spacer() + Text("\(fillUpCount)") + .bold() } } + // Average MPG Section with NavigationLink to chart. + Section { + NavigationLink(destination: MPGTrendChartView(fuelLogs: filteredFuelLogs)) { + HStack { + Text("Average MPG:") + Spacer() + if let avgMPG = averageMPG { + Text("\(avgMPG, specifier: "%.1f") MPG") + .bold() + } else { + Text("N/A") + .foregroundColor(.secondary) + } + Image(systemName: "chart.bar.fill") + .foregroundColor(.blue) + } + } + } + + // Price per Gallon Stats Section(header: Text("Price per Gallon")) { let stats = pricePerGallonStats HStack { @@ -191,6 +206,7 @@ struct StatsView: View { } } + // Gallons per Fill-Up Stats Section(header: Text("Gallons per Fill-Up")) { let gStats = gallonsStats HStack {