// ----------------------------------------------------------------------------- // House-Ka — Homes-for-sale aggregator (Canada outside Québec, Ontario first) // Author: Simon-Pierre Boucher — contact@spboucher.ai // App.tsx : global layout (header + live ticker + footer) and routing. // ----------------------------------------------------------------------------- import { Ico } from "./components/Icons"; import { useEffect, useState } from "react"; import { NavLink, Route, Routes, useLocation } from "react-router-dom"; import { fetchFacets, fetchSources, fetchStats, registerSourceNames, sourceName } from "./api"; import { kaLogin, useAccount } from "./account"; import AccountPage from "./pages/Account"; import AgenciesPage from "./pages/Agencies"; import ContactPage from "./pages/Contact"; import Home from "./pages/Home"; import { PrivacyPage, TermsPage } from "./pages/Legal"; import ListingPage from "./pages/Listing"; import RatesPage from "./pages/Rates"; import StatsPage from "./pages/Stats"; function Ticker() { const [items, setItems] = useState([]); useEffect(() => { Promise.all([fetchStats(), fetchFacets(), fetchSources()]) .then(([stats, facets, src]) => { registerSourceNames(src.sources); const parts: string[] = [`${stats.total.toLocaleString("en-CA")} homes for sale`]; if (stats.cities) parts.push(`${stats.cities.toLocaleString("en-CA")} cities & towns`); if (stats.avg_price != null) parts.push(`Average price $${Math.round(stats.avg_price).toLocaleString("en-CA")}`); for (const s of facets.sources.slice(0, 12)) parts.push(`${sourceName(s.source)} · ${s.n.toLocaleString("en-CA")}`); parts.push("Continuously updated"); setItems(parts); }) .catch(() => setItems(["House-Ka — homes for sale across Canada"])); }, []); if (items.length === 0) return null; return ( ); } /** Header account zone: "Sign in with KA" or a link to the profile. */ function AccountNav() { const { user, enabled, loaded } = useAccount(); if (!loaded || !enabled) return null; if (user) { return ( `ka-acct ${isActive ? "active" : ""}`} > {user.picture ? ( ) : ( )} {user.name ? user.name.split(" ")[0] : "Account"} ); } return (
Create an account
); } /** Same account zone, mobile-menu version. */ function AccountMobile({ close }: { close: () => void }) { const { user, enabled, loaded } = useAccount(); if (!loaded || !enabled) return null; if (user) { return ( My account {user.name ? `— ${user.name.split(" ")[0]}` : ""} ); } return (
Create an account
); } const NAV_LINKS = [ { to: "/", label: "Homes", icon: "home", end: true }, { to: "/?view=map", label: "Map", icon: "map", end: false, force: true }, { to: "/rates", label: "Mortgage rates", icon: "trendup", end: false }, { to: "/stats", label: "Stats", icon: "chart", end: false }, { to: "/agencies", label: "Brokerages", icon: "building", end: false }, { to: "/contact", label: "Contact", icon: "arrow", end: false }, ]; function Header() { const [open, setOpen] = useState(false); const location = useLocation(); useEffect(() => { setOpen(false); }, [location]); useEffect(() => { document.documentElement.classList.toggle("ka-scroll-lock", open); return () => { document.documentElement.classList.remove("ka-scroll-lock"); }; }, [open]); return ( <>
HouseKa A Groupe KA service
{/* fixed ✕ of the panel: visible regardless of scroll; the header burger is hidden while the panel is open */} {NAV_LINKS.map((l, i) => ( `mm-link ${isActive && !l.force ? "active" : ""}`} onClick={() => setOpen(false)} > {l.label} ))} setOpen(false)} />

Independent aggregator — continuously updated, every listing links back to the brokerage's original page.

{open &&
setOpen(false)} aria-hidden="true" />} ); } /** Bottom navigation bar (mobile) — floating pill detached from the edges, accent underline below the active tab. */ function MobileTabBar() { const location = useLocation(); const isMap = new URLSearchParams(location.search).get("view") === "map"; const tabs = [ { to: "/", label: "Discover", icon: , on: location.pathname === "/" && !isMap }, { to: "/?view=map", label: "Map", icon: , on: location.pathname === "/" && isMap }, { to: "/rates", label: "Rates", icon: , on: location.pathname.startsWith("/rates") }, { to: "/account", label: "Account", icon: , on: location.pathname.startsWith("/account") }, ]; return ( ); } /** House-Ka footer — ink panel, Groupe KA credit + sister sites. */ function Footer() { const year = new Date().getFullYear(); const sisters = [ { name: "Groupe·Ka", url: "https://www.groupe-ka.com", note: "the Groupe KA portal" }, { name: "Immo·Ka", url: "https://www.immo-ka.com", note: "homes for sale in Québec" }, { name: "Lou·Ka", url: "https://www.lou-ka.com", note: "rentals in Québec" }, { name: "Vrai·Prix", url: "https://www.vrai-prix.com", note: "Québec market-value estimates" }, ]; return (
HouseKa

House-Ka continuously aggregates homes for sale publicly listed by Canadian real-estate brokerages, teams and national networks — every province and territory except Québec, which lives on our sister site Immo-Ka. Every listing links back to the source's original page. House-Ka is a service of Groupe KA.

House-Ka is an independent aggregator: it is not a brokerage, does not represent buyers or sellers, and is not affiliated with the sources it indexes. Prices and availability are those displayed by each source.

Terms of use Privacy Contact © {year} Groupe-Ka
); } export default function App() { return ( <>
} /> } /> {/* canonical SEO URL: /property/{uid}/{slug} (server 301) — without this route any shared/direct link would land on the 404 page */} } /> } /> } /> } /> } /> } /> } /> } />
🧭

Page not found

The requested link does not exist.

} />