Improve UI

This commit is contained in:
Andras Schmelczer 2026-02-05 21:19:19 +00:00
parent 5fe192d25a
commit ae29662c92
14 changed files with 221 additions and 313 deletions

View file

@ -3,8 +3,10 @@ import type { FeatureFilters, FeatureMeta, HexagonStatsResponse, PostcodeData }
import type { HexagonLocation } from '../lib/external-search';
import { formatValue, calculateHistogramMean } from '../lib/format';
import { groupFeaturesByCategory } from '../lib/features';
import { CRIME_BREAKDOWNS } from '../lib/consts';
import { DualHistogram, LoadingSkeleton } from './DualHistogram';
import EnumBarChart from './EnumBarChart';
import StackedBarChart from './StackedBarChart';
import ExternalSearchLinks from './ExternalSearchLinks';
import { InfoIcon, CloseIcon } from './ui/Icons';
import { IconButton } from './ui/IconButton';
@ -108,17 +110,67 @@ export default function AreaPane({
);
if (!hasData) return null;
// For Crime group, only show aggregate features with stacked breakdown
const isCrimeGroup = group.name === 'Crime';
const featuresToRender = isCrimeGroup
? group.features.filter((f) => f.name in CRIME_BREAKDOWNS)
: group.features;
return (
<div key={group.name}>
<h3 className="text-xs font-semibold text-warm-500 dark:text-warm-400 uppercase tracking-wider mb-2">
{group.name}
</h3>
<div className="space-y-3">
{group.features.map((feature) => {
{featuresToRender.map((feature) => {
const numericStats = numericByName.get(feature.name);
const enumStats = enumByName.get(feature.name);
if (numericStats) {
// Check if this is a crime aggregate that should show breakdown
const breakdown = CRIME_BREAKDOWNS[feature.name];
if (breakdown) {
// Build segments from component crime means
const segments = breakdown
.map((componentName) => {
const componentStats = numericByName.get(componentName);
return {
name: componentName,
value: componentStats?.mean ?? 0,
};
})
.filter((s) => s.value > 0);
return (
<div
key={feature.name}
className="bg-warm-50 dark:bg-warm-800 rounded p-2"
>
<div className="flex justify-between items-baseline mb-1.5">
<div className="flex items-center gap-1 min-w-0 mr-2">
<span className="text-xs text-warm-700 dark:text-warm-300 truncate">
{feature.name}
</span>
{feature.detail && (
<button
onClick={() => setInfoFeature(feature)}
className="text-warm-400 hover:text-warm-700 dark:hover:text-warm-300 shrink-0"
title="Feature info"
>
<InfoIcon className="w-3 h-3" />
</button>
)}
</div>
<span className="text-xs font-semibold text-teal-700 dark:text-teal-400 whitespace-nowrap">
{formatValue(numericStats.mean)} avg/yr
</span>
</div>
<StackedBarChart segments={segments} total={numericStats.mean} />
</div>
);
}
// Regular numeric feature with histogram
const globalFeature = globalFeatureByName.get(feature.name);
const globalHistogram = globalFeature?.histogram;
const globalMean = globalHistogram