// app.jsx — Main app composition const { useState: aUseState, useEffect: aUseEffect, useRef: aUseRef } = React; function Nav({ t, lang, setLang, theme, setTheme }) { const active = useActiveSection(["hero", "heritage", "mission", "voices", "features", "agent", "contrast", "journey", "destinations", "operators", "vision", "faq", "contact"]); const scrolled = useScrolled(80); const sections = [ { id: "heritage", label: t.nav.heritage || (lang === "fr" ? "Héritage" : "Heritage") }, { id: "mission", label: t.nav.mission }, { id: "features", label: t.nav.features }, { id: "agent", label: t.nav.agent }, { id: "journey", label: t.nav.journey }, { id: "vision", label: t.nav.vision }, { id: "operators", label: t.nav.operators }, { id: "contact", label: t.nav.contact }, ]; const toggleTheme = () => setTheme(theme === "dark" ? "light" : "dark"); // État du menu mobile (drawer plein écran depuis le haut). Fermé par // défaut, ouvert quand l'utilisateur clique sur le bouton burger qui // n'apparaît qu'en dessous de 900px (CSS). const [menuOpen, setMenuOpen] = aUseState(false); // Ferme le menu dès qu'un lien est cliqué + à chaque changement de // section active (utile si l'utilisateur scrolle pendant que c'est ouvert). aUseEffect(() => { setMenuOpen(false); }, [active]); // Lock du scroll body quand le menu est ouvert aUseEffect(() => { document.body.style.overflow = menuOpen ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [menuOpen]); const themeBtn = ( ); const langBtn = (
); return ( <>
FasoTravel FasoTravel
{sections.map(s => ( {s.label} ))}
{themeBtn} {langBtn}
{/* Drawer mobile — apparaît uniquement < 900px via CSS */}
{sections.map(s => ( setMenuOpen(false)} > {s.label} ))}
{menuOpen &&
setMenuOpen(false)}/>} ); } function App() { const [lang, setLang] = aUseState(() => { try { return localStorage.getItem("ft_lang") || "fr"; } catch { return "fr"; } }); const [theme, setTheme] = aUseState(() => { try { return localStorage.getItem("ft_theme") || "dark"; } catch { return "dark"; } }); aUseEffect(() => { try { localStorage.setItem("ft_lang", lang); } catch {} window.FT_T = window.FT_I18N[lang]; }, [lang]); aUseEffect(() => { try { localStorage.setItem("ft_theme", theme); } catch {} document.documentElement.classList.toggle("light", theme === "light"); }, [theme]); const t = window.FT_I18N[lang]; window.FT_T = t; const progress = useScrollProgress(); const [notifyOpen, setNotifyOpen] = aUseState(false); const [notifyPhone, setNotifyPhone] = aUseState(""); const [notifySent, setNotifySent] = aUseState(false); // Modal Partenaire (transporteurs) — collecte d'infos riches. const [partnerOpen, setPartnerOpen] = aUseState(false); const [partnerForm, setPartnerForm] = aUseState({ company: "", contact: "", phone: "", city: "", fleet: "", message: "" }); const [partnerSent, setPartnerSent] = aUseState(false); const handleNotify = () => setNotifyOpen(true); const handlePartner = () => setPartnerOpen(true); // Helpers d'envoi (mock pour l'instant) — POST sera branché quand le // backend exposera /waitlist et /partners. En attendant on persiste en // localStorage pour ne perdre aucune candidature collectée pendant le // pré-lancement, et on ouvre WhatsApp côté utilisateur en option. const persistLead = (key, payload) => { try { const raw = localStorage.getItem(key); const arr = raw ? JSON.parse(raw) : []; arr.push({ ...payload, ts: new Date().toISOString() }); localStorage.setItem(key, JSON.stringify(arr)); } catch (e) { /* ignore */ } }; const submitNotify = (e) => { e.preventDefault(); if (!notifyPhone || notifyPhone.trim().length < 6) return; persistLead("ft_waitlist", { phone: notifyPhone.trim(), lang }); setNotifySent(true); setTimeout(() => { setNotifyOpen(false); setNotifySent(false); setNotifyPhone(""); }, 2200); }; const submitPartner = (e) => { e.preventDefault(); const { company, contact, phone, city, fleet, message } = partnerForm; if (!company.trim() || !contact.trim() || !phone.trim()) return; const text = [ `🚌 *Candidature Partenaire FasoTravel*`, ``, `*Société :* ${company.trim()}`, `*Contact :* ${contact.trim()}`, `*Téléphone :* +226 ${phone.trim()}`, city.trim() ? `*Ville :* ${city.trim()}` : null, fleet ? `*Flotte :* ${fleet} véhicules` : null, message.trim() ? `*Message :* ${message.trim()}` : null, ].filter(Boolean).join("\n"); const waUrl = `https://wa.me/22651591414?text=${encodeURIComponent(text)}`; window.open(waUrl, "_blank"); setPartnerSent(true); setTimeout(() => { setPartnerOpen(false); setPartnerSent(false); setPartnerForm({ company: "", contact: "", phone: "", city: "", fleet: "", message: "" }); }, 2400); }; return ( <>