feat: add Follow Team Mode (Scenario D) for road trip planning
Adds a new planning mode that lets users follow a team's schedule (home + away games) and builds multi-city routes accordingly. Key changes: - New ScenarioDPlanner with team filtering and route generation - Team picker UI with sport grouping and search - Fix TravelEstimator 5-day limit (was 2-day) for cross-country routes - Fix DateInterval end boundary to include games on last day - Comprehensive test suite covering edge cases: - Multi-city routes with adequate/insufficient time - Optimal game selection per city for feasibility - 5-day driving segment limits - Multiple driver scenarios Enables trips like Houston → Chicago → Anaheim following the Astros. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ enum PlanningMode: String, Codable, CaseIterable, Identifiable {
|
||||
case dateRange // Start date + end date, find games in range
|
||||
case gameFirst // Pick games first, trip around those games
|
||||
case locations // Start/end locations, optional games along route
|
||||
case followTeam // Follow one team's schedule (home + away)
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
@@ -20,6 +21,7 @@ enum PlanningMode: String, Codable, CaseIterable, Identifiable {
|
||||
case .dateRange: return "By Dates"
|
||||
case .gameFirst: return "By Games"
|
||||
case .locations: return "By Route"
|
||||
case .followTeam: return "Follow Team"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +30,7 @@ enum PlanningMode: String, Codable, CaseIterable, Identifiable {
|
||||
case .dateRange: return "Shows a curated sample of possible routes — use filters to find your ideal trip"
|
||||
case .gameFirst: return "Build trip around specific games"
|
||||
case .locations: return "Plan route between locations"
|
||||
case .followTeam: return "Follow your team on the road"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +39,7 @@ enum PlanningMode: String, Codable, CaseIterable, Identifiable {
|
||||
case .dateRange: return "calendar"
|
||||
case .gameFirst: return "sportscourt"
|
||||
case .locations: return "map"
|
||||
case .followTeam: return "person.3.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,6 +234,12 @@ struct TripPreferences: Codable, Hashable {
|
||||
var allowRepeatCities: Bool
|
||||
var selectedRegions: Set<Region>
|
||||
|
||||
/// Team to follow (for Follow Team mode)
|
||||
var followTeamId: UUID?
|
||||
|
||||
/// Whether to start/end from a home location (vs fly-in/fly-out)
|
||||
var useHomeLocation: Bool
|
||||
|
||||
init(
|
||||
planningMode: PlanningMode = .dateRange,
|
||||
startLocation: LocationInput? = nil,
|
||||
@@ -250,7 +260,9 @@ struct TripPreferences: Codable, Hashable {
|
||||
numberOfDrivers: Int = 1,
|
||||
maxDrivingHoursPerDriver: Double? = nil,
|
||||
allowRepeatCities: Bool = true,
|
||||
selectedRegions: Set<Region> = [.east, .central, .west]
|
||||
selectedRegions: Set<Region> = [.east, .central, .west],
|
||||
followTeamId: UUID? = nil,
|
||||
useHomeLocation: Bool = true
|
||||
) {
|
||||
self.planningMode = planningMode
|
||||
self.startLocation = startLocation
|
||||
@@ -272,6 +284,8 @@ struct TripPreferences: Codable, Hashable {
|
||||
self.maxDrivingHoursPerDriver = maxDrivingHoursPerDriver
|
||||
self.allowRepeatCities = allowRepeatCities
|
||||
self.selectedRegions = selectedRegions
|
||||
self.followTeamId = followTeamId
|
||||
self.useHomeLocation = useHomeLocation
|
||||
}
|
||||
|
||||
var totalDriverHoursPerDay: Double {
|
||||
|
||||
Reference in New Issue
Block a user