// Auteur : Simon-Pierre Boucher — contact@spboucher.ai "use client"; import { useState } from "react"; import Link from "next/link"; import { useLang } from "./LangContext"; import LeadForm from "./LeadForm"; import RadarMap from "./RadarMap"; import { BuildingGlyph, ConfidenceDial, CountUp, EraLine, locNum, LotDiagram, ValueSplit, } from "./MetricViz"; import type { EstimateResult } from "@/lib/engine"; import type { UnitEstimate } from "@/lib/estimator"; import type { TKey } from "@/lib/i18n"; const fmt = (v: number | null | undefined, lang: string) => v == null ? "—" : new Intl.NumberFormat(lang === "fr" ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0, }).format(v); const signed = (v: number, lang: string) => `${v >= 0 ? "+" : "−"}${fmt(Math.abs(Math.round(v)), lang)}`; function Chip({ k, v }: { k: string; v: string | number }) { return (

{k}

{v}

); } export default function ResultView({ data }: { data: UnitEstimate }) { const { lang, t } = useLang(); const [activeComp, setActiveComp] = useState(null); const r: EstimateResult = data.result; const u = data.unit; const s = u?.specs; const fr = lang === "fr"; const histMax = Math.max(...(u?.history.map((h) => h.value ?? 0) ?? [0]), 1); const moneyFmt = (v: number) => new Intl.NumberFormat(fr ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0, }).format(Math.round(v / 100) * 100); return (
{/* fil d'ariane */} {/* ---- Panneau prix ---- */}
{t("estimatedValue")} {u && (

{u.adresse} {s?.apt ? ` app. ${s.apt}` : ""} {u.municipalite ? · {u.municipalite} : null}

)}

{t("range")}

{fmt(r.low, lang)} {fmt(r.high, lang)}

{u?.valeurRole != null && (

{t("assessed")}

{fmt(u.valeurRole, lang)}

)} {u?.valeurRole != null && u.valeurRole > 0 && r.estimate > 0 && (

{fr ? "Écart vs rôle" : "vs assessment"}

{r.estimate >= u.valeurRole ? "+" : ""} {((r.estimate / u.valeurRole - 1) * 100).toFixed(0)} %

)}
{u && ( )}

{t("disclaimer")}

{/* ---- Portrait de la propriété (bento du registre) ---- */} {u && s && (
{fr ? "Registre officiel — dessiné par les données" : "Official record — drawn by the data"}

{fr ? "Portrait de la propriété" : "Property portrait"}

{/* Bâtiment */}

{fr ? "Bâtiment" : "Building"}

{/* Terrain */}

{fr ? "Terrain — à l'échelle" : "Lot — to scale"}

= 1 && !u.superficieTerrainM2)} />
{/* Valeur au rôle */}

{fr ? "Valeur au rôle 2026 — composition" : "2026 assessed value — split"}

{fmt(u.valeurRole, lang)}

{/* registre brut complet */}
{fr ? "▸ Toutes les données du registre (brutes)" : "▸ All registry data (raw)"}
)} {/* ---- Transparence du calcul ---- */}
{fr ? "Transparence" : "Transparency"}

{t("whyTitle")}

{t("modelPart")}

{r.modelEstimate != null ? ( ) : ( "—" )}

{t("weightModel")} : {(r.modelWeight * 100).toFixed(0)} %

{t("compsPart")}

{r.compsEstimate != null ? ( ) : ( "—" )}

{t("weightComps")} : {((1 - r.modelWeight) * 100).toFixed(0)} % · {r.nCompsUsed} comps {r.compsDispersionPct != null ? ` · ±${r.compsDispersionPct} %` : ""}

{/* ---- Historique ---- */} {u && u.history.some((h) => h.value != null) && (
2021 → 2026

{t("history")}

{u.history.map((h, i) => { const prev = i > 0 ? u.history[i - 1].value : null; const yoy = h.value != null && prev != null && prev > 0 ? ((h.value / prev - 1) * 100) : null; return (
{h.year} {fmt(h.value, lang)} {yoy != null && ( = 0 ? "bg-lime-soft text-green-deep" : "bg-[var(--danger-soft)] text-[var(--danger)]"}`}> {yoy >= 0 ? "+" : ""} {yoy.toFixed(0)} % )}
); })}
)} {/* ---- Plan des comparables (carte maison) ---- */}
{r.nCompsUsed} {fr ? "ventes réelles" : "real sales"}

{t("compsTitle")}

({ id: c.id, lat: c.lat, lng: c.lng, label: `${c.street ?? ""}, ${c.city ?? ""}`, price: fmt(c.amount, lang), adjusted: fmt(Math.round(c.adjustedPrice), lang), date: c.date, distanceM: c.distanceM, weight: c.weight, }))} highlight={activeComp} onHover={setActiveComp} />
{r.comps.map((c, i) => ( setActiveComp(c.id)} onMouseLeave={() => setActiveComp(null)} className={activeComp === c.id ? "!bg-lime-soft" : ""} > ))}
# {fr ? "Vente" : "Sale"} {t("adjusted")}
{i + 1}

{c.street}, {c.city}

{c.date} · {c.distanceM < 1000 ? `${Math.round(c.distanceM)} m` : `${(c.distanceM / 1000).toFixed(1)} km`} ·{" "} {t("sold")} {fmt(c.amount, lang)}

{t("adjTime")} {signed(c.adjTime, lang)} · {t("adjArea")} {signed(c.adjArea, lang)} ·{" "} {t("adjAge")} {signed(c.adjAge, lang)}

{fmt(Math.round(c.adjustedPrice), lang)}

{t("weight")} {(c.weight * 100).toFixed(0)} %

{/* ---- Lead ---- */}

{t("leadTitle")}

); }