diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md new file mode 100644 index 0000000..5f86329 --- /dev/null +++ b/.planning/REQUIREMENTS.md @@ -0,0 +1,120 @@ +# Requirements: Itinerary Editor + +**Defined:** 2026-01-18 +**Core Value:** Drag-and-drop that operates on semantic positions (day + sortOrder), not row indices — so user intent is preserved across data reloads. + +## v1 Requirements + +Requirements for initial release. Each maps to roadmap phases. + +### Data Model + +- [ ] **DATA-01**: All movable items have semantic position `(day: Int, sortOrder: Double)` +- [ ] **DATA-02**: Travel segments are positioned items with their own sortOrder, not structural day properties +- [ ] **DATA-03**: Games are immovable anchors ordered by game time within each day +- [ ] **DATA-04**: Custom items can be placed anywhere within any day (including between games) +- [ ] **DATA-05**: Items always belong to exactly one day (no liminal "between days" state) + +### Constraints + +- [ ] **CONS-01**: Games cannot be moved (fixed by schedule) +- [ ] **CONS-02**: Travel segments are constrained to valid day range (between last from-city game and first to-city game) +- [ ] **CONS-03**: Travel segments must be positioned after from-city games and before to-city games on same day +- [ ] **CONS-04**: Custom items have no constraints (any position within any day) + +### Flattening + +- [ ] **FLAT-01**: Visual flattening sorts by sortOrder within each day (not hard-coded order) +- [ ] **FLAT-02**: Flattening is deterministic and stateless (same semantic state → same row order) +- [ ] **FLAT-03**: sortOrder < 0 convention for "before games", sortOrder >= 0 for "after/between games" + +### Drag Interaction + +- [ ] **DRAG-01**: Lift animation on grab (shadow + slight scale) +- [ ] **DRAG-02**: Insertion line appears between items showing exact drop target +- [ ] **DRAG-03**: Items shuffle out of the way during drag (100ms animation) +- [ ] **DRAG-04**: Magnetic snap on drop (item settles into position) +- [ ] **DRAG-05**: Invalid drops rejected with snap-back animation +- [ ] **DRAG-06**: Haptic feedback on grab (light) and drop (medium) +- [ ] **DRAG-07**: Auto-scroll when dragging to viewport edge +- [ ] **DRAG-08**: Slight tilt during drag (2-3 degrees, Trello-style) + +### Persistence + +- [ ] **PERS-01**: Semantic position survives data reloads from SwiftUI/SwiftData +- [ ] **PERS-02**: No visual-only state; all positions are persisted semantically +- [ ] **PERS-03**: Midpoint insertion for sortOrder (1.0, 2.0 → 1.5) enables unlimited insertions + +## v2 Requirements + +Deferred to future release. Tracked but not in current roadmap. + +### Accessibility + +- **ACC-01**: VoiceOver Move Up/Down actions for keyboard reordering +- **ACC-02**: VoiceOver announcements on drag start/end +- **ACC-03**: Focus management after reorder + +### External Drops + +- **EXT-01**: Accept drops from outside the table (UITableViewDropDelegate) +- **EXT-02**: External items converted to semantic position on drop + +### Polish + +- **POL-01**: Undo toast after drop (5-second timeout) +- **POL-02**: Drag handle visual affordance + +## Out of Scope + +Explicitly excluded. Documented to prevent scope creep. + +| Feature | Reason | +|---------|--------| +| Reordering games | Games are fixed by schedule; core constraint | +| Reordering day headers | Structural, one per day | +| Multi-day travel segments | Complexity; travel belongs to exactly one day | +| Multi-item drag | Overkill for itinerary; single item at a time | +| Custom drag preview images | Unnecessary; default cell preview is fine | +| Drag between screens | Overkill; all editing within single table | +| Physics-based spring animations | Diminishing returns; standard easing is sufficient | +| Zone-based drop highlighting | Using insertion lines for precision | + +## Traceability + +Which phases cover which requirements. Updated during roadmap creation. + +| Requirement | Phase | Status | +|-------------|-------|--------| +| DATA-01 | — | Pending | +| DATA-02 | — | Pending | +| DATA-03 | — | Pending | +| DATA-04 | — | Pending | +| DATA-05 | — | Pending | +| CONS-01 | — | Pending | +| CONS-02 | — | Pending | +| CONS-03 | — | Pending | +| CONS-04 | — | Pending | +| FLAT-01 | — | Pending | +| FLAT-02 | — | Pending | +| FLAT-03 | — | Pending | +| DRAG-01 | — | Pending | +| DRAG-02 | — | Pending | +| DRAG-03 | — | Pending | +| DRAG-04 | — | Pending | +| DRAG-05 | — | Pending | +| DRAG-06 | — | Pending | +| DRAG-07 | — | Pending | +| DRAG-08 | — | Pending | +| PERS-01 | — | Pending | +| PERS-02 | — | Pending | +| PERS-03 | — | Pending | + +**Coverage:** +- v1 requirements: 23 total +- Mapped to phases: 0 +- Unmapped: 23 ⚠️ + +--- +*Requirements defined: 2026-01-18* +*Last updated: 2026-01-18 after initial definition*