// ----------------------------------------------------------------------------- // Rent-Ka — Rental listings aggregator (Canada, outside Québec) // Author: Simon-Pierre Boucher — contact@spboucher.ai // components/ImmeubleBloc.tsx: "Building passport" block (detail page) // Everything is computed from what Rent-Ka actually observes (active AND // historical listings of the same building): units, median rents, turnover, // rent pressure. An indicator without a sufficient sample is shown as // "insufficient data" — never invented. // ----------------------------------------------------------------------------- import { Immeuble, fmtPrice } from "../api"; const NBSP = " "; const fmtTs = (ts: number | null | undefined): string => ts ? new Date(ts * 1000).toLocaleDateString("en-CA", { month: "long", year: "numeric" }) : "—"; const ROTATION_CLS: Record = { // backend classes (bilingual for migration safety) "low": "zi-ok", "normal": "zi-nc", "high": "zi-modere", "very high": "zi-eleve", "faible": "zi-ok", "normale": "zi-nc", "élevée": "zi-modere", "très élevée": "zi-eleve", }; export default function ImmeubleBloc({ im }: { im: Immeuble }) { // at least 2 grouped listings, otherwise the "passport" adds nothing if (!im || im.annonces_total < 2) return null; const rot = im.rotation; const pres = im.pression_loyers; const parCc = im.loyer_median_par_cc; return (

Building passport

Listings observed
{im.annonces_total} incl. {im.annonces_actives} active
Estimated units
≥{NBSP}{im.unites_estimees}
{im.loyer_median != null && (
Median rent (active)
{fmtPrice(im.loyer_median)}
)} {im.pi2_median != null && (
Median $/sq ft
${im.pi2_median.toFixed(2)}
)}
{parCc && Object.keys(parCc).length > 0 && (

By bedroom count:{" "} {Object.entries(parCc) .map(([cc, v]) => `${cc}${NBSP}bd ${fmtPrice(v)}`) .join(" · ")}

)}
Unit turnover{" "} {rot.statut === "calculated" ? ( <> {rot.classe} {" "} {rot.annonces_12m} listing{(rot.annonces_12m ?? 0) > 1 ? "s" : ""} over 12 months for ≥{NBSP}{rot.unites_estimees} units ) : ( insufficient data )}
Rent pressure{" "} {pres ? ( <> 0.05 ? "zi-modere" : pres.variation_12m < -0.02 ? "zi-ok" : "zi-nc"}`}> {pres.variation_12m > 0 ? "+" : "−"} {Math.abs(Math.round(pres.variation_12m * 100))}{NBSP}% over 12 months {" "} median asking price: {fmtPrice(pres.mediane_12_24m)} →{" "} {fmtPrice(pres.mediane_12m)} ({pres.n_12_24m} vs {pres.n_12m} listings) ) : ( insufficient sample )}

Building tracked by Rent-Ka since {fmtTs(im.premiere_observation)} {rot.statut === "calculated" && rot.methode ? ` — ${rot.methode}` : ( " — indicators computed only from listings observed by Rent-Ka " + "syncs (not a census of the building)." )}

); }