Move into folders

This commit is contained in:
Andras Schmelczer 2026-02-07 13:34:50 +00:00
parent ee73ab77fd
commit 5cbb180c57
24 changed files with 181 additions and 185 deletions

View file

@ -1,16 +1,16 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { trackPageview } from './usePlausible';
import Map from './components/Map';
import type { SearchedPostcode } from './components/PostcodeSearch';
import Filters from './components/Filters';
import POIPane from './components/POIPane';
import { PropertiesPane } from './components/PropertiesPane';
import AreaPane from './components/AreaPane';
import DataSources from './components/DataSources';
import DataSourcesPage from './components/DataSourcesPage';
import FAQPage from './components/FAQPage';
import HomePage from './components/HomePage';
import Header, { type Page } from './components/Header';
import Map from './components/map/Map';
import type { SearchedPostcode } from './components/map/PostcodeSearch';
import Filters from './components/map/Filters';
import POIPane from './components/map/POIPane';
import { PropertiesPane } from './components/map/PropertiesPane';
import AreaPane from './components/map/AreaPane';
import DataSources from './components/data-sources/DataSources';
import DataSourcesPage from './components/data-sources/DataSourcesPage';
import FAQPage from './components/faq/FAQPage';
import HomePage from './components/home/HomePage';
import Header, { type Page } from './components/shared/Header';
import { TabButton } from './components/ui/TabButton';
import type {
FeatureMeta,
@ -18,7 +18,7 @@ import type {
FeatureFilters,
Bounds,
HexagonData,
PostcodeData,
PostcodeFeature,
ViewChangeParams,
ApiResponse,
POI,
@ -58,7 +58,7 @@ export default function App() {
const [dragValue, setDragValue] = useState<[number, number] | null>(null);
const [pinnedFeature, setPinnedFeature] = useState<string | null>(null);
const [rawData, setRawData] = useState<HexagonData[]>([]);
const [postcodeData, setPostcodeData] = useState<PostcodeData[]>([]);
const [postcodeData, setPostcodeData] = useState<PostcodeFeature[]>([]);
const [dragData, setDragData] = useState<HexagonData[] | null>(null);
const [resolution, setResolution] = useState<number>(8);
const [bounds, setBounds] = useState<Bounds | null>(null);
@ -251,7 +251,7 @@ export default function App() {
const res = await fetch(apiUrl('postcodes', params), {
signal: abortControllerRef.current.signal,
});
const json: { features: PostcodeData[] } = await res.json();
const json: { features: PostcodeFeature[] } = await res.json();
setPostcodeData(json.features || []);
setRawData([]); // Clear hexagon data
} else {
@ -300,20 +300,30 @@ export default function App() {
// If dragData hasn't loaded yet, return null to trigger fallback
if (activeFeature && !dragData) return null;
// Choose the appropriate data source based on zoom level
const sourceData = usePostcodeView ? postcodeData : data;
if (sourceData.length === 0) return null;
// Only use min_<feature> values since that's what hexagon coloring uses
let min = Infinity;
let max = -Infinity;
for (const item of sourceData) {
const val = item[`min_${viewFeature}`];
if (typeof val === 'number' && !isNaN(val)) {
min = Math.min(min, val);
max = Math.max(max, val);
if (usePostcodeView) {
if (postcodeData.length === 0) return null;
for (const feat of postcodeData) {
const val = feat.properties[`min_${viewFeature}`];
if (typeof val === 'number' && !isNaN(val)) {
min = Math.min(min, val);
max = Math.max(max, val);
}
}
} else {
if (data.length === 0) return null;
for (const item of data) {
const val = item[`min_${viewFeature}`];
if (typeof val === 'number' && !isNaN(val)) {
min = Math.min(min, val);
max = Math.max(max, val);
}
}
}
if (min === Infinity || max === -Infinity) return null;
return [min, max];
}, [viewFeature, data, dragData, postcodeData, usePostcodeView, features, activeFeature]);
@ -505,28 +515,29 @@ export default function App() {
[filters, features]
);
/** Build stats from already-loaded PostcodeData (min/max per feature). */
/** Build stats from already-loaded PostcodeFeature (min/max per feature). */
const buildPostcodeStats = useCallback(
(postcode: string): HexagonStatsResponse | null => {
const pc = postcodeData.find((d) => d.postcode === postcode);
if (!pc) return null;
const feat = postcodeData.find((f) => f.properties.postcode === postcode);
if (!feat) return null;
const props = feat.properties;
const numeric_features: NumericFeatureStats[] = [];
for (const f of features) {
if (f.type !== 'numeric') continue;
const minVal = pc[`min_${f.name}`];
const maxVal = pc[`max_${f.name}`];
const minVal = props[`min_${f.name}`];
const maxVal = props[`max_${f.name}`];
if (typeof minVal !== 'number' || typeof maxVal !== 'number') continue;
numeric_features.push({
name: f.name,
count: pc.count,
count: props.count,
min: minVal,
max: maxVal,
mean: (minVal + maxVal) / 2,
});
}
return { count: pc.count, numeric_features, enum_features: [] };
return { count: props.count, numeric_features, enum_features: [] };
},
[postcodeData, features]
);
@ -815,19 +826,16 @@ export default function App() {
<div className="flex border-b border-warm-200 dark:border-navy-700 text-sm">
<TabButton
label="Area"
count={areaStats?.count}
isActive={rightPaneTab === 'area'}
onClick={() => setRightPaneTab('area')}
/>
<TabButton
label="Properties"
count={propertiesTotal > 0 ? propertiesTotal : undefined}
isActive={rightPaneTab === 'properties'}
onClick={handlePropertiesTabClick}
/>
<TabButton
label="POIs"
count={pois.length > 0 ? pois.length : undefined}
isActive={rightPaneTab === 'pois'}
onClick={() => setRightPaneTab('pois')}
/>
@ -843,7 +851,7 @@ export default function App() {
isPostcode={selectedHexagon?.type === 'postcode'}
postcodeData={
selectedHexagon?.type === 'postcode'
? postcodeData.find((d) => d.postcode === selectedHexagon.id) || null
? postcodeData.find((f) => f.properties.postcode === selectedHexagon.id) || null
: null
}
onViewProperties={handleViewPropertiesFromArea}