feat/authentication #21
@ -273,12 +273,13 @@ export default function Login() {
|
||||
try {
|
||||
const result = await verifyOtp(otpToVerify);
|
||||
|
||||
// If verification is successful, redirect to login page
|
||||
toast.success("Email verified successfully! Redirecting to login...");
|
||||
// Redirect to login page with email pre-filled
|
||||
setTimeout(() => {
|
||||
router.push(`/login?email=${encodeURIComponent(emailToVerify)}`);
|
||||
}, 1000);
|
||||
// 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);
|
||||
|
||||
@ -134,15 +134,15 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent
|
||||
showCloseButton={false}
|
||||
className={`sm:max-w-md ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}
|
||||
className={`max-w-md max-h-[90vh] overflow-hidden flex flex-col p-0 ${isDark ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}
|
||||
>
|
||||
{/* Header with Close Button */}
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<DialogHeader className="flex-1">
|
||||
<DialogTitle className="text-3xl font-bold bg-gradient-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent">
|
||||
{/* Header with Close Button - Fixed */}
|
||||
<div className="flex items-start justify-between p-6 pb-4 flex-shrink-0 border-b border-gray-200 dark:border-gray-700">
|
||||
<DialogHeader className="flex-1 pr-2">
|
||||
<DialogTitle className="text-2xl sm:text-3xl font-bold bg-gradient-to-r from-rose-600 via-pink-600 to-rose-600 bg-clip-text text-transparent">
|
||||
{isSignup ? "Create an account" : "Welcome back"}
|
||||
</DialogTitle>
|
||||
<DialogDescription className={isDark ? 'text-gray-400' : 'text-gray-600'}>
|
||||
<DialogDescription className={`text-sm sm:text-base mt-1 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||
{isSignup
|
||||
? "Sign up to complete your booking"
|
||||
: "Please log in to complete your booking"}
|
||||
@ -158,9 +158,11 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Scrollable Content */}
|
||||
<div className="overflow-y-auto flex-1 px-6">
|
||||
{/* Signup Form */}
|
||||
{isSignup ? (
|
||||
<form className="space-y-6 mt-4" onSubmit={handleSignup}>
|
||||
<form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleSignup}>
|
||||
{error && (
|
||||
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
|
||||
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
|
||||
@ -168,7 +170,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
)}
|
||||
|
||||
{/* First Name Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-firstName" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
First Name *
|
||||
</label>
|
||||
@ -178,13 +180,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="John"
|
||||
value={signupData.first_name}
|
||||
onChange={(e) => setSignupData({ ...signupData, first_name: 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'}`}
|
||||
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
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Last Name Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-lastName" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Last Name *
|
||||
</label>
|
||||
@ -194,13 +196,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Doe"
|
||||
value={signupData.last_name}
|
||||
onChange={(e) => setSignupData({ ...signupData, last_name: 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'}`}
|
||||
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
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Email address *
|
||||
</label>
|
||||
@ -210,13 +212,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Email address"
|
||||
value={signupData.email}
|
||||
onChange={(e) => setSignupData({ ...signupData, 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'}`}
|
||||
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
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-phone" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Phone Number (Optional)
|
||||
</label>
|
||||
@ -226,12 +228,12 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="+1 (555) 123-4567"
|
||||
value={signupData.phone_number || ""}
|
||||
onChange={(e) => setSignupData({ ...signupData, phone_number: 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'}`}
|
||||
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'}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-password" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Password *
|
||||
</label>
|
||||
@ -242,7 +244,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Password (min 8 characters)"
|
||||
value={signupData.password}
|
||||
onChange={(e) => setSignupData({ ...signupData, 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'}`}
|
||||
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
|
||||
/>
|
||||
<Button
|
||||
@ -250,20 +252,20 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
className={`absolute right-3 sm:right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-5 h-5" />
|
||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
) : (
|
||||
<Eye className="w-5 h-5" />
|
||||
<Eye className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Confirm Password Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="signup-password2" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Confirm Password *
|
||||
</label>
|
||||
@ -274,7 +276,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Confirm password"
|
||||
value={signupData.password2}
|
||||
onChange={(e) => setSignupData({ ...signupData, password2: 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'}`}
|
||||
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
|
||||
/>
|
||||
<Button
|
||||
@ -282,13 +284,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowPassword2(!showPassword2)}
|
||||
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
className={`absolute right-3 sm:right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
aria-label={showPassword2 ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword2 ? (
|
||||
<EyeOff className="w-5 h-5" />
|
||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
) : (
|
||||
<Eye className="w-5 h-5" />
|
||||
<Eye className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@ -298,7 +300,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={registerMutation.isPending}
|
||||
className="w-full h-12 text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full h-11 sm:h-12 text-sm sm:text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed mt-4 sm:mt-6"
|
||||
>
|
||||
{registerMutation.isPending ? (
|
||||
<>
|
||||
@ -311,7 +313,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</Button>
|
||||
|
||||
{/* Switch to Login */}
|
||||
<p className={`text-sm text-center ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||
<p className={`text-xs sm:text-sm text-center pt-2 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||
Already have an account?{" "}
|
||||
<button
|
||||
type="button"
|
||||
@ -324,7 +326,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</form>
|
||||
) : (
|
||||
/* Login Form */
|
||||
<form className="space-y-6 mt-4" onSubmit={handleLogin}>
|
||||
<form className="space-y-4 sm:space-y-5 py-4 sm:py-6" onSubmit={handleLogin}>
|
||||
{error && (
|
||||
<div className={`p-3 rounded-lg border ${isDark ? 'bg-red-900/20 border-red-800' : 'bg-red-50 border-red-200'}`}>
|
||||
<p className={`text-sm ${isDark ? 'text-red-200' : 'text-red-800'}`}>{error}</p>
|
||||
@ -332,7 +334,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
)}
|
||||
|
||||
{/* Email Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="login-email" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Email address
|
||||
</label>
|
||||
@ -342,13 +344,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Email address"
|
||||
value={loginData.email}
|
||||
onChange={(e) => setLoginData({ ...loginData, 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'}`}
|
||||
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
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Password Field */}
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1.5 sm:space-y-2">
|
||||
<label htmlFor="login-password" className={`text-sm font-medium ${isDark ? 'text-gray-300' : 'text-black'}`}>
|
||||
Your password
|
||||
</label>
|
||||
@ -359,7 +361,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
placeholder="Your password"
|
||||
value={loginData.password}
|
||||
onChange={(e) => setLoginData({ ...loginData, 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'}`}
|
||||
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
|
||||
/>
|
||||
<Button
|
||||
@ -367,13 +369,13 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className={`absolute right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
className={`absolute right-3 sm:right-4 top-1/2 -translate-y-1/2 h-auto w-auto p-0 ${isDark ? 'text-gray-400 hover:text-gray-300' : 'text-gray-500 hover:text-gray-700'}`}
|
||||
aria-label={showPassword ? "Hide password" : "Show password"}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-5 h-5" />
|
||||
<EyeOff className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
) : (
|
||||
<Eye className="w-5 h-5" />
|
||||
<Eye className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
@ -383,7 +385,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loginMutation.isPending}
|
||||
className="w-full h-12 text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full h-11 sm:h-12 text-sm sm:text-base font-semibold bg-gradient-to-r from-rose-500 to-pink-600 hover:from-rose-600 hover:to-pink-700 text-white shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed mt-4 sm:mt-6"
|
||||
>
|
||||
{loginMutation.isPending ? (
|
||||
<>
|
||||
@ -396,7 +398,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</Button>
|
||||
|
||||
{/* Remember Me & Forgot Password */}
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center justify-between text-xs sm:text-sm">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -408,7 +410,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
className={`font-medium ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||
className={`font-medium text-xs sm:text-sm ${isDark ? 'text-blue-400 hover:text-blue-300' : 'text-blue-600 hover:text-blue-700'}`}
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Forgot password?
|
||||
@ -416,7 +418,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</div>
|
||||
|
||||
{/* Sign Up Prompt */}
|
||||
<p className={`text-sm text-center ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||
<p className={`text-xs sm:text-sm text-center pt-2 ${isDark ? 'text-gray-400' : 'text-gray-600'}`}>
|
||||
New to Attune Heart Therapy?{" "}
|
||||
<button
|
||||
type="button"
|
||||
@ -428,6 +430,7 @@ export function LoginDialog({ open, onOpenChange, onLoginSuccess }: LoginDialogP
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user