Make RoutePlannerView the home; merge "Where can I go?" into it

Single unified search at the app root. TO is optional: filled goes through
/route (connections); blank flips to /departures with a time-window picker
("Where can I go?"). Same per-leg load card detail screen for any tap, so
direct flights and multi-stop connections share the same UX.

- Drop ContentView entirely (favorites + browse + entry cards). FlightsApp
  instantiates RoutePlannerView directly.
- Delete WhereToGoView; DepartureLegRow is inlined into RoutePlannerView
  as the where-can-I-go result row.
- SearchRoute enum trimmed to just the cases DestinationsListView still
  references and moved to its own file (Models/SearchRoute.swift).

Sort bar moved out of the controls cards into a dedicated row between the
Search button and the results — only visible once results exist. Switched
from segmented to dropdown menu picker. Options narrowed to the four
the user asked for: Departure Earliest / Departure Latest / Fewest Stops
/ Most Stops in connection mode, just the two time-based options for
where-can-I-go (single-leg, stop-count is meaningless). All sorts apply
client-side; upstream still gets `departure_time` for a stable base order.

Two real bugs fixed in connection search:
- Past flights weren't filtered. Same-day searches return mostly already-
  departed itineraries because the API sorts earliest-first. Added a
  `firstDeparture > now` filter applied before sort. Header surfaces the
  dropped count ("12 itineraries · 38 already departed"). When every
  result is past, the error message says so explicitly instead of going
  blank.
- 100-result limit was way too low for hub→hub with maxStops:2 — the
  combinatorial explosion of valid permutations filled the cap with
  morning flights and never reached afternoon. Bumped to 500.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-28 11:50:02 -05:00
parent df4a74726c
commit 0c4777216e
8 changed files with 650 additions and 860 deletions
+4 -8
View File
@@ -14,7 +14,6 @@
303821C9668A44F38FFA02CA /* AirportSearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD303E3EDCC4BF2BCF31722 /* AirportSearchField.swift */; };
35D016EBA93C40BB873AB304 /* Airline.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AC23D8748D42C9A7115FAC /* Airline.swift */; };
4C770C55CB3643BAB7B9D622 /* AirportBrowserSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15676B4BD35745D1BD1DC947 /* AirportBrowserSheet.swift */; };
57A463AB3CFD44DC93444E59 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9934B0FCA757403A94AB963C /* ContentView.swift */; };
61F8E3DD7D434DA7854C20E2 /* FlightsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6D822B4ABF741F890A4400C /* FlightsApp.swift */; };
6558A31ADEC740FC8C56EA22 /* FlightSchedule.swift in Sources */ = {isa = PBXBuildFile; fileRef = B913D04A4E51436595308A21 /* FlightSchedule.swift */; };
7FFD9A2D25F9421D8929C027 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36862683C4F44A95AFE234EB /* SearchViewModel.swift */; };
@@ -42,10 +41,10 @@
RE1100001111000011110001 /* RouteExplorerModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE1100001111000011110002 /* RouteExplorerModels.swift */; };
RE2200002222000022220001 /* RouteExplorerClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE2200002222000022220002 /* RouteExplorerClient.swift */; };
RE3300003333000033330001 /* RoutePlannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE3300003333000033330002 /* RoutePlannerView.swift */; };
RE4400004444000044440001 /* WhereToGoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE4400004444000044440002 /* WhereToGoView.swift */; };
RE5500005555000055550001 /* IATAAirportPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE5500005555000055550002 /* IATAAirportPicker.swift */; };
RE6600006666000066660001 /* ConnectionRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE6600006666000066660002 /* ConnectionRow.swift */; };
RE7700007777000077770001 /* ConnectionLoadDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE7700007777000077770002 /* ConnectionLoadDetailView.swift */; };
RE8800008888000088880001 /* SearchRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = RE8800008888000088880002 /* SearchRoute.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -62,7 +61,6 @@
7C2EB471F011450DA7BBEFAD /* AirportMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirportMapView.swift; sourceTree = "<group>"; };
85EC89DEE12942B49DF51984 /* Airport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Airport.swift; sourceTree = "<group>"; };
8A3CB0CCC2524542AFB0D1D2 /* Flights.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Flights.app; sourceTree = BUILT_PRODUCTS_DIR; };
9934B0FCA757403A94AB963C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
9A58C339D6084657B0538E9C /* AirportDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AirportDatabase.swift; sourceTree = "<group>"; };
9BEAC0EBABFD41569FE69B1B /* DestinationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestinationsViewModel.swift; sourceTree = "<group>"; };
A65682BD902141BAA686D101 /* FlightService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlightService.swift; sourceTree = "<group>"; };
@@ -85,10 +83,10 @@
RE1100001111000011110002 /* RouteExplorerModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteExplorerModels.swift; sourceTree = "<group>"; };
RE2200002222000022220002 /* RouteExplorerClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouteExplorerClient.swift; sourceTree = "<group>"; };
RE3300003333000033330002 /* RoutePlannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutePlannerView.swift; sourceTree = "<group>"; };
RE4400004444000044440002 /* WhereToGoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WhereToGoView.swift; sourceTree = "<group>"; };
RE5500005555000055550002 /* IATAAirportPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IATAAirportPicker.swift; sourceTree = "<group>"; };
RE6600006666000066660002 /* ConnectionRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionRow.swift; sourceTree = "<group>"; };
RE7700007777000077770002 /* ConnectionLoadDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionLoadDetailView.swift; sourceTree = "<group>"; };
RE8800008888000088880002 /* SearchRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchRoute.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -105,7 +103,6 @@
1B20C5393D8F432A93097C2C /* Views */ = {
isa = PBXGroup;
children = (
9934B0FCA757403A94AB963C /* ContentView.swift */,
0CD303E3EDCC4BF2BCF31722 /* AirportSearchField.swift */,
300153508F8445B6A78CEC52 /* DestinationsListView.swift */,
1C1176F877BF496ABF079040 /* RouteDetailView.swift */,
@@ -115,7 +112,6 @@
15676B4BD35745D1BD1DC947 /* AirportBrowserSheet.swift */,
BB1100001111000011110006 /* FlightLoadDetailView.swift */,
RE3300003333000033330002 /* RoutePlannerView.swift */,
RE4400004444000044440002 /* WhereToGoView.swift */,
RE7700007777000077770002 /* ConnectionLoadDetailView.swift */,
AA5555555555555555555555 /* Styles */,
AA6666666666666666666666 /* Components */,
@@ -208,6 +204,7 @@
E7987BD4832D44F1A0851933 /* Country.swift */,
BB1100001111000011110002 /* FlightLoad.swift */,
RE1100001111000011110002 /* RouteExplorerModels.swift */,
RE8800008888000088880002 /* SearchRoute.swift */,
);
path = Models;
sourceTree = "<group>";
@@ -286,7 +283,6 @@
7FFD9A2D25F9421D8929C027 /* SearchViewModel.swift in Sources */,
C36C490556254AC88EC02C80 /* DestinationsViewModel.swift in Sources */,
9C1300E497B049FE8DA677E0 /* RouteDetailViewModel.swift in Sources */,
57A463AB3CFD44DC93444E59 /* ContentView.swift in Sources */,
303821C9668A44F38FFA02CA /* AirportSearchField.swift in Sources */,
D0EC717347974D668C77B9D2 /* DestinationsListView.swift in Sources */,
BB3E647E4A07477F9F37E607 /* RouteDetailView.swift in Sources */,
@@ -310,10 +306,10 @@
RE1100001111000011110001 /* RouteExplorerModels.swift in Sources */,
RE2200002222000022220001 /* RouteExplorerClient.swift in Sources */,
RE3300003333000033330001 /* RoutePlannerView.swift in Sources */,
RE4400004444000044440001 /* WhereToGoView.swift in Sources */,
RE5500005555000055550001 /* IATAAirportPicker.swift in Sources */,
RE6600006666000066660001 /* ConnectionRow.swift in Sources */,
RE7700007777000077770001 /* ConnectionLoadDetailView.swift in Sources */,
RE8800008888000088880001 /* SearchRoute.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};