has issues
This commit is contained in:
parent
2e112d7398
commit
c645b0f1d4
96 changed files with 2147083 additions and 5787 deletions
|
|
@ -15,16 +15,28 @@ import { useAiFilters } from '../../hooks/useAiFilters';
|
|||
import { useUrlSync } from '../../hooks/useUrlSync';
|
||||
import { useTutorial } from '../../hooks/useTutorial';
|
||||
import { getTutorialStyles } from '../../lib/tutorial-styles';
|
||||
import { travelFieldKey, useTravelTime } from '../../hooks/useTravelTime';
|
||||
import {
|
||||
MAX_TRAVEL_MINUTES,
|
||||
parseServerMode,
|
||||
resolveTransitVariant,
|
||||
travelFieldKey,
|
||||
useTravelTime,
|
||||
} from '../../hooks/useTravelTime';
|
||||
import { apiUrl, authHeaders, buildFilterString } from '../../lib/api';
|
||||
import { useFilterCounts } from '../../hooks/useFilterCounts';
|
||||
import { trackEvent } from '../../lib/analytics';
|
||||
import { INITIAL_VIEW_STATE, POSTCODE_SEARCH_ZOOM } from '../../lib/consts';
|
||||
import {
|
||||
INITIAL_VIEW_STATE,
|
||||
POSTCODE_SEARCH_ZOOM,
|
||||
POSTCODE_ZOOM_THRESHOLD,
|
||||
} from '../../lib/consts';
|
||||
import type { OverlayId } from '../../lib/overlays';
|
||||
import { useLicense } from '../../hooks/useLicense';
|
||||
import { stateToParams } from '../../lib/url-state';
|
||||
import {
|
||||
AreaPane,
|
||||
Filters,
|
||||
OverlayPane,
|
||||
POIPane,
|
||||
PropertiesPane,
|
||||
UpgradeModal,
|
||||
|
|
@ -62,6 +74,7 @@ export default function MapPage({
|
|||
initialFilters,
|
||||
initialViewState,
|
||||
initialPOICategories,
|
||||
initialOverlays,
|
||||
initialTab,
|
||||
initialLoading,
|
||||
theme,
|
||||
|
|
@ -92,11 +105,15 @@ export default function MapPage({
|
|||
const { t } = useTranslation();
|
||||
const [selectedPOICategories, setSelectedPOICategories] =
|
||||
useState<Set<string>>(initialPOICategories);
|
||||
const [activeOverlays, setActiveOverlays] = useState<Set<OverlayId>>(
|
||||
() => new Set(initialOverlays ?? [])
|
||||
);
|
||||
const [leftPaneWidth, leftPaneHandlers] = usePaneResize(384, 200, 0.45, 'left');
|
||||
const [rightPaneWidth, rightPaneHandlers] = usePaneResize(384, 200, 0.45, 'right');
|
||||
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
|
||||
const [mobileBottomSheetHeight, setMobileBottomSheetHeight] = useState(0);
|
||||
const [poiPaneOpen, setPoiPaneOpen] = useState(false);
|
||||
const [overlayPaneOpen, setOverlayPaneOpen] = useState(false);
|
||||
const [currentLocation, setCurrentLocation] = useState<{ lat: number; lng: number } | null>(null);
|
||||
|
||||
const {
|
||||
|
|
@ -141,6 +158,8 @@ export default function MapPage({
|
|||
handleSetEntries,
|
||||
handleTimeRangeChange,
|
||||
handleToggleBest,
|
||||
handleToggleNoChange,
|
||||
handleToggleNoBuses,
|
||||
} = useTravelTime(initialTravelTime);
|
||||
|
||||
const mapFlyToRef = useRef<MapFlyTo | null>(null);
|
||||
|
|
@ -181,8 +200,10 @@ export default function MapPage({
|
|||
async (query: string) => {
|
||||
const context = {
|
||||
filters,
|
||||
// Send the resolved variant so the AI sees the actual server mode in use,
|
||||
// not just the UI-side base mode. Lets the model refine no-change/no-buses.
|
||||
travelTime: activeEntries.map((entry) => ({
|
||||
mode: entry.mode,
|
||||
mode: resolveTransitVariant(entry),
|
||||
label: entry.label,
|
||||
min: entry.timeRange?.[0],
|
||||
max: entry.timeRange?.[1],
|
||||
|
|
@ -194,17 +215,29 @@ export default function MapPage({
|
|||
if (!result) return;
|
||||
|
||||
handleSetFilters(result.filters);
|
||||
// Filter out variants the UI cannot represent (e.g. transit-one-change*)
|
||||
// FIRST so the same filtered list drives both entry state and fly-to.
|
||||
// Otherwise we'd fly to a destination for a mode the user can't see.
|
||||
const representable = result.travelTimeFilters
|
||||
.map((tt) => ({ tt, parsed: parseServerMode(tt.mode) }))
|
||||
.filter((x): x is { tt: typeof x.tt; parsed: NonNullable<typeof x.parsed> } => !!x.parsed);
|
||||
|
||||
handleSetEntries(
|
||||
result.travelTimeFilters.map((travelTimeFilter) => ({
|
||||
mode: travelTimeFilter.mode,
|
||||
slug: travelTimeFilter.slug,
|
||||
label: travelTimeFilter.label,
|
||||
timeRange: [travelTimeFilter.min ?? 0, travelTimeFilter.max ?? 120] as [number, number],
|
||||
representable.map(({ tt, parsed }) => ({
|
||||
mode: parsed.mode,
|
||||
noChange: parsed.noChange,
|
||||
noBuses: parsed.noBuses,
|
||||
slug: tt.slug,
|
||||
label: tt.label,
|
||||
timeRange: [
|
||||
tt.min ?? 0,
|
||||
Math.min(tt.max ?? MAX_TRAVEL_MINUTES, MAX_TRAVEL_MINUTES),
|
||||
] as [number, number],
|
||||
useBest: false,
|
||||
}))
|
||||
);
|
||||
|
||||
const firstTravelTime = result.travelTimeFilters[0];
|
||||
const firstTravelTime = representable[0]?.tt;
|
||||
if (!firstTravelTime?.slug) return;
|
||||
|
||||
try {
|
||||
|
|
@ -411,6 +444,7 @@ export default function MapPage({
|
|||
}, []);
|
||||
|
||||
const pois = usePOIData(mapData.bounds, selectedPOICategories);
|
||||
const overlaysZoomedIn = (mapData.currentView?.zoom ?? 0) >= POSTCODE_ZOOM_THRESHOLD;
|
||||
const actualListingsFilterParam = useMemo(
|
||||
() => buildFilterString(filters, features),
|
||||
[filters, features]
|
||||
|
|
@ -430,7 +464,8 @@ export default function MapPage({
|
|||
selectedPOICategories,
|
||||
rightPaneTab,
|
||||
entries,
|
||||
shareCode
|
||||
shareCode,
|
||||
activeOverlays
|
||||
);
|
||||
|
||||
useInitialMapPageView(mapData, initialViewState, initialTab, setRightPaneTab);
|
||||
|
|
@ -485,6 +520,7 @@ export default function MapPage({
|
|||
filters,
|
||||
features,
|
||||
travelTimeEntries: entries,
|
||||
selectedOverlays: activeOverlays,
|
||||
shareCode,
|
||||
t,
|
||||
onExportStateChange,
|
||||
|
|
@ -502,9 +538,19 @@ export default function MapPage({
|
|||
selectedPOICategories,
|
||||
rightPaneTab,
|
||||
entries,
|
||||
shareCode
|
||||
shareCode,
|
||||
activeOverlays
|
||||
).toString(),
|
||||
[entries, features, filters, rightPaneTab, selectedPOICategories, shareCode, shareAndSaveView]
|
||||
[
|
||||
activeOverlays,
|
||||
entries,
|
||||
features,
|
||||
filters,
|
||||
rightPaneTab,
|
||||
selectedPOICategories,
|
||||
shareCode,
|
||||
shareAndSaveView,
|
||||
]
|
||||
);
|
||||
const handleSaveSearch = useCallback(
|
||||
async (name: string) => {
|
||||
|
|
@ -540,6 +586,7 @@ export default function MapPage({
|
|||
theme={theme}
|
||||
ogMode={ogMode}
|
||||
travelTimeEntries={entries}
|
||||
activeOverlays={activeOverlays}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -595,6 +642,17 @@ export default function MapPage({
|
|||
</Suspense>
|
||||
);
|
||||
|
||||
const renderOverlayPane = () => (
|
||||
<Suspense fallback={<PaneFallback />}>
|
||||
<OverlayPane
|
||||
selectedOverlays={activeOverlays}
|
||||
onOverlaysChange={setActiveOverlays}
|
||||
zoomedIn={overlaysZoomedIn}
|
||||
onClose={() => setOverlayPaneOpen(false)}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
const renderFilters = (options?: { destinationDropdownPortal?: boolean }) => (
|
||||
<Suspense fallback={<PaneFallback />}>
|
||||
<Filters
|
||||
|
|
@ -620,6 +678,8 @@ export default function MapPage({
|
|||
onTravelTimeRangeChange={handleTimeRangeChange}
|
||||
onTravelTimeDragEnd={handleTravelTimeDragEnd}
|
||||
onTravelTimeToggleBest={handleToggleBest}
|
||||
onTravelTimeToggleNoChange={handleToggleNoChange}
|
||||
onTravelTimeToggleNoBuses={handleToggleNoBuses}
|
||||
aiFilterLoading={aiFilterLoading}
|
||||
aiFilterError={aiFilterError}
|
||||
aiFilterErrorType={aiFilterErrorType}
|
||||
|
|
@ -645,7 +705,14 @@ export default function MapPage({
|
|||
</Suspense>
|
||||
);
|
||||
|
||||
const handleTogglePoiPane = () => setPoiPaneOpen((open) => !open);
|
||||
const handleTogglePoiPane = () => {
|
||||
setOverlayPaneOpen(false);
|
||||
setPoiPaneOpen((open) => !open);
|
||||
};
|
||||
const handleToggleOverlayPane = () => {
|
||||
setPoiPaneOpen(false);
|
||||
setOverlayPaneOpen((open) => !open);
|
||||
};
|
||||
const handleMobileDrawerTabChange = (tab: 'area' | 'properties') => {
|
||||
if (tab === 'properties') {
|
||||
handlePropertiesTabClick();
|
||||
|
|
@ -713,6 +780,7 @@ export default function MapPage({
|
|||
initialLoading={initialLoading}
|
||||
mapData={mapData}
|
||||
pois={pois}
|
||||
activeOverlays={activeOverlays}
|
||||
mapViewFeature={mapViewFeature}
|
||||
filterRange={filterRange}
|
||||
viewSource={viewSource}
|
||||
|
|
@ -743,6 +811,9 @@ export default function MapPage({
|
|||
onTogglePoiPane={handleTogglePoiPane}
|
||||
poiButtonLabel={t('poiPane.pointsOfInterest')}
|
||||
poiPane={renderPOIPane()}
|
||||
overlayPaneOpen={overlayPaneOpen}
|
||||
onToggleOverlayPane={handleToggleOverlayPane}
|
||||
overlayPane={renderOverlayPane()}
|
||||
filtersPane={renderFilters({ destinationDropdownPortal: false })}
|
||||
mobileLegend={
|
||||
<MobileMapLegend
|
||||
|
|
@ -777,6 +848,7 @@ export default function MapPage({
|
|||
filtersPane={renderFilters()}
|
||||
mapData={mapData}
|
||||
pois={pois}
|
||||
activeOverlays={activeOverlays}
|
||||
mapViewFeature={mapViewFeature}
|
||||
filterRange={filterRange}
|
||||
viewSource={viewSource}
|
||||
|
|
@ -801,6 +873,9 @@ export default function MapPage({
|
|||
poiPaneOpen={poiPaneOpen}
|
||||
onTogglePoiPane={handleTogglePoiPane}
|
||||
poiPane={renderPOIPane()}
|
||||
overlayPaneOpen={overlayPaneOpen}
|
||||
onToggleOverlayPane={handleToggleOverlayPane}
|
||||
overlayPane={renderOverlayPane()}
|
||||
showSelectionPane={!!selectedHexagon}
|
||||
rightPaneWidth={rightPaneWidth}
|
||||
rightPaneHandlers={rightPaneHandlers}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue