// ----------------------------------------------------------------------------- // Lou-Ka — Agrégateur de logements à louer (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // fiche/PropertyQuickFacts.tsx : « Le logement » — grille compacte icône + // valeur + libellé (chambres, salle de bain, type, superficie, étage, // meublé, animaux, disponibilité, bail…) puis rangées pratiques // (gestionnaire, prix affiché, en ligne depuis, synchronisé, historique). // ----------------------------------------------------------------------------- import { ReactNode } from "react"; import { Listing, fmtAvailability, fmtPrice, sourceName } from "../api"; import { IcoBath, IcoBed, IcoBuilding, IcoCalendar, IcoDoc, IcoHouse, IcoPaw, IcoRuler, IcoSofa, IcoUsers, } from "../components/Icons"; import { SectionCard, NBSP, relTime } from "./ui"; const PETS: Record = { oui: "Acceptés", non: "Refusés", conditions: "Sous conditions" }; export default function PropertyQuickFacts({ l }: { l: Listing }) { const f = l.digest?.faits; const conf = l.digest?.confiance ?? {}; const d = l.details ?? {}; const dispo = fmtAvailability(l.availability_date); const facts: { ico: ReactNode; v: string; l: string }[] = []; if (l.bedrooms != null) facts.push({ ico: , v: l.bedrooms === 0 ? "Studio" : `${Math.round(l.bedrooms)}`, l: l.bedrooms === 0 ? "Aire ouverte" : `Chambre${l.bedrooms > 1 ? "s" : ""}` }); const sdb = (l as unknown as { bathrooms?: number | null }).bathrooms; if (sdb != null) facts.push({ ico: , v: `${sdb}`, l: `Salle${sdb > 1 ? "s" : ""} de bain` }); else if (f?.salle_de_bain && conf.salle_de_bain !== "faible") facts.push({ ico: , v: f.salle_de_bain === "commune" ? "Partagée" : "Privée", l: "Salle de bain" }); if (l.unit_type) facts.push({ ico: , v: l.unit_type, l: "Type" }); if (l.area_sqft) facts.push({ ico: , v: `${Math.round(l.area_sqft).toLocaleString("fr-CA")}${NBSP}pi²`, l: "Superficie" }); if (d.floor != null) facts.push({ ico: , v: `${d.floor}ᵉ`, l: "Étage" }); if (l.furnished != null) facts.push({ ico: , v: l.furnished ? "Meublé" : "Non meublé", l: "Ameublement" }); if (l.pets) facts.push({ ico: , v: PETS[l.pets] ?? l.pets, l: "Animaux" }); if (dispo) { const iso = l.availability_date; const court = iso && iso !== "now" ? new Date(iso + "T00:00:00").toLocaleDateString("fr-CA", { day: "numeric", month: "short", year: "numeric" }).replace(/^1 /, "1ᵉʳ ") : "Maintenant"; facts.push({ ico: , v: court, l: "Disponible" }); } if (f?.duree_bail_minimale_mois) facts.push({ ico: , v: `${f.duree_bail_minimale_mois} mois`, l: "Bail minimum" }); if (f?.nb_occupants_total && conf.nb_occupants_total !== "faible") facts.push({ ico: , v: `${f.nb_occupants_total}`, l: "Occupants" }); const enLigne = l.first_seen ? Math.max(0, Math.round((Date.now() / 1000 - l.first_seen) / 86400)) : null; const rows: [string, string][] = [["Gestionnaire", sourceName(l.source)]]; if (l.price_label && l.price != null && l.price_label.replace(/\s/g, "") !== `${fmtPrice(l.price)}/mois`.replace(/\s/g, "")) rows.push(["Prix affiché par la source", l.price_label]); if (enLigne != null) rows.push(["En ligne sur Lou-Ka", enLigne === 0 ? "depuis aujourd'hui" : `depuis ${enLigne}${NBSP}jour${enLigne > 1 ? "s" : ""}`]); const maj = relTime(l.updated_at); if (maj) rows.push(["Synchronisé", maj]); if (f?.depot_mentionne && conf.depot_mentionne !== "faible") rows.push(["Dépôt mentionné", f.depot_mentionne]); if (facts.length === 0 && rows.length <= 1) return null; return ( {facts.length > 0 && (
{facts.map((x) => (
{x.v}
{x.l}
))}
)}
{rows.map(([k, v]) => (
{k}{v}
))}
{(l.digest?.incoherences?.length ?? 0) > 0 && (

Incohérence relevée dans l'annonce : {l.digest!.incoherences.join(" ; ")}

)}
); }