Add featured trips carousel on home screen

- Generate 8 suggested trips on app launch (2 per region: East, Central, West, Cross-Country)
- Each region has single-sport and multi-sport trip options
- Region classification based on stadium longitude
- Animated loading state with shimmer placeholders
- Loading messages use Foundation Models when available, fallback otherwise
- Tap card to view trip details in sheet
- Refresh button to regenerate trips
- Fixed-height cards with aligned top/bottom layout

New files:
- Region.swift: Geographic region enum with longitude classification
- LoadingTextGenerator.swift: On-device AI loading messages
- SuggestedTripsGenerator.swift: Trip generation service
- SuggestedTripCard.swift: Carousel card component
- LoadingTripsView.swift: Animated loading state

🤖 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-08 10:33:44 -06:00
parent aadc82db73
commit 415202e7f4
7 changed files with 984 additions and 2 deletions

View File

@@ -0,0 +1,54 @@
//
// Region.swift
// SportsTime
//
// Geographic regions for trip classification.
//
import Foundation
enum Region: String, CaseIterable, Identifiable {
case east = "East Coast"
case central = "Central"
case west = "West Coast"
case crossCountry = "Cross-Country"
var id: String { rawValue }
var displayName: String { rawValue }
var shortName: String {
switch self {
case .east: return "East"
case .central: return "Central"
case .west: return "West"
case .crossCountry: return "Coast to Coast"
}
}
var iconName: String {
switch self {
case .east: return "building.2"
case .central: return "building"
case .west: return "sun.max"
case .crossCountry: return "arrow.left.arrow.right"
}
}
/// Classifies a stadium's region based on longitude.
///
/// Longitude boundaries:
/// - East: > -85 (NYC, Boston, Miami, Toronto, Montreal, Atlanta)
/// - Central: -110 to -85 (Chicago, Houston, Dallas, Minneapolis, Denver)
/// - West: < -110 (LA, SF, Seattle, Phoenix, Las Vegas)
static func classify(longitude: Double) -> Region {
switch longitude {
case _ where longitude > -85:
return .east
case -110...(-85):
return .central
default:
return .west
}
}
}

View File

@@ -51,6 +51,10 @@ struct Stadium: Identifiable, Codable, Hashable {
"\(city), \(state)"
}
var region: Region {
Region.classify(longitude: longitude)
}
func distance(to other: Stadium) -> CLLocationDistance {
location.distance(from: other.location)
}