Refactor travel segments and simplify trip options
Travel segment architecture: - Remove departureTime/arrivalTime from TravelSegment (location-based, not date-based) - Fix travel sections appearing after destination instead of between cities - Fix missing travel segments when revisiting same city (consecutive grouping) - Remove unwanted rest day at end of trip Planning engine fixes: - All three planners now group only consecutive games at same stadium - Visiting A → B → A creates 3 stops with proper travel between UI simplification: - Remove redundant sort options (mostDriving/leastDriving, mostCities/leastCities) - Remove unused "Find Other Sports Along Route" toggle (was dead code) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -262,24 +262,47 @@ struct TripDetailView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Build itinerary sections: days and travel between days
|
||||
/// Build itinerary sections: days with travel between different cities
|
||||
private var itinerarySections: [ItinerarySection] {
|
||||
var sections: [ItinerarySection] = []
|
||||
let calendar = Calendar.current
|
||||
|
||||
// Build day sections for days with games
|
||||
var daySections: [(dayNumber: Int, date: Date, city: String, games: [RichGame])] = []
|
||||
let days = tripDays
|
||||
|
||||
for (index, dayDate) in days.enumerated() {
|
||||
let dayNum = index + 1
|
||||
let gamesOnDay = gamesOn(date: dayDate)
|
||||
|
||||
if !gamesOnDay.isEmpty || index == 0 || index == days.count - 1 {
|
||||
sections.append(.day(dayNumber: dayNum, date: dayDate, games: gamesOnDay))
|
||||
// Get city from games (preferred) or from stops as fallback
|
||||
let cityForDay = gamesOnDay.first?.stadium.city ?? cityOn(date: dayDate) ?? ""
|
||||
|
||||
// Include days with games
|
||||
// Skip empty days at the end (departure day after last game)
|
||||
if !gamesOnDay.isEmpty {
|
||||
daySections.append((dayNum, dayDate, cityForDay, gamesOnDay))
|
||||
}
|
||||
}
|
||||
|
||||
// Build sections: insert travel BEFORE each day when coming from different city
|
||||
for (index, daySection) in daySections.enumerated() {
|
||||
|
||||
// Check if we need travel BEFORE this day (coming from different city)
|
||||
if index > 0 {
|
||||
let prevSection = daySections[index - 1]
|
||||
let prevCity = prevSection.city
|
||||
let currentCity = daySection.city
|
||||
|
||||
// If cities differ, find travel segment from prev -> current
|
||||
if !prevCity.isEmpty && !currentCity.isEmpty && prevCity != currentCity {
|
||||
if let travelSegment = findTravelSegment(from: prevCity, to: currentCity) {
|
||||
sections.append(.travel(travelSegment))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let travelAfterDay = travelDepartingAfter(date: dayDate, beforeNextGameDay: days.indices.contains(index + 1) ? days[index + 1] : nil)
|
||||
for segment in travelAfterDay {
|
||||
sections.append(.travel(segment))
|
||||
}
|
||||
// Add the day section
|
||||
sections.append(.day(dayNumber: daySection.dayNumber, date: daySection.date, games: daySection.games))
|
||||
}
|
||||
|
||||
return sections
|
||||
@@ -311,13 +334,27 @@ struct TripDetailView: View {
|
||||
}.sorted { $0.game.dateTime < $1.game.dateTime }
|
||||
}
|
||||
|
||||
private func travelDepartingAfter(date: Date, beforeNextGameDay: Date?) -> [TravelSegment] {
|
||||
/// Get the city for a given date (from the stop that covers that date)
|
||||
private func cityOn(date: Date) -> String? {
|
||||
let calendar = Calendar.current
|
||||
let dayEnd = calendar.startOfDay(for: date)
|
||||
let dayStart = calendar.startOfDay(for: date)
|
||||
|
||||
return trip.travelSegments.filter { segment in
|
||||
let segmentDay = calendar.startOfDay(for: segment.departureTime)
|
||||
return segmentDay == dayEnd
|
||||
return trip.stops.first { stop in
|
||||
let arrivalDay = calendar.startOfDay(for: stop.arrivalDate)
|
||||
let departureDay = calendar.startOfDay(for: stop.departureDate)
|
||||
return dayStart >= arrivalDay && dayStart <= departureDay
|
||||
}?.city
|
||||
}
|
||||
|
||||
/// Find travel segment that goes from one city to another
|
||||
private func findTravelSegment(from fromCity: String, to toCity: String) -> TravelSegment? {
|
||||
let fromLower = fromCity.lowercased().trimmingCharacters(in: .whitespaces)
|
||||
let toLower = toCity.lowercased().trimmingCharacters(in: .whitespaces)
|
||||
|
||||
return trip.travelSegments.first { segment in
|
||||
let segmentFrom = segment.fromLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
|
||||
let segmentTo = segment.toLocation.name.lowercased().trimmingCharacters(in: .whitespaces)
|
||||
return segmentFrom == fromLower && segmentTo == toLower
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user