Fix coast-to-coast trips and improve itinerary display

- Fix same-day different-city validation in C2C routes (no more impossible
  games like Detroit 7:30pm AND Milwaukee 8pm on the same day)
- Cap C2C trips at 14 days max with 3 middle stops, prefer shortest routes
- Add sport icon and name to game rows in trip itinerary
- Add horizontal scroll to route dots in suggested trip cards
- Allow swipe-to-dismiss on home sheet (trip planner still blocks)
- Generate travel segments for suggested trips
- Increase DAG route lookahead to 5 days for multi-day drives

🤖 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 11:42:27 -06:00
parent 7efcea7bd4
commit 3e778473e6
7 changed files with 531 additions and 120 deletions

View File

@@ -120,7 +120,6 @@ struct HomeView: View {
NavigationStack {
TripDetailView(trip: suggestedTrip.trip, games: suggestedTrip.richGames)
}
.interactiveDismissDisabled()
}
}

View File

@@ -79,33 +79,45 @@ struct SuggestedTripCard: View {
private var routePreview: some View {
let cities = suggestedTrip.trip.stops.map { $0.city }
let displayCities: [String]
let startCity = cities.first ?? ""
let endCity = cities.last ?? ""
if cities.count <= 3 {
displayCities = cities
} else {
displayCities = [cities.first ?? "", "...", cities.last ?? ""]
}
return VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
// Start End display
HStack(spacing: 6) {
Text(startCity)
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.foregroundStyle(Theme.textPrimary(colorScheme))
.lineLimit(1)
return VStack(alignment: .leading, spacing: 0) {
ForEach(Array(displayCities.enumerated()), id: \.offset) { index, city in
if index > 0 {
// Connector
HStack(spacing: 4) {
Text("|")
.font(.system(size: 10))
Image(systemName: "chevron.down")
.font(.system(size: 8))
}
.foregroundStyle(Theme.warmOrange.opacity(0.6))
.padding(.leading, 4)
}
Image(systemName: "arrow.right")
.font(.system(size: 10, weight: .semibold))
.foregroundStyle(Theme.warmOrange)
Text(city)
.font(.system(size: Theme.FontSize.caption, weight: index == 0 ? .semibold : .regular))
.foregroundStyle(index == 0 ? Theme.textPrimary(colorScheme) : Theme.textSecondary(colorScheme))
Text(endCity)
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.foregroundStyle(Theme.textPrimary(colorScheme))
.lineLimit(1)
}
// Scrollable stop dots
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 4) {
ForEach(0..<cities.count, id: \.self) { index in
Circle()
.fill(index == 0 || index == cities.count - 1 ? Theme.warmOrange : Theme.routeGold.opacity(0.6))
.frame(width: 6, height: 6)
if index < cities.count - 1 {
Rectangle()
.fill(Theme.routeGold.opacity(0.4))
.frame(width: 8, height: 2)
}
}
}
.padding(.horizontal, Theme.Spacing.xs)
}
.frame(height: 12)
}
}