refactor(itinerary): replace anchor-based positioning with day/sortOrder

Replace complex anchor system (anchorType, anchorId, anchorDay) with
simple (day: Int, sortOrder: Double) positioning for custom items.

Changes:
- CustomItineraryItem: Remove anchor fields, add day and sortOrder
- CKModels: Add migration fallback from old CloudKit fields
- ItineraryTableViewController: Add calculateSortOrder() for midpoint insertion
- TripDetailView: Simplify callbacks, itinerarySections, and routeWaypoints
- AddItemSheet: Take simple day parameter instead of anchor
- SavedTrip: Update LocalCustomItem SwiftData model

Benefits:
- Items freely movable via drag-and-drop
- Route waypoints follow exact visual order
- Simpler mental model: position = (day, sortOrder)
- Midpoint insertion allows unlimited reordering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-17 09:47:11 -06:00
parent 59ba2c6965
commit 2a8bfeeff8
10 changed files with 238 additions and 385 deletions

View File

@@ -109,9 +109,8 @@ final class LocalCustomItem {
var tripId: UUID
var category: String
var title: String
var anchorType: String
var anchorId: String?
var anchorDay: Int
var day: Int
var sortOrder: Double
var createdAt: Date
var modifiedAt: Date
var pendingSync: Bool // True if needs to sync to CloudKit
@@ -121,9 +120,8 @@ final class LocalCustomItem {
tripId: UUID,
category: CustomItineraryItem.ItemCategory,
title: String,
anchorType: CustomItineraryItem.AnchorType = .startOfDay,
anchorId: String? = nil,
anchorDay: Int,
day: Int,
sortOrder: Double = 0.0,
createdAt: Date = Date(),
modifiedAt: Date = Date(),
pendingSync: Bool = false
@@ -132,17 +130,15 @@ final class LocalCustomItem {
self.tripId = tripId
self.category = category.rawValue
self.title = title
self.anchorType = anchorType.rawValue
self.anchorId = anchorId
self.anchorDay = anchorDay
self.day = day
self.sortOrder = sortOrder
self.createdAt = createdAt
self.modifiedAt = modifiedAt
self.pendingSync = pendingSync
}
var toItem: CustomItineraryItem? {
guard let category = CustomItineraryItem.ItemCategory(rawValue: category),
let anchorType = CustomItineraryItem.AnchorType(rawValue: anchorType)
guard let category = CustomItineraryItem.ItemCategory(rawValue: category)
else { return nil }
return CustomItineraryItem(
@@ -150,9 +146,8 @@ final class LocalCustomItem {
tripId: tripId,
category: category,
title: title,
anchorType: anchorType,
anchorId: anchorId,
anchorDay: anchorDay,
day: day,
sortOrder: sortOrder,
createdAt: createdAt,
modifiedAt: modifiedAt
)
@@ -164,9 +159,8 @@ final class LocalCustomItem {
tripId: item.tripId,
category: item.category,
title: item.title,
anchorType: item.anchorType,
anchorId: item.anchorId,
anchorDay: item.anchorDay,
day: item.day,
sortOrder: item.sortOrder,
createdAt: item.createdAt,
modifiedAt: item.modifiedAt,
pendingSync: pendingSync