Can't even keep track anymore

This commit is contained in:
Andras Schmelczer 2026-02-13 09:16:28 +00:00
parent dccc1e439d
commit 3a3f899ea2
50 changed files with 1144 additions and 560 deletions

View file

@ -15,26 +15,26 @@ import { usePOIData } from '../../hooks/usePOIData';
import { useFilters } from '../../hooks/useFilters';
import { useHexagonSelection } from '../../hooks/useHexagonSelection';
import { usePaneResize } from '../../hooks/usePaneResize';
import { useAiFilters } from '../../hooks/useAiFilters';
import { useAreaSummary } from '../../hooks/useAreaSummary';
import { useUrlSync } from '../../hooks/useUrlSync';
import { useTravelTime, type TravelTimeInitial } from '../../hooks/useTravelTime';
import { apiUrl, buildFilterString } from '../../lib/api';
import { SpinnerIcon } from '../ui/icons/SpinnerIcon';
import { MapPinIcon } from '../ui/icons/MapPinIcon';
export interface ExportState {
onExport: () => void;
exporting: boolean;
}
type MobileBottomTab = 'filters' | 'pois' | 'area';
interface MapPageProps {
features: FeatureMeta[];
poiCategoryGroups: POICategoryGroup[];
initialFilters: FeatureFilters;
initialViewState: ViewState;
initialPOICategories: Set<string>;
initialTab: 'pois' | 'properties' | 'area';
initialTab: 'properties' | 'area';
initialLoading: boolean;
theme: 'light' | 'dark';
pendingInfoFeature: string | null;
@ -73,9 +73,11 @@ export default function MapPage({
const [rightPaneWidth, rightPaneHandlers] = usePaneResize(288, 200, 500, 'right');
// Mobile state
const [mobileBottomTab, setMobileBottomTab] = useState<MobileBottomTab>('filters');
const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
// POI floating panel state
const [poiPaneOpen, setPoiPaneOpen] = useState(false);
// Initialize filters first
const {
filters,
@ -90,6 +92,7 @@ export default function MapPage({
handleAddFilter,
handleFilterChange,
handleRemoveFilter,
handleSetFilters,
handleDragStart,
handleDragChange,
handleDragEnd,
@ -101,6 +104,16 @@ export default function MapPage({
features,
});
// AI filters hook
const aiFilters = useAiFilters();
const handleAiFilterSubmit = useCallback(
async (query: string) => {
const result = await aiFilters.fetchAiFilters(query);
if (result) handleSetFilters(result);
},
[aiFilters.fetchAiFilters, handleSetFilters]
);
// Travel time hook
const travelTime = useTravelTime(initialTravelTime);
@ -161,7 +174,6 @@ export default function MapPage({
handleHexagonClick(id, isPostcode);
if (id) {
setMobileDrawerOpen(true);
setMobileBottomTab('area');
}
},
[handleHexagonClick]
@ -289,6 +301,10 @@ export default function MapPage({
screenshotMode
ogMode={ogMode}
bounds={mapData.bounds}
travelTimeEnabled={travelTime.enabled}
travelTimeDestination={travelTime.destination}
travelTimeColorRange={mapData.travelTimeColorRange}
travelTimeRange={travelTime.timeRange}
/>
</div>
);
@ -368,6 +384,9 @@ export default function MapPage({
onTravelTimeSetDestination={travelTime.handleSetDestination}
onTravelTimeModeChange={travelTime.handleModeChange}
onTravelTimeRangeChange={travelTime.handleTimeRangeChange}
aiFilterLoading={aiFilters.loading}
aiFilterError={aiFilters.error}
onAiFilterSubmit={handleAiFilterSubmit}
/>
);
@ -421,6 +440,19 @@ export default function MapPage({
Loading...
</div>
)}
{/* Floating POI button */}
<button
onClick={() => setPoiPaneOpen((p) => !p)}
className={`absolute bottom-2 right-2 z-10 p-2 rounded-lg shadow-lg bg-white dark:bg-warm-800 ${poiPaneOpen ? 'text-teal-600 dark:text-teal-400' : 'text-warm-500 dark:text-warm-400 hover:text-teal-600 dark:hover:text-teal-400'}`}
>
<MapPinIcon className="w-5 h-5" />
</button>
{/* Floating POI panel */}
{poiPaneOpen && (
<div className="absolute bottom-12 right-2 z-10 w-[calc(100%-1rem)] max-h-[60%] overflow-hidden flex flex-col rounded-lg shadow-xl border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-900">
{renderPOIPane()}
</div>
)}
</div>
{/* Bottom panel — 55% */}
@ -466,27 +498,9 @@ export default function MapPage({
inline
/>
)}
{/* Tab bar */}
<div className="flex shrink-0 border-b border-warm-200 dark:border-warm-700 text-sm">
<TabButton
label="Filters"
isActive={mobileBottomTab === 'filters'}
onClick={() => setMobileBottomTab('filters')}
/>
<TabButton
label="POIs"
isActive={mobileBottomTab === 'pois'}
onClick={() => setMobileBottomTab('pois')}
/>
</div>
{/* Tab content */}
{/* Filters content */}
<div className="flex-1 min-h-0">
{mobileBottomTab === 'pois' ? (
<div className="h-full overflow-y-auto">{renderPOIPane()}</div>
) : (
renderFilters()
)}
{renderFilters()}
</div>
</div>
@ -496,7 +510,6 @@ export default function MapPage({
onClose={() => setMobileDrawerOpen(false)}
renderArea={renderAreaPane}
renderProperties={renderPropertiesPane}
renderPOIs={renderPOIPane}
/>
)}
</div>
@ -565,6 +578,19 @@ export default function MapPage({
Loading...
</div>
)}
{/* Floating POI button */}
<button
onClick={() => setPoiPaneOpen((p) => !p)}
className={`absolute bottom-4 right-4 z-10 p-2 rounded-lg shadow-lg bg-white dark:bg-warm-800 ${poiPaneOpen ? 'text-teal-600 dark:text-teal-400' : 'text-warm-500 dark:text-warm-400 hover:text-teal-600 dark:hover:text-teal-400'}`}
>
<MapPinIcon className="w-5 h-5" />
</button>
{/* Floating POI panel */}
{poiPaneOpen && (
<div className="absolute bottom-14 right-4 z-10 w-80 max-h-[60vh] overflow-hidden flex flex-col rounded-lg shadow-xl border border-warm-200 dark:border-warm-700 bg-white dark:bg-warm-900">
{renderPOIPane()}
</div>
)}
</div>
{/* Right Pane */}
@ -590,19 +616,12 @@ export default function MapPage({
isActive={selection.rightPaneTab === 'properties'}
onClick={selection.handlePropertiesTabClick}
/>
<TabButton
label="POIs"
isActive={selection.rightPaneTab === 'pois'}
onClick={() => selection.setRightPaneTab('pois')}
/>
</div>
<div className="flex-1 overflow-hidden">
{selection.rightPaneTab === 'area'
? renderAreaPane()
: selection.rightPaneTab === 'properties'
? renderPropertiesPane()
: renderPOIPane()}
{selection.rightPaneTab === 'properties'
? renderPropertiesPane()
: renderAreaPane()}
</div>
</div>
</div>