From 343c2c66b6bdf5cda4599461fdc979210b799bb5 Mon Sep 17 00:00:00 2001 From: iamkiddy Date: Mon, 24 Nov 2025 22:35:07 +0000 Subject: [PATCH] Refactor LoginDialog to support pre-filling email and integrate Forgot Password functionality. Update Navbar to manage dialog states for login and signup, enhancing user experience with smoother transitions between dialogs. Remove unused signup logic from LoginDialog for a cleaner implementation. --- app/(admin)/_components/header.tsx | 14 +- components/ForgotPasswordDialog.tsx | 459 ++++++++++++++++++++++++++ components/LoginDialog.tsx | 375 ++++++---------------- components/Navbar.tsx | 25 ++ components/SignupDialog.tsx | 479 ++++++++++++++++++++++++++++ 5 files changed, 1053 insertions(+), 299 deletions(-) create mode 100644 components/ForgotPasswordDialog.tsx create mode 100644 components/SignupDialog.tsx diff --git a/app/(admin)/_components/header.tsx b/app/(admin)/_components/header.tsx index a8b6e5f..0475a10 100644 --- a/app/(admin)/_components/header.tsx +++ b/app/(admin)/_components/header.tsx @@ -99,19 +99,7 @@ export function Header() { Book Appointment - - - Documentation - + {/* Right Side Actions */} diff --git a/components/ForgotPasswordDialog.tsx b/components/ForgotPasswordDialog.tsx new file mode 100644 index 0000000..db59f4c --- /dev/null +++ b/components/ForgotPasswordDialog.tsx @@ -0,0 +1,459 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { useAppTheme } from "@/components/ThemeProvider"; +import { Input } from "@/components/ui/input"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Eye, EyeOff, Loader2, X, CheckCircle2 } from "lucide-react"; +import { useAuth } from "@/hooks/useAuth"; +import { + forgotPasswordSchema, + verifyPasswordResetOtpSchema, + resetPasswordSchema, + type ForgotPasswordInput, + type VerifyPasswordResetOtpInput, + type ResetPasswordInput +} from "@/lib/schema/auth"; +import { toast } from "sonner"; + +interface ForgotPasswordDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSuccess?: () => void; +} + +type Step = "request" | "verify" | "reset"; + +export function ForgotPasswordDialog({ open, onOpenChange, onSuccess }: ForgotPasswordDialogProps) { + const { theme } = useAppTheme(); + const isDark = theme === "dark"; + const { + forgotPasswordMutation, + verifyPasswordResetOtpMutation, + resetPasswordMutation, + resendOtpMutation + } = useAuth(); + const [step, setStep] = useState("request"); + const [email, setEmail] = useState(""); + const [otpData, setOtpData] = useState({ + email: "", + otp: "", + }); + const [resetData, setResetData] = useState({ + email: "", + otp: "", + new_password: "", + confirm_password: "", + }); + const [showPassword, setShowPassword] = useState(false); + const [showPassword2, setShowPassword2] = useState(false); + + const handleRequestOtp = async (e: React.FormEvent) => { + e.preventDefault(); + + const validation = forgotPasswordSchema.safeParse({ email }); + if (!validation.success) { + const firstError = validation.error.issues[0]; + toast.error(firstError.message); + return; + } + + try { + await forgotPasswordMutation.mutateAsync({ email }); + setOtpData({ email, otp: "" }); + setResetData({ email, otp: "", new_password: "", confirm_password: "" }); + setStep("verify"); + toast.success("Password reset OTP sent! Please check your email."); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Failed to send OTP. Please try again."; + toast.error(errorMessage); + } + }; + + const handleVerifyOtp = async (e: React.FormEvent) => { + e.preventDefault(); + + const emailToVerify = email || otpData.email; + if (!emailToVerify) { + toast.error("Email is required"); + return; + } + + const validation = verifyPasswordResetOtpSchema.safeParse({ + email: emailToVerify, + otp: otpData.otp, + }); + + if (!validation.success) { + const firstError = validation.error.issues[0]; + toast.error(firstError.message); + return; + } + + try { + await verifyPasswordResetOtpMutation.mutateAsync({ + email: emailToVerify, + otp: otpData.otp, + }); + setResetData({ + email: emailToVerify, + otp: otpData.otp, + new_password: "", + confirm_password: "" + }); + setStep("reset"); + toast.success("OTP verified! Please set your new password."); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "OTP verification failed. Please try again."; + toast.error(errorMessage); + } + }; + + const handleResetPassword = async (e: React.FormEvent) => { + e.preventDefault(); + + const validation = resetPasswordSchema.safeParse(resetData); + if (!validation.success) { + const firstError = validation.error.issues[0]; + toast.error(firstError.message); + return; + } + + try { + await resetPasswordMutation.mutateAsync(resetData); + toast.success("Password reset successful! Please log in with your new password."); + handleDialogChange(false); + if (onSuccess) { + onSuccess(); + } + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Password reset failed. Please try again."; + toast.error(errorMessage); + } + }; + + const handleResendOtp = async () => { + const emailToResend = email || otpData.email; + if (!emailToResend) { + toast.error("Email is required"); + return; + } + + try { + await resendOtpMutation.mutateAsync({ + email: emailToResend, + context: "password_reset" + }); + toast.success("OTP resent successfully! Please check your email."); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Failed to resend OTP"; + toast.error(errorMessage); + } + }; + + const handleOtpChange = (field: keyof VerifyPasswordResetOtpInput, value: string) => { + setOtpData((prev) => ({ ...prev, [field]: value })); + }; + + // Reset step when dialog closes + const handleDialogChange = (isOpen: boolean) => { + if (!isOpen) { + setStep("request"); + setEmail(""); + setOtpData({ email: "", otp: "" }); + setResetData({ email: "", otp: "", new_password: "", confirm_password: "" }); + } + onOpenChange(isOpen); + }; + + return ( + + + {/* Header with Close Button - Fixed */} +
+ + + {step === "request" && "Reset Password"} + {step === "verify" && "Verify OTP"} + {step === "reset" && "Set New Password"} + + + {step === "request" && "Enter your email to receive a password reset code"} + {step === "verify" && "Enter the verification code sent to your email"} + {step === "reset" && "Enter your new password"} + + + {/* Close Button */} + +
+ + {/* Scrollable Content */} +
+ {/* Request OTP Form */} + {step === "request" && ( +
+
+ + setEmail(e.target.value)} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ + +
+ )} + + {/* Verify OTP Form */} + {step === "verify" && ( +
+
+
+ +
+

+ Check your email +

+

+ We've sent a 6-digit verification code to {email || otpData.email || "your email address"}. +

+
+
+
+ + {/* Email Field (if not set) */} + {!email && ( +
+ + handleOtpChange("email", e.target.value)} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ )} + + {/* OTP Field */} +
+ +
+ handleOtpChange("otp", value)} + > + + + + + + + + + +
+
+ + {/* Resend OTP */} +
+ +
+ + {/* Submit Button */} + + + {/* Back to request */} +
+ +
+
+ )} + + {/* Reset Password Form */} + {step === "reset" && ( +
+ {/* New Password Field */} +
+ +
+ setResetData({ ...resetData, new_password: e.target.value })} + className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> + +
+
+ + {/* Confirm Password Field */} +
+ +
+ setResetData({ ...resetData, confirm_password: e.target.value })} + className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> + +
+
+ + {/* Submit Button */} + + + {/* Back to verify */} +
+ +
+
+ )} +
+
+
+ ); +} + diff --git a/components/LoginDialog.tsx b/components/LoginDialog.tsx index ab1ddf0..85ef759 100644 --- a/components/LoginDialog.tsx +++ b/components/LoginDialog.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { useAppTheme } from "@/components/ThemeProvider"; import { Input } from "@/components/ui/input"; @@ -13,43 +13,41 @@ import { } from "@/components/ui/dialog"; import { Eye, EyeOff, Loader2, X } from "lucide-react"; import { useAuth } from "@/hooks/useAuth"; -import { loginSchema, registerSchema, type LoginInput, type RegisterInput } from "@/lib/schema/auth"; +import { loginSchema, type LoginInput } from "@/lib/schema/auth"; import { toast } from "sonner"; import { useRouter } from "next/navigation"; +import { ForgotPasswordDialog } from "./ForgotPasswordDialog"; interface LoginDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onLoginSuccess: () => void; + prefillEmail?: string; + onSwitchToSignup?: () => void; } // Login Dialog component -export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogProps) { +export function LoginDialog({ open, onOpenChange, onLoginSuccess, prefillEmail, onSwitchToSignup }: LoginDialogProps) { const { theme } = useAppTheme(); const isDark = theme === "dark"; const router = useRouter(); - const { login, register, loginMutation, registerMutation } = useAuth(); - const [isSignup, setIsSignup] = useState(false); + const { login, loginMutation } = useAuth(); const [loginData, setLoginData] = useState({ email: "", password: "", }); - const [signupData, setSignupData] = useState({ - first_name: "", - last_name: "", - email: "", - phone_number: "", - password: "", - password2: "", - }); const [showPassword, setShowPassword] = useState(false); - const [showPassword2, setShowPassword2] = useState(false); - const [rememberMe, setRememberMe] = useState(false); - const [error, setError] = useState(null); + const [forgotPasswordDialogOpen, setForgotPasswordDialogOpen] = useState(false); + + // Pre-fill email if provided + useEffect(() => { + if (prefillEmail && open) { + setLoginData(prev => ({ ...prev, email: prefillEmail })); + } + }, [prefillEmail, open]); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); - setError(null); // Validate form const validation = loginSchema.safeParse(loginData); @@ -64,9 +62,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP if (result.tokens && result.user) { toast.success("Login successful!"); - setShowPassword(false); - onOpenChange(false); - onLoginSuccess(); + setShowPassword(false); + onOpenChange(false); + // Reset form + setLoginData({ email: "", password: "" }); + // Redirect to user dashboard + router.push("/user/dashboard"); + onLoginSuccess(); } } catch (err) { const errorMessage = err instanceof Error ? err.message : "Login failed. Please try again."; @@ -74,62 +76,16 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP } }; - const handleSignup = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - - // Validate form - const validation = registerSchema.safeParse(signupData); - if (!validation.success) { - const firstError = validation.error.issues[0]; - toast.error(firstError.message); - return; + // Reset form when dialog closes + const handleDialogChange = (isOpen: boolean) => { + if (!isOpen) { + setLoginData({ email: "", password: "" }); } - - try { - const result = await register(signupData); - - if (result.message) { - toast.success("Registration successful! Please check your email for OTP verification."); - // Switch to login after successful registration - setIsSignup(false); - setLoginData({ email: signupData.email, password: "" }); - setSignupData({ - first_name: "", - last_name: "", - email: "", - phone_number: "", - password: "", - password2: "", - }); - } - } catch (err) { - const errorMessage = err instanceof Error ? err.message : "Signup failed. Please try again."; - toast.error(errorMessage); - } - }; - - const handleSwitchToSignup = () => { - setIsSignup(true); - setError(null); - setLoginData({ email: "", password: "" }); - }; - - const handleSwitchToLogin = () => { - setIsSignup(false); - setError(null); - setSignupData({ - first_name: "", - last_name: "", - email: "", - phone_number: "", - password: "", - password2: "", - }); + onOpenChange(isOpen); }; return ( - + - {isSignup ? "Create an account" : "Welcome back"} + Welcome back - {isSignup - ? "Sign up to complete your booking" - : "Please log in to complete your booking"} + Please log in to complete your booking {/* Close Button */} - + {/* Scrollable Content */}
- {/* Signup Form */} - {isSignup ? ( -
- - {/* First Name Field */} -
- - setSignupData({ ...signupData, first_name: e.target.value })} - className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - required - /> -
- - {/* Last Name Field */} -
- - setSignupData({ ...signupData, last_name: e.target.value })} - className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - required - /> -
- + {/* Login Form */} + {/* Email Field */}
-
- {/* Phone Field */} -
- - setSignupData({ ...signupData, phone_number: e.target.value })} - className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - /> -
- {/* Password Field */}
-
- {/* Confirm Password Field */} -
- -
- setSignupData({ ...signupData, password2: e.target.value })} - className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - required - /> - -
-
- {/* Submit Button */} - {/* Switch to Login */} -

- Already have an account?{" "} - -

-
- ) : ( - /* Login Form */ -
- - {/* Email Field */} -
- - setLoginData({ ...loginData, email: e.target.value })} - className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - required - /> -
- - {/* Password Field */} -
- -
- setLoginData({ ...loginData, password: e.target.value })} - className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} - required - /> + {/* Forgot Password */} +
-
- {/* Submit Button */} - - - {/* Remember Me & Forgot Password */} -
- - -
- - {/* Sign Up Prompt */} -

- New to Attune Heart Therapy?{" "} - -

- - )} + {/* Sign Up Prompt */} +

+ New to Attune Heart Therapy?{" "} + +

+
+ + {/* Forgot Password Dialog */} +
); } - diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 368b1a5..04a8a73 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -6,6 +6,7 @@ import { Heart, Menu, X, LogOut } from "lucide-react"; import { ThemeToggle } from "@/components/ThemeToggle"; import { useEffect, useState } from "react"; import { LoginDialog } from "@/components/LoginDialog"; +import { SignupDialog } from "@/components/SignupDialog"; import { useRouter, usePathname } from "next/navigation"; import Link from "next/link"; import { useAppTheme } from "@/components/ThemeProvider"; @@ -16,6 +17,8 @@ export function Navbar() { const { theme } = useAppTheme(); const isDark = theme === "dark"; const [loginDialogOpen, setLoginDialogOpen] = useState(false); + const [signupDialogOpen, setSignupDialogOpen] = useState(false); + const [prefillEmail, setPrefillEmail] = useState(undefined); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const router = useRouter(); const pathname = usePathname(); @@ -258,6 +261,28 @@ export function Navbar() { open={loginDialogOpen} onOpenChange={setLoginDialogOpen} onLoginSuccess={handleLoginSuccess} + prefillEmail={prefillEmail} + onSwitchToSignup={() => { + setLoginDialogOpen(false); + // Small delay to ensure dialog closes before opening signup + setTimeout(() => { + setSignupDialogOpen(true); + }, 100); + }} + /> + + {/* Signup Dialog */} + { + setSignupDialogOpen(false); + setPrefillEmail(email); + // Small delay to ensure dialog closes before opening login + setTimeout(() => { + setLoginDialogOpen(true); + }, 100); + }} /> ); diff --git a/components/SignupDialog.tsx b/components/SignupDialog.tsx new file mode 100644 index 0000000..8f7ca9a --- /dev/null +++ b/components/SignupDialog.tsx @@ -0,0 +1,479 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@/components/ui/button"; +import { useAppTheme } from "@/components/ThemeProvider"; +import { Input } from "@/components/ui/input"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Eye, EyeOff, Loader2, X, CheckCircle2 } from "lucide-react"; +import { useAuth } from "@/hooks/useAuth"; +import { registerSchema, verifyOtpSchema, type RegisterInput, type VerifyOtpInput } from "@/lib/schema/auth"; +import { toast } from "sonner"; + +interface SignupDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSignupSuccess?: () => void; + onSwitchToLogin?: (email?: string) => void; +} + +type Step = "signup" | "verify"; + +export function SignupDialog({ open, onOpenChange, onSignupSuccess, onSwitchToLogin }: SignupDialogProps) { + const { theme } = useAppTheme(); + const isDark = theme === "dark"; + const { register, verifyOtp, registerMutation, verifyOtpMutation, resendOtpMutation } = useAuth(); + const [step, setStep] = useState("signup"); + const [registeredEmail, setRegisteredEmail] = useState(""); + const [signupData, setSignupData] = useState({ + first_name: "", + last_name: "", + email: "", + phone_number: "", + password: "", + password2: "", + }); + const [otpData, setOtpData] = useState({ + email: "", + otp: "", + }); + const [showPassword, setShowPassword] = useState(false); + const [showPassword2, setShowPassword2] = useState(false); + + const handleSignup = async (e: React.FormEvent) => { + e.preventDefault(); + + // Validate form + const validation = registerSchema.safeParse(signupData); + if (!validation.success) { + const firstError = validation.error.issues[0]; + toast.error(firstError.message); + return; + } + + try { + const result = await register(signupData); + + // Always switch to OTP verification step after successful registration + const email = signupData.email; + setRegisteredEmail(email); + setOtpData({ email: email, otp: "" }); + + // Clear signup form + setSignupData({ + first_name: "", + last_name: "", + email: "", + phone_number: "", + password: "", + password2: "", + }); + + // Switch to verify step + setStep("verify"); + toast.success("Registration successful! Please check your email for OTP verification."); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Signup failed. Please try again."; + toast.error(errorMessage); + } + }; + + const handleVerifyOtp = async (e: React.FormEvent) => { + e.preventDefault(); + + const emailToVerify = registeredEmail || otpData.email; + if (!emailToVerify) { + toast.error("Email is required"); + return; + } + + const validation = verifyOtpSchema.safeParse({ + email: emailToVerify, + otp: otpData.otp, + }); + + if (!validation.success) { + const firstError = validation.error.issues[0]; + toast.error(firstError.message); + return; + } + + try { + const result = await verifyOtp({ + email: emailToVerify, + otp: otpData.otp, + }); + + if (result.message) { + toast.success("Email verified successfully! Please log in."); + // Close signup dialog and open login dialog with email + const emailToPass = emailToVerify; + onOpenChange(false); + // Call onSwitchToLogin with email to open login dialog with pre-filled email + if (onSwitchToLogin) { + onSwitchToLogin(emailToPass); + } + } + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "OTP verification failed. Please try again."; + toast.error(errorMessage); + } + }; + + const handleResendOtp = async () => { + const emailToResend = registeredEmail || otpData.email; + if (!emailToResend) { + toast.error("Email is required"); + return; + } + + try { + await resendOtpMutation.mutateAsync({ email: emailToResend, context: "registration" }); + toast.success("OTP resent successfully! Please check your email."); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : "Failed to resend OTP"; + toast.error(errorMessage); + } + }; + + const handleOtpChange = (field: keyof VerifyOtpInput, value: string) => { + setOtpData((prev) => ({ ...prev, [field]: value })); + }; + + // Reset step when dialog closes + const handleDialogChange = (isOpen: boolean) => { + if (!isOpen) { + setStep("signup"); + setRegisteredEmail(""); + setOtpData({ email: "", otp: "" }); + setSignupData({ + first_name: "", + last_name: "", + email: "", + phone_number: "", + password: "", + password2: "", + }); + } + onOpenChange(isOpen); + }; + + return ( + + + {/* Header with Close Button - Fixed */} +
+ + + {step === "signup" && "Create an account"} + {step === "verify" && "Verify your email"} + + + {step === "signup" && "Sign up to complete your booking"} + {step === "verify" && "Enter the verification code sent to your email"} + + + {/* Close Button */} + +
+ + {/* Scrollable Content */} +
+ {/* Signup Form */} + {step === "signup" && ( +
+ {/* First Name Field */} +
+ + setSignupData({ ...signupData, first_name: e.target.value })} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ + {/* Last Name Field */} +
+ + setSignupData({ ...signupData, last_name: e.target.value })} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ + {/* Email Field */} +
+ + setSignupData({ ...signupData, email: e.target.value })} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ + {/* Phone Field */} +
+ + setSignupData({ ...signupData, phone_number: e.target.value })} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + /> +
+ + {/* Password Field */} +
+ +
+ setSignupData({ ...signupData, password: e.target.value })} + className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> + +
+
+ + {/* Confirm Password Field */} +
+ +
+ setSignupData({ ...signupData, password2: e.target.value })} + className={`h-11 sm:h-12 pr-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> + +
+
+ + {/* Submit Button */} + + + {/* Switch to Login */} +

+ Already have an account?{" "} + +

+
+ )} + + {/* OTP Verification Form */} + {step === "verify" && ( +
+
+
+ +
+

+ Check your email +

+

+ We've sent a 6-digit verification code to {registeredEmail || otpData.email || "your email address"}. +

+
+
+
+ + {/* Email Field (if not set) */} + {!registeredEmail && ( +
+ + handleOtpChange("email", e.target.value)} + className={`h-11 sm:h-12 text-sm sm:text-base ${isDark ? 'bg-gray-700 border-gray-600 text-white placeholder:text-gray-400' : 'bg-white border-gray-300 text-gray-900'}`} + required + /> +
+ )} + + {/* OTP Field */} +
+ +
+ handleOtpChange("otp", value)} + > + + + + + + + + + +
+
+ + {/* Resend OTP */} +
+ +
+ + {/* Submit Button */} + + + {/* Back to signup */} +
+ +
+
+ )} +
+
+
+ ); +} +