"use client" import type React from "react" import { useState, useRef, useEffect } from "react" import { useRouter, useSearchParams } from "next/navigation" import Link from "next/link" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Alert, AlertDescription } from "@/components/ui/alert" import { useAuth } from "@/contexts/supabase-auth-context" import { Cpu, Eye, EyeOff, Loader2 } from "lucide-react" export default function LoginPage() { const [email, setEmail] = useState("") const [password, setPassword] = useState("") const [showPassword, setShowPassword] = useState(false) const [rememberMe, setRememberMe] = useState(false) const [error, setError] = useState("") const [step, setStep] = useState<'login' | 'verify'>('login') const [verificationCode, setVerificationCode] = useState("") const [userId, setUserId] = useState(null) const [isSendingCode, setIsSendingCode] = useState(false) const [isVerifying, setIsVerifying] = useState(false) const router = useRouter() const searchParams = useSearchParams() const { login, sendLoginVerificationCode, verifyLoginCode, isLoading } = useAuth() const [isLoggingIn, setIsLoggingIn] = useState(false) const errorRef = useRef(null) // Safe redirect: only allow relative paths (e.g. /admin) to avoid open redirect const redirectTo = (() => { const r = searchParams.get("redirect") if (!r || typeof r !== "string") return "/dashboard" const path = r.startsWith("/") ? r : `/${r}` if (!path.startsWith("/") || path.startsWith("//")) return "/dashboard" return path })() // When error is set, scroll it into view and announce to screen readers useEffect(() => { if (error && errorRef.current) { errorRef.current.focus({ preventScroll: false }) errorRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" }) } }, [error]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); setIsLoggingIn(true); // Client-side validation if (!email || !password) { setError("Email and password are required"); setIsLoggingIn(false); return; } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { setError("Please enter a valid email address"); setIsLoggingIn(false); return; } if (password.length < 6) { setError("Password must be at least 6 characters long"); setIsLoggingIn(false); return; } try { const result = await login(email, password, rememberMe); if (result.success && result.requiresVerification && result.userId) { // Send verification code setUserId(result.userId); setIsSendingCode(true); const codeResult = await sendLoginVerificationCode(email, result.userId); setIsSendingCode(false); if (codeResult.success) { setStep('verify'); } else { setError(codeResult.error || "Failed to send verification code"); } } else if (result.success) { // No verification needed, redirect based on userType if (result.userType === "admin") { router.push("/admin/page"); } else { router.push(redirectTo); } } else { setError(result.error || "Invalid email or password. Please try again."); } } catch (err: any) { console.error("Login error:", err); setError(err.message || "Login failed. Please try again."); } finally { setIsLoggingIn(false); } }; const handleVerifyCode = async (e: React.FormEvent) => { e.preventDefault(); setError(""); if (!verificationCode || verificationCode.length !== 6) { setError("Please enter the 6-digit verification code"); return; } setIsVerifying(true); try { const verifyResult = await verifyLoginCode(email, verificationCode); if (verifyResult.success) { // Complete login, skip verification this time const loginResult = await login(email, password, rememberMe, true); if (loginResult.success) { // Redirect based on userType if (loginResult.userType === "admin") { router.push("/admin"); } else { router.push(redirectTo); } } else { setError(loginResult.error || "Login verification successful but failed to complete login. Please try again."); } } else { setError(verifyResult.error || "Invalid verification code"); } } catch (err: any) { console.error("Verify code error:", err); setError(err.message || "Failed to verify code"); } finally { setIsVerifying(false); } }; const handleResendCode = async () => { if (!userId) return setError("") setIsSendingCode(true) try { const codeResult = await sendLoginVerificationCode(email, userId) if (codeResult.success) { setError("") // Clear any previous errors } else { setError(codeResult.error || "Failed to resend verification code") } } catch (err: any) { setError(err.message || "Failed to resend code") } finally { setIsSendingCode(false) } } return (
{/* BuildMate Logo - Centered */}

BuildMate

{/* Header */}

Welcome Back

Sign in to continue building your dream PC

{/* Login Form or Verification Form */} {step === 'login' ? 'Sign In' : 'Verify Email'} {step === 'login' ? 'Enter your credentials to access your account' : `Enter the 6-digit verification code sent to ${email}` } {step === 'login' ? (
{error && (
{error}
)}
setEmail(e.target.value)} autoComplete="email" autoFocus required aria-invalid={!!error} aria-describedby={error ? "login-error" : undefined} />
setPassword(e.target.value)} autoComplete="current-password" required aria-invalid={!!error} />
setRememberMe(e.target.checked)} className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
Forgot password?
) : (
{error && (
{error}
)}
{ const value = e.target.value.replace(/\D/g, '').slice(0, 6) setVerificationCode(value) }} maxLength={6} className="text-center text-2xl tracking-widest font-mono" required aria-invalid={!!error} aria-describedby={error ? "verify-error" : undefined} />

Check your email for the verification code

)}

Don't have an account?{" "} Sign up

← Back to Home
) }