Files
SportstimeAPI/.planning/phases/03-visual-flattening/03-01-PLAN.md
Trey t e8ae80e02b docs(03): create phase 3 visual flattening plans
Phase 03: Visual Flattening
- 2 plans in 2 waves
- Plan 01: Create ItineraryFlattener utility, refactor reloadData()
- Plan 02: Add determinism tests for flattening behavior
- Ready for execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 15:49:49 -06:00

6.2 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
03-visual-flattening 01 execute 1
SportsTime/Core/Models/Domain/ItineraryFlattener.swift
SportsTime/Features/Trip/Views/ItineraryTableViewController.swift
true
truths artifacts key_links
Items with lower sortOrder appear before items with higher sortOrder within each day
Items with sortOrder < 0 appear before games
Items with sortOrder >= 0 appear after or between games
Day headers always appear first in their day's section
path provides exports
SportsTime/Core/Models/Domain/ItineraryFlattener.swift Pure flatten function
flatten(days:itineraryItems:gamesByDay:)
path provides contains
SportsTime/Features/Trip/Views/ItineraryTableViewController.swift Refactored reloadData() using ItineraryFlattener ItineraryFlattener.flatten
from to via pattern
ItineraryTableViewController.reloadData() ItineraryFlattener.flatten() function call ItineraryFlattener.flatten
Create a pure ItineraryFlattener utility that transforms hierarchical day data into display rows sorted by sortOrder.

Purpose: Replace the bucket-based flattening (beforeGames/afterGames arrays) with pure sortOrder sorting, ensuring deterministic display order.

Output: New ItineraryFlattener.swift file and refactored reloadData() method using it.

<execution_context> @/.claude/get-shit-done/workflows/execute-plan.md @/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-visual-flattening/03-RESEARCH.md @.planning/phases/03-visual-flattening/03-CONTEXT.md @SportsTime/Core/Models/Domain/SortOrderProvider.swift @SportsTime/Features/Trip/Views/ItineraryTableViewController.swift @SportsTime/Features/Trip/Views/ItineraryTableViewWrapper.swift Task 1: Create ItineraryFlattener utility SportsTime/Core/Models/Domain/ItineraryFlattener.swift Create a new `ItineraryFlattener.swift` file with a pure flatten function.

The function signature:

enum ItineraryFlattener {
    static func flatten(
        days: [ItineraryDayData],
        itineraryItems: [ItineraryItem],
        gamesByDay: [Int: [RichGame]]
    ) -> [ItineraryRowItem]
}

Implementation logic:

  1. For each day in order: a. Add .dayHeader row (always first for each day) b. Collect all positionable items with their sortOrder:
    • Games: use SortOrderProvider.initialSortOrder(forGameTime:) for first game's time
    • Custom items: use item.sortOrder directly
    • Travel items: look up sortOrder from itineraryItems array by matching city names c. Sort collected items by sortOrder (ascending) d. Append sorted items to result

Key decisions (from CONTEXT.md):

  • sortOrder < 0 appears BEFORE games (games have sortOrder 100-1540)
  • sortOrder >= 0 appears AFTER/BETWEEN games
  • Day header is NOT a positioned item - always first

Do NOT:

  • Split into beforeGames/afterGames buckets
  • Use hardcoded order assumptions
  • Special-case travel with travelBefore property Build succeeds: xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build 2>&1 | tail -5 ItineraryFlattener.swift exists with flatten() function that:
  • Sorts all items within a day by sortOrder
  • Day headers appear first in each day
  • Function is pure (no side effects, no instance state)
Task 2: Refactor reloadData() to use ItineraryFlattener SportsTime/Features/Trip/Views/ItineraryTableViewController.swift Refactor the `reloadData()` method (currently lines 484-545) to use the new `ItineraryFlattener.flatten()` function.

Current flow to replace:

// OLD: Bucket-based approach
var beforeGames: [ItineraryRowItem] = []
var afterGames: [ItineraryRowItem] = []
for row in day.items {
    if sortOrder < 0 { beforeGames.append(row) }
    else { afterGames.append(row) }
}
flatItems.append(contentsOf: beforeGames)
flatItems.append(.games(...))
flatItems.append(contentsOf: afterGames)

New flow:

// NEW: Pure sortOrder-based approach
let gamesByDay = Dictionary(grouping: days.flatMap { ($0.games, $0.dayNumber) }) { ... }
flatItems = ItineraryFlattener.flatten(
    days: days,
    itineraryItems: allItineraryItems,
    gamesByDay: gamesByDay
)

The refactored method should:

  1. Build gamesByDay dictionary: [Int: [RichGame]]
  2. Call ItineraryFlattener.flatten() to get flatItems
  3. Reload table view

Preserve:

  • self.travelValidRanges = travelValidRanges
  • self.allItineraryItems = itineraryItems
  • self.tripDayCount = days.count
  • self.constraints = ItineraryConstraints(...) rebuild
  • tableView.reloadData() call at end

Remove:

  • The manual flattening loop with beforeGames/afterGames buckets
  • Any remaining travelBefore references in the flatten logic Build succeeds: xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build 2>&1 | tail -5 reloadData() method:
  • Uses ItineraryFlattener.flatten() instead of manual loop
  • No longer contains beforeGames/afterGames bucket logic
  • Still updates travelValidRanges, allItineraryItems, tripDayCount, constraints
1. Build succeeds with no errors 2. `ItineraryFlattener.swift` exists in `SportsTime/Core/Models/Domain/` 3. `reloadData()` in `ItineraryTableViewController.swift` calls `ItineraryFlattener.flatten()` 4. No `beforeGames` or `afterGames` variables remain in `reloadData()`

<success_criteria>

  • FLAT-01: Items sorted by sortOrder within each day (implemented in ItineraryFlattener)
  • FLAT-03: sortOrder < 0 before games, >= 0 after (handled by natural sort + game sortOrder range 100-1540) </success_criteria>
After completion, create `.planning/phases/03-visual-flattening/03-01-SUMMARY.md`