// ----------------------------------------------------------------------------- // Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // Home.tsx : recherche — hero, filtres auto, grille de véhicules, pagination // ----------------------------------------------------------------------------- import { useEffect, useMemo, useState } from "react"; import { useSearchParams } from "react-router-dom"; import { Facets, Vehicle, VehicleQuery, fetchFacets, fetchStats, fetchVehicles, } from "../api"; import VehicleCard from "../components/VehicleCard"; const PAGE_SIZE = 24; const SORTS = [ { v: "price_asc", label: "Prix croissant" }, { v: "price_desc", label: "Prix décroissant" }, { v: "km_asc", label: "Kilométrage croissant" }, { v: "year_desc", label: "Année récente" }, { v: "recent", label: "Arrivages récents" }, ]; const PRICE_STEPS = [5000, 10000, 15000, 20000, 25000, 30000, 40000, 50000, 75000, 100000]; const KM_STEPS = [20000, 40000, 60000, 80000, 100000, 130000, 160000, 200000]; const KIND_COPY: Record = { auto: { label: "voitures usagées", hero: "voitures usagées", sub: "Auto-Ka visite les sites des concessionnaires et marchands d'occasion de toutes les régions, normalise chaque annonce et détecte les nouveautés, les ventes et les baisses de prix — automatiquement.", }, moto: { label: "motos usagées", hero: "motos usagées", sub: "Les inventaires des concessionnaires moto du Québec — Harley-Davidson, Honda, Yamaha, Kawasaki, BMW et plus — agrégés à la source et tenus à jour automatiquement.", }, scooter: { label: "scooters usagés", hero: "scooters usagés", sub: "Les scooters usagés des concessionnaires du Québec — Vespa, Honda, Yamaha, Kymco et plus — agrégés à la source et tenus à jour automatiquement.", }, }; export default function Home({ kind = "auto" }: { kind?: string }) { const [params, setParams] = useSearchParams(); const [facets, setFacets] = useState(null); const [vehicles, setVehicles] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(true); const [heroStats, setHeroStats] = useState<{ total: number; sources: number; regions: number } | null>(null); const query: VehicleQuery = useMemo(() => { const g = (k: string) => params.get(k) || undefined; const gn = (k: string) => (params.get(k) ? Number(params.get(k)) : undefined); return { kind, make: g("make"), model: g("model"), body_type: g("body"), fuel: g("fuel"), transmission: g("trans"), region: g("region"), source: g("source"), year_min: gn("ymin"), year_max: gn("ymax"), price_max: gn("pmax"), km_max: gn("kmax"), q: g("q"), sort: g("sort") || "price_asc", limit: PAGE_SIZE, offset: (Math.max(1, gn("page") || 1) - 1) * PAGE_SIZE, }; }, [params, kind]); const page = Math.max(1, Number(params.get("page") || 1)); const pages = Math.max(1, Math.ceil(total / PAGE_SIZE)); useEffect(() => { fetchFacets(params.get("make") || undefined, kind).then(setFacets).catch(() => {}); }, [params.get("make"), kind]); useEffect(() => { fetchStats() .then((s) => setHeroStats({ total: s.total, sources: s.sources, regions: s.regions })) .catch(() => {}); }, []); useEffect(() => { setLoading(true); fetchVehicles(query) .then((r) => { setVehicles(r.vehicles); setTotal(r.total); }) .catch(() => { setVehicles([]); setTotal(0); }) .finally(() => setLoading(false)); }, [query]); const setFilter = (key: string, value: string) => { const next = new URLSearchParams(params); if (value) next.set(key, value); else next.delete(key); if (key !== "page") next.delete("page"); if (key === "make") next.delete("model"); // le modèle dépend de la marque setParams(next, { replace: false }); }; const clearAll = () => setParams(new URLSearchParams(), { replace: false }); const years: number[] = []; const yb = facets?.years?.[0]; if (yb?.y_min && yb?.y_max) { for (let y = yb.y_max; y >= yb.y_min; y--) years.push(y); } return (
Agrégateur indépendant — direct des concessionnaires

{kind === "scooter" ? "Tous les " : "Toutes les "} {KIND_COPY[kind]?.hero ?? "véhicules"} à vendre au Québec. Un seul endroit.

{KIND_COPY[kind]?.sub}

{kind === "auto" && heroStats && (
{heroStats.total.toLocaleString("fr-CA")} véhicules en vente
{heroStats.sources} concessionnaires
{heroStats.regions} régions couvertes
)}
{ if (e.key === "Enter") setFilter("q", (e.target as HTMLInputElement).value); }} />

{total.toLocaleString("fr-CA")}{" "} véhicule{total !== 1 ? "s" : ""} trouvé{total !== 1 ? "s" : ""}

{loading ? (
{Array.from({ length: 8 }).map((_, i) => (
))}
) : vehicles.length === 0 ? (
🔍

Aucun véhicule ne correspond

Essayez d'élargir vos filtres — l'inventaire évolue chaque jour.

) : ( <>
{vehicles.map((v) => ( ))}
{pages > 1 && (
page {page} / {pages}
)} )}
); }