feat: rewrite bootstrap, fix CloudKit sync, update canonical data, and UI fixes

- Rewrite BootstrapService: remove all legacy code paths (JSONStadium,
  JSONGame, bootstrapStadiumsLegacy, bootstrapGamesLegacy, venue aliases,
  createDefaultLeagueStructure), require canonical JSON files only
- Add clearCanonicalData() to handle partial bootstrap recovery (prevents
  duplicate key crashes from interrupted first-launch)
- Fix nullable stadium_canonical_id in games (4 MLS games have null)
- Fix CKModels: logoUrl case, conference/division field keys
- Fix CanonicalSyncService: sync conferenceCanonicalId/divisionCanonicalId
- Add sports_canonical.json and DemoMode.swift
- Delete legacy stadiums.json and games.json
- Update all canonical resource JSON files with latest data
- Fix TripWizardView horizontal scrolling with GeometryReader constraint
- Update RegionMapSelector, TripDetailView, TripOptionsView UI improvements
- Add DateRangePicker, PlanningModeStep, SportsStep enhancements
- Update UI tests and marketing-videos config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-06 00:06:19 -06:00
parent 12f959ab8d
commit fdcecafaa3
29 changed files with 93279 additions and 157943 deletions

View File

@@ -19,6 +19,8 @@ struct RegionMapSelector: View {
let onToggle: (Region) -> Void
@Environment(\.colorScheme) private var colorScheme
@Environment(\.isDemoMode) private var isDemoMode
@State private var hasAppliedDemoSelection = false
// Camera position centered on continental US
@State private var cameraPosition: MapCameraPosition = .camera(
@@ -33,29 +35,44 @@ struct RegionMapSelector: View {
var body: some View {
VStack(spacing: Theme.Spacing.sm) {
// Map with region overlays
MapReader { proxy in
Map(position: $cameraPosition, interactionModes: []) {
// West region polygon
MapPolygon(coordinates: RegionCoordinates.west)
.foregroundStyle(fillColor(for: .west))
.stroke(strokeColor(for: .west), lineWidth: strokeWidth(for: .west))
ZStack {
MapReader { proxy in
Map(position: $cameraPosition, interactionModes: []) {
// West region polygon
MapPolygon(coordinates: RegionCoordinates.west)
.foregroundStyle(fillColor(for: .west))
.stroke(strokeColor(for: .west), lineWidth: strokeWidth(for: .west))
// Central region polygon
MapPolygon(coordinates: RegionCoordinates.central)
.foregroundStyle(fillColor(for: .central))
.stroke(strokeColor(for: .central), lineWidth: strokeWidth(for: .central))
// Central region polygon
MapPolygon(coordinates: RegionCoordinates.central)
.foregroundStyle(fillColor(for: .central))
.stroke(strokeColor(for: .central), lineWidth: strokeWidth(for: .central))
// East region polygon
MapPolygon(coordinates: RegionCoordinates.east)
.foregroundStyle(fillColor(for: .east))
.stroke(strokeColor(for: .east), lineWidth: strokeWidth(for: .east))
}
.mapStyle(.standard(elevation: .flat, pointsOfInterest: .excludingAll))
.onTapGesture { location in
if let coordinate = proxy.convert(location, from: .local) {
let tappedRegion = regionForCoordinate(coordinate)
onToggle(tappedRegion)
// East region polygon
MapPolygon(coordinates: RegionCoordinates.east)
.foregroundStyle(fillColor(for: .east))
.stroke(strokeColor(for: .east), lineWidth: strokeWidth(for: .east))
}
.mapStyle(.standard(elevation: .flat, pointsOfInterest: .excludingAll))
.onTapGesture { location in
if let coordinate = proxy.convert(location, from: .local) {
let tappedRegion = regionForCoordinate(coordinate)
onToggle(tappedRegion)
}
}
}
// Invisible button overlays for UI testing accessibility
HStack(spacing: 0) {
Button { onToggle(.west) } label: { Color.clear }
.accessibilityIdentifier("wizard.regions.west")
.frame(maxWidth: .infinity)
Button { onToggle(.central) } label: { Color.clear }
.accessibilityIdentifier("wizard.regions.central")
.frame(maxWidth: .infinity)
Button { onToggle(.east) } label: { Color.clear }
.accessibilityIdentifier("wizard.regions.east")
.frame(maxWidth: .infinity)
}
}
.frame(height: 160)
@@ -71,6 +88,14 @@ struct RegionMapSelector: View {
// Selection footer
selectionFooter
}
.onAppear {
if isDemoMode && !hasAppliedDemoSelection && selectedRegions.isEmpty {
hasAppliedDemoSelection = true
DispatchQueue.main.asyncAfter(deadline: .now() + DemoConfig.selectionDelay) {
onToggle(DemoConfig.demoRegion)
}
}
}
}
// MARK: - Coordinate to Region