Files
SportstimeAPI/.planning/PROJECT.md
Trey t 98941a3b4d docs: initialize project
Interactive drag-and-drop itinerary editor with semantic positioning
2026-01-18 13:08:13 -06:00

3.7 KiB

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