- Add `displayName` computed property to Trip model that always
generates city list with " → " separator for consistent display
- Replace all `trip.name` usages with `trip.displayName` in UI files
- Update SuggestedTripsGenerator to use " → " separator
- Update PDFGenerator to use displayName for PDF titles
This ensures all trip names display consistently regardless of when
the trip was created or how the name was originally stored.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Refactor TripDetailView to fetch games from AppDataProvider when not
provided, adding loading state indicator for better UX
- Update all callers (25+ HomeContent variants, TripOptionsView, HomeView)
to use simpler TripDetailView(trip:) initializer
- Fix PollDetailView sheet issue by using sheet(item:) instead of
sheet(isPresented:) to prevent blank screen on first tap
- Improve PollDetailView UI with Theme styling, icons, and better
visual hierarchy for share code, voting status, and results sections
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Delete TripCreationView.swift and TripCreationViewModel.swift (unused)
- Extract TripOptionsView to standalone file
- Extract DateRangePicker and DayCell to standalone file
- Extract LocationSearchSheet and CityInputType to standalone file
- Fix TripWizardView to pass games dictionary to TripOptionsView
- Remove debug print statements from TripDetailView
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix suggested trips showing wrong sports for cross-country trips
- Remove quick start sections from home variants (Classic, Spotify)
- Remove dead quickActions code from HomeView
- Fix pace capsule animation in TripCreationView
- Add text wrapping to achievement descriptions
- Improve poll parsing with better error handling
- Various sharing system improvements
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace old ProgressCardGenerator with protocol-based sharing architecture
supporting trips, achievements, and stadium progress. Features 8 color
themes, Instagram Stories optimization (1080x1920), and reusable card
components with map snapshots.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete group trip polling feature allowing users to share trips
with friends for voting using Borda count scoring.
New components:
- TripPoll and PollVote domain models with share codes and rankings
- LocalTripPoll and LocalPollVote SwiftData models for persistence
- CKTripPoll and CKPollVote CloudKit record wrappers
- PollService actor for CloudKit CRUD operations and subscriptions
- PollCreation/Detail/Voting views and view models
- Deep link handling for sportstime://poll/{code} URLs
- Debug Pro status override toggle in Settings
Integration:
- HomeView shows polls section in My Trips
- SportsTimeApp registers SwiftData models and handles deep links
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add debug toggle in Settings to override Pro subscription status (DEBUG builds only, defaults to true)
- Auto-validate wizard step flags on appear so button enables without explicit user interaction:
- DatesStep: calls updateHasSetDates() on appear
- RoutePreferenceStep: sets hasSetRoutePreference on appear
- RepeatCitiesStep: sets hasSetRepeatCities on appear
Previously, canPlanTrip required all flags to be explicitly set by user interaction, even when valid defaults were showing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add themed backgrounds for each onboarding feature page:
- Unlimited Trips: animated route map with dotted paths and traveling car
- Export & Share: floating documents with radiating share lines
- Track Your Journey: stadium map with pins and achievement badges
- Add sports-themed pricing page background with random glow effects
- Display introductory offer pricing in subscription rows
- Add feature bullets to each onboarding page for better value prop
- Add crown icon header and feature pills to pricing page
- Add debug button in Settings to preview onboarding flow
- Create StoreKit configuration file for testing IAP
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add design style system with 23 unique home screen aesthetics:
- Classic (original SportsTime design, now default)
- 12 experimental styles (Brutalist, Luxury Editorial, etc.)
- 10 polished app-inspired styles (Flighty, SeatGeek, Apple Maps,
Things 3, Airbnb, Spotify, Nike Run Club, Fantastical, Strava,
Carrot Weather)
Includes settings picker to switch between styles and persists
selection via UserDefaults.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- PaywallView: remove unnecessary nil coalescing for currencyCode
- GameDAGRouter: change var to let for immutable compositeKeys
- GamesHistoryRow/View: add missing wnba and nwsl switch cases
- VisitDetailView: fix unused variable in preview
- AchievementEngine: use convenience init to avoid default parameter warning
- ProgressCardGenerator: use method overload instead of default parameter
- StadiumProximityMatcher: extract constants to ProximityConstants enum
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implement freemium model with StoreKit 2:
- StoreManager singleton for purchase/restore/entitlements
- ProFeature enum defining gated features
- PaywallView and OnboardingPaywallView for upsell UI
- ProGate view modifier and ProBadge component
Feature gating:
- Trip saving: 1 free trip, then requires Pro
- PDF export: Pro only with badge indicator
- Progress tab: Shows ProLockedView for free users
- Settings: Subscription management section
Also fixes pre-existing test issues with StadiumVisit
and ItineraryOption model signature changes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add LoadingSpinner component with small/medium/large sizes using system gray color
- Add LoadingPlaceholder for skeleton loading states
- Add LoadingSheet for full-screen blocking overlays
- Replace ThemedSpinner/ThemedSpinnerCompact across all views
- Remove deprecated loading components from AnimatedComponents.swift
- Delete LoadingTextGenerator.swift
- Fix PhotoImportView layout to fill full width
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add scenePhase observer to sync when app returns to foreground
- Remove misleading "Sync Schedules" button from Settings (it only
reloaded local data, didn't actually sync from CloudKit)
- Fix GamesHistoryView to refresh list after deleting a visit
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add PlanningTips data model with 105 tips across 7 categories
- Wire random tips into HomeView (3 tips per session)
- Add TripOptionsGrouper for grouping by city/game count and mileage
- Update TripOptionsView with sectioned display when sorting
- Recommended and Best Efficiency remain flat lists
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Enable zoom/pan on progress map with reset button
- Add visit count badges to stadium chips
- Create GamesHistoryView with year grouping and sport filters
- Create StadiumVisitHistoryView for viewing all visits to a stadium
- Add VisitListCard and GamesHistoryRow components
- Add "See All" navigation from Recent Visits to Games History
- Add tests for map interactions, visit lists, and games history
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace progressive reveal with single fade-in of all steps
- Add canPlanTrip validation requiring all fields before planning
- Disable Plan Trip button until all selections are made
- Simplify ViewModel by removing step-by-step visibility logic
- Update tests for new validation-based approach
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Reorder wizard steps: dates before sports (enables availability check)
- Add contentShape(Rectangle()) for full tap targets on all cards
- Fix route preference showing preselected value
- Fix sport cards having inconsistent heights
- Speed up step reveal animation (0.3s → 0.15s)
- Add debounced scroll delay to avoid interrupting selection
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Auto-scroll to newly revealed sections
- Integration with TripPlanningEngine
- Navigation to TripOptionsView on success
- Error handling with alerts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- DatesStep: date range selection with duration indicator
- RegionsStep: geographic region selection
- RoutePreferenceStep: direct/scenic/balanced preference
- RepeatCitiesStep: unique vs repeat city visits
- MustStopsStep: optional must-stop locations
- ReviewStep: summary and plan button
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add city/state location and broadcast channel to GameCalendarRow
in the "By Games" trip building flow. Users now see:
- Clock icon with game time
- Building icon with stadium name
- Location pin with city, state
- TV icon with broadcast channel (when available)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace upfront loading of all games with lazy Sport → Team → Game
hierarchy. Games are now fetched per-team when expanded and cached
to prevent re-fetching. Also removes pagination workaround and
pre-computes groupings in ScheduleViewModel to avoid per-render work.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes lag and color inconsistency in trip creation game selection.
- Add pagination with 50 games per page to TripCreationViewModel
- Add displayedAvailableGames, loadInitialAvailableGames, loadMoreAvailableGames
- Update GamePickerSheet to use paginated data with infinite scroll trigger
- Show "Load more games" button with progress indicator and count
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Loads 50 games at a time to fix lag with large datasets.
- Add pagination state (displayedGames, currentPage, allFilteredGames)
- Add loadInitialGames() and loadMoreGames() methods
- Update gamesBySport to use displayedGames
- Add infinite scroll trigger via onAppear on last game
- Add ProgressView indicator when more games available
- Reset pagination when search text changes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds contentTransition(.identity) to pace filter icon and text to prevent
SwiftUI's default text morphing animation when changing pace filter
(All/Packed/Moderate/Relaxed). Also adds animation(nil, value:) to
disable animations on the capsule container.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Wrap selectedIds.removeAll() in withTransaction with disablesAnimations
to prevent animation glitches when clearing all selections at once.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Disables implicit animation when toggling game selection to prevent
visual glitches/morphing effect. Uses both withTransaction to disable
animations on the state change and .animation(nil, value:) on the
selection indicator and row background.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds stadium name and city to game rows in the schedule list.
The GameRowView now has a showLocation parameter that displays
a location row with mappin icon when enabled.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previously, browsing by team (e.g., Houston Astros) only showed home games.
Now games are associated with both home AND away teams, so selecting a team
shows all their games regardless of whether they're playing at home or away.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add allRichGames method to DataProvider
- Update TripCreationViewModel.loadGamesForBrowsing to use allGames (removes 90-day limit)
- Update MockCloudKitService sync methods to use new delta sync signatures
- Update MockAppDataProvider with renamed methods and new allGames/allRichGames
- Fix all callers: ScheduleViewModel, TripCreationViewModel, SuggestedTripsGenerator, GameMatcher
- Update CLAUDE.md documentation with new method names
This completes the delta sync implementation:
- CloudKit sync now uses modificationDate for proper delta sync
- First sync fetches ALL data, subsequent syncs only fetch modified records
- "By Games" mode now shows all available games (not just 90 days)
- All data types (stadiums, teams, games) use consistent delta sync pattern
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This refactor fixes the achievement system by using stable canonical string
IDs (e.g., "stadium_mlb_fenway_park") instead of random UUIDs. This ensures
stadium mappings for achievements are consistent across app launches and
CloudKit sync operations.
Changes:
- Stadium, Team, Game: id property changed from UUID to String
- Trip, TripStop, TripPreferences: updated to use String IDs for games/stadiums
- CKModels: removed UUID parsing, use canonical IDs directly
- AchievementEngine: now matches against canonical stadium IDs
- All test files updated to use String IDs instead of UUID()
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The Green Monster (Fenway) and Ivy League (Wrigley) achievements
weren't working because:
1. Symbolic IDs use lowercase sport (stadium_mlb_bos)
2. Sport enum uses uppercase raw values (MLB)
3. Visits store stadium UUIDs, not symbolic IDs
Added resolveSymbolicStadiumId() helper that:
- Uppercases the sport string before Sport(rawValue:)
- Looks up team by abbreviation and sport
- Returns the team's stadiumId as UUID string
Also fixed:
- getStadiumIdsForLeague returns UUID strings (not symbolic IDs)
- AchievementProgress.isEarned computed from progress OR stored record
- getStadiumIdsForDivision queries CanonicalTeam properly
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a trip duration stepper (2-21 days) to the By Games planning mode.
When users select specific games, the planner now generates ALL possible
N-day windows containing those games (e.g., 7-day trips starting on
different days), finding additional games in each window to maximize
route options.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The resolveLocations() function was overwriting valid LocationInput
objects (with coordinates) from LocationSearchSheet. Now it only
resolves locations that don't already have coordinates.
Added debug logging to trace scenario selection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces inline text fields with buttons that open LocationSearchSheet,
matching the must-stop location picker UI for consistency.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Lock all maps to North America (no pan/zoom) in ProgressMapView and TripDetailView
- Sort saved trips by most cities (stops count)
- Filter cross-country trips to top 2 by stops on home screen
- Use LocationSearchSheet for Follow Team home location (consistent with must-stop)
- Initialize DateRangePicker to show selected dates on appear
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace crowded segmented control with clean card-based grid.
Each card shows icon and label with selected state highlight.
Fixes text truncation issue with "Follow Team" option.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a new planning mode that lets users follow a team's schedule
(home + away games) and builds multi-city routes accordingly.
Key changes:
- New ScenarioDPlanner with team filtering and route generation
- Team picker UI with sport grouping and search
- Fix TravelEstimator 5-day limit (was 2-day) for cross-country routes
- Fix DateInterval end boundary to include games on last day
- Comprehensive test suite covering edge cases:
- Multi-city routes with adequate/insufficient time
- Optimal game selection per city for feasibility
- 5-day driving segment limits
- Multiple driver scenarios
Enables trips like Houston → Chicago → Anaheim following the Astros.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add HTML files for hosting at 88oakapps.com/privacy and /terms
- Update SettingsView links to use 88oakapps.com domain
- Plain English, minimal legal documents for freemium app
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create unified sport selector grid used across Home (Quick Start),
Trip Creation, and Progress views. Removes duplicate button implementations
and ensures consistent grid layout with centered bottom row.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use GeometryReader to calculate button width as 1/4 of available space,
ensuring all buttons have the same size. Bottom row (MLS, WNBA, NWSL)
is now centered with WNBA in the middle.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace all custom Theme.FontSize values and hardcoded font sizes with
Apple's built-in text styles (.largeTitle, .title2, .headline, .body,
.subheadline, .caption, .caption2) to support accessibility scaling.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace abstract colored rectangles with actual North America map
using MapKit. Regions now follow state borders with tappable
overlays for West/Central/East selection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Buffer days added no value to trip planning. Itineraries now show
only game days and necessary travel days without extra padding.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>