// ----------------------------------------------------------------------------- // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Project: Toit-Ka // pages/Stats.tsx : statistiques des deux univers — tuiles louer/acheter, // loyers et prix moyens par ville et par type (données /api/stats). // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { Stats, Tx, TxStats, fetchStats, slugify } from "../api"; const fmt = (n: number | null | undefined, suffix = "") => n == null ? "…" : Math.round(n).toLocaleString("fr-CA") + suffix; function Bars({ rows, tx, unit }: { rows: { key: string; n: number; avg: number | null; href?: string }[]; tx: Tx; unit?: string; }) { const max = Math.max(1, ...rows.map((r) => r.n)); return (
{rows.map((r) => (
{r.href ? {r.key} : r.key}
{r.n.toLocaleString("fr-CA")} {r.avg != null && · {fmt(r.avg)} ${tx === "louer" ? "/mois" : ""}{unit ?? ""}}
))}
); } function Univers({ tx, s }: { tx: Tx; s: TxStats }) { const louer = tx === "louer"; return (

{fmt(s.total)}
{louer ? "logements à louer" : "propriétés à vendre"}
{fmt(s.avg_price, " $")}
{louer ? "loyer moyen /mois" : "prix moyen demandé"}
{fmt(s.cities)}
villes couvertes
{fmt(s.sources)}
sources agrégées

Top villes

({ key: c.city, n: c.n, avg: c.avg_price, href: `/${tx}/${slugify(c.city)}`, }))} />

Par type

({ key: t.type, n: t.n, avg: t.avg_price, href: `/${tx}/type/${slugify(t.type)}`, }))} />
); } export default function StatsPage() { const [stats, setStats] = useState(null); useEffect(() => { fetchStats().then(setStats).catch(() => {}); }, []); return (
Les deux marchés, côte à côte

Statistiques

Portrait en direct des deux univers agrégés par Toit-Ka : la location (données Lou-Ka) et la vente (données Immo-Ka), mises à jour en continu.

{stats ? (
) : (
Chargement…
)}

Mise à jour automatique — chaque fiche renvoie à l'annonce originale de la source.

); }