fix(pdf): include all trip days in PDF export

Fixed PDF export missing the last day when games occur on departure date.

Root cause: Trip.itineraryDays() calculated lastActivityDate as departure - 1,
assuming departure is always after the last activity. When games happen ON the
departure date, that day was skipped.

Fix: Check if the last stop has games. If so, include the departure date in
the itinerary loop.

Also includes:
- buildCompleteItineraryItems() to merge games, travel, and custom items
- Magazine-style PDF layout with sport-specific accent colors
- Proper iteration over all trip days in PDF generator

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-19 11:47:25 -06:00
parent 239d22a872
commit 0e7fcb65fc
3 changed files with 540 additions and 822 deletions

View File

@@ -6,7 +6,7 @@
//
// - Expected Behavior:
// - itineraryDays() returns one ItineraryDay per calendar day from first arrival to last activity
// - Last activity day is departure - 1 (departure is when you leave)
// - Last activity day includes departure day if there are games on that day
// - tripDuration is max(1, days between first arrival and last departure + 1)
// - cities returns deduplicated city list preserving visit order
// - displayName uses " " separator between cities
@@ -113,16 +113,19 @@ struct Trip: Identifiable, Codable, Hashable {
var days: [ItineraryDay] = []
let calendar = Calendar.current
guard let firstDate = stops.first?.arrivalDate else { return days }
guard let firstDate = stops.first?.arrivalDate,
let lastStop = stops.last else { return days }
// Find the last day with actual activity (last game date or last arrival)
// Departure date is the day AFTER the last game, so we use day before departure
// Find the last day with actual activity
// If the last stop has games, include the departure day (games can happen on departure day)
// Otherwise, use departure - 1 (departure is a pure travel day)
let lastActivityDate: Date
if let lastDeparture = stops.last?.departureDate {
// Last activity is day before departure (departure is when you leave)
lastActivityDate = calendar.date(byAdding: .day, value: -1, to: lastDeparture) ?? lastDeparture
if !lastStop.games.isEmpty {
// Last stop has games - include departure day since games may occur on it
lastActivityDate = lastStop.departureDate
} else {
lastActivityDate = stops.last?.arrivalDate ?? firstDate
// No games at last stop - departure is just when you leave
lastActivityDate = calendar.date(byAdding: .day, value: -1, to: lastStop.departureDate) ?? lastStop.departureDate
}
var currentDate = calendar.startOfDay(for: firstDate)