Redesign the More info sheet

The sheet leads with the station name and address inside the card, above the map, then a two-column facts row (fuel price on the left, distance and return on the right, split by a hairline) and the two station actions divided the same way. The Best Value block becomes a tinted card: verdict badge, the two figures the estimate is made of, the working and the total. "How Best Value works" moves to its own row below it, using the shared HowBestValueRow the Key sheet already shows.

Presentation only: every figure still comes from the shared sources (FuelStore.priceText, stationDistanceDisplay, FuelSavingsResult), so the sheet cannot disagree with the listing row, the widget or the Live Activity, and no calculation, ranking or freshness rule changed.

StationDistanceDisplay.returnLabel(unit:) and FuelSavingsFormat.extraTravelDistanceLabel(...) are split out of their caption forms so a surface printing the return or extra-travel figure on its own line takes it from the same place, confidence marker included; their existing output is unchanged, pinned by tests. FuelSavingsSummaryView is deleted - the sheet's card supersedes it and nothing referenced it.

DEBUG-only QA hooks: -qaConfigureSavings configures a Fuel Savings plan (the Best Value card and the Worth the Drive sort are gated on it), -qaDetailBottom scrolls to that card (defaultScrollAnchor is a no-op on a List) and -qaCopyPostcode holds the copied confirmation long enough to screenshot.
This commit is contained in:
FuelBoard Contributor
2026-10-16 14:51:36 +01:00
parent ff8a1e18d0
commit 05c76f21a9
9 changed files with 519 additions and 299 deletions
+17 -3
View File
@@ -136,11 +136,25 @@ enum FuelSavingsFormat {
static func extraTravelLabel(extraDistanceMiles: Double,
unit: DistanceUnit,
confidence: JourneyConfidence = .confirmed) -> String? {
guard let distanceText = extraTravelDistanceLabel(extraDistanceMiles: extraDistanceMiles,
unit: unit,
confidence: confidence) else { return nil }
return "\(distanceText) extra return travel"
}
/// The extra-travel FIGURE on its own, with its confidence marker: "4.3 mi"
/// or "Approx. 4.3 mi".
///
/// A row form ("Extra return travel | 4.3 mi") names the journey in its own
/// label, so this is the same figure and the same "Approx." rule as
/// `extraTravelLabel`, from the same inputs — never rebuilt at a call site,
/// or one surface marks an estimate while another drops the marker.
static func extraTravelDistanceLabel(extraDistanceMiles: Double,
unit: DistanceUnit,
confidence: JourneyConfidence = .confirmed) -> String? {
guard extraDistanceMiles > 0.05 else { return nil }
let distanceText = distance(extraDistanceMiles, unit: unit)
return confidence == .estimated
? "Approx. \(distanceText) extra return travel"
: "\(distanceText) extra return travel"
return confidence == .estimated ? "Approx. \(distanceText)" : distanceText
}
/// Compact comparison copy kept outside the coloured verdict capsule.
+18 -3
View File
@@ -1806,9 +1806,24 @@ func label(unit: DistanceUnit) -> String {
/// Row/sheet label that keeps the one-way station distance primary, then
/// adds the independently measured or estimated out-and-back distance.
func labelWithReturn(unit: DistanceUnit) -> String {
let returnText = unit.format(returnKM)
let returnLabel = returnIsApproximate ? "Approx. \(returnText)" : returnText
return "\(label(unit: unit)) (\(returnLabel) return)"
"\(label(unit: unit)) (\(returnLabel(unit: unit)))"
}
/// The out-and-back figure on its own: "5.0 mi return" / "Approx. 5.0 mi return".
///
/// Split out of `labelWithReturn` for surfaces that print the one-way and the
/// return figure on SEPARATE lines (the More info sheet's distance column). Both
/// figures still come from this one type, so a routed value can never be marked
/// "Approx." while its return figure is not — or the other way round.
func returnLabel(unit: DistanceUnit) -> String {
let text = unit.format(returnKM)
return returnIsApproximate ? "Approx. \(text) return" : "\(text) return"
}
/// VoiceOver form of `returnLabel(unit:)`.
func spokenReturnLabel(unit: DistanceUnit) -> String {
let text = unit.spoken(returnKM)
return returnIsApproximate ? "Approximately \(text) return" : "\(text) return"
}
/// VoiceOver form of `label(unit:)`.