/* Voyage Assistant — main app shell. Gère le routing, le theme, l'auth (useAuth) et l'AuthModal global. */ const ROUTES = ['home', 'results', 'booking', 'confirm', 'mes-billets', 'assistant']; const useTheme = () => { const [mode, setMode] = React.useState(() => { try { return localStorage.getItem('va.theme') || 'light'; } catch { return 'light'; } }); React.useEffect(() => { try { localStorage.setItem('va.theme', mode); } catch {} }, [mode]); const resolved = mode === 'system' ? (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : mode; return { mode, setMode, resolved }; }; const useRoute = () => { const [route, setRouteRaw] = React.useState(() => { try { return localStorage.getItem('va.route') || 'home'; } catch { return 'home'; } }); const [ctx, setCtx] = React.useState(() => { try { return JSON.parse(localStorage.getItem('va.ctx') || '{}'); } catch { return {}; } }); const go = React.useCallback((next, nextCtx) => { if (!ROUTES.includes(next)) next = 'home'; setRouteRaw(next); if (nextCtx) setCtx((c) => ({ ...c, ...nextCtx })); try { localStorage.setItem('va.route', next); if (nextCtx) localStorage.setItem('va.ctx', JSON.stringify({ ...ctx, ...nextCtx })); } catch {} requestAnimationFrame(() => { const sc = document.querySelector('.va-app__main'); if (sc) sc.scrollTo(0, 0); }); }, [ctx]); return { route, ctx, go }; }; const UserChip = ({ user, onLogout, onGo }) => { const [open, setOpen] = React.useState(false); const initials = ((user.prenom || '')[0] || '') + ((user.nom || '')[0] || ''); return (
{open && ( <>
setOpen(false)} />
{user.prenom} {user.nom}
{user.email}
)}
); }; const LangMenu = ({ lang, setLang }) => { const [open, setOpen] = React.useState(false); const LANGS = (window.VA_I18N && window.VA_I18N.LANGS) || ['fr', 'en', 'es']; const LABELS = (window.VA_I18N && window.VA_I18N.LABELS) || { fr: 'Français', en: 'English', es: 'Español' }; return (
{open && ( <>
setOpen(false)} />
{LANGS.map((l) => ( ))}
)}
); }; const NavInt = ({ route, go, themeMode, setTheme, auth, onLogin, lang, setLang }) => { const linkCls = (r) => 'va-navlink' + (route === r ? ' is-active' : ''); return ( ); }; const FloatingFAB = ({ go, route }) => { if (route === 'assistant') return null; return ( ); }; const VoyageApp = () => { // Abonnement i18n à la racine : tout l'arbre se re-rend au changement de langue. const { lang, setLang } = useT(); const { mode: themeMode, setMode: setTheme, resolved } = useTheme(); const { route, ctx, go } = useRoute(); const auth = useAuth(); window.VA_AUTH = auth; const [authModalOpen, setAuthModalOpen] = React.useState(false); const [authTab, setAuthTab] = React.useState('login'); const [welcomeUser, setWelcomeUser] = React.useState(null); const openAuth = (tab = 'login') => { setAuthTab(tab); setAuthModalOpen(true); }; let page; if (route === 'home') page = ; else if (route === 'results') page = ; else if (route === 'booking') page = ; else if (route === 'confirm') page = ; else if (route === 'mes-billets') page = ; else if (route === 'assistant') page = openAuth('login')} />; else page = ; const isChat = route === 'assistant'; return (
{page}
setAuthModalOpen(false)} defaultTab={authTab} onSuccess={(u) => setWelcomeUser(u)} /> {welcomeUser && ( setWelcomeUser(null)} /> )}
); }; Object.assign(window, { VoyageApp });