add ability to edit fuel log records

This commit is contained in:
Kameron Kenny 2025-03-19 12:37:22 -04:00
parent 28c676fc76
commit 42188066f5
5 changed files with 251 additions and 31 deletions

View File

@ -289,7 +289,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements"; CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 202503191007; CURRENT_PROJECT_VERSION = 202503191221;
DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\"";
DEVELOPMENT_TEAM = Z734T5CD6B; DEVELOPMENT_TEAM = Z734T5CD6B;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
@ -332,7 +332,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements"; CODE_SIGN_ENTITLEMENTS = "Gas Man/Gas_Man.entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 202503191007; CURRENT_PROJECT_VERSION = 202503191221;
DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Gas Man/Preview Content\"";
DEVELOPMENT_TEAM = Z734T5CD6B; DEVELOPMENT_TEAM = Z734T5CD6B;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;

View File

@ -8,34 +8,7 @@
import SwiftUI import SwiftUI
import CoreData 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 { struct AddFuelLogView: View {
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext

View File

@ -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)")
}
}
}

View File

@ -15,7 +15,8 @@ private let dateFormatter: DateFormatter = {
}() }()
struct FuelLogDetailView: View { struct FuelLogDetailView: View {
var fuelLog: FuelLog @ObservedObject var fuelLog: FuelLog
@State private var showingEditFuelLog = false
var body: some View { var body: some View {
Form { Form {
@ -57,7 +58,6 @@ struct FuelLogDetailView: View {
Text("$\(fuelLog.pricePerGalon, specifier: "%.3f")") Text("$\(fuelLog.pricePerGalon, specifier: "%.3f")")
} }
} }
// New Tank Status Section
Section(header: Text("Tank Status")) { Section(header: Text("Tank Status")) {
HStack { HStack {
Text("Tank:") Text("Tank:")
@ -94,5 +94,16 @@ struct FuelLogDetailView: View {
} }
} }
.navigationTitle("Fuel Log Detail") .navigationTitle("Fuel Log Detail")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Edit") {
showingEditFuelLog = true
}
}
}
.sheet(isPresented: $showingEditFuelLog) {
EditFuelLogView(fuelLog: fuelLog)
.environment(\.managedObjectContext, fuelLog.managedObjectContext!)
}
} }
} }

View File

@ -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
}
}