import React, { useState, useEffect } from 'react'; import { Box, Card, CardContent, Typography, TextField, Button, Alert, CircularProgress, Link, Divider, InputAdornment, Dialog, DialogTitle, DialogContent, DialogActions, Checkbox, FormControlLabel, Grid } from '@mui/material'; import { Email as EmailIcon, Security as SecurityIcon, CheckCircle as CheckCircleIcon, ErrorOutline as ErrorIcon, Refresh as RefreshIcon, DevicesOther as DevicesIcon } from '@mui/icons-material'; import { ApiClient } from 'services/api-client'; // Email Verification Component export function EmailVerificationPage() { const [verificationToken, setVerificationToken] = useState(''); const [loading, setLoading] = useState(false); const [status, setStatus] = useState<'pending' | 'success' | 'error'>('pending'); const [message, setMessage] = useState(''); const [userType, setUserType] = useState(''); const apiClient = new ApiClient(); useEffect(() => { // Get token from URL parameters const urlParams = new URLSearchParams(window.location.search); const token = urlParams.get('token'); if (token) { setVerificationToken(token); handleVerifyEmail(token); } }, []); const handleVerifyEmail = async (token: string) => { if (!token) { setStatus('error'); setMessage('Invalid verification link'); return; } setLoading(true); try { const response = await fetch('/api/1.0/auth/verify-email', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ token }), }); const data = await response.json(); if (data.success) { setStatus('success'); setMessage(data.data.message); setUserType(data.data.userType); // Redirect to login after 3 seconds setTimeout(() => { window.location.href = '/login'; }, 3000); } else { setStatus('error'); setMessage(data.error?.message || 'Verification failed'); } } catch (error) { setStatus('error'); setMessage('Network error occurred. Please try again.'); } finally { setLoading(false); } }; const handleResendVerification = async () => { // This would need the email address - you might want to add an input for it // or store it in localStorage from the registration process try { setLoading(true); const response = await fetch('/api/1.0/auth/resend-verification', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email: localStorage.getItem('pendingVerificationEmail') || '' }), }); const data = await response.json(); if (data.success) { setMessage('Verification email sent! Please check your inbox.'); } } catch (error) { setMessage('Failed to resend verification email.'); } finally { setLoading(false); } }; return ( {status === 'pending' && ( <> Verifying Email Please wait while we verify your email address... )} {status === 'success' && ( <> Email Verified! Your {userType} account has been successfully activated. )} {status === 'error' && ( <> Verification Failed We couldn't verify your email address. )} {loading && ( )} {message && ( {message} )} {status === 'success' && ( You will be redirected to the login page in a few seconds... )} {status === 'error' && ( )} ); } // MFA Verification Component export function MFAVerificationDialog({ open, onClose, email, deviceId, deviceName, onVerificationSuccess }: { open: boolean; onClose: () => void; email: string; deviceId: string; deviceName: string; onVerificationSuccess: (authData: any) => void; }) { const [code, setCode] = useState(''); const [rememberDevice, setRememberDevice] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [timeLeft, setTimeLeft] = useState(600); // 10 minutes in seconds const apiClient = new ApiClient(); useEffect(() => { if (!open) return; const timer = setInterval(() => { setTimeLeft((prev) => { if (prev <= 1) { clearInterval(timer); setError('MFA code has expired. Please try logging in again.'); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [open]); const formatTime = (seconds: number) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; }; const handleVerifyMFA = async () => { if (!code || code.length !== 6) { setError('Please enter a valid 6-digit code'); return; } setLoading(true); setError(''); try { const response = await fetch('/api/1.0/auth/mfa/verify', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, code, deviceId, rememberDevice, }), }); const data = await response.json(); if (data.success) { onVerificationSuccess(data.data); onClose(); } else { setError(data.error?.message || 'Invalid verification code'); } } catch (error) { setError('Network error occurred. Please try again.'); } finally { setLoading(false); } }; const handleResendCode = async () => { setLoading(true); try { const response = await fetch('/api/1.0/auth/mfa/request', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email, password: '', // This would need to be stored securely or re-entered deviceId, deviceName, }), }); const data = await response.json(); if (data.success) { setTimeLeft(600); // Reset timer setError(''); alert('New verification code sent to your email'); } } catch (error) { setError('Failed to resend code'); } finally { setLoading(false); } }; return ( Verify Your Identity We've detected a login from a new device: {deviceName} We've sent a 6-digit verification code to: {email} { const value = e.target.value.replace(/\D/g, '').slice(0, 6); setCode(value); setError(''); }} placeholder="000000" inputProps={{ maxLength: 6, style: { fontSize: 24, textAlign: 'center', letterSpacing: 8 } }} sx={{ mt: 2, mb: 2 }} error={!!error} helperText={error} /> Code expires in: {formatTime(timeLeft)} setRememberDevice(e.target.checked)} /> } label="Remember this device for 90 days" /> If you didn't attempt to log in, please change your password immediately. ); } // Enhanced Registration Success Component export function RegistrationSuccessDialog({ open, onClose, email, userType }: { open: boolean; onClose: () => void; email: string; userType: string; }) { const [resendLoading, setResendLoading] = useState(false); const [resendMessage, setResendMessage] = useState(''); const handleResendVerification = async () => { setResendLoading(true); try { const response = await fetch('/api/1.0/auth/resend-verification', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ email }), }); const data = await response.json(); setResendMessage(data.success ? 'Verification email sent!' : 'Failed to resend email. Please try again later.' ); } catch (error) { setResendMessage('Network error. Please try again.'); } finally { setResendLoading(false); } }; return ( Check Your Email We've sent a verification link to: {email} Next steps:
1. Check your email inbox (and spam folder)
2. Click the verification link
3. Your {userType} account will be activated
{resendMessage && ( {resendMessage} )}
); } // Enhanced Login Component with MFA Support export function EnhancedLoginForm() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [mfaRequired, setMfaRequired] = useState(false); const [mfaData, setMfaData] = useState(null); const apiClient = new ApiClient(); // Generate device fingerprint (simplified) const getDeviceFingerprint = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx!.textBaseline = 'top'; ctx!.font = '14px Arial'; ctx!.fillText('Device fingerprint', 2, 2); const fingerprint = canvas.toDataURL() + navigator.userAgent + navigator.language + screen.width + 'x' + screen.height; return btoa(fingerprint).slice(0, 16); }; const getDeviceName = () => { const ua = navigator.userAgent; const browserName = ua.includes('Chrome') ? 'Chrome' : ua.includes('Firefox') ? 'Firefox' : ua.includes('Safari') ? 'Safari' : 'Browser'; const osName = ua.includes('Windows') ? 'Windows' : ua.includes('Mac') ? 'macOS' : ua.includes('Linux') ? 'Linux' : ua.includes('Android') ? 'Android' : ua.includes('iOS') ? 'iOS' : 'Unknown OS'; return `${browserName} on ${osName}`; }; const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setError(''); try { const response = await fetch('/api/1.0/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ login: email, password, }), }); const data = await response.json(); if (data.success) { if (data.data.mfaRequired) { // MFA required for new device setMfaRequired(true); setMfaData({ email, deviceId: data.data.deviceId, deviceName: data.data.deviceName, }); } else { // Normal login success handleLoginSuccess(data.data); } } else { setError(data.error?.message || 'Login failed'); } } catch (error) { setError('Network error occurred. Please try again.'); } finally { setLoading(false); } }; const handleMFASuccess = (authData: any) => { handleLoginSuccess(authData); }; const handleLoginSuccess = (authData: any) => { // Store tokens localStorage.setItem('accessToken', authData.accessToken); localStorage.setItem('refreshToken', authData.refreshToken); localStorage.setItem('user', JSON.stringify(authData.user)); // Redirect based on user type const userType = authData.user.userType; window.location.href = userType === 'employer' ? '/employer-dashboard' : '/candidate-dashboard'; }; return ( setEmail(e.target.value)} autoComplete="email" autoFocus /> setPassword(e.target.value)} autoComplete="current-password" /> {error && ( {error} )} {/* MFA Dialog */} {mfaRequired && mfaData && ( setMfaRequired(false)} email={mfaData.email} deviceId={mfaData.deviceId} deviceName={mfaData.deviceName} onVerificationSuccess={handleMFASuccess} /> )} ); } // Device Management Component export function TrustedDevicesManager() { const [devices, setDevices] = useState([]); const [loading, setLoading] = useState(true); // This would need API endpoints to manage trusted devices useEffect(() => { // Load trusted devices setLoading(false); }, []); return ( Trusted Devices Manage devices that you've marked as trusted. You won't need to verify your identity when signing in from these devices. {devices.length === 0 ? ( No trusted devices yet. When you log in from a new device and choose to remember it, it will appear here. ) : ( {devices.map((device, index) => ( {device.deviceName} Added: {new Date(device.addedAt).toLocaleDateString()} Last used: {new Date(device.lastUsed).toLocaleDateString()} ))} )} ); }