Commit Graph

162 Commits

Author SHA1 Message Date
Trey t
37a1347ce3 perf: lazy hierarchical loading for game picker
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>
2026-01-12 20:19:46 -06:00
Trey t
9531ed1008 docs: add Trip Planning Enhancements implementation plan
12 tasks with TDD approach:
- TripWizardViewModel with reveal state logic
- 8 step components (PlanningMode, Sports, Dates, Regions, etc.)
- TripWizardView container with auto-scroll
- Settings toggle for classic vs wizard mode
- Integration tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:38:05 -06:00
Trey t
4999c90595 docs: add dynamic sports via CloudKit design
Hybrid approach: keep Sport enum for existing 7 sports,
add DynamicSport struct for CloudKit-defined leagues.
Unified via AnySport protocol for seamless UI integration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:32:42 -06:00
Trey t
1ef3b0335e perf: add pagination to game selection view
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>
2026-01-12 19:16:42 -06:00
Trey t
746b56bc77 perf: add pagination to schedule list
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>
2026-01-12 19:07:54 -06:00
Trey t
3530b31cca docs: add 3 feature enhancement design plans
- Trip Planning Enhancements: progressive reveal single-screen wizard
- Progress Tracking Enhancements: multiple visits, games history, zoomable map
- Polish Enhancements: grouped sorting, 100+ planning tips

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:07:46 -06:00
Trey t
9f3a762324 fix: add AT&T Center alias for Frost Bank Center
NBA scraper now resolves old stadium name to current Spurs arena.
The AT&T Center was renamed to Frost Bank Center in July 2024.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:04:16 -06:00
Trey t
64c64093c4 feat: add timezone support for stadium-local game times
Adds timeZoneIdentifier to Stadium model and localGameTime/localGameTimeShort
computed properties to RichGame. Game times can now display in venue local
timezone. Also adds timezone field to Python StadiumInfo dataclass with
example entries for Pacific, Central, Mountain, and Toronto timezones.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:58:35 -06:00
Trey t
402a8877f3 fix: remove redundant My Trips title from tab view
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:52:18 -06:00
Trey t
1ffd6122c9 fix: pace capsule animation glitch in trip options view
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>
2026-01-12 18:48:16 -06:00
Trey t
cf939c4ba3 fix: disable animation on Reset button in game picker
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>
2026-01-12 18:45:08 -06:00
Trey t
d255e28dcc fix: remove weird animation on game selection
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>
2026-01-12 18:41:40 -06:00
Trey t
7946161945 feat: show location info in schedule view
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>
2026-01-12 18:38:17 -06:00
Trey t
b14f72a0fb perf: move trip generation off main actor
Fixes launch freeze by running TripPlanningEngine in background task.
UI now responsive immediately while trips generate.

- Move heavy computation to Task.detached with .userInitiated priority
- Change all helper methods to nonisolated static functions
- Create local TripPlanningEngine in background task instead of instance property

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:27:49 -06:00
Trey t
aa0bc4def8 docs: add bug fixes design for 9 TODO issues
Covers performance (launch freeze, list lag), UI polish (animations,
missing location info, timezone display), and scraper alias fix.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:06:27 -06:00
Trey t
e70b9faab8 fix: show all team games (home and away) when browsing by team
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>
2026-01-12 11:09:50 -06:00
Trey t
3978429716 feat: complete delta sync implementation - add allGames, update callers
- 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>
2026-01-12 11:04:52 -06:00
Trey t
b3ad386d2b refactor: rename fetch methods to filter, add allGames
- fetchGames → filterGames (clarifies local SwiftData query)
- fetchRichGames → filterRichGames
- Add allGames(for:) method for unfiltered game access

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:51:26 -06:00
Trey t
b89c0d58e2 feat(sync): update CanonicalSyncService to use delta sync
- syncStadiums now passes lastSync to CloudKit fetch
- syncTeams simplified to single CloudKit call (not per-sport loop)
- syncGames removes 6-month date range, uses modificationDate delta

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:49:53 -06:00
Trey t
3bb903ab09 feat(sync): update CloudKit sync methods to use modificationDate delta
- fetchStadiumsForSync now accepts since: Date? parameter
- fetchTeamsForSync changed from per-sport to all teams with delta sync
- fetchGamesForSync uses modificationDate instead of game dateTime

When lastSync is nil, fetches all records (first sync).
When lastSync has value, fetches only modified records (delta sync).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:48:34 -06:00
Trey t
b514d2119c docs: add delta sync implementation plan
16-task TDD implementation plan for:
- CloudKit delta sync using modificationDate
- Remove 90-day game browsing limit
- Rename fetch* to filter* for clarity
- Add allGames/allRichGames methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:44:42 -06:00
Trey t
ffe5c0b6f7 docs: add delta sync and game browsing design
Addresses issue where Houston Astros only shows 7 games in "By Games" mode.
Documents plan to remove arbitrary date restrictions and implement proper
delta sync using CloudKit modificationDate.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 10:41:17 -06:00
Trey t
1703ca5b0f refactor: change domain model IDs from UUID to String canonical IDs
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>
2026-01-12 09:24:33 -06:00
Trey t
4b2cacaeba docs: add canonical ID refactor design
Design to eliminate UUID layer and use canonical IDs as the single
stadium/team/game identifier throughout the client. Key changes:
- Domain models use String IDs (canonical IDs)
- Remove UUID mapping dictionaries from DataProvider
- Simplify AchievementEngine (delete resolution helper)
- StadiumVisit uses single stadiumId field

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:56:37 -06:00
Trey t
5c13650742 fix: resolve specificStadium achievement ID mismatch
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>
2026-01-11 22:22:29 -06:00
Trey t
dcd5edb229 feat: add sliding window trip generation for By Games mode
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>
2026-01-11 19:19:46 -06:00
Trey t
3f80a16201 fix: preserve LocationSearchSheet coordinates in By Route mode
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>
2026-01-11 18:59:44 -06:00
Trey t
1967eecd52 fix: use LocationSearchSheet for Start/End Location fields
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>
2026-01-11 18:50:19 -06:00
Trey t
81095a8170 fix: resolve 4 UI/planning bugs from issue tracker
- 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>
2026-01-11 18:46:40 -06:00
Trey t
c2f52aaccc docs: add bug fixes design for 12 planning and UI issues
Covers:
- Planning mode bugs (follow team location, must-stop filtering, date range)
- UI improvements (sort options, map locking, today highlight)
- Coast-to-coast filter to show top 2 by stops

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 17:55:08 -06:00
Trey t
b4ac1f7c24 refactor: redesign planning mode picker as 2x2 card grid
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>
2026-01-11 12:48:58 -06:00
Trey t
f7faec01b1 feat: add Follow Team Mode (Scenario D) for road trip planning
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>
2026-01-11 12:42:43 -06:00
Trey t
e7fb3cfbbe docs: add WCAG 2.1 AA accessibility design
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 11:14:40 -06:00
Trey t
5d1e9a3f48 feat: add privacy policy and terms of service
- 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>
2026-01-11 11:00:03 -06:00
Trey t
769342addc docs: add privacy policy and terms of service design
Plain English privacy policy and ToS for freemium iOS app.
Key decisions: 13+ age requirement, no accounts, CloudKit
analytics, Texas jurisdiction.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 10:55:34 -06:00
Trey t
475f444288 refactor: extract reusable SportSelectorGrid component
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>
2026-01-11 10:38:10 -06:00
Trey t
a292b5c20c fix: center Quick Start bottom row with consistent button sizing
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>
2026-01-11 10:29:14 -06:00
Trey t
2d48f1411a feat: implement Dynamic Type with Apple text styles
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>
2026-01-11 10:23:16 -06:00
Trey t
8affa3ce0d docs: add Dynamic Type implementation design
Replace all custom font sizes with Apple's built-in text styles
for accessibility compliance. Remove Theme.FontSize enum entirely.
Export files keep fixed sizes for consistent PDF output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 10:04:13 -06:00
Trey t
3aef39adba docs: add Follow Team mode design
New 4th planning mode for fans to build trips around their team's
schedule (home + away games). Includes region/date filtering,
flexible start/end location, and repeat city handling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 09:56:13 -06:00
Trey t
5fba9e6052 docs: add localization design
String Catalogs + AI translation pipeline for 5 languages:
Spanish, French, German, Japanese, Chinese (Simplified)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:55:42 -06:00
Trey t
c9e5bd9909 chore: remove college basketball (CBB) from iOS app
Remove CBB (~5,000+ games per season) to reduce complexity.

Changes:
- Remove .cbb enum case from Sport
- Remove CBB theme color (cbbMint)
- Update documentation to reflect 7 supported leagues

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:44:35 -06:00
Trey t
171221af0e docs: add project README
- Project overview and features
- Supported leagues (8 sports)
- Installation and setup instructions
- Project structure and architecture
- Development commands
- Test suite summary

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:22:03 -06:00
Trey t
5ad20f797e chore: update project docs and clean up obsolete files
- Add parser output directories to .gitignore
- Update PROJECT_STATE.md with completed tasks and new checkpoint
- Clean up TO-DOS.md (remove completed items)
- Delete obsolete sample screenshots

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:20:15 -06:00
Trey t
55c6d6e5e8 feat(planning): add trip filtering and fix departure date logic
- Add Trip.status property for status tracking
- Add RouteFilters trip list methods (filterBySport, filterByDateRange, filterByStatus, applyFilters)
- Add TravelEstimator max driving hours validation
- Fix ScenarioA/B departureDate to use last game day (not day after)
- Update GameDAGRouter comments for buffer logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:18:52 -06:00
Trey t
1bd248c255 test(planning): complete test suite with Phase 11 edge cases
Implement comprehensive test infrastructure and all 124 tests across 11 phases:

- Phase 0: Test infrastructure (fixtures, mocks, helpers)
- Phases 1-10: Core planning engine tests (previously implemented)
- Phase 11: Edge case omnibus (11 new tests)
  - Data edge cases: nil stadiums, malformed dates, invalid coordinates
  - Boundary conditions: driving limits, radius boundaries
  - Time zone cases: cross-timezone games, DST transitions

Reorganize test structure under Planning/ directory with proper organization.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 01:14:40 -06:00
Trey t
eeaf900e5a feat(scripts): rewrite parser as modular Python CLI
Replace monolithic scraping scripts with sportstime_parser package:

- Multi-source scrapers with automatic fallback for 7 sports
- Canonical ID generation for games, teams, and stadiums
- Fuzzy matching with configurable thresholds for name resolution
- CloudKit Web Services uploader with JWT auth, diff-based updates
- Resumable uploads with checkpoint state persistence
- Validation reports with manual review items and suggested matches
- Comprehensive test suite (249 tests)

CLI: sportstime-parser scrape|validate|upload|status|retry|clear

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 21:06:12 -06:00
Trey t
284a10d9e1 feat(ui): add interactive MapKit region picker
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>
2026-01-10 18:18:10 -06:00
Trey t
1a835d369a remove gsd 2026-01-10 17:54:31 -06:00
Trey t
12a219d826 fix(trip): remove buffer days from trip planner
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>
2026-01-10 17:47:34 -06:00