refactor(itinerary): replace CustomItineraryItem with ItineraryItem across codebase
- Update CKModels.swift to remove deleted type references - Migrate LocalCustomItem to LocalItineraryItem in SavedTrip.swift - Update AppDelegate to handle subscription removal - Refactor AddItemSheet to create ItineraryItem with CustomInfo - Update ItineraryTableViewController and Wrapper for new model - Refactor TripDetailView state, methods and callbacks - Fix TripMapView to display custom items with new model structure This completes the migration from the legacy CustomItineraryItem/TravelDayOverride model to the unified ItineraryItem model with ItemKind enum. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,14 +8,43 @@
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
/// Category for custom itinerary items with emoji icons
|
||||
enum ItemCategory: String, CaseIterable {
|
||||
case restaurant
|
||||
case attraction
|
||||
case fuel
|
||||
case hotel
|
||||
case other
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .restaurant: return "\u{1F37D}" // 🍽️
|
||||
case .attraction: return "\u{1F3A2}" // 🎢
|
||||
case .fuel: return "\u{26FD}" // ⛽
|
||||
case .hotel: return "\u{1F3E8}" // 🏨
|
||||
case .other: return "\u{1F4CC}" // 📌
|
||||
}
|
||||
}
|
||||
|
||||
var label: String {
|
||||
switch self {
|
||||
case .restaurant: return "Eat"
|
||||
case .attraction: return "See"
|
||||
case .fuel: return "Fuel"
|
||||
case .hotel: return "Stay"
|
||||
case .other: return "Other"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddItemSheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
|
||||
let tripId: UUID
|
||||
let day: Int
|
||||
let existingItem: CustomItineraryItem?
|
||||
var onSave: (CustomItineraryItem) -> Void
|
||||
let existingItem: ItineraryItem?
|
||||
var onSave: (ItineraryItem) -> Void
|
||||
|
||||
// Entry mode
|
||||
enum EntryMode: String, CaseIterable {
|
||||
@@ -24,7 +53,7 @@ struct AddItemSheet: View {
|
||||
}
|
||||
|
||||
@State private var entryMode: EntryMode = .searchPlaces
|
||||
@State private var selectedCategory: CustomItineraryItem.ItemCategory = .restaurant
|
||||
@State private var selectedCategory: ItemCategory = .restaurant
|
||||
@State private var title: String = ""
|
||||
@State private var isSaving = false
|
||||
|
||||
@@ -80,9 +109,10 @@ struct AddItemSheet: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if let existing = existingItem {
|
||||
selectedCategory = existing.category
|
||||
title = existing.title
|
||||
if let existing = existingItem, let info = existing.customInfo {
|
||||
// Find matching category by icon, default to .other
|
||||
selectedCategory = ItemCategory.allCases.first { $0.icon == info.icon } ?? .other
|
||||
title = info.title
|
||||
// If editing a mappable item, switch to custom mode
|
||||
entryMode = .custom
|
||||
}
|
||||
@@ -231,7 +261,7 @@ struct AddItemSheet: View {
|
||||
@ViewBuilder
|
||||
private var categoryPicker: some View {
|
||||
HStack(spacing: Theme.Spacing.md) {
|
||||
ForEach(CustomItineraryItem.ItemCategory.allCases, id: \.self) { category in
|
||||
ForEach(ItemCategory.allCases, id: \.self) { category in
|
||||
CategoryButton(
|
||||
category: category,
|
||||
isSelected: selectedCategory == category
|
||||
@@ -245,54 +275,68 @@ struct AddItemSheet: View {
|
||||
private func saveItem() {
|
||||
isSaving = true
|
||||
|
||||
let item: CustomItineraryItem
|
||||
let item: ItineraryItem
|
||||
|
||||
if let existing = existingItem {
|
||||
if let existing = existingItem, let existingInfo = existing.customInfo {
|
||||
// Editing existing item - preserve day and sortOrder
|
||||
let trimmedTitle = title.trimmingCharacters(in: .whitespaces)
|
||||
guard !trimmedTitle.isEmpty else { return }
|
||||
|
||||
item = CustomItineraryItem(
|
||||
let customInfo = CustomInfo(
|
||||
title: trimmedTitle,
|
||||
icon: selectedCategory.icon,
|
||||
time: existingInfo.time,
|
||||
latitude: existingInfo.latitude,
|
||||
longitude: existingInfo.longitude,
|
||||
address: existingInfo.address
|
||||
)
|
||||
|
||||
item = ItineraryItem(
|
||||
id: existing.id,
|
||||
tripId: existing.tripId,
|
||||
category: selectedCategory,
|
||||
title: trimmedTitle,
|
||||
day: existing.day,
|
||||
sortOrder: existing.sortOrder,
|
||||
createdAt: existing.createdAt,
|
||||
modifiedAt: Date(),
|
||||
latitude: existing.latitude,
|
||||
longitude: existing.longitude,
|
||||
address: existing.address
|
||||
kind: .custom(customInfo),
|
||||
modifiedAt: Date()
|
||||
)
|
||||
} else if entryMode == .searchPlaces, let place = selectedPlace {
|
||||
// Creating from MapKit search
|
||||
let placeName = place.name ?? "Unknown Place"
|
||||
let coordinate = place.placemark.coordinate
|
||||
|
||||
// New items get sortOrder 0 (will be placed at beginning, caller can adjust)
|
||||
item = CustomItineraryItem(
|
||||
tripId: tripId,
|
||||
category: selectedCategory,
|
||||
let customInfo = CustomInfo(
|
||||
title: placeName,
|
||||
day: day,
|
||||
sortOrder: 0.0,
|
||||
icon: selectedCategory.icon,
|
||||
time: nil,
|
||||
latitude: coordinate.latitude,
|
||||
longitude: coordinate.longitude,
|
||||
address: formatAddress(for: place)
|
||||
)
|
||||
|
||||
// New items get sortOrder 0 (will be placed at beginning, caller can adjust)
|
||||
item = ItineraryItem(
|
||||
tripId: tripId,
|
||||
day: day,
|
||||
sortOrder: 0.0,
|
||||
kind: .custom(customInfo)
|
||||
)
|
||||
} else {
|
||||
// Creating custom item (no location)
|
||||
let trimmedTitle = title.trimmingCharacters(in: .whitespaces)
|
||||
guard !trimmedTitle.isEmpty else { return }
|
||||
|
||||
// New items get sortOrder 0 (will be placed at beginning, caller can adjust)
|
||||
item = CustomItineraryItem(
|
||||
tripId: tripId,
|
||||
category: selectedCategory,
|
||||
let customInfo = CustomInfo(
|
||||
title: trimmedTitle,
|
||||
icon: selectedCategory.icon,
|
||||
time: nil
|
||||
)
|
||||
|
||||
// New items get sortOrder 0 (will be placed at beginning, caller can adjust)
|
||||
item = ItineraryItem(
|
||||
tripId: tripId,
|
||||
day: day,
|
||||
sortOrder: 0.0
|
||||
sortOrder: 0.0,
|
||||
kind: .custom(customInfo)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -353,7 +397,7 @@ private struct PlaceResultRow: View {
|
||||
// MARK: - Category Button
|
||||
|
||||
private struct CategoryButton: View {
|
||||
let category: CustomItineraryItem.ItemCategory
|
||||
let category: ItemCategory
|
||||
let isSelected: Bool
|
||||
let action: () -> Void
|
||||
|
||||
|
||||
Reference in New Issue
Block a user