{p.name}
{p.category}Compatibilité : {p.models.join(', ')}
import React, { useEffect, useMemo, useState } from "react"; // ------------------------------------------------------------ // Boutique 100% Ford – Starter en React (prévisualisable ici) // ------------------------------------------------------------ // ✅ Ce fichier est autonome : recherche, filtres, panier (localStorage), // fiche produit simple et bouton de commande (WhatsApp ou lien Stripe). // ✅ "Site miroir" légal : branche un flux CSV/JSON fournisseur dans fetchCatalog() // au lieu de copier/aspirer un autre site. Mets tes propres textes/photos. // 🔧 À personnaliser : // - REMPLACE const WHATSAPP_PHONE par ton numéro au format international // (ex: Réunion +262xxxxxxxxx sans + -> 262XXXXXXXXX). // - Ajoute un lien Stripe Checkout si tu veux un paiement immédiat. // - Remplis le tableau seedProducts avec ton catalogue réel, ou branche un flux. // ------------------------------------------------------------ const WHATSAPP_PHONE = "262000000000"; // <- A MODIFIER ! Ex: 262692123456 (sans +) const seedProducts = [ { id: "rs-mk3-intercooler", name: "Intercooler Performance — Focus RS MK3", price: 589.9, sku: "IC-RS3-001", category: "Refroidissement", models: ["Focus RS MK3 (2016–2018)"], stock: 8, image: "https://placehold.co/640x420?text=Intercooler+RS+MK3", }, { id: "fiesta-mk8-plaquettes", name: "Plaquettes de frein sport — Fiesta ST MK8", price: 149.0, sku: "BRK-FST8-010", category: "Freinage", models: ["Fiesta ST MK8 (2018–)", "Fiesta (2017–)"], stock: 24, image: "https://placehold.co/640x420?text=Plaquettes+Fiesta+ST+MK8", }, { id: "mustang-s550-filtre-air", name: "Filtre à air performance — Mustang GT S550", price: 89.0, sku: "AIR-MUS-550", category: "Admission", models: ["Mustang GT S550 (2015–2023)"], stock: 15, image: "https://placehold.co/640x420?text=Filtre+Air+Mustang+S550", }, { id: "ranger-t6-amortisseurs", name: "Kit amortisseurs renforcés — Ranger T6", price: 799.0, sku: "SUS-RNG-T6", category: "Suspension", models: ["Ranger T6 (2011–2022)"], stock: 5, image: "https://placehold.co/640x420?text=Amortisseurs+Ranger+T6", }, ]; function classNames(...c) { return c.filter(Boolean).join(" "); } function useLocalStorage(key, initial) { const [state, setState] = useState(() => { try { const raw = localStorage.getItem(key); return raw ? JSON.parse(raw) : initial; } catch { return initial; } }); useEffect(() => { try { localStorage.setItem(key, JSON.stringify(state)); } catch {} }, [key, state]); return [state, setState]; } // Simule l'import d'un catalogue (CSV/JSON). Ici on renvoie seedProducts. async function fetchCatalog() { // Exemple pour brancher un CSV : // const res = await fetch('/catalog.csv'); // const text = await res.text(); // parse CSV -> produits return seedProducts; } function formatPrice(v) { return new Intl.NumberFormat('fr-FR', { style:'currency', currency:'EUR' }).format(v); } export default function BoutiqueFord() { const [products, setProducts] = useState([]); const [query, setQuery] = useState(""); const [modelFilter, setModelFilter] = useState(""); const [category, setCategory] = useState(""); const [sort, setSort] = useState("pop"); const [cart, setCart] = useLocalStorage("ford_cart", []); const [openCart, setOpenCart] = useState(false); useEffect(() => { (async () => setProducts(await fetchCatalog()))(); }, []); const models = useMemo(() => { const s = new Set(); products.forEach(p => p.models?.forEach(m => s.add(m))); return Array.from(s).sort(); }, [products]); const categories = useMemo(() => { const s = new Set(); products.forEach(p => s.add(p.category)); return Array.from(s).sort(); }, [products]); const filtered = useMemo(() => { let list = [...products]; if (query.trim()) { const q = query.toLowerCase(); list = list.filter(p => p.name.toLowerCase().includes(q) || p.sku.toLowerCase().includes(q) || p.models.join(" ").toLowerCase().includes(q) ); } if (modelFilter) list = list.filter(p => p.models.includes(modelFilter)); if (category) list = list.filter(p => p.category === category); switch (sort) { case 'asc': list.sort((a,b)=>a.price-b.price); break; case 'desc': list.sort((a,b)=>b.price-a.price); break; default: /* popularité fake => stock */ list.sort((a,b)=>b.stock-a.stock); } return list; }, [products, query, modelFilter, category, sort]); const total = cart.reduce((s,i)=> s + i.price * i.qty, 0); function addToCart(p) { setCart(prev => { const found = prev.find(x => x.id === p.id); if (found) return prev.map(x => x.id===p.id? {...x, qty: Math.min(x.qty+1, 99)} : x); return [...prev, { id: p.id, name: p.name, price: p.price, sku: p.sku, qty:1 }]; }); setOpenCart(true); } function removeFromCart(id) { setCart(prev => prev.filter(x=>x.id!==id)); } function setQty(id, q) { setCart(prev => prev.map(x=> x.id===id? {...x, qty: Math.max(1, Math.min(99, q||1))}: x)); } function buildOrderMessage() { const lines = cart.map(i => `• ${i.name} (x${i.qty}) — ${formatPrice(i.price*i.qty)} [SKU ${i.sku}]`).join('\n'); return `Bonjour, je souhaite commander :\n${lines}\nTotal: ${formatPrice(total)}\nNom: __\nAdresse: __\nMode de retrait/livraison: __`; } const whatsappUrl = `https://wa.me/${WHATSAPP_PHONE}?text=${encodeURIComponent(buildOrderMessage())}`; const stripeCheckoutUrl = "#"; // <- Remplace par ton lien Stripe Checkout si dispo. return (
Boutique démo — Réunion / DOM
Compatibilité : {p.models.join(', ')}
Démarquez-vous.
Notre offre produit
Tout commence par une idée. Peut-être voulez-vous créer une entreprise. Peut-être voulez-vous donner une nouvelle dimension à un passe-temps.
Tout commence par une idée. Peut-être voulez-vous créer une entreprise. Peut-être voulez-vous donner une nouvelle dimension à un passe-temps.
Tout commence par une idée. Peut-être voulez-vous créer une entreprise. Peut-être voulez-vous donner une nouvelle dimension à un passe-temps.