diff --git a/Gas Man.xcodeproj/project.pbxproj b/Gas Man.xcodeproj/project.pbxproj index 1b1ed6d..bfebcf2 100644 --- a/Gas Man.xcodeproj/project.pbxproj +++ b/Gas Man.xcodeproj/project.pbxproj @@ -289,7 +289,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202503191007; + CURRENT_PROJECT_VERSION = 202503191221; DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\""; DEVELOPMENT_TEAM = Z734T5CD6B; ENABLE_HARDENED_RUNTIME = YES; @@ -332,7 +332,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 202503191007; + CURRENT_PROJECT_VERSION = 202503191221; DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\""; DEVELOPMENT_TEAM = Z734T5CD6B; ENABLE_HARDENED_RUNTIME = YES; diff --git a/Gas Man/AddFuelLogView.swift b/Gas Man/AddFuelLogView.swift index 810c8ad..cc1f8c6 100644 --- a/Gas Man/AddFuelLogView.swift +++ b/Gas Man/AddFuelLogView.swift @@ -8,34 +8,7 @@ import SwiftUI import CoreData -// Helper function to check if a string is properly formatted (e.g., "18.526") -func isProperlyFormatted(_ input: String) -> Bool { - let pattern = #"^\d+\.\d{3}$"# - return input.range(of: pattern, options: .regularExpression) != nil -} -// helper function to format input. -func formatInput(_ input: String) -> String { - // Extract only numeric characters. - let digitsOnly = input.filter { $0.isNumber } - // If no digits, return empty. - guard !digitsOnly.isEmpty else { return "" } - // Convert to an integer to remove any leading zeros. - let numberValue = Int(digitsOnly) ?? 0 - // Convert back to a string. - let digits = String(numberValue) - - if digits.count > 3 { - // Insert decimal point so that the last 3 digits are decimals. - let integerPart = digits.dropLast(3) - let decimalPart = digits.suffix(3) - return "\(integerPart).\(decimalPart)" - } else { - // If fewer than 4 digits, pad with zeros on the left to 3 digits. - let padded = String(repeating: "0", count: 3 - digits.count) + digits - return "0." + padded - } -} struct AddFuelLogView: View { @Environment(\.managedObjectContext) private var viewContext diff --git a/Gas Man/EditFuelLogView.swift b/Gas Man/EditFuelLogView.swift new file mode 100644 index 0000000..571a3ea --- /dev/null +++ b/Gas Man/EditFuelLogView.swift @@ -0,0 +1,199 @@ +// +// EditFuelLogView.swift +// Gas Man +// +// Created by Kameron Kenny on 3/19/25. +// + + +import SwiftUI +import CoreData + +struct EditFuelLogView: View { + @Environment(\.managedObjectContext) private var viewContext + @Environment(\.dismiss) var dismiss + + @ObservedObject var fuelLog: FuelLog + + // Form fields for fuel log + @State private var date = Date() + @State private var odometer = "" + @State private var fuelVolume: String = "" + @State private var cost = "" + @State private var locationCoordinates = "" + @State private var locationName = "" + @State private var selectedOctane: Int = 87 + @State private var pricePerGalon = "" + // Full Tank toggle + @State private var fullTank: Bool = true + + // Allowed octane options + let octaneOptions = [87, 89, 91, 92, 93, 95] + + // For calculation update control + @State private var isUpdatingCalculation = false + + var body: some View { + NavigationView { + Form { + Section(header: Text("Fuel Log Details")) { + DatePicker("Date", selection: $date, displayedComponents: [.date, .hourAndMinute]) + + HStack { + Text("Odometer:") + TextField("", text: $odometer) + .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + } + + HStack { + Text("Gallons:") + TextField("", text: $fuelVolume) + .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + .onChange(of: fuelVolume) { newValue in + if !newValue.contains(".") { + let formatted = formatInput(newValue) + if formatted != newValue { + fuelVolume = formatted + } + } + updateCalculatedValues() + } + } + + HStack { + Text("Price/Gal:") + TextField("", text: $pricePerGalon) + .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + .onChange(of: pricePerGalon) { newValue in + if !newValue.contains(".") { + let formatted = formatInput(newValue) + if formatted != newValue { + pricePerGalon = formatted + } + } + updateCalculatedValues() + } + } + + HStack { + Text("Cost:") + TextField("", text: $cost) + .keyboardType(.decimalPad) + .multilineTextAlignment(.trailing) + .onChange(of: cost) { _ in updateCalculatedValues() } + } + + Toggle("Full Tank", isOn: $fullTank) + .toggleStyle(SwitchToggleStyle(tint: .blue)) + + Button("Get Current Location") { + // Optionally trigger location update + } + + if !locationCoordinates.isEmpty { + Text("Coordinates: \(locationCoordinates)") + } + + TextField("Location Name", text: $locationName) + .multilineTextAlignment(.trailing) + + Picker("Octane", selection: $selectedOctane) { + ForEach(octaneOptions, id: \.self) { option in + Text("\(option)").tag(option) + } + } + .pickerStyle(MenuPickerStyle()) + } + } + .navigationTitle("Edit Fuel Log") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Save") { saveFuelLog() } + } + ToolbarItem(placement: .navigationBarLeading) { + Button("Cancel") { dismiss() } + } + } + } + .onAppear { + loadFuelLogData() + } + } + + private func loadFuelLogData() { + date = fuelLog.date ?? Date() + odometer = String(format: "%.0f", fuelLog.odometer) + fuelVolume = String(format: "%.3f", fuelLog.fuelVolume) + cost = String(format: "%.2f", fuelLog.cost) + pricePerGalon = String(format: "%.3f", fuelLog.pricePerGalon) + fullTank = fuelLog.fullTank + locationCoordinates = fuelLog.locationCoordinates ?? "" + locationName = fuelLog.locationName ?? "" + selectedOctane = Int(fuelLog.octane) + } + + private func roundToThree(_ value: Double) -> Double { + return (value * 1000).rounded() / 1000 + } + + private func roundToTwo(_ value: Double) -> Double { + return (value * 100).rounded() / 100 + } + + private func updateCalculatedValues() { + guard !isUpdatingCalculation else { return } + isUpdatingCalculation = true + + let fuel = Double(fuelVolume) + let costVal = Double(cost) + let price = Double(pricePerGalon) + + if let f = fuel, let p = price, f > 0 { + let computedCost = roundToTwo(f * p) + let computedCostStr = String(format: "%.2f", computedCost) + if cost != computedCostStr { + cost = computedCostStr + } + } else if let f = fuel, let c = costVal, f > 0, pricePerGalon.trimmingCharacters(in: .whitespaces).isEmpty { + let computedPrice = roundToThree(c / f) + let computedPriceStr = String(format: "%.3f", computedPrice) + if pricePerGalon != computedPriceStr { + pricePerGalon = computedPriceStr + } + } else if let p = price, let c = costVal, p > 0, fuelVolume.trimmingCharacters(in: .whitespaces).isEmpty { + let computedFuel = roundToThree(c / p) + let computedFuelStr = String(format: "%.3f", computedFuel) + if fuelVolume != computedFuelStr { + fuelVolume = computedFuelStr + } + } + + isUpdatingCalculation = false + } + + private func saveFuelLog() { + guard let newOdometer = Double(odometer) else { return } + // Optional: Validate odometer vs. previous logs if needed. + + fuelLog.date = date + fuelLog.odometer = newOdometer + fuelLog.fuelVolume = Double(fuelVolume) ?? 0 + fuelLog.cost = Double(cost) ?? 0 + fuelLog.locationCoordinates = locationCoordinates + fuelLog.locationName = locationName + fuelLog.octane = Int16(selectedOctane) + fuelLog.pricePerGalon = Double(pricePerGalon) ?? 0 + fuelLog.fullTank = fullTank + + do { + try viewContext.save() + dismiss() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } +} diff --git a/Gas Man/FuelLogDetailView.swift b/Gas Man/FuelLogDetailView.swift index 377c362..ad5bd64 100644 --- a/Gas Man/FuelLogDetailView.swift +++ b/Gas Man/FuelLogDetailView.swift @@ -15,7 +15,8 @@ private let dateFormatter: DateFormatter = { }() struct FuelLogDetailView: View { - var fuelLog: FuelLog + @ObservedObject var fuelLog: FuelLog + @State private var showingEditFuelLog = false var body: some View { Form { @@ -57,7 +58,6 @@ struct FuelLogDetailView: View { Text("$\(fuelLog.pricePerGalon, specifier: "%.3f")") } } - // New Tank Status Section Section(header: Text("Tank Status")) { HStack { Text("Tank:") @@ -94,5 +94,16 @@ struct FuelLogDetailView: View { } } .navigationTitle("Fuel Log Detail") + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Edit") { + showingEditFuelLog = true + } + } + } + .sheet(isPresented: $showingEditFuelLog) { + EditFuelLogView(fuelLog: fuelLog) + .environment(\.managedObjectContext, fuelLog.managedObjectContext!) + } } } diff --git a/Gas Man/HelperFormat.swift b/Gas Man/HelperFormat.swift new file mode 100644 index 0000000..a4bd11c --- /dev/null +++ b/Gas Man/HelperFormat.swift @@ -0,0 +1,37 @@ +// +// HelperFormat.swift +// Gas Man +// +// Created by Kameron Kenny on 3/19/25. +// + +import SwiftUI + +// Helper function to check if a string is properly formatted (e.g., "18.526") +func isProperlyFormatted(_ input: String) -> Bool { + let pattern = #"^\d+\.\d{3}$"# + return input.range(of: pattern, options: .regularExpression) != nil +} + +// helper function to format input. +func formatInput(_ input: String) -> String { + // Extract only numeric characters. + let digitsOnly = input.filter { $0.isNumber } + // If no digits, return empty. + guard !digitsOnly.isEmpty else { return "" } + // Convert to an integer to remove any leading zeros. + let numberValue = Int(digitsOnly) ?? 0 + // Convert back to a string. + let digits = String(numberValue) + + if digits.count > 3 { + // Insert decimal point so that the last 3 digits are decimals. + let integerPart = digits.dropLast(3) + let decimalPart = digits.suffix(3) + return "\(integerPart).\(decimalPart)" + } else { + // If fewer than 4 digits, pad with zeros on the left to 3 digits. + let padded = String(repeating: "0", count: 3 - digits.count) + digits + return "0." + padded + } +}