// ----------------------------------------------------------------------------- // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Project: Toit-Ka // App.tsx : layout global (header + ticker en direct + footer encre) et routage. // L'ACCENT DU SITE SUIT L'UNIVERS AFFICHÉ (html[data-mode]) : lime en mode // Louer, rouge en mode Acheter, terracotta (marque) sur les pages neutres. // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { Link, NavLink, Route, Routes, useLocation } from "react-router-dom"; import { fetchStats, logout, setMode } from "./api"; import { AccountProvider, useAccount } from "./account"; import { Ico, IcoHeart } from "./components/Icons"; import CategoryPage from "./pages/Category"; import Home from "./pages/Home"; import { PrivacyPage, TermsPage } from "./pages/Legal"; import ListingPage from "./pages/Listing"; import ProfilPage from "./pages/Profil"; import StatsPage from "./pages/Stats"; /** L'accent global suit la route : /louer/* -> lime, /acheter/* -> rouge, * accueil -> géré par Home (selon le toggle), ailleurs -> marque terracotta. */ function ModeSync() { const location = useLocation(); useEffect(() => { const p = location.pathname; if (p.startsWith("/louer")) setMode("louer"); else if (p.startsWith("/acheter")) setMode("acheter"); else if (p === "/" || p.startsWith("/annonce/")) { /* géré par la page */ } else setMode(null); }, [location]); return null; } function Ticker() { const [items, setItems] = useState([]); useEffect(() => { fetchStats() .then((stats) => { const parts: string[] = [ `${stats.louer.total.toLocaleString("fr-CA")} logements à louer`, `${stats.acheter.total.toLocaleString("fr-CA")} propriétés à vendre`, ]; if (stats.louer.avg_price != null) parts.push(`Loyer moyen ${Math.round(stats.louer.avg_price).toLocaleString("fr-CA")} $`); if (stats.acheter.avg_price != null) parts.push(`Prix moyen ${Math.round(stats.acheter.avg_price).toLocaleString("fr-CA")} $`); for (const c of stats.louer.top_cities.slice(0, 5)) parts.push(`Louer à ${c.city} · ${c.n}`); for (const c of stats.acheter.top_cities.slice(0, 5)) parts.push(`Acheter à ${c.city} · ${c.n}`); parts.push("Mise à jour automatique"); setItems(parts); }) .catch(() => setItems(["Toit-Ka — louer ou acheter un toit au Québec"])); }, []); if (items.length === 0) return null; return ( ); } function AuthWidget() { const { user, enabled, refresh } = useAccount(); const [open, setOpen] = useState(false); if (!user && !enabled) return null; if (!user) return ( KA ID ); return (
{open && (
{user.name} {user.email}
{user.ka_id && (
KA-ID : {user.ka_id}
)}
Compte Groupe KA — le même KA-ID sur toutes les plateformes du groupe
setOpen(false)}> Mon profil setOpen(false)}> Mes favoris — Mon univers Ka ↗
)}
); } const NAV_LINKS = [ { to: "/?tx=louer", label: "Louer", icon: "key", dot: "louer" }, { to: "/?tx=acheter", label: "Acheter", icon: "home", dot: "acheter" }, { to: "/stats", label: "Stats", icon: "chart", dot: "" }, { to: "/profil", label: "Mon profil", icon: "people", dot: "" }, ]; function Header() { const [open, setOpen] = useState(false); const location = useLocation(); const tx = new URLSearchParams(location.search).get("tx") ?? "louer"; useEffect(() => { setOpen(false); }, [location]); useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [open]); const navActive = (label: string) => { if (label === "Louer") return location.pathname === "/" && tx === "louer"; if (label === "Acheter") return location.pathname === "/" && tx === "acheter"; return false; }; return ( <>
ToitKa Louer ou acheter — un seul endroit
{NAV_LINKS.map((l, i) => ( setOpen(false)} > {l.label} ))}
Agrégateur indépendant — location (Lou-Ka) et vente (Immo-Ka) réunies. Chaque fiche renvoie à l'annonce originale de la source.
{open &&
setOpen(false)} aria-hidden="true" />} ); } function Footer() { return (
ToitKa
Agrégateur indépendant des logements à louer et des propriétés à vendre dans la province de Québec — la fusion de Lou-Ka (location) et d'Immo-Ka (vente). Les annonces proviennent des sites publics des gestionnaires immobiliers, agences de courtage et plateformes, rafraîchies automatiquement — chaque fiche renvoie vers l'annonce originale.
Conditions d'utilisation Politique de confidentialité Lou-Ka Immo-Ka
Toit-Ka est une application du Groupe-Ka · © {new Date().getFullYear()} Groupe-Ka —{" "} contact@groupe-ka.com
); } export default function App() { return (
} /> } /> } /> {/* pages programmatiques SEO — mêmes URL que le rendu serveur */} } /> } /> } /> } /> } /> } /> } /> } /> } /> } />

Page introuvable

Le lien demandé n'existe pas.

} />