/* Voyage Assistant — Auth modal + hook + state. - useAuth() : { user, token, login, signup, googleSignIn, logout, isAuth } - AuthModal : modale 2 onglets (Connexion / Inscription) + bouton Google Persistance via localStorage. */ const AUTH_STORAGE = { TOKEN: 'va.token', USER: 'va.user' }; const useAuth = () => { const [token, setToken] = React.useState(() => { try { return localStorage.getItem(AUTH_STORAGE.TOKEN); } catch { return null; } }); const [user, setUser] = React.useState(() => { try { return JSON.parse(localStorage.getItem(AUTH_STORAGE.USER) || 'null'); } catch { return null; } }); const _persist = (tok, u) => { try { if (tok) localStorage.setItem(AUTH_STORAGE.TOKEN, tok); else localStorage.removeItem(AUTH_STORAGE.TOKEN); if (u) localStorage.setItem(AUTH_STORAGE.USER, JSON.stringify(u)); else localStorage.removeItem(AUTH_STORAGE.USER); } catch {} setToken(tok); setUser(u); }; const login = async (email, password) => { const r = await window.VA_API.login(email, password); _persist(r.token, r.user); return r.user; }; const signup = async (payload) => { const r = await window.VA_API.signup(payload); _persist(r.token, r.user); return r.user; }; const googleSignIn = async () => { return new Promise((resolve, reject) => { const clientId = window.VA_CONFIG && window.VA_CONFIG.GOOGLE_CLIENT_ID; if (!clientId || !window.google || !window.google.accounts) { // Fallback stub si SDK Google non chargé ou client_id manquant const email = (prompt(t('SDK Google indisponible — entrez votre email :')) || '').trim(); if (!email) return resolve(null); const name = email.split('@')[0].replace(/[._]/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); window.VA_API.googleAuth({ google_sub: 'stub-' + btoa(email).slice(0, 24), email, name, given_name: name.split(' ')[0] || 'User', family_name: name.split(' ').slice(1).join(' ') || 'Google', picture: '', }).then(r => { _persist(r.token, r.user); resolve(r.user); }).catch(reject); return; } // Flow OAuth2 popup (évite FedCM qui peut être bloqué par le navigateur) if (!window.google.accounts.oauth2) { reject(new Error(t('Google OAuth2 SDK indisponible'))); return; } const tokenClient = window.google.accounts.oauth2.initTokenClient({ client_id: clientId, scope: 'openid email profile', callback: async (resp) => { if (resp.error) { reject(new Error(resp.error)); return; } try { const ui = await fetch('https://www.googleapis.com/oauth2/v3/userinfo', { headers: { Authorization: `Bearer ${resp.access_token}` }, }).then(r => r.json()); const r = await window.VA_API.googleAuth({ google_sub: ui.sub, email: ui.email, name: ui.name || `${ui.given_name || ''} ${ui.family_name || ''}`.trim(), given_name: ui.given_name || '', family_name: ui.family_name || '', picture: ui.picture || '', }); _persist(r.token, r.user); resolve(r.user); } catch (err) { reject(err); } }, }); tokenClient.requestAccessToken(); }); }; const logout = () => _persist(null, null); return { user, token, isAuth: !!token, login, signup, googleSignIn, logout }; }; const AuthModal = ({ open, onClose, defaultTab = 'login', onSuccess }) => { const auth = window.VA_AUTH; const [tab, setTab] = React.useState(defaultTab); const [busy, setBusy] = React.useState(false); const [error, setError] = React.useState(null); React.useEffect(() => { setTab(defaultTab); setError(null); }, [defaultTab, open]); if (!open) return null; const submit = async (e) => { e.preventDefault(); setBusy(true); setError(null); const form = e.target; const data = Object.fromEntries(new FormData(form).entries()); try { let user; if (tab === 'login') { user = await auth.login(data.email, data.password); } else { user = await auth.signup({ nom: data.nom, prenom: data.prenom, date_naissance: data.date_naissance, email: data.email, password: data.password, }); } onSuccess && onSuccess(user); onClose && onClose(); } catch (err) { setError(err.message); } finally { setBusy(false); } }; const doGoogle = async () => { setBusy(true); setError(null); try { const u = await auth.googleSignIn(); if (u) { onSuccess && onSuccess(u); onClose && onClose(); } } catch (err) { setError(err.message); } finally { setBusy(false); } }; return (
{tab === 'login' ? t('Connectez-vous pour réserver et retrouver vos conversations.') : t('Quelques infos suffisent. Vous pourrez réserver immédiatement.')}