feat(itinerary): add draggable travel day positioning with CloudKit persistence

- Add TravelDayOverride model for storing user-customized travel day positions
- Add TravelOverrideService for CloudKit CRUD operations on travel overrides
- Add CKTravelDayOverride CloudKit model wrapper
- Refactor itinerarySections to validate travel day bounds (must be after last game in departure city)
- Travel segments can now be dragged to different days within valid range
- Persist travel day overrides to CloudKit for cross-device sync

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-16 19:00:52 -06:00
parent 29942eee57
commit bf9619a207
4 changed files with 670 additions and 156 deletions

View File

@@ -21,6 +21,7 @@ enum CKRecordType {
static let tripPoll = "TripPoll"
static let pollVote = "PollVote"
static let customItineraryItem = "CustomItineraryItem"
static let travelDayOverride = "TravelDayOverride"
}
// MARK: - CKTeam
@@ -694,3 +695,55 @@ struct CKCustomItineraryItem {
)
}
}
// MARK: - CKTravelDayOverride
struct CKTravelDayOverride {
static let overrideIdKey = "overrideId"
static let tripIdKey = "tripId"
static let travelAnchorIdKey = "travelAnchorId"
static let displayDayKey = "displayDay"
static let createdAtKey = "createdAt"
static let modifiedAtKey = "modifiedAt"
let record: CKRecord
init(record: CKRecord) {
self.record = record
}
init(override: TravelDayOverride) {
let record = CKRecord(
recordType: CKRecordType.travelDayOverride,
recordID: CKRecord.ID(recordName: override.id.uuidString)
)
record[CKTravelDayOverride.overrideIdKey] = override.id.uuidString
record[CKTravelDayOverride.tripIdKey] = override.tripId.uuidString
record[CKTravelDayOverride.travelAnchorIdKey] = override.travelAnchorId
record[CKTravelDayOverride.displayDayKey] = override.displayDay
record[CKTravelDayOverride.createdAtKey] = override.createdAt
record[CKTravelDayOverride.modifiedAtKey] = override.modifiedAt
self.record = record
}
func toOverride() -> TravelDayOverride? {
guard let overrideIdString = record[CKTravelDayOverride.overrideIdKey] as? String,
let overrideId = UUID(uuidString: overrideIdString),
let tripIdString = record[CKTravelDayOverride.tripIdKey] as? String,
let tripId = UUID(uuidString: tripIdString),
let travelAnchorId = record[CKTravelDayOverride.travelAnchorIdKey] as? String,
let displayDay = record[CKTravelDayOverride.displayDayKey] as? Int,
let createdAt = record[CKTravelDayOverride.createdAtKey] as? Date,
let modifiedAt = record[CKTravelDayOverride.modifiedAtKey] as? Date
else { return nil }
return TravelDayOverride(
id: overrideId,
tripId: tripId,
travelAnchorId: travelAnchorId,
displayDay: displayDay,
createdAt: createdAt,
modifiedAt: modifiedAt
)
}
}