This commit is contained in:
Andras Schmelczer 2026-05-14 20:42:48 +01:00
parent 273d7a83ee
commit 084117cea8
48 changed files with 2283 additions and 890 deletions

View file

@ -14,6 +14,7 @@ import { buildFilterString, apiUrl, assertOk, logNonAbortError, authHeaders } fr
import { findOverlappingSelectableHexagon } from '../lib/h3-selection';
import { SMALLEST_VISIBLE_HEXAGON_RESOLUTION } from '../lib/consts';
import type { TravelTimeEntry } from './useTravelTime';
import { buildTravelParam } from '../lib/travel-params';
interface SelectedHexagon {
id: string;
@ -91,19 +92,7 @@ export function useHexagonSelection({
return propertiesRequestIdRef.current === requestId;
}, []);
const travelParam = useMemo(() => {
const segments: string[] = [];
for (const entry of travelTimeEntries) {
if (!entry.slug) continue;
let segment = `${entry.mode}:${entry.slug}`;
if (entry.useBest) segment += ':best';
if (entry.timeRange) {
segment += `:${entry.timeRange[0]}:${entry.timeRange[1]}`;
}
segments.push(segment);
}
return segments.join('|');
}, [travelTimeEntries]);
const travelParam = useMemo(() => buildTravelParam(travelTimeEntries), [travelTimeEntries]);
const fetchHexagonStats = useCallback(
async (
@ -165,9 +154,9 @@ export function useHexagonSelection({
);
const fetchUnfilteredAreaCount = useCallback(
async (selection: SelectedHexagon, signal?: AbortSignal) => {
async (selection: SelectedHexagon, requestId: number, signal?: AbortSignal) => {
if (!hasStatsFilters) {
setUnfilteredAreaCount(null);
if (isCurrentAreaRequest(requestId)) setUnfilteredAreaCount(null);
return;
}
@ -175,9 +164,9 @@ export function useHexagonSelection({
selection.type === 'postcode'
? await fetchPostcodeStats(selection.id, signal, false)
: await fetchHexagonStats(selection.id, selection.resolution, signal, undefined, false);
setUnfilteredAreaCount(stats.count);
if (isCurrentAreaRequest(requestId)) setUnfilteredAreaCount(stats.count);
},
[fetchHexagonStats, fetchPostcodeStats, hasStatsFilters]
[fetchHexagonStats, fetchPostcodeStats, hasStatsFilters, isCurrentAreaRequest]
);
const refreshUnfilteredAreaCount = useCallback(
@ -185,18 +174,19 @@ export function useHexagonSelection({
selection: SelectedHexagon,
statsCount: number,
includeFilters: boolean,
requestId: number,
signal?: AbortSignal
) => {
if (!includeFilters || !hasStatsFilters || statsCount > 0) {
setUnfilteredAreaCount(null);
if (isCurrentAreaRequest(requestId)) setUnfilteredAreaCount(null);
return;
}
fetchUnfilteredAreaCount(selection, signal).catch((error) =>
fetchUnfilteredAreaCount(selection, requestId, signal).catch((error) =>
logNonAbortError('Failed to fetch unfiltered area count', error)
);
},
[fetchUnfilteredAreaCount, hasStatsFilters]
[fetchUnfilteredAreaCount, hasStatsFilters, isCurrentAreaRequest]
);
const fetchPostcodeLookup = useCallback(async (postcode: string, signal?: AbortSignal) => {
@ -321,6 +311,7 @@ export function useHexagonSelection({
setProperties([]);
setPropertiesTotal(0);
setPropertiesOffset(0);
setAreaStats(null);
setUnfilteredAreaCount(null);
setRightPaneTab('area');
@ -330,7 +321,7 @@ export function useHexagonSelection({
.then((stats) => {
if (!isCurrentAreaRequest(requestId)) return;
setAreaStats(stats);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters, requestId);
})
.catch((error) => logNonAbortError('Failed to fetch postcode stats', error))
.finally(() => {
@ -342,7 +333,7 @@ export function useHexagonSelection({
.then((stats) => {
if (!isCurrentAreaRequest(requestId)) return;
setAreaStats(stats);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters, requestId);
})
.catch((error) => logNonAbortError('Failed to fetch area stats', error))
.finally(() => {
@ -514,6 +505,7 @@ export function useHexagonSelection({
nextSelection,
nextStats.count,
areaStatsUseFilters,
requestId,
controller.signal
);
refreshProperties(nextSelection);
@ -569,6 +561,9 @@ export function useHexagonSelection({
setProperties([]);
setPropertiesTotal(0);
setPropertiesOffset(0);
invalidatePropertyRequests();
setAreaStats(null);
setUnfilteredAreaCount(null);
setLoadingAreaStats(true);
let cancelled = false;
@ -589,7 +584,7 @@ export function useHexagonSelection({
.then((stats) => {
if (cancelled || !isCurrentAreaRequest(requestId)) return;
setAreaStats(stats);
refreshUnfilteredAreaCount(selectedHexagon, stats.count, areaStatsUseFilters);
refreshUnfilteredAreaCount(selectedHexagon, stats.count, areaStatsUseFilters, requestId);
// Re-fetch properties if the properties tab is active and the filtered area still has matches.
if (areaStatsUseFilters && rightPaneTab === 'properties' && stats.count > 0) {
if (selectedHexagon.type === 'postcode') {
@ -620,6 +615,7 @@ export function useHexagonSelection({
fetchHexagonProperties,
fetchPostcodeProperties,
invalidateAreaRequests,
invalidatePropertyRequests,
isCurrentAreaRequest,
refreshUnfilteredAreaCount,
]);
@ -656,7 +652,7 @@ export function useHexagonSelection({
.then((stats) => {
if (!isCurrentAreaRequest(requestId)) return;
setAreaStats(stats);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters, requestId);
if (openProperties && stats.count > 0) {
fetchPostcodeProperties(postcode, 0, focusAddress);
}
@ -696,6 +692,7 @@ export function useHexagonSelection({
setProperties([]);
setPropertiesTotal(0);
setPropertiesOffset(0);
setAreaStats(null);
setUnfilteredAreaCount(null);
setRightPaneTab('area');
setLoadingAreaStats(true);
@ -710,7 +707,7 @@ export function useHexagonSelection({
.then((stats) => {
if (!isCurrentAreaRequest(requestId)) return;
setAreaStats(stats);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters, requestId);
})
.catch((error) => logNonAbortError('Failed to fetch current location hex stats', error))
.finally(() => {