Fmt
This commit is contained in:
parent
63713c3a2b
commit
bd6b511f16
17 changed files with 544 additions and 377 deletions
|
|
@ -54,7 +54,10 @@ describe('useHexagonSelection', () => {
|
|||
requests.push(`${url.pathname}${url.search}`);
|
||||
|
||||
if (url.pathname === '/api/postcode-stats') {
|
||||
return Promise.resolve(jsonResponse(stats(url.searchParams.has('filters') ? 0 : 4)));
|
||||
const emptyPostcode = url.searchParams.get('postcode') === 'EMPTY 1AA';
|
||||
return Promise.resolve(
|
||||
jsonResponse(stats(url.searchParams.has('filters') || emptyPostcode ? 0 : 4))
|
||||
);
|
||||
}
|
||||
|
||||
if (url.pathname === '/api/hexagon-stats') {
|
||||
|
|
@ -91,12 +94,15 @@ describe('useHexagonSelection', () => {
|
|||
id: 'SW1A 1AA',
|
||||
type: 'postcode',
|
||||
resolution: 9,
|
||||
lockedResolution: true,
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.selectedPostcodeGeometry).toBe(postcodeGeometry);
|
||||
expect(result.current.areaStats?.count).toBe(0);
|
||||
expect(result.current.unfilteredAreaCount).toBe(4);
|
||||
await waitFor(() => {
|
||||
expect(result.current.areaStats?.count).toBe(0);
|
||||
expect(result.current.unfilteredAreaCount).toBe(4);
|
||||
});
|
||||
expect(requests.some((url) => url.startsWith('/api/hexagon-stats'))).toBe(false);
|
||||
});
|
||||
|
||||
|
|
@ -124,6 +130,7 @@ describe('useHexagonSelection', () => {
|
|||
id: 'SW1A 1AA',
|
||||
type: 'postcode',
|
||||
resolution: 9,
|
||||
lockedResolution: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -131,4 +138,65 @@ describe('useHexagonSelection', () => {
|
|||
expect(result.current.unfilteredAreaCount).toBeNull();
|
||||
expect(requests.some((url) => url.startsWith('/api/hexagon-stats'))).toBe(false);
|
||||
});
|
||||
|
||||
it('keeps an empty postcode search selected instead of widening to hexagons', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useHexagonSelection({
|
||||
filters: {},
|
||||
features,
|
||||
hexagonData: [],
|
||||
resolution: 9,
|
||||
usePostcodeView: true,
|
||||
travelTimeEntries: [],
|
||||
})
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.handleLocationSearch('EMPTY 1AA', postcodeGeometry, 51.505, -0.115);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.areaStats?.count).toBe(0);
|
||||
});
|
||||
|
||||
expect(result.current.selectedHexagon).toEqual({
|
||||
id: 'EMPTY 1AA',
|
||||
type: 'postcode',
|
||||
resolution: 9,
|
||||
lockedResolution: true,
|
||||
});
|
||||
expect(result.current.selectedPostcodeGeometry).toBe(postcodeGeometry);
|
||||
expect(requests.some((url) => url.startsWith('/api/hexagon-stats'))).toBe(false);
|
||||
});
|
||||
|
||||
it('does not convert a searched postcode back to a hexagon while the map reaches postcode zoom', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useHexagonSelection({
|
||||
filters: {},
|
||||
features,
|
||||
hexagonData: [],
|
||||
resolution: 9,
|
||||
usePostcodeView: false,
|
||||
travelTimeEntries: [],
|
||||
})
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.handleLocationSearch('SW1A 1AA', postcodeGeometry, 51.505, -0.115);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.areaStats?.count).toBe(4);
|
||||
});
|
||||
|
||||
expect(result.current.selectedHexagon).toEqual({
|
||||
id: 'SW1A 1AA',
|
||||
type: 'postcode',
|
||||
resolution: 9,
|
||||
lockedResolution: true,
|
||||
});
|
||||
expect(result.current.selectedPostcodeGeometry).toBe(postcodeGeometry);
|
||||
expect(requests.some((url) => url.startsWith('/api/postcode/'))).toBe(false);
|
||||
expect(requests.some((url) => url.startsWith('/api/hexagon-stats'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -426,7 +426,7 @@ export function useHexagonSelection({
|
|||
selection.type === 'hexagon' &&
|
||||
!selection.lockedResolution &&
|
||||
areaStats?.central_postcode != null) ||
|
||||
(!usePostcodeView && selection.type === 'postcode') ||
|
||||
(!usePostcodeView && selection.type === 'postcode' && !selection.lockedResolution) ||
|
||||
(!usePostcodeView &&
|
||||
selection.type === 'hexagon' &&
|
||||
!selection.lockedResolution &&
|
||||
|
|
@ -628,103 +628,38 @@ export function useHexagonSelection({
|
|||
(
|
||||
postcode: string,
|
||||
geometry: PostcodeGeometry,
|
||||
lat?: number,
|
||||
lng?: number,
|
||||
_lat?: number,
|
||||
_lng?: number,
|
||||
openProperties = false,
|
||||
focusAddress?: string
|
||||
) => {
|
||||
const requestId = invalidateAreaRequests();
|
||||
invalidatePropertyRequests();
|
||||
const selection = {
|
||||
id: postcode,
|
||||
type: 'postcode' as const,
|
||||
resolution,
|
||||
lockedResolution: true,
|
||||
};
|
||||
trackEvent(openProperties ? 'Address Search' : 'Postcode Search');
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(geometry);
|
||||
setProperties([]);
|
||||
setPropertiesTotal(0);
|
||||
setPropertiesOffset(0);
|
||||
setAreaStats(null);
|
||||
setUnfilteredAreaCount(null);
|
||||
setRightPaneTab(openProperties ? 'properties' : 'area');
|
||||
setLoadingAreaStats(true);
|
||||
|
||||
// First try the postcode; if it only has no matches because of active filters,
|
||||
// keep the searched postcode selected instead of widening to nearby hexagons.
|
||||
fetchPostcodeStats(postcode, undefined, areaStatsUseFilters)
|
||||
.then(async (stats) => {
|
||||
.then((stats) => {
|
||||
if (!isCurrentAreaRequest(requestId)) return;
|
||||
if (stats.count > 0) {
|
||||
const selection = { id: postcode, type: 'postcode' as const, resolution };
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(geometry);
|
||||
setAreaStats(stats);
|
||||
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
|
||||
if (openProperties) {
|
||||
fetchPostcodeProperties(postcode, 0, focusAddress);
|
||||
}
|
||||
return;
|
||||
setAreaStats(stats);
|
||||
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
|
||||
if (openProperties && stats.count > 0) {
|
||||
fetchPostcodeProperties(postcode, 0, focusAddress);
|
||||
}
|
||||
|
||||
if (areaStatsUseFilters && hasStatsFilters) {
|
||||
const unfilteredStats = await fetchPostcodeStats(postcode, undefined, false);
|
||||
if (!isCurrentAreaRequest(requestId)) return;
|
||||
if (unfilteredStats.count > 0) {
|
||||
const selection = { id: postcode, type: 'postcode' as const, resolution };
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(geometry);
|
||||
setAreaStats(stats);
|
||||
setUnfilteredAreaCount(unfilteredStats.count);
|
||||
setRightPaneTab(openProperties ? 'properties' : 'area');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No properties in this postcode — fall back to hexagons
|
||||
if (lat == null || lng == null) {
|
||||
// No coordinates available, show empty postcode anyway
|
||||
const selection = { id: postcode, type: 'postcode' as const, resolution };
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(geometry);
|
||||
setAreaStats(stats);
|
||||
refreshUnfilteredAreaCount(selection, stats.count, areaStatsUseFilters);
|
||||
setRightPaneTab('area');
|
||||
return;
|
||||
}
|
||||
|
||||
// Try progressively coarser H3 resolutions until we find >1 property
|
||||
const resolutions = [9, 8, 7, 6, 5];
|
||||
for (const res of resolutions) {
|
||||
const h3 = latLngToCell(lat, lng, res);
|
||||
const hexStats = await fetchHexagonStats(
|
||||
h3,
|
||||
res,
|
||||
undefined,
|
||||
undefined,
|
||||
areaStatsUseFilters
|
||||
);
|
||||
if (!isCurrentAreaRequest(requestId)) return;
|
||||
if (hexStats.count > 1) {
|
||||
const selection = { id: h3, type: 'hexagon' as const, resolution: res };
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(null);
|
||||
setAreaStats(hexStats);
|
||||
refreshUnfilteredAreaCount(selection, hexStats.count, areaStatsUseFilters);
|
||||
setRightPaneTab('area');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Even the coarsest hexagon has ≤1 property — show whatever the finest has
|
||||
const h3 = latLngToCell(lat, lng, 9);
|
||||
const fallbackStats = await fetchHexagonStats(
|
||||
h3,
|
||||
9,
|
||||
undefined,
|
||||
undefined,
|
||||
areaStatsUseFilters
|
||||
);
|
||||
if (!isCurrentAreaRequest(requestId)) return;
|
||||
const selection = { id: h3, type: 'hexagon' as const, resolution: 9 };
|
||||
setSelectedHexagon(selection);
|
||||
setSelectedPostcodeGeometry(null);
|
||||
setAreaStats(fallbackStats);
|
||||
refreshUnfilteredAreaCount(selection, fallbackStats.count, areaStatsUseFilters);
|
||||
setRightPaneTab('area');
|
||||
})
|
||||
.catch((error) => logNonAbortError('Failed to fetch postcode stats', error))
|
||||
.finally(() => {
|
||||
|
|
@ -734,9 +669,7 @@ export function useHexagonSelection({
|
|||
[
|
||||
resolution,
|
||||
areaStatsUseFilters,
|
||||
hasStatsFilters,
|
||||
fetchPostcodeStats,
|
||||
fetchHexagonStats,
|
||||
fetchPostcodeProperties,
|
||||
invalidateAreaRequests,
|
||||
invalidatePropertyRequests,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue