# Itinerary Editor ## What This Is An interactive drag-and-drop itinerary editor for the SportsTime iOS app. Users can rearrange travel segments and custom items within their trip itinerary while respecting game schedules and city-based travel constraints. Built as a UITableView bridged into SwiftUI to enable precise drag-and-drop with insertion line feedback. ## Core Value Drag-and-drop that operates on semantic positions (day + sortOrder), not row indices — so user intent is preserved across data reloads. ## Requirements ### Validated - ✓ Trip itineraries display with day headers and games — existing (`TripDetailView`) - ✓ Conflict detection for same-day games in different cities — existing - ✓ SwiftUI + SwiftData architecture — existing ### Active - [ ] Semantic position model using `(day: Int, sortOrder: Double)` for all movable items - [ ] Custom items can be placed anywhere within any day (including between games) - [ ] Travel segments respect city constraints (after from-city games, before to-city games) - [ ] Travel segments respect day range constraints (within valid travel window) - [ ] Invalid drops are rejected with snap-back behavior - [ ] Insertion lines show precise drop targets during drag - [ ] External drops (from outside the table) work with same semantic rules - [ ] Position persists correctly across data reloads - [ ] No visual jumpiness or dead zones during drag operations ### Out of Scope - Reordering games — games are fixed by schedule - Reordering day headers — structural, one per day - Zone-based drop highlighting — using insertion lines instead - Multi-day travel segments — travel belongs to exactly one day ## Context **Existing codebase:** SportsTime iOS app with Clean MVVM architecture. `TripDetailView` already displays itineraries with conflict detection. The new editor replaces the display-only view with an interactive one. **Technical environment:** - iOS 26+, Swift 6 concurrency - SwiftUI drives data, UITableView handles drag-and-drop - SwiftData for persistence - Frequent reloads from data changes; visual-only state is not acceptable **What went wrong in previous attempts:** - Row-based snapping instead of semantic (day, sortOrder) - Treating travel as structural ("travelBefore") instead of positional - Losing sortOrder for travel during flattening - Hard-coded flatten order (header → games → customs) that ignored sortOrder - Drag logic and reload logic fighting each other ## Constraints - **Architecture**: SwiftUI wrapper must drive data; UIKit table handles drag/drop mechanics - **Persistence**: All positions must be semantic (day, sortOrder) — no ephemeral visual state - **Reload tolerance**: Reloads must not undo valid user actions; position must survive reload - **iOS version**: iOS 26+ (per existing app target) ## Key Decisions | Decision | Rationale | Outcome | |----------|-----------|---------| | UITableView over SwiftUI List | SwiftUI drag-and-drop lacks insertion line precision and external drop support | — Pending | | (day, sortOrder) position model | Row indices break on reload; semantic position is reload-stable | — Pending | | Insertion lines (not zones) | User wants precise feedback on exact drop location | — Pending | | Custom items interleave with games | Maximum flexibility for user — can add notes between games | — Pending | | Travel position-constrained within day | After from-city games, before to-city games on same day | — Pending | | Invalid drops rejected (snap back) | Cleaner than auto-clamping; user knows exactly what happened | — Pending | | Items always belong to a day | No liminal "between days" state; visual gap is end of previous day | — Pending | --- *Last updated: 2026-01-18 after initialization*