diff --git a/web/src/App.tsx b/web/src/App.tsx index dfda205..71d86bd 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -207,6 +207,8 @@ export default function App() { )} {!deals.isFetching && !deals.error && !deals.data && ( diff --git a/web/src/components/DealsTable.tsx b/web/src/components/DealsTable.tsx index b2f42de..9dd5cc4 100644 --- a/web/src/components/DealsTable.tsx +++ b/web/src/components/DealsTable.tsx @@ -4,10 +4,13 @@ import { formatCurrencyILS, formatDate, formatNumber } from "../lib/utils"; interface Props { deals: Deal[]; resolvedAddress?: string; + searchedBlock?: string; + searchedPlot?: string; } // Extract street name from a Govmap address string like "הילדסהיימר 14, ירושלים". -// Takes all leading tokens that contain no digits. +// Takes all leading tokens that contain no digits. Used as fallback only — +// for parcel searches Govmap echoes back "גוש N חלקה M" with no street info. function streetFromAddress(address: string | undefined): string | null { if (!address) return null; const tokens = address.split(/[\s,]+/); @@ -19,6 +22,31 @@ function streetFromAddress(address: string | undefined): string | null { return parts.length ? parts.join(" ") : null; } +// Find the street name of the searched parcel by picking the deal with the +// closest parcelNum within the same gush. Cadastral parcel numbers are usually +// assigned along street frontage, so the nearest plot is almost always the +// same street. +function streetByClosestParcel( + deals: Deal[], + block: string | undefined, + plot: string | undefined +): string | null { + if (!plot) return null; + const target = parseInt(plot, 10); + if (!Number.isFinite(target)) return null; + const blockNum = block ? parseInt(block, 10) : null; + let best: { dist: number; street: string } | null = null; + for (const d of deals) { + if (!d.streetNameHeb || d.parcelNum == null) continue; + if (blockNum !== null && d.gushNum !== blockNum) continue; + const dist = Math.abs((d.parcelNum as number) - target); + if (!best || dist < best.dist) { + best = { dist, street: d.streetNameHeb }; + } + } + return best?.street ?? null; +} + function proximityLabel(deal: Deal, inferredStreet: string | null): string { if (!deal.deal_source) return "—"; if (deal.deal_source === "same_building") return "אותו בניין"; @@ -36,7 +64,7 @@ function proximityClass(deal: Deal, inferredStreet: string | null): string { return "bg-slate-100 text-slate-700"; } -export function DealsTable({ deals, resolvedAddress }: Props) { +export function DealsTable({ deals, resolvedAddress, searchedBlock, searchedPlot }: Props) { if (!deals.length) { return (
@@ -45,7 +73,11 @@ export function DealsTable({ deals, resolvedAddress }: Props) { ); } - const inferredStreet = streetFromAddress(resolvedAddress); + // Prefer closest-parcel inference (works for both address and block_plot + // searches); fall back to parsing the resolved autocomplete text. + const inferredStreet = + streetByClosestParcel(deals, searchedBlock, searchedPlot) ?? + streetFromAddress(resolvedAddress); return (