SPB Git

spb/ora-ka Public

Ora-Ka — cinq agrégateurs Ka, une barre de recherche hybride (exact + sémantique)

Python 80% TypeScript 12.9% CSS 6.8%
4.7 KB · 133 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// App.tsx : layout global (header + ticker en direct + footer encre) et routage5// -----------------------------------------------------------------------------6import { useEffect, useState } from "react";7import { NavLink, Route, Routes } from "react-router-dom";8import { fetchFacets, fetchSources, fetchStats, registerSourceNames } from "./api";9import Home from "./pages/Home";10import VehiclePage from "./pages/Vehicle";11import SourcesPage from "./pages/Sources";12import StatsPage from "./pages/Stats";1314function Ticker() {15  const [items, setItems] = useState<string[]>([]);1617  useEffect(() => {18    Promise.all([fetchStats(), fetchFacets(), fetchSources()])19      .then(([stats, facets, src]) => {20        registerSourceNames(src.sources);21        const parts: string[] = [22          `${stats.total.toLocaleString("fr-CA")} véhicules à vendre`,23          `${stats.sources} concessionnaires connectés`,24        ];25        if (stats.avg_price != null)26          parts.push(`Prix moyen ${Math.round(stats.avg_price).toLocaleString("fr-CA")} $`);27        for (const r of stats.by_region.slice(0, 8))28          parts.push(`${r.region} · ${r.n}`);29        for (const m of facets.makes.slice(0, 8))30          parts.push(`${m.make} · ${m.n}`);31        parts.push("Directement des sites des concessionnaires");32        setItems(parts);33      })34      .catch(() => setItems(["Auto-Ka — voitures usagées à vendre au Québec"]));35  }, []);3637  if (items.length === 0) return null;38  // contenu doublé pour une boucle de défilement continue39  return (40    <div className="ticker" aria-hidden="true">41      <div className="ticker-track">42        {[...items, ...items].map((t, i) => (43          <span key={i}>{t}</span>44        ))}45      </div>46    </div>47  );48}4950function Header() {51  return (52    <>53      <header className="header">54        <div className="container header-inner">55          <NavLink to="/" className="brand" aria-label="Auto-Ka — accueil">56            Auto<span className="ka">Ka</span>57            <span className="brand-tag">Voitures usagées · tout le Québec · toujours à jour</span>58          </NavLink>59          <nav className="nav" aria-label="Navigation principale">60            <NavLink to="/" end className={({ isActive }) => (isActive ? "active" : "")}>61              Autos62            </NavLink>63            <NavLink to="/motos" className={({ isActive }) => (isActive ? "active" : "")}>64              Motos65            </NavLink>66            <NavLink to="/scooters" className={({ isActive }) => (isActive ? "active" : "")}>67              Scooters68            </NavLink>69            <NavLink to="/stats" className={({ isActive }) => (isActive ? "active" : "")}>70              Stats71            </NavLink>72            <NavLink to="/sources" className={({ isActive }) => (isActive ? "active" : "")}>73              Sources74            </NavLink>75          </nav>76        </div>77      </header>78      <Ticker />79    </>80  );81}8283function Footer() {84  return (85    <footer className="footer">86      <div className="container">87        <div className="fbrand">88          Auto<span className="ka">Ka</span>89        </div>90        <div className="frow">91          <b>Agrégateur indépendant</b> de voitures usagées à vendre dans la92          province de Québec. Les annonces proviennent directement des sites93          publics des concessionnaires — pas des plateformes d'annonces — et94          sont rafraîchies automatiquement. Chaque fiche renvoie vers l'annonce95          originale du commerçant.96        </div>97        <div className="fmono">98          © {new Date().getFullYear()} Simon-Pierre Boucher — contact@spboucher.ai99        </div>100      </div>101    </footer>102  );103}104105export default function App() {106  return (107    <>108      <Header />109      <main>110        <Routes>111          <Route path="/" element={<Home kind="auto" />} />112          <Route path="/motos" element={<Home kind="moto" />} />113          <Route path="/scooters" element={<Home kind="scooter" />} />114          <Route path="/vehicule/:uid" element={<VehiclePage />} />115          <Route path="/stats" element={<StatsPage />} />116          <Route path="/sources" element={<SourcesPage />} />117          <Route118            path="*"119            element={120              <div className="notice container">121                <div className="big">🧭</div>122                <h2>Page introuvable</h2>123                <p>Le lien demandé n'existe pas.</p>124              </div>125            }126          />127        </Routes>128      </main>129      <Footer />130    </>131  );132}133