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%
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