"use client"; import { useState, useEffect, Suspense } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { InputOTP, InputOTPGroup, InputOTPSlot, } from "@/components/ui/input-otp"; import { Heart, Eye, EyeOff, X, Loader2, CheckCircle2, Mail } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { useAppTheme } from "@/components/ThemeProvider"; import { useAuth } from "@/hooks/useAuth"; import { loginSchema, registerSchema, verifyOtpSchema, type LoginInput, type RegisterInput, type VerifyOtpInput } from "@/lib/schema/auth"; import { toast } from "sonner"; type Step = "login" | "signup" | "verify"; function LoginContent() { const { theme } = useAppTheme(); const isDark = theme === "dark"; const [step, setStep] = useState("login"); const [showPassword, setShowPassword] = useState(false); const [showPassword2, setShowPassword2] = useState(false); const [rememberMe, setRememberMe] = useState(false); const [registeredEmail, setRegisteredEmail] = useState(""); const [showResendOtp, setShowResendOtp] = useState(false); // Login form data const [loginData, setLoginData] = useState({ email: "", password: "", }); // Signup form data const [signupData, setSignupData] = useState({ first_name: "", last_name: "", email: "", phone_number: "", password: "", password2: "", }); // OTP verification data const [otpData, setOtpData] = useState({ email: "", otp: "", }); const [errors, setErrors] = useState>>({}); const router = useRouter(); const searchParams = useSearchParams(); const { login, register, verifyOtp, isAuthenticated, isAdmin, loginMutation, registerMutation, verifyOtpMutation, resendOtpMutation } = useAuth(); // Check for verify step or email from query parameters useEffect(() => { const verifyEmail = searchParams.get("verify"); const emailParam = searchParams.get("email"); const errorParam = searchParams.get("error"); // Don't show verify step if there's an error indicating OTP sending failed if (errorParam && errorParam.toLowerCase().includes("failed to send")) { setStep("login"); return; } if (verifyEmail === "true" && emailParam) { // Show verify step if verify=true setStep("verify"); setRegisteredEmail(emailParam); setOtpData({ email: emailParam, otp: "" }); } else if (emailParam && step === "login") { // Pre-fill email in login form if email parameter is present setLoginData(prev => ({ ...prev, email: emailParam })); } }, [searchParams, step]); // Redirect if already authenticated useEffect(() => { if (isAuthenticated) { // Use a small delay to ensure cookies are set and middleware has processed const timer = setTimeout(() => { // Always redirect based on user role, ignore redirect parameter if user is admin const redirectParam = searchParams.get("redirect"); const defaultRedirect = isAdmin ? "/admin/dashboard" : "/user/dashboard"; const finalRedirect = isAdmin ? "/admin/dashboard" : (redirectParam || defaultRedirect); // Use window.location.href to ensure full page reload and cookie reading window.location.href = finalRedirect; }, 200); return () => clearTimeout(timer); } }, [isAuthenticated, isAdmin, searchParams]); // Handle login const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setErrors({}); // Validate form const validation = loginSchema.safeParse(loginData); if (!validation.success) { const fieldErrors: Partial> = {}; validation.error.issues.forEach((err) => { if (err.path[0]) { fieldErrors[err.path[0] as string] = err.message; } }); setErrors(fieldErrors); return; } try { const result = await login(loginData); if (result.tokens && result.user) { toast.success("Login successful!"); // Wait a moment for cookies to be set, then redirect // Check if user is admin/staff/superuser - check all possible field names const user = result.user as any; const isTruthy = (value: any): boolean => { if (value === true || value === "true" || value === 1 || value === "1") return true; return false; }; const userIsAdmin = isTruthy(user.is_admin) || isTruthy(user.isAdmin) || isTruthy(user.is_staff) || isTruthy(user.isStaff) || isTruthy(user.is_superuser) || isTruthy(user.isSuperuser); // Wait longer for cookies to be set and middleware to process setTimeout(() => { // Always redirect based on user role, ignore redirect parameter if user is admin // This ensures admins always go to admin dashboard const defaultRedirect = userIsAdmin ? "/admin/dashboard" : "/user/dashboard"; // Only use redirect parameter if user is NOT admin const redirectParam = searchParams.get("redirect"); const finalRedirect = userIsAdmin ? "/admin/dashboard" : (redirectParam || defaultRedirect); // Use window.location.href instead of router.push to ensure full page reload // This ensures cookies are read correctly by middleware window.location.href = finalRedirect; }, 300); } } catch (error) { const errorMessage = error instanceof Error ? error.message : "Login failed. Please try again."; toast.error(errorMessage); // Check if error is about email verification if (errorMessage.toLowerCase().includes("verify your email") || errorMessage.toLowerCase().includes("email address before logging")) { setShowResendOtp(true); } else { setShowResendOtp(false); } setErrors({}); } }; // Handle signup const handleSignup = async (e: React.FormEvent) => { e.preventDefault(); setErrors({}); // Validate form const validation = registerSchema.safeParse(signupData); if (!validation.success) { const fieldErrors: Partial> = {}; validation.error.issues.forEach((err) => { if (err.path[0]) { fieldErrors[err.path[0] as string] = err.message; } }); setErrors(fieldErrors); return; } try { const result = await register(signupData); // Check if registration was successful (user created) // Even if OTP sending failed, we should allow user to proceed to verification // and use resend OTP feature if (result && result.message) { // Registration successful - proceed to OTP verification toast.success("Registration successful! Please check your email for OTP verification."); setRegisteredEmail(signupData.email); setOtpData({ email: signupData.email, otp: "" }); setStep("verify"); } else { // If no message but no error, still proceed (some APIs might not return message) toast.success("Registration successful! Please check your email for OTP verification."); setRegisteredEmail(signupData.email); setOtpData({ email: signupData.email, otp: "" }); setStep("verify"); } } catch (error) { // Handle different types of errors let errorMessage = "Registration failed. Please try again."; if (error instanceof Error) { errorMessage = error.message; // If OTP sending failed, don't show OTP verification - just show error if (errorMessage.toLowerCase().includes("failed to send") || errorMessage.toLowerCase().includes("failed to send otp")) { toast.error("Registration failed: OTP could not be sent. Please try again later or contact support."); setErrors({}); return; } // Check if it's an OTP sending error but registration might have succeeded if (errorMessage.toLowerCase().includes("otp") || errorMessage.toLowerCase().includes("email") || errorMessage.toLowerCase().includes("send")) { // If OTP sending failed but user might be created, allow proceeding to verification // User can use resend OTP toast.warning("Registration completed, but OTP email could not be sent. You can request a new OTP on the next screen."); setRegisteredEmail(signupData.email); setOtpData({ email: signupData.email, otp: "" }); setStep("verify"); return; } } toast.error(errorMessage); setErrors({}); } }; // Handle OTP verification const handleVerifyOtp = async (e: React.FormEvent) => { e.preventDefault(); setErrors({}); // Use registeredEmail if available, otherwise use otpData.email const emailToVerify = registeredEmail || otpData.email; if (!emailToVerify) { setErrors({ email: "Email address is required" }); return; } // Prepare OTP data with email const otpToVerify = { email: emailToVerify, otp: otpData.otp, }; // Validate OTP const validation = verifyOtpSchema.safeParse(otpToVerify); if (!validation.success) { const fieldErrors: Partial> = {}; validation.error.issues.forEach((err) => { if (err.path[0]) { fieldErrors[err.path[0] as string] = err.message; } }); setErrors(fieldErrors); return; } try { const result = await verifyOtp(otpToVerify); // If verification is successful, switch to login step toast.success("Email verified successfully! You can now login."); // Switch to login step and pre-fill email setStep("login"); setLoginData(prev => ({ ...prev, email: emailToVerify })); setOtpData({ email: "", otp: "" }); setRegisteredEmail(""); } catch (error) { const errorMessage = error instanceof Error ? error.message : "OTP verification failed. Please try again."; toast.error(errorMessage); setErrors({}); } }; // Handle resend OTP const handleResendOtp = async () => { const emailToUse = registeredEmail || otpData.email; if (!emailToUse) { toast.error("Email address is required to resend OTP."); return; } try { await resendOtpMutation.mutateAsync({ email: emailToUse, context: "registration" }); toast.success("OTP resent successfully! Please check your email."); // Update registeredEmail if it wasn't set if (!registeredEmail) { setRegisteredEmail(emailToUse); } } catch (error) { let errorMessage = "Failed to resend OTP. Please try again."; if (error instanceof Error) { errorMessage = error.message; // Provide more helpful error messages if (errorMessage.toLowerCase().includes("ssl") || errorMessage.toLowerCase().includes("certificate")) { errorMessage = "Email service is currently unavailable. Please contact support or try again later."; } else if (errorMessage.toLowerCase().includes("not found") || errorMessage.toLowerCase().includes("does not exist")) { errorMessage = "Email address not found. Please check your email or register again."; } } toast.error(errorMessage); } }; // Handle form field changes const handleLoginChange = (field: keyof LoginInput, value: string) => { setLoginData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: undefined })); } }; const handleSignupChange = (field: keyof RegisterInput, value: string) => { setSignupData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: undefined })); } }; const handleOtpChange = (field: keyof VerifyOtpInput, value: string) => { setOtpData((prev) => ({ ...prev, [field]: value })); if (errors[field]) { setErrors((prev) => ({ ...prev, [field]: undefined })); } }; return (
{/* Background Image */}
Therapy and counseling session with African American clients {/* Overlay for better readability */}
{/* Branding - Top Left */}
Attune Heart Therapy
{/* Centered White Card */}
{/* Header with Close Button */}
{/* Heading */}

{step === "login" && "Welcome back"} {step === "verify" && "Verify your email"}

{/* Subtitle */} {step === "login" && (

Sign in to access your admin dashboard

)} {step === "verify" && registeredEmail && (

We've sent a verification code to {registeredEmail}

)} {step === "verify" && !registeredEmail && (

Enter the verification code sent to your email

)}
{/* Close Button */}
{/* Login Form */} {step === "login" && (
{/* Email Field */}
handleLoginChange("email", e.target.value)} className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.email ? 'border-red-500' : ''}`} required /> {errors.email && (

{errors.email}

)}
{/* Password Field */}
handleLoginChange("password", e.target.value)} className={`h-12 pr-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.password ? 'border-red-500' : ''}`} required />
{errors.password && (

{errors.password}

)}
{/* Submit Button */} {/* Resend OTP - Show when email verification error occurs */} {showResendOtp && (

Email verification required

Please verify your email address before logging in. We can resend the verification code to {loginData.email}.

)} {/* Remember Me & Forgot Password */}
)} {/* OTP Verification Form */} {step === "verify" && (

Check your email

We've sent a 6-digit verification code to your email address.

{/* Email Field (if not set) */} {!registeredEmail && (
handleOtpChange("email", e.target.value)} className={`h-12 ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'} ${errors.email ? 'border-red-500' : ''}`} required /> {errors.email && (

{errors.email}

)}
)} {/* OTP Field */}
handleOtpChange("otp", value)} aria-invalid={!!errors.otp} >
{errors.otp && (

{errors.otp}

)}
{/* Resend OTP */}
{/* Submit Button */} {/* Back to login */}
)}
); } export default function Login() { return ( }> ); }