// ----------------------------------------------------------------------------- // Home-Ka — US real-estate aggregator (Groupe KA) // Author: Simon-Pierre Boucher — contact@spboucher.ai // App.tsx : global layout (header + live ticker + Groupe KA footer) + 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 GroupeKaBadge from "./ka/GroupeKaBadge"; import KaFooter from "./ka/KaFooter"; import ContactPage from "./pages/Contact"; import Home from "./pages/Home"; import { PrivacyPage, TermsPage } from "./pages/Legal"; import ListingPage from "./pages/Listing"; import SourcesPage from "./pages/Sources"; import StatsPage from "./pages/Stats"; import AdminPage from "./pages/Admin"; 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-US")} homes for sale`]; if (stats.states) parts.push(`${stats.states} states`); if (stats.cities) parts.push(`${stats.cities.toLocaleString("en-US")} cities`); if (stats.avg_price != null) parts.push(`Average price $${Math.round(stats.avg_price).toLocaleString("en-US")}`); for (const s of facets.sources.slice(0, 12)) parts.push(`${sourceName(s.source)} · ${s.n}`); parts.push("Updated automatically"); setItems(parts); }) .catch(() => setItems(["Home-Ka — every home for sale in America"])); }, []); if (items.length === 0) return null; return ( ); } const NAV_LINKS = [ { to: "/", label: "Homes", icon: "home", end: true }, { to: "/?view=map", label: "Map", icon: "map", end: false, force: true }, { to: "/stats", label: "Stats", icon: "chart", end: false }, { to: "/sources", label: "Sources", 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 ( <>
HomeKa
{NAV_LINKS.map((l, i) => ( `mm-link ${isActive && !l.force ? "active" : ""}`} onClick={() => setOpen(false)} > {l.label} ))}

Independent aggregator — updated automatically; every listing links back to the original announcement at the source.

{open &&
setOpen(false)} aria-hidden="true" />} ); } const LOCAL_LEGAL = [ { label: "Terms of Use (Home-Ka)", href: "/terms" }, { label: "Privacy Policy (Home-Ka)", href: "/privacy" }, { label: "Contact", href: "/contact" }, ]; /** Bottom navigation bar (mobile) — floating pill, same language as Lou-Ka v2. */ 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: "/stats", label: "Stats", icon: , on: location.pathname.startsWith("/stats") }, { to: "/sources", label: "Sources", icon: , on: location.pathname.startsWith("/sources") }, ]; return ( ); } export default function App() { return ( <>
} /> } /> {/* SEO canonical URL: /property/{uid}/{slug} — without this route any shared/direct link would land on the 404 page */} } /> } /> } /> } /> } /> } /> {/* Admin — reachable by URL only, not linked in the nav */} } /> } /> } /> } />
🧭

Page not found

The requested link does not exist.

} /> ); }