// ----------------------------------------------------------------------------- // Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // fiche/VehicleHero.tsx : héro de la fiche — concessionnaire · ville, prix + // ancien prix barré + capsule marché, titre (H1) + résumé (année · km · // transmission · carburant · carrosserie), galerie photo (balayage natif, // compteur, plein écran via Lightbox existante, vignettes ≥ 768 px), actions // (favoris · partager · Carfax · Voir chez le concessionnaire). // ----------------------------------------------------------------------------- import { useRef, useState } from "react"; import { VehicleDetail, fmtPrice } from "../api"; import { Ico, IcoHeart } from "../components/Icons"; import Lightbox from "../components/Lightbox"; import { ComparaisonPrix, ligneResume } from "./synthese"; export function PriceCapsule({ cmp, short = false }: { cmp: ComparaisonPrix | null; short?: boolean }) { if (!cmp) return null; return ( {short ? {cmp.court} : <>{cmp.label}·{cmp.court}>} ); } export function VehicleGallery({ images, titre }: { images: string[]; titre: string }) { const [idx, setIdx] = useState(0); const [zoom, setZoom] = useState(false); const [dead, setDead] = useState>(new Set()); const track = useRef(null); const alive = images.filter((u) => !dead.has(u)); const cur = Math.min(idx, Math.max(0, alive.length - 1)); const goto = (i: number) => track.current?.scrollTo({ left: i * track.current.clientWidth, behavior: "smooth" }); if (alive.length === 0) return Aucune photo fournie par le concessionnaire; return ( <> { const el = track.current; if (el) setIdx(Math.round(el.scrollLeft / el.clientWidth)); }}> {alive.map((u, i) => ( setDead((d) => new Set(d).add(u))} onClick={() => setZoom(true)} /> ))} {cur + 1} / {alive.length} setZoom(true)}> {cur > 0 && goto(cur - 1)}>} {cur < alive.length - 1 && goto(cur + 1)}>} {alive.length > 1 && ( {alive.slice(0, 12).map((u, i) => ( goto(i)} aria-label={`Photo ${i + 1}`} aria-current={i === cur} role="listitem"> ))} )} {zoom && { setIdx(i); goto(i); }} onClose={() => setZoom(false)} alt={titre} />} > ); } export default function VehicleHero({ v, cmp, fav, onFav, onShare, actionsRef }: { v: VehicleDetail; cmp: ComparaisonPrix | null; fav: boolean; onFav: () => void; onShare: () => void; actionsRef: React.RefObject; }) { const resume = ligneResume(v); const hist = (v.price_history ?? []).filter((h) => h.price != null); const avant = hist.length >= 2 && hist[0].price! < hist[1].price! ? hist[1].price! : null; return ( {v.dealer_name || v.source}{v.city ? ` — ${v.city}` : ""}{v.region ? ` · ${v.region}` : ""} {fmtPrice(v.price)} {avant != null && {fmtPrice(avant)}} {v.details?.price_from ? à partir de : null} {v.year != null && {v.year}} {v.ka_reco && Recommandé pour vous} {v.title} {resume.length > 0 && {resume.join(" · ")}} {v.carfax_url && } Voir l'annonce chez {v.dealer_name || "le concessionnaire"} ); }