feat: Trips home screen — inspired by Apple's Wishlist sample
Mapped Wishlist → Trips:
- WishlistView → TripsHomeView (NavigationStack + ScrollView)
- RecentTripsPageView → UpcomingTripsPageView (paged TabView hero)
- TripCollectionView → PastTripsSection (horizontal scroll compact)
- TripCard → TripCard (.compact/.expanded sizes)
- TripImageView → TripImageView (Rectangle overlay + AsyncImage)
- ExpandedNavigationTitle → same pattern for "Trips" title
- AddTripView → Plan Trip button (.glassProminent, Phase 2)
Structure (9 files):
- TripModels.swift: Trip model with date helpers, image URL builder
- TripsAPI.swift: getTrips() via gateway /api/trips/trips
- TripsViewModel.swift: upcoming/past sorting, @Observable
- TripsHomeView.swift: main screen, Wishlist layout pattern
- UpcomingTripsPageView.swift: full-width paged hero cards
- PastTripsSection.swift: horizontal compact card scroll
- TripCard.swift: reusable card with compact/expanded sizes
- TripImageView.swift: AsyncImage with gradient placeholder
- TripPlaceholderView.swift: simple detail for Phase 1
New tab: Trips (airplane icon) in tab bar for all users.
Images served via gateway: /api/trips/images/{filename}
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,15 @@
|
||||
A10005 /* AuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10005 /* AuthManager.swift */; };
|
||||
A10050 /* AppearanceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10050 /* AppearanceManager.swift */; };
|
||||
A10051 /* EditableDraftCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10051 /* EditableDraftCard.swift */; };
|
||||
A10060 /* TripModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10060 /* TripModels.swift */; };
|
||||
A10061 /* TripsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10061 /* TripsAPI.swift */; };
|
||||
A10062 /* TripsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10062 /* TripsViewModel.swift */; };
|
||||
A10063 /* TripsHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10063 /* TripsHomeView.swift */; };
|
||||
A10064 /* UpcomingTripsPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10064 /* UpcomingTripsPageView.swift */; };
|
||||
A10065 /* PastTripsSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10065 /* PastTripsSection.swift */; };
|
||||
A10066 /* TripCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10066 /* TripCard.swift */; };
|
||||
A10067 /* TripImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10067 /* TripImageView.swift */; };
|
||||
A10068 /* TripPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10068 /* TripPlaceholderView.swift */; };
|
||||
A10006 /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10006 /* LoginView.swift */; };
|
||||
A10007 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10007 /* HomeView.swift */; };
|
||||
A10008 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B10008 /* HomeViewModel.swift */; };
|
||||
@@ -90,6 +99,15 @@
|
||||
B10005 /* AuthManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthManager.swift; sourceTree = "<group>"; };
|
||||
B10050 /* AppearanceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceManager.swift; sourceTree = "<group>"; };
|
||||
B10051 /* EditableDraftCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditableDraftCard.swift; sourceTree = "<group>"; };
|
||||
B10060 /* TripModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripModels.swift; sourceTree = "<group>"; };
|
||||
B10061 /* TripsAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripsAPI.swift; sourceTree = "<group>"; };
|
||||
B10062 /* TripsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripsViewModel.swift; sourceTree = "<group>"; };
|
||||
B10063 /* TripsHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripsHomeView.swift; sourceTree = "<group>"; };
|
||||
B10064 /* UpcomingTripsPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingTripsPageView.swift; sourceTree = "<group>"; };
|
||||
B10065 /* PastTripsSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PastTripsSection.swift; sourceTree = "<group>"; };
|
||||
B10066 /* TripCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripCard.swift; sourceTree = "<group>"; };
|
||||
B10067 /* TripImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripImageView.swift; sourceTree = "<group>"; };
|
||||
B10068 /* TripPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TripPlaceholderView.swift; sourceTree = "<group>"; };
|
||||
B10006 /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
|
||||
B10007 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||
B10008 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -201,6 +219,7 @@
|
||||
F10014 /* Assistant */,
|
||||
F10021 /* Feedback */,
|
||||
F10030 /* Reader */,
|
||||
F10050 /* Trips */,
|
||||
);
|
||||
path = Features;
|
||||
sourceTree = "<group>";
|
||||
@@ -400,6 +419,54 @@
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F10050 /* Trips */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F10051 /* Models */,
|
||||
F10052 /* API */,
|
||||
F10053 /* ViewModels */,
|
||||
F10054 /* Views */,
|
||||
);
|
||||
path = Trips;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F10051 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B10060 /* TripModels.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F10052 /* API */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B10061 /* TripsAPI.swift */,
|
||||
);
|
||||
path = API;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F10053 /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B10062 /* TripsViewModel.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F10054 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B10063 /* TripsHomeView.swift */,
|
||||
B10064 /* UpcomingTripsPageView.swift */,
|
||||
B10065 /* PastTripsSection.swift */,
|
||||
B10066 /* TripCard.swift */,
|
||||
B10067 /* TripImageView.swift */,
|
||||
B10068 /* TripPlaceholderView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@@ -518,6 +585,15 @@
|
||||
A10005 /* AuthManager.swift in Sources */,
|
||||
A10050 /* AppearanceManager.swift in Sources */,
|
||||
A10051 /* EditableDraftCard.swift in Sources */,
|
||||
A10060 /* TripModels.swift in Sources */,
|
||||
A10061 /* TripsAPI.swift in Sources */,
|
||||
A10062 /* TripsViewModel.swift in Sources */,
|
||||
A10063 /* TripsHomeView.swift in Sources */,
|
||||
A10064 /* UpcomingTripsPageView.swift in Sources */,
|
||||
A10065 /* PastTripsSection.swift in Sources */,
|
||||
A10066 /* TripCard.swift in Sources */,
|
||||
A10067 /* TripImageView.swift in Sources */,
|
||||
A10068 /* TripPlaceholderView.swift in Sources */,
|
||||
A10006 /* LoginView.swift in Sources */,
|
||||
A10007 /* HomeView.swift in Sources */,
|
||||
A10008 /* HomeViewModel.swift in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user