docs: initialize project

Interactive drag-and-drop itinerary editor with semantic positioning
This commit is contained in:
Trey t
2026-01-18 13:08:13 -06:00
commit 98941a3b4d

75
.planning/PROJECT.md Normal file
View File

@@ -0,0 +1,75 @@
# 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*