import React, { useState, useEffect } from 'react'; import { Navigate, useSearchParams } from 'react-router-dom'; import { Lock, Mail, Eye, EyeOff, AlertCircle } from 'lucide-react'; import { toast } from 'react-toastify'; import { useQuery } from '@tanstack/react-query'; import { Button, Input, Card, ReCaptcha } from '../../components/common'; import { useAdminAuth } from '../../contexts'; import { authService } from '../../services/auth.service'; import { getAuthToken, api } from '../../config/api'; export const AdminLoginPage: React.FC = () => { const { isAuthenticated, login } = useAdminAuth(); const [searchParams] = useSearchParams(); const [formData, setFormData] = useState({ email: '', password: '', }); const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); const [errors, setErrors] = useState>({}); const [loginSuccess, setLoginSuccess] = useState(false); const [recaptchaToken, setRecaptchaToken] = useState(null); // Fetch branding settings const { data: settingsData } = useQuery({ queryKey: ['admin-login-settings'], queryFn: async () => { const response = await api.get('/public/settings'); return response.data; }, staleTime: 5 * 60 * 1000, // Cache for 5 minutes }); // Check for session expired message useEffect(() => { if (searchParams.get('session') === 'expired') { toast.info('Your session has expired. Please log in again.'); } }, [searchParams]); // Redirect if already authenticated or login successful if (isAuthenticated || loginSuccess) { return ; } const validateForm = (): boolean => { const newErrors: Record = {}; if (!formData.email) { newErrors.email = 'Email is required'; } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { newErrors.email = 'Invalid email format'; } if (!formData.password) { newErrors.password = 'Password is required'; } else if (formData.password.length < 6) { newErrors.password = 'Password must be at least 6 characters'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!validateForm()) { return; } setIsLoading(true); setErrors({}); try { const response = await authService.adminLogin({ ...formData, recaptchaToken }); login(response.token, response.user); toast.success('Login successful!'); setLoginSuccess(true); } catch (error: any) { console.error('Login error:', error); // Handle network errors gracefully if (error.code === 'ERR_NETWORK' || error.code === 'ERR_CONNECTION_RESET') { // Check if we actually got logged in despite the error const token = getAuthToken(true); if (token) { // Login was successful, just had a connection issue setLoginSuccess(true); return; } toast.error('Network error. Please check your connection and try again.'); } else if (error.response?.status === 429) { toast.error('Too many login attempts. Please try again later.'); } else if (error.response?.status === 401) { setErrors({ form: 'Invalid email or password' }); } else { toast.error('An error occurred. Please try again.'); } } finally { setIsLoading(false); } }; const handleInputChange = (field: string) => (e: React.ChangeEvent) => { setFormData(prev => ({ ...prev, [field]: e.target.value })); // Clear error when user starts typing if (errors[field]) { setErrors(prev => ({ ...prev, [field]: '' })); } }; return (
{/* Logo/Header */}
PicPeak

Admin Login

Sign in to manage your photo galleries

{/* Login Form */}
{/* Form Error */} {errors.form && (

{errors.form}

)} {/* Email Field */}
} autoComplete="email" autoFocus />
{/* Password Field */}
} autoComplete="current-password" />
{/* Remember Me & Forgot Password */}
Forgot password?
{/* reCAPTCHA */} setRecaptchaToken(null)} /> {/* Submit Button */}
{/* Footer */} {/* Development Hint */} {import.meta.env.DEV && (

Development Mode: Use email: admin@example.com, password: admin123

)}
); }; AdminLoginPage.displayName = 'AdminLoginPage';