// 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
{themeBtn}
{langBtn}
{/* Drawer mobile — apparaît uniquement < 900px via CSS */}
{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 (
<>