import React, { useState, useEffect, useRef } from 'react'; import { X, Mail, Lock, ArrowRight, Loader2, LogIn } from 'lucide-react'; import { useNotification } from '@/hooks/useNotification'; interface JoinTourLoginModalProps { isOpen: boolean; onClose: () => void; inviteToken: string; onJoinSuccess?: (user: any, tourData: any) => void; onSwitchToSignup?: () => void; } export const JoinTourLoginModal: React.FC = ({ isOpen, onClose, inviteToken, onJoinSuccess, onSwitchToSignup }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); const notify = useNotification(); // Ref to store the latest inviteToken to avoid stale closure issue const inviteTokenRef = useRef(inviteToken); useEffect(() => { inviteTokenRef.current = inviteToken; }, [inviteToken]); const handleGoogleLogin = async (googleResponse: any) => { setError(''); setIsLoading(true); try { const response = await fetch(`/api/v1/auth/google`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ credential: googleResponse.credential }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || 'Đăng nhập Google thất bại'); } localStorage.setItem('token', data.access_token); localStorage.setItem('user', JSON.stringify(data.user)); // Use the latest inviteToken from ref let currentToken = inviteTokenRef.current; console.log('[JoinTourLogin] Attempting to join with token:', currentToken?.substring(0, 10) + '...'); // Also check pendingInviteToken as fallback let pendingToken = localStorage.getItem('pendingInviteToken'); // If no token found, try to get from URL if (!currentToken && !pendingToken) { const params = new URLSearchParams(window.location.search); const urlToken = params.get('token'); if (urlToken) { currentToken = urlToken; localStorage.setItem('pendingInviteToken', urlToken); console.log('[JoinTourLogin] Using token from URL params:', urlToken.substring(0, 10) + '...'); } } const tokenToUse = currentToken || pendingToken; if (!tokenToUse) { console.error('[JoinTourLogin] No invite token available'); throw new Error('Không có mã lời mời để tham gia tour'); } try { const joinRes = await fetch(`/api/v1/tours/join-by-token`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${data.access_token}`, }, body: JSON.stringify({ token: tokenToUse }), }); console.log('[JoinTourLogin] Join response status:', joinRes.status); const joinData = await joinRes.json().catch(() => ({})); if (joinRes.ok) { console.log('[JoinTourLogin] Join successful'); notify({ title: 'Thành công', message: joinData.message || 'Bạn đã gia nhập tour!', type: 'success' }); localStorage.removeItem('pendingInviteToken'); if (onJoinSuccess) { onJoinSuccess(data.user, joinData); } onClose(); } else { // Token invalid or expired - clear it and show error console.log('[JoinTourLogin] Token invalid, clearing from storage'); localStorage.removeItem('pendingInviteToken'); // Email mismatch or other error const errorMessage = joinData.message || `Không thể gia nhập tour (HTTP ${joinRes.status})`; console.error('[JoinTourLogin] Join failed:', errorMessage); setError(`Lỗi gia nhập tour: ${errorMessage}`); setIsLoading(false); } } catch (e: any) { console.error('[JoinTourLogin] Join exception:', e); setError(`Lỗi khi gia nhập: ${e.message}`); setIsLoading(false); } } catch (err: any) { console.error('[JoinTourLogin] Google login error:', err); setError(err.message); setIsLoading(false); } }; const handleEmailPasswordJoin = async (e: React.FormEvent) => { e.preventDefault(); setError(''); setIsLoading(true); try { const response = await fetch(`/api/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }); const data = await response.json(); if (!response.ok) { throw new Error(data.message || 'Đăng nhập thất bại'); } localStorage.setItem('token', data.access_token); localStorage.setItem('user', JSON.stringify(data.user)); // Use the latest inviteToken from ref const tokenToUse = inviteTokenRef.current || localStorage.getItem('pendingInviteToken'); try { const joinRes = await fetch(`/api/v1/tours/join-by-token`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${data.access_token}`, }, body: JSON.stringify({ token: tokenToUse }), }); const joinData = await joinRes.json().catch(() => ({})); if (joinRes.ok) { notify({ title: 'Thành công', message: joinData.message || 'Bạn đã gia nhập tour!', type: 'success' }); localStorage.removeItem('pendingInviteToken'); if (onJoinSuccess) { onJoinSuccess(data.user, joinData); } onClose(); } else { const errorMessage = joinData.message || 'Không thể gia nhập tour'; setError(`Lỗi gia nhập tour: ${errorMessage}`); setIsLoading(false); } } catch (e: any) { setError(`Lỗi khi gia nhập: ${e.message}`); setIsLoading(false); } } catch (err: any) { console.error('[JoinTourLogin] Login error:', err); setError(err.message); setIsLoading(false); } }; useEffect(() => { if (!isOpen) return; const timer = setTimeout(() => { if (typeof window !== 'undefined' && (window as any).google) { try { (window as any).google.accounts.id.initialize({ client_id: (import.meta as any).env.VITE_GOOGLE_CLIENT_ID || '864264639911-dummyid.apps.googleusercontent.com', callback: handleGoogleLogin, }); (window as any).google.accounts.id.renderButton( document.getElementById('google-signin-btn-join-tour'), { theme: 'outline', size: 'large', width: '380' } ); } catch (e) { console.error('Lỗi khởi tạo Google Sign-in:', e); } } }, 100); return () => clearTimeout(timer); }, [isOpen]); if (!isOpen) return null; return (
{/* Header */}
{/* Content */}

Gia nhập tour

Đăng nhập để tham gia chuyến du lịch này

{error && (
{error}
)} {/* Google OAuth Button */}
Hoặc
{/* Email/Password Form */}
setEmail(e.target.value)} placeholder="your@email.com" required className="w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition" />
setPassword(e.target.value)} placeholder="Nhập mật khẩu" required className="w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition" />
{/* Signup Link */}
Chưa có tài khoản?{' '}
); };