# Bug Fixes Design Document **Date:** 2026-01-11 **Status:** Ready for Implementation **Priority:** High (ASAP) ## Overview This document outlines fixes for 12 bugs affecting trip planning and UI functionality. Issues are grouped by category and prioritized by impact on core functionality. --- ## Category A: Planning Mode Bugs (7 issues) These affect the main trip planning flow and should be fixed first. ### Issue #1 & #6: Follow Team Start/End Location **Problem:** Follow Team mode uses a plain text field for home location input, while Must-Stop uses `LocationSearchSheet` with MKLocalSearch. The text field doesn't provide proper geocoding or location suggestions. **Root Cause:** `TripCreationView` uses different UI patterns for location input depending on context. **Files to Modify:** - `SportsTime/Features/Trip/Views/TripCreationView.swift` - `SportsTime/Features/Trip/ViewModels/TripCreationViewModel.swift` **Fix:** 1. Replace the text field in Follow Team mode with a button that opens `LocationSearchSheet` 2. On selection, set both `startLocation` (resolved LocationInput) and `startLocationText` (display name) 3. Preserve `startLocationText` when switching to `.followTeam` mode (currently cleared in `switchPlanningMode()`) **Implementation:** ```swift // In Follow Team section, replace TextField with: Button { cityInputType = .homeLocation // Add new case showCityInput = true } label: { HStack { Text(viewModel.startLocationText.isEmpty ? "Set Home Location" : viewModel.startLocationText) Spacer() Image(systemName: "magnifyingglass") } } // In LocationSearchSheet callback: case .homeLocation: viewModel.startLocation = location viewModel.startLocationText = location.name ``` --- ### Issue #2: "By Game" Shows Date Range Error **Problem:** Selecting games in "By Game" mode shows "date range required" error even though the date range should be computed from selected games. **Root Cause:** `gameFirstDateRange` computed property may return nil if no games selected yet, but the error message appears prematurely. **Files to Modify:** - `SportsTime/Features/Trip/ViewModels/TripCreationViewModel.swift` **Fix:** 1. In `planTrip()`, ensure `gameFirstDateRange` is computed before validation 2. Update `formValidationMessage` to show "Select at least one game" instead of date range error for gameFirst mode 3. Verify `effectiveStartDate`/`effectiveEndDate` are set from selected games before planning **Verification:** Lines 287-292 in `planTrip()` already handle this, but need to verify the error isn't coming from `ScenarioBPlanner`. --- ### Issue #3: Date Range Not Showing Current Selection **Problem:** Date range picker doesn't display the currently selected start/end dates. **Files to Modify:** - `SportsTime/Features/Trip/Views/TripCreationView.swift` (DateRangePicker) **Fix:** 1. Verify `DateRangePicker` initializes with `viewModel.startDate` and `viewModel.endDate` 2. Check that `selectionState` is set to `.complete` when both dates exist 3. Ensure visual highlighting shows the selected range on initial render --- ### Issue #4 & #8: Must-Stop Finds Wrong Games / Shows Away Games **Problem:** 1. Must-stop locations are accepted but **never used** in the planning logic 2. When must-stop is used, it should only show games where the must-stop city is the **home team's city** **Root Cause:** `ScenarioAPlanner.swift` receives `mustStopLocations` via `PlanningRequest` but never filters games by it. **Files to Modify:** - `SportsTime/Planning/Engine/ScenarioAPlanner.swift` - `SportsTime/Planning/Models/PlanningModels.swift` (for config) **Fix:** 1. Add must-stop filtering after date/region filtering in Step 2 2. Filter to games where the stadium's city matches the must-stop city AND it's a home game **Implementation:** ```swift // In ScenarioAPlanner.plan(), after line 81: // Step 2b: Filter by must-stop locations (if any) var gamesAfterMustStop = gamesInRange if let mustStop = request.mustStopLocation { gamesAfterMustStop = gamesInRange.filter { game in guard let stadium = request.stadiums[game.stadiumId] else { return false } // Must be in must-stop city AND be a home game (stadium is home team's stadium) let cityMatches = stadium.city.lowercased() == mustStop.name.lowercased() // Home games only: the game's stadium is in the must-stop city return cityMatches } // If must-stop filtering removed all games, fail with clear message if gamesAfterMustStop.isEmpty { return .failure( PlanningFailure( reason: .noGamesInRange, violations: [ ConstraintViolation( type: .mustStop, description: "No home games found in \(mustStop.name) during selected dates", severity: .error ) ] ) ) } } ``` --- ### Issue #5: Scenario Not Updating When Switched **Problem:** Switching planning modes may not update the UI correctly. **Root Cause:** `planningMode` property has no `didSet` observer to trigger dependent state updates. **Files to Modify:** - `SportsTime/Features/Trip/ViewModels/TripCreationViewModel.swift` **Fix:** 1. Add `didSet` to `planningMode` property 2. Call `switchPlanningMode()` from the observer to ensure state is reset 3. Alternatively, verify UI is observing `planningMode` changes correctly (since `@Observable` should handle this) **Implementation:** ```swift var planningMode: PlanningMode = .dateRange { didSet { if oldValue != planningMode { // Reset mode-specific state viewState = .editing availableGames = [] } } } ``` --- ## Category B: UI/Sort/Filter Issues (4 issues) ### Issue #7: Add "Most Cities" Sort to All Trips **Problem:** `SavedTripsListView` has no sort options; trips are shown by `updatedAt` only. **Files to Modify:** - `SportsTime/Features/Home/Views/HomeView.swift` (SavedTripsListView) **Fix:** 1. Add `@State private var sortOption: TripSortOption = .date` 2. Add sort picker to toolbar 3. Sort trips based on selected option **Implementation:** ```swift enum TripSortOption: String, CaseIterable { case date = "Date" case mostCities = "Most Cities" case mostGames = "Most Games" } struct SavedTripsListView: View { let trips: [SavedTrip] @State private var sortOption: TripSortOption = .date var sortedTrips: [SavedTrip] { switch sortOption { case .date: return trips.sorted { $0.updatedAt > $1.updatedAt } case .mostCities: return trips.sorted { ($0.trip?.stops.count ?? 0) > ($1.trip?.stops.count ?? 0) } case .mostGames: return trips.sorted { ($0.trip?.totalGames ?? 0) > ($1.trip?.totalGames ?? 0) } } } var body: some View { // ... use sortedTrips instead of trips .toolbar { ToolbarItem(placement: .primaryAction) { Menu { Picker("Sort", selection: $sortOption) { ForEach(TripSortOption.allCases, id: \.self) { option in Text(option.rawValue).tag(option) } } } label: { Image(systemName: "arrow.up.arrow.down") } } } } } ``` --- ### Issue #9: Coast-to-Coast Filter by Most Stops **Problem:** Coast-to-coast suggested trips should only show the top 2 trips with the most stops (C2C should be massive, epic trips). **Files to Modify:** - `SportsTime/Features/Home/Views/HomeView.swift` (suggestedTripsSection) - Or `SportsTime/Core/Services/SuggestedTripsGenerator.swift` **Fix:** 1. Filter coast-to-coast trips to only the top 2 by stop count 2. Sort by `stops.count` descending before taking top 2 **Implementation:** ```swift // In suggestedTripsSection or SuggestedTripsGenerator: var coastToCoastTrips: [SuggestedTrip] { suggestedTripsGenerator.suggestedTrips .filter { $0.type == .coastToCoast } .sorted { $0.trip.stops.count > $1.trip.stops.count } .prefix(2) .map { $0 } // Convert ArraySlice to Array } ``` --- ### Issue #11: Maps Should Not Be Movable **Problem:** Maps on various screens can be panned/zoomed, but should be locked to show North America only. **Files to Modify:** - `SportsTime/Features/Progress/Views/ProgressMapView.swift` - Any other views with maps **Fix:** 1. Disable map interaction using `interactionModes: []` (iOS 17+) or `.allowsHitTesting(false)` 2. Set fixed region to show continental US **Implementation:** ```swift // iOS 17+ approach: Map(initialPosition: .region(usRegion), interactionModes: []) { // annotations } // Or disable hit testing: Map(coordinateRegion: .constant(usRegion), annotationItems: stadiums) { ... } .allowsHitTesting(false) // Fixed US region: let usRegion = MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 39.8283, longitude: -98.5795), span: MKCoordinateSpan(latitudeDelta: 50, longitudeDelta: 60) ) ``` --- ### Issue #12: Highlight Today in Schedule View **Problem:** Schedule view doesn't visually distinguish games happening today. **Files to Modify:** - `SportsTime/Features/Schedule/Views/ScheduleListView.swift` **Fix:** 1. Add "TODAY" badge or background highlight for games on current date 2. Use `Calendar.current.isDateInToday()` to detect today's games **Implementation:** ```swift struct GameRowView: View { let game: RichGame var showDate: Bool = false private var isToday: Bool { Calendar.current.isDateInToday(game.game.dateTime) } var body: some View { VStack(alignment: .leading, spacing: 8) { if showDate { HStack { Text(formattedDate) .font(.caption) .fontWeight(.medium) .foregroundStyle(.secondary) if isToday { Text("TODAY") .font(.caption2) .fontWeight(.bold) .foregroundStyle(.white) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.orange) .clipShape(Capsule()) } } } // ... rest of view } .listRowBackground(isToday ? Color.orange.opacity(0.1) : nil) } } ``` --- ## Category C: Other Issues (1 issue) ### Issue #10: Photo Import Loses Metadata **Problem:** Importing photos from iPhone doesn't preserve EXIF metadata. **Investigation Needed:** 1. Check current photo picker implementation (`PHPickerViewController` vs `UIImagePickerController`) 2. Verify if using `UIImage` compression strips EXIF 3. May need to use `PHAsset` directly to preserve metadata **Potential Fix:** ```swift // Use PHPickerConfiguration with .current to get PHAsset let config = PHPickerConfiguration(photoLibrary: .shared()) config.selectionLimit = 1 config.preferredAssetRepresentationMode = .current // Preserves original // Or request full asset data: let result: PHPickerResult = ... if let assetId = result.assetIdentifier { let assets = PHAsset.fetchAssets(withLocalIdentifiers: [assetId], options: nil) // Work with PHAsset to preserve metadata } ``` **Status:** Requires further investigation to identify exact implementation. --- ## Implementation Priority 1. **Critical (Core Planning):** - #4 & #8: Must-stop filtering (completely broken) - #1 & #6: Follow Team location search - #2: By Game date range error 2. **High (UX Impact):** - #5: Scenario switching - #3: Date range display - #12: Today highlight - #11: Lock maps 3. **Medium (Enhancement):** - #7: Sort options - #9: C2C filter 4. **Low (Separate Investigation):** - #10: Photo metadata --- ## Testing Checklist - [ ] Follow Team mode: Can search and select home location via MKLocalSearch - [ ] By Game mode: No false "date range required" error - [ ] Date range picker: Shows currently selected dates on open - [ ] Must-stop with Chicago: Finds Cubs/White Sox HOME games only - [ ] Switching modes: UI updates correctly, stale state cleared - [ ] All Trips: Can sort by Most Cities, Most Games - [ ] Coast-to-coast: Shows only top 2 trips by stop count - [ ] Maps: Cannot pan or zoom, fixed to North America - [ ] Schedule: Today's games have visual indicator