Worth the Drive: calculation audit + listing/detail polish

Calculation audit (brief config: 30 MPG imperial, £60 fill-up, baseline
Rontec Shelf 171.9p at 0.4 mi):

- One mile<->kilometre constant pair (kilometresPerMile 1.609344,
  metresPerMile 1609.344) replaces the mixed 1609.34 / 0.621371 / 1.60934
  used in the savings path. Effect ~2 ppm (invisible), but the conversions
  are now auditable in one place.
- New FuelSavingsCalculationAuditTests (20 deterministic tests, no live
  MKDirections) pins the brief's three stations - Tesco 170.9p/2.0 mi,
  MFG Hare & Hounds 172.9p/2.8 mi, PGG Napoleon 171.9p/2.8 mi - plus
  imperial-vs-US gallon, MPG and budget scaling, the break-even tolerance
  boundary, the nearer-candidate clamp, route-unavailable, mixed-basis
  estimates and display-rounding boundaries.
- Formula unchanged, and verified correct: fixed quantity (budget / baseline
  price), pump saving, round-trip extra distance clamped at zero, travel fuel
  priced at the candidate station, +/-5p break even, baseline labelled
  "Closest station", sorting on the unrounded net saving. The brief's "£0.18"
  for Tesco is the rounded 1 dp extra-distance caption (2.02-2.05 mi reads as
  "2.0 mi"), not a rounding bug in the money.

UI (uncommitted since d7a0ecb):

- Done bar drawn in the bottom safe area from keyboard notifications:
  .toolbar(placement: .keyboard) is device-dependent (present on the 15 Pro,
  absent on the SE on the same iOS build).
- Favourite star in the More-info heading, wired to the same fuel-scoped
  toggle as the listing rows at all three call sites.
- CHEAPEST/TOP badge moved above the price so it reads as part of it.
- Listing rows carry the real session fill-up amount instead of a hard zero
  (the compact banner does not print it today; a future row layout would).

153 tests, 0 failures. Simulator and device builds succeed.
This commit is contained in:
FuelBoard Contributor
2026-10-11 17:19:10 +01:00
parent d7a0ecb79a
commit d0a89d97d5
10 changed files with 780 additions and 51 deletions
+16 -1
View File
@@ -155,6 +155,7 @@ struct StationsView: View {
topBadgeText: sortMode == .worthTheDrive ? "CHEAPEST" : "TOP",
tapAction: stationTapAction,
fuelSavings: savingsForStation(station),
fuelSavingsAmount: fuelSavingsAmount,
onToggleFavourite: { onToggleFavourite(station, selectedFuel) },
onShowDetails: { detailStation = $0 }
)
@@ -215,6 +216,18 @@ struct StationsView: View {
if ProcessInfo.processInfo.arguments.contains("-showKeySheet") {
showKey = true
}
#endif
}
.task(id: stations.count) {
#if DEBUG
// QA hook: `-showDetail` opens the More info sheet for the first
// listed station, so the sheet can be screenshotted without a
// tap. Waits for the list to arrive first.
guard ProcessInfo.processInfo.arguments.contains("-showDetail"),
detailStation == nil,
let first = stations.first else { return }
try? await Task.sleep(nanoseconds: 2_000_000_000)
detailStation = first
#endif
}
.toolbar {
@@ -302,7 +315,9 @@ struct StationsView: View {
distanceUnit: distanceUnit,
fuelSavings: savingsForStation(station),
fuelSavingsAmount: fuelSavingsAmount,
savingsBaselineStationName: savingsBaselineStationName
savingsBaselineStationName: savingsBaselineStationName,
isFavourite: favouriteIDs.contains(station.id),
onToggleFavourite: { onToggleFavourite(station, selectedFuel) }
)
}
}