These work

This commit is contained in:
Andras Schmelczer 2026-02-11 21:32:33 +00:00
parent 3599803589
commit 1588c01b19
19 changed files with 260 additions and 201 deletions

View file

@ -6,7 +6,8 @@ import { CollapsibleGroupHeader } from '../ui/CollapsibleGroupHeader';
import { PillToggle } from '../ui/PillToggle';
import { PillGroup } from '../ui/PillGroup';
import type { FeatureMeta, FeatureFilters } from '../../types';
import { formatFilterValue } from '../../lib/format';
import { formatFilterValue, buildPercentileScale } from '../../lib/format';
import type { PercentileScale } from '../../lib/format';
import { groupFeaturesByCategory } from '../../lib/features';
import { useCollapsibleGroups } from '../../hooks/useCollapsibleGroups';
import InfoPopup from '../ui/InfoPopup';
@ -21,27 +22,30 @@ function SliderLabels({
min,
max,
value,
displayValues,
}: {
min: number;
max: number;
value: [number, number];
displayValues?: [number, number];
}) {
const range = max - min || 1;
const leftPct = ((value[0] - min) / range) * 100;
const rightPct = ((value[1] - min) / range) * 100;
const labels = displayValues || value;
return (
<div className="relative h-4 mt-2 mx-2.5 text-[10px] text-warm-500 dark:text-warm-400 leading-tight">
<span
className="absolute -translate-x-1/2"
style={{ left: `${leftPct}%` }}
>
{formatFilterValue(value[0])}
{formatFilterValue(labels[0])}
</span>
<span
className="absolute -translate-x-1/2"
style={{ left: `${rightPct}%` }}
>
{formatFilterValue(value[1])}
{formatFilterValue(labels[1])}
</span>
</div>
);
@ -119,6 +123,16 @@ export default memo(function Filters({
[enabledFeatureList]
);
const percentileScales = useMemo(() => {
const scales = new Map<string, PercentileScale>();
for (const f of features) {
if (f.type === 'numeric' && f.histogram) {
scales.set(f.name, buildPercentileScale(f.histogram));
}
}
return scales;
}, [features]);
return (
<div className="flex flex-col bg-white dark:bg-navy-950 overflow-y-auto md:overflow-hidden h-full">
<div className="shrink-0 flex items-center gap-2 px-3 py-2 border-b border-warm-200 dark:border-navy-700">
@ -230,7 +244,10 @@ export default memo(function Filters({
isActive && dragValue
? dragValue
: (filters[feature.name] as [number, number]) || [feature.min!, feature.max!];
const step = feature.step ?? (feature.max! - feature.min!) / 100;
const scale = percentileScales.get(feature.name);
const sliderValue: [number, number] = scale
? [Math.round(scale.toPercentile(displayValue[0])), Math.round(scale.toPercentile(displayValue[1]))]
: displayValue;
return (
<div
@ -248,15 +265,24 @@ export default memo(function Filters({
</div>
<div>
<Slider
min={feature.min!}
max={feature.max!}
step={step}
value={[displayValue[0], displayValue[1]]}
onValueChange={([min, max]) => onDragChange([min, max])}
min={scale ? 0 : feature.min!}
max={scale ? 100 : feature.max!}
step={scale ? 1 : (feature.step ?? (feature.max! - feature.min!) / 100)}
value={sliderValue}
onValueChange={
scale
? ([pMin, pMax]) => onDragChange([scale.toValue(pMin), scale.toValue(pMax)])
: ([min, max]) => onDragChange([min, max])
}
onPointerDown={() => onDragStart(feature.name)}
onPointerUp={() => onDragEnd()}
/>
<SliderLabels min={feature.min!} max={feature.max!} value={displayValue} />
<SliderLabels
min={scale ? 0 : feature.min!}
max={scale ? 100 : feature.max!}
value={sliderValue}
displayValues={scale ? displayValue : undefined}
/>
</div>
</div>
);

View file

@ -142,18 +142,14 @@ function PropertyLoadingSkeleton() {
}
function PropertyCard({ property }: { property: Property }) {
const price = getNum(property, 'Last known price', 'latest_price');
const price = getNum(property, 'Last known price');
const estimatedPrice = getNum(property, 'Estimated current price');
const pricePerSqm = getNum(property, 'Price per sqm', 'price_per_sqm');
const pricePerSqm = getNum(property, 'Price per sqm');
const estPricePerSqm = getNum(property, 'Est. price per sqm');
const floorArea = getNum(property, 'Total floor area (sqm)', 'total_floor_area');
const rooms = getNum(
property,
'Rooms (including bedrooms & bathrooms)',
'number_habitable_rooms'
);
const age = getNum(property, 'Approximate construction age', 'construction_age_band');
const transactionDate = getNum(property, 'Date of last transaction', 'date_of_transfer');
const floorArea = getNum(property, 'Total floor area (sqm)');
const rooms = getNum(property, 'Rooms (including bedrooms & bathrooms)');
const age = getNum(property, 'Approximate construction age');
const transactionDate = getNum(property, 'Date of last transaction');
const councilTax = getNum(property, 'Council tax (£/yr)');
const councilTaxD = getNum(property, 'Council tax Band D (£/yr)');