This commit is contained in:
Andras Schmelczer 2026-02-15 22:39:49 +00:00
parent 03445188ea
commit 524580eb25
102 changed files with 36625 additions and 1295 deletions

View file

@ -23,12 +23,13 @@ import { getTutorialStyles } from '../../lib/tutorial-styles';
import Joyride from 'react-joyride';
import {
useTravelTime,
TRANSPORT_MODES,
MODE_LABELS,
type TransportMode,
travelFieldKey,
type TravelTimeInitial,
} from '../../hooks/useTravelTime';
import { apiUrl, assertOk, buildFilterString, logNonAbortError } from '../../lib/api';
import { apiUrl, assertOk, authHeaders, buildFilterString, logNonAbortError } from '../../lib/api';
import { useLicense } from '../../hooks/useLicense';
import UpgradeModal from '../ui/UpgradeModal';
import { SpinnerIcon } from '../ui/icons/SpinnerIcon';
import { MapPinIcon } from '../ui/icons/MapPinIcon';
@ -54,6 +55,9 @@ interface MapPageProps {
ogMode?: boolean;
isMobile?: boolean;
initialTravelTime?: TravelTimeInitial;
user?: { id: string; subscription: string } | null;
onLoginClick?: () => void;
onRegisterClick?: () => void;
}
export default function MapPage({
@ -73,6 +77,9 @@ export default function MapPage({
ogMode,
isMobile = false,
initialTravelTime,
user,
onLoginClick,
onRegisterClick,
}: MapPageProps) {
const [selectedPOICategories, setSelectedPOICategories] =
useState<Set<string>>(initialPOICategories);
@ -125,6 +132,9 @@ export default function MapPage({
// Travel time hook
const travelTime = useTravelTime(initialTravelTime);
// License hook
const license = useLicense();
// Map data hook
const mapData = useMapData({
filters,
@ -164,20 +174,21 @@ export default function MapPage({
// POI data
const pois = usePOIData(mapData.bounds, selectedPOICategories);
// Compute data range for travel time slider per mode (full min/max for slider bounds)
const travelTimeDataRanges = useMemo((): Partial<Record<TransportMode, [number, number]>> => {
const ranges: Partial<Record<TransportMode, [number, number]>> = {};
for (const mode of TRANSPORT_MODES) {
const entry = travelTime.entries[mode];
if (!entry?.destination) continue;
// Compute data range for travel time slider per entry index (full min/max for slider bounds)
const travelTimeDataRanges = useMemo((): globalThis.Map<number, [number, number]> => {
const ranges = new globalThis.Map<number, [number, number]>();
for (let i = 0; i < travelTime.entries.length; i++) {
const entry = travelTime.entries[i];
if (!entry.slug) continue;
const fieldName = `avg_${travelFieldKey(entry)}`;
const vals: number[] = [];
for (const item of mapData.data) {
const val = item[`travel_time_${mode}`];
const val = item[fieldName];
if (typeof val === 'number' && !isNaN(val)) vals.push(val);
}
if (vals.length === 0) continue;
vals.sort((a, b) => a - b);
ranges[mode] = [vals[0], vals[vals.length - 1]];
ranges.set(i, [vals[0], vals[vals.length - 1]]);
}
return ranges;
}, [travelTime.entries, mapData.data]);
@ -253,7 +264,7 @@ export default function MapPage({
const url = apiUrl('export', params);
setExporting(true);
fetch(url)
fetch(url, authHeaders())
.then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.blob();
@ -397,8 +408,8 @@ export default function MapPage({
onClearOpenInfoFeature={onClearPendingInfoFeature}
travelTimeEntries={travelTime.entries}
travelTimeDataRanges={travelTimeDataRanges}
onTravelTimeEnableMode={travelTime.handleEnableMode}
onTravelTimeDisableMode={travelTime.handleDisableMode}
onTravelTimeAddEntry={travelTime.handleAddEntry}
onTravelTimeRemoveEntry={travelTime.handleRemoveEntry}
onTravelTimeSetDestination={travelTime.handleSetDestination}
onTravelTimeRangeChange={travelTime.handleTimeRangeChange}
aiFilterLoading={aiFilters.loading}
@ -478,14 +489,14 @@ export default function MapPage({
>
{/* Legend */}
{(() => {
const primaryMode = TRANSPORT_MODES.find(
(m) => travelTime.entries[m]?.destination && mapData.travelTimeColorRanges[m]
const primaryIdx = travelTime.entries.findIndex(
(e, i) => e.slug && mapData.travelTimeColorRanges.get(i)
);
if (primaryMode) {
if (primaryIdx >= 0) {
return (
<MapLegend
featureLabel={`Travel time (${MODE_LABELS[primaryMode]})`}
range={mapData.travelTimeColorRanges[primaryMode]!}
featureLabel={`Travel time (${MODE_LABELS[travelTime.entries[primaryIdx].mode]})`}
range={mapData.travelTimeColorRanges.get(primaryIdx)!}
showCancel={false}
onCancel={handleCancelPin}
mode="feature"
@ -539,6 +550,16 @@ export default function MapPage({
renderProperties={renderPropertiesPane}
/>
)}
{mapData.licenseRequired && (
<UpgradeModal
isLoggedIn={!!user}
onLoginClick={onLoginClick ?? (() => {})}
onRegisterClick={onRegisterClick ?? (() => {})}
onStartCheckout={() => license.startCheckout()}
onDismiss={() => {}}
/>
)}
</div>
);
}
@ -664,6 +685,16 @@ export default function MapPage({
</div>
</div>
</div>
{mapData.licenseRequired && (
<UpgradeModal
isLoggedIn={!!user}
onLoginClick={onLoginClick ?? (() => {})}
onRegisterClick={onRegisterClick ?? (() => {})}
onStartCheckout={() => license.startCheckout()}
onDismiss={() => {}}
/>
)}
</div>
);
}