Add region-based filtering and route length diversity

- Add RegionMapSelector UI for geographic trip filtering (East/Central/West)
- Add RouteFilters module for allowRepeatCities preference
- Improve GameDAGRouter to preserve route length diversity
  - Routes now grouped by city count before scoring
  - Ensures 2-city trips appear alongside longer trips
  - Increased beam width and max options for better coverage
- Add TripOptionsView filters (max cities slider, pace filter)
- Remove TravelStyle section from trip creation (replaced by region selector)
- Clean up debug logging from DataProvider and ScenarioAPlanner

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-09 15:18:37 -06:00
parent 3e778473e6
commit f5e509a9ae
20 changed files with 952 additions and 3245 deletions

View File

@@ -7,7 +7,7 @@
import Foundation
enum Region: String, CaseIterable, Identifiable {
enum Region: String, CaseIterable, Identifiable, Codable, Hashable {
case east = "East Coast"
case central = "Central"
case west = "West Coast"

View File

@@ -227,7 +227,8 @@ struct TripPreferences: Codable, Hashable {
var lodgingType: LodgingType
var numberOfDrivers: Int
var maxDrivingHoursPerDriver: Double?
var maxTripOptions: Int
var allowRepeatCities: Bool
var selectedRegions: Set<Region>
init(
planningMode: PlanningMode = .dateRange,
@@ -248,7 +249,8 @@ struct TripPreferences: Codable, Hashable {
lodgingType: LodgingType = .hotel,
numberOfDrivers: Int = 1,
maxDrivingHoursPerDriver: Double? = nil,
maxTripOptions: Int = 10
allowRepeatCities: Bool = true,
selectedRegions: Set<Region> = [.east, .central, .west]
) {
self.planningMode = planningMode
self.startLocation = startLocation
@@ -268,7 +270,8 @@ struct TripPreferences: Codable, Hashable {
self.lodgingType = lodgingType
self.numberOfDrivers = numberOfDrivers
self.maxDrivingHoursPerDriver = maxDrivingHoursPerDriver
self.maxTripOptions = maxTripOptions
self.allowRepeatCities = allowRepeatCities
self.selectedRegions = selectedRegions
}
var totalDriverHoursPerDay: Double {

View File

@@ -156,7 +156,7 @@ final class AppDataProvider: ObservableObject {
let canonicalGames = try context.fetch(descriptor)
// Filter by sport and convert to domain models
return canonicalGames.compactMap { canonical -> Game? in
let result = canonicalGames.compactMap { canonical -> Game? in
guard sportStrings.contains(canonical.sport) else { return nil }
let homeTeamUUID = canonicalTeamUUIDs[canonical.homeTeamCanonicalId] ?? UUID()
@@ -169,6 +169,8 @@ final class AppDataProvider: ObservableObject {
stadiumUUID: stadiumUUID
)
}
return result
}
/// Fetch a single game by ID

View File

@@ -243,8 +243,7 @@ final class SuggestedTripsGenerator {
sports: sports,
startDate: tripStartDate,
endDate: tripEndDate,
leisureLevel: .moderate,
maxTripOptions: 1
leisureLevel: .moderate
)
let request = PlanningRequest(
@@ -421,8 +420,7 @@ final class SuggestedTripsGenerator {
sports: sports,
startDate: tripStartDate,
endDate: tripEndDate,
leisureLevel: .moderate,
maxTripOptions: 1
leisureLevel: .moderate
)
// Generate travel segments between stops