Commit Graph

260 Commits

Author SHA1 Message Date
Trey t
0e7fcb65fc fix(pdf): include all trip days in PDF export
Fixed PDF export missing the last day when games occur on departure date.

Root cause: Trip.itineraryDays() calculated lastActivityDate as departure - 1,
assuming departure is always after the last activity. When games happen ON the
departure date, that day was skipped.

Fix: Check if the last stop has games. If so, include the departure date in
the itinerary loop.

Also includes:
- buildCompleteItineraryItems() to merge games, travel, and custom items
- Magazine-style PDF layout with sport-specific accent colors
- Proper iteration over all trip days in PDF generator

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:47:25 -06:00
Trey t
239d22a872 feat(trip): add Open in Apple Maps button to trip detail map
- Add floating action button (bottom-right) on trip map
- Create AppleMapsLauncher service to handle route opening
- Collect all routable stops (trip stops + custom items with coords)
- Handle >16 waypoints with alert offering to open in parts
- Silently skip stops without coordinates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:59:53 -06:00
Trey t
1b0abe2cc1 feat(trip): redesign add custom item UI with polished visuals
Replace confusing dual-mode AddItemSheet with streamlined QuickAddItemSheet:
- Single flow with optional location (vs Search/Custom toggle)
- Card-based layout with shadows, borders, and theme colors
- Enhanced CategoryPicker with emoji circles and press animations
- Separate PlaceSearchSheet for focused location search
- Improved header button with capsule style and accessibility labels

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:49:13 -06:00
Trey t
e72da7c5a7 fix(itinerary): add city to game items for proper constraint validation
Travel constraint validation was not working because ItineraryConstraints
had no game items to validate against - games came from RichGame objects
but were never converted to ItineraryItem for constraint checking.

Changes:
- Add city parameter to ItemKind.game enum case
- Create game ItineraryItems from RichGame data in buildItineraryData()
- Update isValidTravelPosition to compare against actual game sortOrders
- Fix tests to use appropriate game sortOrder conventions

Now travel is properly constrained to appear before arrival city games
and after departure city games.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 22:46:40 -06:00
Trey t
72447c61fe refactor(itinerary): extract reordering logic into pure functions
Extract all itinerary reordering logic from ItineraryTableViewController into
ItineraryReorderingLogic.swift for testability. Key changes:

- Add flattenDays, dayNumber, travelRow, simulateMove pure functions
- Add calculateSortOrder with proper region classification (before/after games)
- Add computeValidDestinationRowsProposed with simulation+validation pattern
- Add coordinate space conversion helpers (proposedToOriginal, originalToProposed)
- Fix DragZones coordinate space mismatch (was mixing proposed/original indices)
- Add comprehensive documentation of coordinate space conventions

Test coverage includes:
- Row flattening order and semantic travel model
- Sort order calculation for before/after games regions
- Travel constraints validation
- DragZones coordinate space correctness
- Coordinate conversion helpers
- Edge cases (empty days, multi-day trips)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 20:04:52 -06:00
Trey t
143b364553 wip 2026-01-18 12:32:58 -06:00
Trey t
cd1666e7d1 not working, claude is ass 2026-01-17 23:23:22 -06:00
Trey t
64e54b3b11 fix: improve debounce task cancellation handling
- Add cancelPendingSync() method for explicit cleanup
- Use [weak self] capture to prevent potential retain issues
- Check Task.isCancelled before and after sleep
- Catch CancellationError from Task.sleep for immediate cancellation response
- Extract debounceInterval as constant

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 22:19:55 -06:00
Trey t
aa18ed13d7 test: remove legacy CustomItineraryItemTests
Test file for removed CustomItineraryItem model.
All 869 tests pass after itinerary refactor.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 22:14:46 -06:00
Trey t
e79f29d9c7 feat(export): respect custom itinerary order in PDF export
Update PDFGenerator to accept optional ItineraryItem array and render
items in user-specified sortOrder within each day. Adds support for:
- Custom items with icon, title, time, and address
- Travel segments from ItineraryItem (not just TravelSegment)
- Fallback to derived order when no itinerary items provided

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 22:07:06 -06:00
Trey t
9c40721af0 feat(itinerary): add constraint-aware drag and drop with visual feedback
- Add constraint validation during drag using ItineraryConstraints
- Calculate invalid zones and barrier games when drag starts
- Apply visual dimming (alpha 0.3) to invalid drop zones during drag
- Highlight barrier games with gold border when dragging travel segments
- Block invalid drops using ItineraryConstraints.isValidPosition validation
- Add haptic feedback for drag interactions:
  - Medium impact on pickup
  - Light impact when entering valid zone
  - Warning notification when entering invalid zone
  - Soft impact on drop

The drag state is tracked via draggingItem, invalidRowIndices, and
barrierGameIds properties. Visual feedback is applied and removed
via applyDragVisualFeedback/removeDragVisualFeedback methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:59:57 -06:00
Trey t
cd00384010 refactor(itinerary): replace CustomItineraryItem with ItineraryItem across codebase
- Update CKModels.swift to remove deleted type references
- Migrate LocalCustomItem to LocalItineraryItem in SavedTrip.swift
- Update AppDelegate to handle subscription removal
- Refactor AddItemSheet to create ItineraryItem with CustomInfo
- Update ItineraryTableViewController and Wrapper for new model
- Refactor TripDetailView state, methods and callbacks
- Fix TripMapView to display custom items with new model structure

This completes the migration from the legacy CustomItineraryItem/TravelDayOverride
model to the unified ItineraryItem model with ItemKind enum.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:51:32 -06:00
Trey t
b008af1c71 refactor(itinerary): consolidate duplicate optional unwrapping in CustomItemRow 2026-01-17 21:34:18 -06:00
Trey t
3a4b9f23e8 fix(itinerary): add empty state and single-line date format to DayHeaderRow
- Add isEmpty parameter to show "No items yet, tap + to add" text
- Change date format to single line: "Day 1 - Friday, January 17"
- Update preview to test both empty and non-empty states

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:32:18 -06:00
Trey t
7700104597 feat(itinerary): add row views for itinerary items
Add specialized row components for the new unified itinerary system:
- DayHeaderRow: Day number, date display, and add item button
- GameItemRow: Prominent card with sport color bar for games
- TravelItemRow: Gold-styled travel segments with drag handle
- CustomItemRow: Minimal custom items with icon, title, and optional time

All views follow existing Theme patterns and use SportColorBar for consistency.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:28:11 -06:00
Trey t
af2a5cd204 chore: remove legacy itinerary models and services
Removed:
- CustomItineraryItem
- TravelDayOverride
- CustomItemService
- CustomItemSubscriptionService
- TravelOverrideService
- CustomItemRow

These are replaced by unified ItineraryItem model and service.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:24:47 -06:00
Trey t
cae24efa90 feat: add ItineraryItemService with CloudKit sync
Debounced updates (1.5s), local-first with silent retry.
Supports game, travel, and custom item kinds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:23:01 -06:00
Trey t
12c2de8a1b feat: add ItineraryConstraints with full test coverage
Validates travel positions based on game locations:
- Travel must be after ALL departure city games
- Travel must be before ALL arrival city games
- Custom items have no constraints
- Games are fixed (cannot be moved)

12 tests covering all constraint scenarios including edge cases.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 21:14:02 -06:00
Trey t
d14976812a feat: add unified ItineraryItem model
Replaces CustomItineraryItem and TravelDayOverride with single model.
Supports game, travel, and custom item kinds.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 20:57:56 -06:00
Trey t
49aaba0594 chore: add .worktrees to gitignore
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 20:31:33 -06:00
Trey t
05a3efd9c8 docs: comprehensive itinerary reorder refactor design
Complete design for TripDetailView refactor with constrained drag-and-drop:
- Games are fixed anchors (immovable, ordered by time)
- Travel is movable with hard constraints (after departure games, before arrival games)
- Custom items are freely movable anywhere

Key decisions from 100-question brainstorming session:
- Unified ItineraryItem model (replaces CustomItineraryItem + TravelDayOverride)
- Separate testable ItineraryConstraints type
- Full structure stored (not derived)
- Debounced sync, local-first, last-write-wins
- Invalid zones dim during drag, barrier games highlight
- Gap opens at drop position, haptic feedback throughout

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 20:29:28 -06:00
Trey t
c82029abe7 docs: add itinerary reorder refactor design
Unified data model approach:
- Single ItineraryItem type (travel becomes a category)
- Constraint validation layer for travel rules
- Red zone visual feedback for invalid drops
- Simplified flattening logic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 17:20:34 -06:00
Trey t
828059a12a docs: add flexible itinerary ordering implementation plan
13-task plan covering:
- TravelDayOverride model update with sortOrder
- CloudKit schema update
- Flattening logic to sort games/travel/custom together
- Drag constraint updates for flexible custom items
- Regression tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 11:07:58 -06:00
Trey t
6e9b9f728b docs: add flexible itinerary ordering design
Design for fully customizable item ordering in trip itineraries:
- Custom items can go anywhere (before/after games, any day)
- Travel constrained to valid day range but freely positioned within days
- Games get sortOrder for positioning but remain immovable
- TravelPosition stored in SwiftData, synced to CloudKit

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 11:01:16 -06:00
Trey t
c658d5f9f4 refactor(itinerary): embed Add button in day header row
Merge Add button into DaySectionHeaderView to prevent items from being
dragged between the day header and Add button. The Add button now uses
a SwiftUI Button with its own tap handler instead of row selection.

Changes:
- Remove .addButton case from ItineraryRowItem enum
- Update DaySectionHeaderView to include Add button on the right
- Pass onAddTapped callback through configureDayHeaderCell
- Remove AddButtonRowView (no longer needed)
- Update documentation to reflect new row structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 10:13:03 -06:00
Trey t
f84addb39d feat(itinerary): reorder Add button after day header + comprehensive docs
- Move Add button to appear immediately after Day header (before games)
- Split games out of dayHeader into separate row for correct ordering
- Add 600+ lines of inline documentation to ItineraryTableViewController
- Document architecture decisions, data flow, constraints, and algorithms
- Add function-level comments explaining drag/drop, sortOrder calculation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 10:07:37 -06:00
Trey t
2a8bfeeff8 refactor(itinerary): replace anchor-based positioning with day/sortOrder
Replace complex anchor system (anchorType, anchorId, anchorDay) with
simple (day: Int, sortOrder: Double) positioning for custom items.

Changes:
- CustomItineraryItem: Remove anchor fields, add day and sortOrder
- CKModels: Add migration fallback from old CloudKit fields
- ItineraryTableViewController: Add calculateSortOrder() for midpoint insertion
- TripDetailView: Simplify callbacks, itinerarySections, and routeWaypoints
- AddItemSheet: Take simple day parameter instead of anchor
- SavedTrip: Update LocalCustomItem SwiftData model

Benefits:
- Items freely movable via drag-and-drop
- Route waypoints follow exact visual order
- Simpler mental model: position = (day, sortOrder)
- Midpoint insertion allows unlimited reordering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:47:11 -06:00
Trey t
59ba2c6965 docs: add detailed itinerary refactor implementation plan
11 bite-sized TDD tasks to replace anchor-based positioning with
simple (day, sortOrder) model. Includes migration path for CloudKit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:12:16 -06:00
Trey t
bd1e24181f docs: add itinerary refactor design
Replace anchor-based positioning with simple sort-order system:
- Custom items use (day, sortOrder: Double) instead of anchors
- Travel segments have hard guardrails based on city game schedules
- Route waypoints follow exact visual display order

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 09:08:42 -06:00
Trey t
8df33a5614 WIP: map route updates and custom item drag/drop fixes (broken)
- Fixed map header not updating in ItineraryTableViewWrapper using Coordinator pattern
- Added routeVersion UUID to force Map re-render when routes change
- Fixed determineAnchor to scan backwards for correct anchor context
- Added location support to CustomItineraryItem (lat/lng/address)
- Added MapKit place search to AddItemSheet
- Added extensive debug logging for route waypoint calculation

Known issues:
- Custom items still not routing correctly after drag/drop
- Anchor type determination may still have bugs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 00:00:57 -06:00
Trey t
43501b6ac1 feat(itinerary): add UITableView-based itinerary with unified scrolling
- Replace SwiftUI drag-drop with native UITableViewController for fluid reordering
- Add ItineraryTableViewController with native cell reordering and validation
- Add ItineraryTableViewWrapper for SwiftUI integration with header support
- Fix infinite layout loop by tracking header adjustment state
- Map and stats now scroll as table header with itinerary content
- Travel segments constrained to valid day ranges during drag
- One Add button per day (after game > after travel > rest day)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 22:35:27 -06:00
Trey t
bf9619a207 feat(itinerary): add draggable travel day positioning with CloudKit persistence
- Add TravelDayOverride model for storing user-customized travel day positions
- Add TravelOverrideService for CloudKit CRUD operations on travel overrides
- Add CKTravelDayOverride CloudKit model wrapper
- Refactor itinerarySections to validate travel day bounds (must be after last game in departure city)
- Travel segments can now be dragged to different days within valid range
- Persist travel day overrides to CloudKit for cross-device sync

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 19:00:52 -06:00
Trey t
29942eee57 fix(polls): prevent jarring reload when switching to My Trips tab
- Only load polls once on initial view appearance, not every tab switch
- Only show spinner when there's no existing data (first load)
- Subsequent refreshes update content in place without showing spinner

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:42:25 -06:00
Trey t
0130141f8a Merge branch 'custom_ish' 2026-01-16 15:38:28 -06:00
Trey t
aca394cefa fix(itinerary): improve drag-drop reordering with stable anchors and visual feedback
- Add visual drop target indicator showing where items will land
- Use stable travel anchor IDs (city names) instead of UUIDs that regenerate
- Fix findDayForTravelSegment to look forward to arrival day, not backward
- Add moveItemToBeginning() for inserting at position 0 when dropping on sections
- Sort custom items by sortOrder in all filters
- Sync shifted items to CloudKit after reorder
- Add opaque backgrounds to CustomItemRow and TravelSection to hide timeline

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 15:35:09 -06:00
Trey t
8162b4a029 refactor(tests): TDD rewrite of all unit tests with spec documentation
Complete rewrite of unit test suite using TDD methodology:

Planning Engine Tests:
- GameDAGRouterTests: Beam search, anchor games, transitions
- ItineraryBuilderTests: Stop connection, validators, EV enrichment
- RouteFiltersTests: Region, time window, scoring filters
- ScenarioA/B/C/D PlannerTests: All planning scenarios
- TravelEstimatorTests: Distance, duration, travel days
- TripPlanningEngineTests: Orchestration, caching, preferences

Domain Model Tests:
- AchievementDefinitionsTests, AnySportTests, DivisionTests
- GameTests, ProgressTests, RegionTests, StadiumTests
- TeamTests, TravelSegmentTests, TripTests, TripPollTests
- TripPreferencesTests, TripStopTests, SportTests

Service Tests:
- FreeScoreAPITests, RouteDescriptionGeneratorTests
- SuggestedTripsGeneratorTests

Export Tests:
- ShareableContentTests (card types, themes, dimensions)

Bug fixes discovered through TDD:
- ShareCardDimensions: mapSnapshotSize exceeded available width (960x480)
- ScenarioBPlanner: Added anchor game validation filter

All tests include:
- Specification tests (expected behavior)
- Invariant tests (properties that must always hold)
- Edge case tests (boundary conditions)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:07:41 -06:00
Trey t
035dd6f5de docs: add TDD test rewrite design
Specification-first + property-based TDD methodology to surface
logic bugs by testing expected behavior, not current implementation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 11:11:46 -06:00
Trey t
966a580def docs: add unit test rewrite design
Comprehensive plan to delete broken tests and create new Swift Testing
coverage for Planning Engine, Domain Models, and Services with parallel
execution across multiple simulators.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 10:22:25 -06:00
Trey t
8bba5a1592 docs(plans): add brutalist app-wide implementation plan
Detailed step-by-step plan for extending brutalist style to:
- TripDetailView
- SavedTripsListView
- ScheduleListView
- SettingsView

Includes StyleProvider protocol, adaptive routers, and complete
code snippets for each task.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 09:13:10 -06:00
Trey t
dc43bf0d53 docs: add brutalist app-wide design spec
Design for extending Brutalist style from home-screen-only to app-wide:
- StyleProvider protocol for shape/typography language
- Adaptive view routers for each major screen
- Brutalist variants for TripDetail, MyTrips, Schedule, Settings
- Colors still controlled by user's selected Theme

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 09:04:32 -06:00
Trey t
ae88dd3807 i dunno 2026-01-16 00:32:14 -06:00
Trey t
495ef88303 feat(itinerary): add custom itinerary items with drag-to-reorder
- Add CustomItineraryItem domain model with sortOrder for ordering
- Add CKCustomItineraryItem CloudKit wrapper for persistence
- Create CustomItemService for CRUD operations
- Create CustomItemSubscriptionService for real-time sync
- Add AppDelegate for push notification handling
- Add AddItemSheet for creating/editing items
- Add CustomItemRow with drag handle
- Update TripDetailView with continuous vertical timeline
- Enable drag-to-reorder using .draggable/.dropDestination
- Add inline "Add" buttons after games and travel segments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 00:31:44 -06:00
Trey t
b534ca771b docs: add custom itinerary items design
Design for allowing users to add personal items (restaurants, hotels,
activities, notes) to saved trip itineraries with drag-to-reorder and
CloudKit sync.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 23:07:18 -06:00
Trey t
00a5e4ef0e fix(planning): enforce daily driving limit for same-day games and group itinerary by city
Two fixes for route planning and display:

1. GameDAGRouter: Same-day game transitions now respect maxDailyDrivingHours
   constraint. Previously, a 12:05 AM game in Arlington could connect to an
   11:40 PM game in Milwaukee (19+ hour drive) because the code only checked
   available time, not the 8-hour daily limit.

2. TripDetailView: Itinerary sections now group by (day, city) not just day.
   Games in different cities on the same calendar day are shown as separate
   sections with travel segments between them.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 09:42:08 -06:00
Trey t
57eab22746 fix(polls): fetch existing CloudKit record before updating vote
When updating a vote, CloudKit requires the server's changeTag to modify
existing records. Creating a new CKRecord caused "record to insert already
exists" errors. Now fetches the existing record first before saving.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 23:59:40 -06:00
Trey t
aa34c6585a feat(wizard): add mode-specific trip wizard inputs
- Add GamePickerStep with sheet-based Sport → Team → Game selection
- Add TeamPickerStep with sheet-based Sport → Team selection
- Add LocationsStep for start/end location selection with round trip toggle
- Update TripWizardViewModel with mode-specific fields and validation
- Update TripWizardView with conditional step rendering per mode
- Update ReviewStep with mode-aware validation display
- Fix gameFirst mode to derive date range from selected games

Each planning mode now shows only relevant steps:
- By Dates: Dates → Sports → Regions → Route → Repeat → Must Stops
- By Games: Game Picker → Route → Repeat → Must Stops
- By Route: Locations → Dates → Sports → Route → Repeat → Must Stops
- Follow Team: Team Picker → Dates → Route → Repeat → Must Stops

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 22:21:57 -06:00
Trey t
1301442604 fix(ui): improve SuggestedTripCard padding and height
Increase card height from 160 to 175 and add extra bottom padding
to prevent date text from being cut off.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 13:28:20 -06:00
Trey t
d34be05d61 feat(design): add Classic Animated home screen style
New design style combines the Classic layout with subtle animated
backgrounds featuring floating sports icons and route lines.
Animations are slow and unobtrusive to avoid distracting from content.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 13:23:13 -06:00
Trey t
f7f1bbd87a feat(settings): add dark/light/system appearance mode toggle
- Add AppearanceMode enum with system, light, and dark options
- Add AppearanceManager singleton to persist user preference
- Add appearance section in SettingsView with icon and description
- Apply preferredColorScheme at app root for immediate effect
- Include appearance mode in reset to defaults

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 13:08:38 -06:00
Trey t
3d4952e5ff feat(ui): add sport backgrounds to share cards, achievement filtering, and wizard validation
- Add ShareCardSportBackground with floating sport icons for share cards
- Share cards now show sport-specific backgrounds (single or multiple sports)
- Achievement collection share respects sport filter selection
- Add ability to share individual achievements from detail sheet
- Trip wizard ReviewStep highlights missing required fields in red
- Add FieldValidation model to TripWizardViewModel

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-14 12:02:57 -06:00