diff --git a/frontend/src/components/site-finder/VelocityBlock.tsx b/frontend/src/components/site-finder/VelocityBlock.tsx index d38f628b..ea4ff0c6 100644 --- a/frontend/src/components/site-finder/VelocityBlock.tsx +++ b/frontend/src/components/site-finder/VelocityBlock.tsx @@ -3,6 +3,17 @@ import type { Velocity } from "@/types/site-finder"; import { SectionLabel } from "@/components/ui/SectionLabel"; import { EmptyState } from "@/components/ui/EmptyState"; +const BUCKET_ORDER = ["студия", "1", "2", "3", "4+"] as const; +type KnownBucket = (typeof BUCKET_ORDER)[number]; + +const BUCKET_LABEL: Record = { + студия: "Студия", + "1": "1-к", + "2": "2-к", + "3": "3-к", + "4+": "4+", +}; + interface VelocityBlockProps { velocity: Velocity | null | undefined; } @@ -121,6 +132,102 @@ export function VelocityBlock({ velocity }: VelocityBlockProps) { ({velocity.months_observed} мес) + {/* By room bucket aggregate */} + {velocity.by_room_bucket && ( +
+ По комнатности + + + + + + + + + + + {BUCKET_ORDER.map((b) => { + const stat = velocity.by_room_bucket?.[b]; + if (!stat) return null; + return ( + + + + + + + ); + })} + +
+ Комната + + Сделок + + м² + + ЖК +
{BUCKET_LABEL[b]} + {stat.units.toLocaleString("ru")} + + {Math.round(stat.sqm).toLocaleString("ru")} + + {stat.complexes_count} +
+
+ )} + {/* Top competitors */} {velocity.sample_competitors.length > 0 && (
@@ -188,6 +295,19 @@ export function VelocityBlock({ velocity }: VelocityBlockProps) { {c.obj_class} )} + {c.by_room_bucket && ( +
+ {BUCKET_ORDER.filter( + (b) => (c.by_room_bucket?.[b] ?? 0) > 0, + ) + .map( + (b) => `${BUCKET_LABEL[b]} ${c.by_room_bucket![b]}`, + ) + .join(" · ")} +
+ )} ; } export interface VelocityPeriod { @@ -229,6 +236,7 @@ export interface Velocity { months_observed: number; period: VelocityPeriod; sample_competitors: VelocityCompetitor[]; + by_room_bucket?: Record; } // G5 (#32) — Gate verdict: can_build_mkd