- Trip Planning Enhancements: progressive reveal single-screen wizard - Progress Tracking Enhancements: multiple visits, games history, zoomable map - Polish Enhancements: grouped sorting, 100+ planning tips Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
136 lines
5.1 KiB
Markdown
136 lines
5.1 KiB
Markdown
# Trip Planning Enhancements Design
|
|
|
|
**Date:** 2026-01-12
|
|
**Status:** Draft
|
|
**Scope:** High-level overview for scoping/prioritization
|
|
|
|
## Goal
|
|
|
|
Transform trip creation from a dense single-form into a guided, progressive-reveal flow that walks users through planning one decision at a time—while keeping everything on a single scrolling screen.
|
|
|
|
## Current State
|
|
|
|
- Single `TripCreationView` (~2500 lines) with all options visible at once
|
|
- Planning mode selector controls which sections appear
|
|
- 4 planning modes: Date Range, Game First, Locations, Follow Team
|
|
- Sports without games in date range are not indicated
|
|
|
|
## Target State
|
|
|
|
- Progressive disclosure: sections reveal as user makes selections
|
|
- Conversational headers guide the user ("Great! When would you like to travel?")
|
|
- Each section is a small, focused view component (~100-150 lines)
|
|
- Sports with no games in selected date range are grayed out
|
|
- Current form remains accessible via Settings toggle for power users
|
|
|
|
## Approach: Progressive Single-Screen Flow
|
|
|
|
### Container Architecture
|
|
|
|
```swift
|
|
struct TripWizardView: View {
|
|
@State var viewModel = TripWizardViewModel()
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(spacing: Theme.Spacing.lg) {
|
|
// Always visible
|
|
PlanningModeStep(selection: $viewModel.planningMode)
|
|
|
|
// Reveals after planning mode selected
|
|
if viewModel.planningMode != nil {
|
|
SportsStep(...)
|
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
}
|
|
|
|
// Reveals after at least one sport selected
|
|
if !viewModel.selectedSports.isEmpty {
|
|
DatesStep(...)
|
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
|
}
|
|
|
|
// Continues for each step...
|
|
}
|
|
.animation(.easeInOut(duration: 0.3), value: viewModel.revealState)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step Sequence
|
|
|
|
| Step | Question | Reveals When |
|
|
|------|----------|--------------|
|
|
| 1 | "How do you want to plan?" | Always visible |
|
|
| 2 | "Which sports interest you?" | After planning mode selected |
|
|
| 3 | "When would you like to travel?" | After sport(s) selected |
|
|
| 4 | "Where do you want to go?" | After dates set |
|
|
| 5 | "What's your route preference?" | After region(s) selected |
|
|
| 6 | "Visit cities more than once?" | After route preference |
|
|
| 7 | "Any must-stop locations?" | After repeat cities choice |
|
|
| 8 | Review & Plan button | After all required steps complete |
|
|
|
|
Note: Step order varies by planning mode (e.g., "Game First" skips dates, shows game picker).
|
|
|
|
### Behavior
|
|
|
|
- Steps slide in from bottom with fade animation
|
|
- Auto-scroll to newly revealed section (300ms after animation starts)
|
|
- User can scroll back up and change earlier answers
|
|
- Changing an answer resets/updates downstream sections as needed
|
|
- Conversational headers: "Nice! Now pick your travel dates"
|
|
|
|
### Sport Availability (Graying)
|
|
|
|
After date selection:
|
|
1. Fetch game counts per sport for selected date range
|
|
2. Use existing `AppDataProvider.shared.filterGames(sports:startDate:endDate:)`
|
|
3. Cache results; re-fetch if dates change
|
|
4. Display: grayed card + disabled + subtitle "No games in this period"
|
|
|
|
## File Structure
|
|
|
|
```
|
|
Features/Trip/Views/Wizard/
|
|
├── TripWizardView.swift # Container with progressive reveal logic
|
|
├── TripWizardViewModel.swift # Shared state across all steps
|
|
├── Steps/
|
|
│ ├── PlanningModeStep.swift # "How do you want to plan?"
|
|
│ ├── SportsStep.swift # Sport grid with availability graying
|
|
│ ├── DatesStep.swift # Date range picker
|
|
│ ├── RegionsStep.swift # Region map
|
|
│ ├── RoutePreferenceStep.swift # Efficient/Scenic/Flexible
|
|
│ ├── RepeatCitiesStep.swift # Yes/No toggle
|
|
│ ├── MustStopsStep.swift # Optional locations
|
|
│ └── ReviewStep.swift # Summary + Plan button
|
|
```
|
|
|
|
## Key Decisions
|
|
|
|
| Decision | Choice | Rationale |
|
|
|----------|--------|-----------|
|
|
| Keep old `TripCreationView`? | Yes | Power users, accessible via Settings toggle |
|
|
| Reset downstream on change? | Yes | Changing sports resets dates, changing dates refetches sport availability |
|
|
| Sport graying | Grayed + disabled + subtitle | Clear feedback why sport unavailable |
|
|
| Auto-scroll | 300ms delay after reveal | Let animation start before scrolling |
|
|
| Step headers | Conversational tone | Guides user, feels less like a form |
|
|
|
|
## Not Included (YAGNI)
|
|
|
|
- Progress indicator (single screen doesn't need it)
|
|
- Save draft functionality (overkill for single-session flow)
|
|
- Undo/redo beyond scrolling back up
|
|
- Swipe gestures between steps (intentional friction for decisions)
|
|
|
|
## Dependencies
|
|
|
|
- None - uses existing data providers and models
|
|
- `AppDataProvider.shared.filterGames()` for sport availability
|
|
|
|
## Testing Considerations
|
|
|
|
- Test progressive reveal with all 4 planning modes
|
|
- Test sport availability graying with various date ranges
|
|
- Test downstream reset when changing earlier selections
|
|
- Test auto-scroll behavior on different screen sizes
|