Redesign trip option cards and fix various UI/planning issues
TripOptionCard improvements: - Replace horizontal route with vertical layout (start → end with arrow) - Remove rank badges (1, 2, 3, etc.) - Split stats into two rows: cities/miles and sports with game counts - Clear selection when navigating back from detail view Settings cleanup: - Remove unused settings (preferred game time, playoff games, notifications) - Convert remaining settings to sliders Planning fixes: - Fix multi-day driving calculation in canTransition - Remove over-restrictive trip rejection in TravelEstimator - Clear games cache when sport selection changes UI polish: - RoutePreviewStrip shows all cities (abbreviated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,62 @@ struct ItineraryOption: Identifiable {
|
||||
var totalGames: Int {
|
||||
stops.reduce(0) { $0 + $1.games.count }
|
||||
}
|
||||
|
||||
/// Sorts and ranks itinerary options based on leisure level preference.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - options: The itinerary options to sort
|
||||
/// - leisureLevel: The user's leisure preference
|
||||
/// - limit: Maximum number of options to return (default 10)
|
||||
/// - Returns: Sorted and ranked options
|
||||
///
|
||||
/// Sorting behavior:
|
||||
/// - Packed: Most games first, then least driving
|
||||
/// - Moderate: Best efficiency (games per driving hour)
|
||||
/// - Relaxed: Least driving first, then fewer games
|
||||
static func sortByLeisure(
|
||||
_ options: [ItineraryOption],
|
||||
leisureLevel: LeisureLevel,
|
||||
limit: Int = 10
|
||||
) -> [ItineraryOption] {
|
||||
let sorted = options.sorted { a, b in
|
||||
let aGames = a.totalGames
|
||||
let bGames = b.totalGames
|
||||
|
||||
switch leisureLevel {
|
||||
case .packed:
|
||||
// Most games first, then least driving
|
||||
if aGames != bGames { return aGames > bGames }
|
||||
return a.totalDrivingHours < b.totalDrivingHours
|
||||
|
||||
case .moderate:
|
||||
// Best efficiency (games per driving hour)
|
||||
let effA = a.totalDrivingHours > 0 ? Double(aGames) / a.totalDrivingHours : Double(aGames)
|
||||
let effB = b.totalDrivingHours > 0 ? Double(bGames) / b.totalDrivingHours : Double(bGames)
|
||||
if effA != effB { return effA > effB }
|
||||
return aGames > bGames
|
||||
|
||||
case .relaxed:
|
||||
// Least driving first, then fewer games is fine
|
||||
if a.totalDrivingHours != b.totalDrivingHours {
|
||||
return a.totalDrivingHours < b.totalDrivingHours
|
||||
}
|
||||
return aGames < bGames
|
||||
}
|
||||
}
|
||||
|
||||
// Re-rank after sorting
|
||||
return Array(sorted.prefix(limit)).enumerated().map { index, option in
|
||||
ItineraryOption(
|
||||
rank: index + 1,
|
||||
stops: option.stops,
|
||||
travelSegments: option.travelSegments,
|
||||
totalDrivingHours: option.totalDrivingHours,
|
||||
totalDistanceMiles: option.totalDistanceMiles,
|
||||
geographicRationale: option.geographicRationale
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Itinerary Stop
|
||||
|
||||
Reference in New Issue
Block a user