UI improvements
This commit is contained in:
parent
83dd2ca87e
commit
1569d116a9
14 changed files with 222 additions and 92 deletions
|
|
@ -414,27 +414,63 @@ export function useDeckLayers({
|
|||
data: postcodeData as PostcodeFeature[],
|
||||
getFillColor: (f) => {
|
||||
const d = f.properties;
|
||||
const dark = isDarkRef.current;
|
||||
const entries = travelTimeEntriesRef.current;
|
||||
|
||||
// Dim-filter: all travel entries with timeRange dim postcodes outside range
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
const entry = entries[i];
|
||||
if (!entry.timeRange || !entry.slug) continue;
|
||||
const fk = travelFieldKey(entry);
|
||||
const modeVal = d[`avg_${fk}`];
|
||||
if (modeVal == null || (modeVal as number) < entry.timeRange[0] || (modeVal as number) > entry.timeRange[1]) {
|
||||
return (dark ? [60, 55, 50, 60] : [180, 180, 180, 60]) as [number, number, number, number];
|
||||
}
|
||||
}
|
||||
|
||||
const vf = viewFeatureRef.current;
|
||||
const clr = colorRangeRef.current;
|
||||
const fr = filterRangeRef.current;
|
||||
const cfm = colorFeatureMetaRef.current;
|
||||
const dark = isDarkRef.current;
|
||||
if (vf && clr && cfm) {
|
||||
const val = d[`avg_${vf}`] ?? d[`min_${vf}`];
|
||||
const minVal = d[`min_${vf}`] as number | undefined;
|
||||
const maxVal = d[`max_${vf}`] as number | undefined;
|
||||
return getFeatureFillColor(
|
||||
val as number | null | undefined,
|
||||
minVal,
|
||||
maxVal,
|
||||
clr,
|
||||
fr,
|
||||
0,
|
||||
densityGradientRef.current,
|
||||
dark,
|
||||
180,
|
||||
enumCountRef.current
|
||||
);
|
||||
|
||||
if (vf && clr) {
|
||||
// Travel time feature: dim postcodes with no data
|
||||
if (vf.startsWith('tt_')) {
|
||||
const ttVal = d[`avg_${vf}`];
|
||||
if (ttVal == null) {
|
||||
return (dark ? [80, 70, 65, 80] : [128, 128, 128, 80]) as [number, number, number, number];
|
||||
}
|
||||
return getFeatureFillColor(
|
||||
ttVal as number,
|
||||
ttVal as number,
|
||||
ttVal as number,
|
||||
clr,
|
||||
null,
|
||||
0,
|
||||
densityGradientRef.current,
|
||||
dark,
|
||||
180
|
||||
);
|
||||
}
|
||||
|
||||
// Regular feature
|
||||
if (cfm) {
|
||||
const val = d[`avg_${vf}`] ?? d[`min_${vf}`];
|
||||
const minVal = d[`min_${vf}`] as number | undefined;
|
||||
const maxVal = d[`max_${vf}`] as number | undefined;
|
||||
return getFeatureFillColor(
|
||||
val as number | null | undefined,
|
||||
minVal,
|
||||
maxVal,
|
||||
clr,
|
||||
fr,
|
||||
0,
|
||||
densityGradientRef.current,
|
||||
dark,
|
||||
180,
|
||||
enumCountRef.current
|
||||
);
|
||||
}
|
||||
}
|
||||
const cr = postcodeCountRangeRef.current;
|
||||
const c = d.count;
|
||||
|
|
|
|||
34
frontend/src/hooks/useTravelModes.ts
Normal file
34
frontend/src/hooks/useTravelModes.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { logNonAbortError } from '../lib/api';
|
||||
import type { TransportMode } from './useTravelTime';
|
||||
|
||||
interface TravelModeInfo {
|
||||
mode: TransportMode;
|
||||
destinations: number;
|
||||
}
|
||||
|
||||
/** Fetches which transport modes have precomputed travel time data. */
|
||||
export function useTravelModes() {
|
||||
const [availableModes, setAvailableModes] = useState<Set<TransportMode> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
|
||||
fetch('/api/travel-modes', { signal: controller.signal })
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then((data: { modes: TravelModeInfo[] }) => {
|
||||
const modes = new Set<TransportMode>(
|
||||
data.modes.filter((m) => m.destinations > 0).map((m) => m.mode),
|
||||
);
|
||||
setAvailableModes(modes);
|
||||
})
|
||||
.catch((err) => logNonAbortError('travel modes', err));
|
||||
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
return availableModes;
|
||||
}
|
||||
|
|
@ -13,6 +13,14 @@ const STEPS: Step[] = [
|
|||
placement: 'right',
|
||||
disableBeacon: true,
|
||||
},
|
||||
{
|
||||
target: '[data-tutorial="ai-filters"]',
|
||||
title: 'AI-Powered Filters',
|
||||
content:
|
||||
'Describe your ideal area in plain English — like "quiet neighbourhood with good schools" — and AI will set up the right filters for you automatically.',
|
||||
placement: 'right',
|
||||
disableBeacon: true,
|
||||
},
|
||||
{
|
||||
target: '[data-tutorial="map"]',
|
||||
title: 'Explore the Map',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue