canina/frontend/application/components/AuthModal.tsx

803 lines
38 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState, useEffect, useRef } from "react";
import { motion, AnimatePresence } from "motion/react";
import { X, User, Building2, Heart, ShieldCheck, Phone, Key, LogIn, ArrowRight, RefreshCw, Lock, Upload, CheckCircle2 } from "lucide-react";
import { useUserStore, UserRole } from "../lib/store/userStore";
import { authService } from "../lib/services/authService";
import { toast } from "sonner";
import { toPersian } from "../lib/utils";
interface AuthModalProps {
isOpen: boolean;
onClose: () => void;
}
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
const { setRole, setLoggedIn, fetchProfile } = useUserStore();
const [view, setView] = useState<"login" | "register" | "otp-phone" | "otp" | "forgot-password" | "forgot-otp" | "reset-password" | "wholesale-request">("login");
const [isWholesaleRequest, setIsWholesaleRequest] = useState(false);
const [medicalLicense, setMedicalLicense] = useState("");
const [businessName, setBusinessName] = useState("");
const [documentUploaded, setDocumentUploaded] = useState(false);
const [phoneNumber, setPhoneNumber] = useState("");
const [password, setPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [email, setEmail] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [otpCode, setOtpCode] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [countdown, setCountdown] = useState(0);
const otpInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (view === "otp") {
const timer = setTimeout(() => {
otpInputRef.current?.focus();
}, 100);
return () => clearTimeout(timer);
}
}, [view]);
// Reset modal state on open/close
useEffect(() => {
if (!isOpen) {
setView("login");
setPhoneNumber("");
setPassword("");
setNewPassword("");
setEmail("");
setFirstName("");
setLastName("");
setOtpCode("");
setIsLoading(false);
setCountdown(0);
}
}, [isOpen]);
// Cooldown countdown timer
useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
}
}, [countdown]);
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
if (!phoneNumber || !password) return toast.error("شماره موبایل و رمز عبور الزامی است");
setIsLoading(true);
try {
const res = await authService.login(phoneNumber, password);
if (res.success) {
await fetchProfile();
toast.success("با موفقیت وارد شدید");
onClose();
}
} catch (err: any) {
toast.error(err.message || "خطا در ورود");
} finally {
setIsLoading(false);
}
};
const handleRegister = async (e: React.FormEvent) => {
e.preventDefault();
if (!firstName || !lastName || !phoneNumber || !password) {
return toast.error("لطفاً فیلدهای الزامی را پر کنید");
}
setIsLoading(true);
try {
const res = await authService.register({ firstName, lastName, mobile: phoneNumber, email, password });
if (res.success) {
await fetchProfile();
toast.success("ثبت‌نام با موفقیت انجام شد");
onClose();
}
} catch (err: any) {
toast.error(err.message || "خطا در ثبت‌نام");
} finally {
setIsLoading(false);
}
};
const handleSendOtp = async (e: React.FormEvent) => {
e.preventDefault();
const cleanPhone = phoneNumber.trim();
if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره موبایل نامعتبر است");
return;
}
setIsLoading(true);
try {
const res = await authService.sendOtp(cleanPhone);
if ((res as any).code) {
toast.info(`کد تایید (تست): ${(res as any).code}`, { duration: 10000 });
} else {
toast.success("کد تایید پیامک شد");
}
setView("otp");
setCountdown(120);
} catch (err: any) {
toast.error(err.message || "خطا در ارسال کد تایید");
} finally {
setIsLoading(false);
}
};
const handleVerifyOtp = async (e: React.FormEvent) => {
e.preventDefault();
const cleanCode = otpCode.trim();
if (cleanCode.length !== 5) {
toast.error("کد باید ۵ رقم باشد");
return;
}
setIsLoading(true);
try {
const response = await authService.verifyOtp(phoneNumber.trim(), cleanCode);
if (response.success) {
await fetchProfile();
toast.success("ورود موفقیت‌آمیز بود");
onClose();
}
} catch (err: any) {
toast.error(err.message || "کد تایید نامعتبر است");
} finally {
setIsLoading(false);
}
};
const handleResendOtp = async () => {
if (countdown > 0) return;
setIsLoading(true);
try {
await authService.sendOtp(phoneNumber.trim());
toast.success("کد تایید جدید ارسال شد");
setCountdown(120);
} catch (err: any) {
toast.error(err.message || "خطا در ارسال مجدد کد");
} finally {
setIsLoading(false);
}
};
const handleForgotSendOtp = async (e: React.FormEvent) => {
e.preventDefault();
const cleanPhone = phoneNumber.trim();
if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره موبایل نامعتبر است");
return;
}
setIsLoading(true);
try {
const res = await authService.sendOtp(cleanPhone);
if ((res as any).code) {
toast.info(`کد تایید (تست): ${(res as any).code}`, { duration: 10000 });
} else {
toast.success("کد بازیابی رمز پیامک شد");
}
setView("forgot-otp");
setCountdown(120);
} catch (err: any) {
toast.error(err.message || "خطا در ارسال کد");
} finally {
setIsLoading(false);
}
};
const handleForgotVerifyOtp = async (e: React.FormEvent) => {
e.preventDefault();
if (otpCode.trim().length !== 5) {
toast.error("کد باید ۵ رقم باشد");
return;
}
setIsLoading(true);
try {
// Verify OTP - if success, allow password reset
await authService.verifyOtp(phoneNumber.trim(), otpCode.trim());
setView("reset-password");
} catch (err: any) {
toast.error(err.message || "کد تایید نامعتبر است");
} finally {
setIsLoading(false);
}
};
const handleResetPassword = async (e: React.FormEvent) => {
e.preventDefault();
if (newPassword.length < 6) {
toast.error("رمز جدید باید حداقل ۶ کاراکتر باشد");
return;
}
setIsLoading(true);
try {
// Use authService to update password after OTP verification
await authService.updateProfile({ password: newPassword } as any);
toast.success("رمز عبور با موفقیت تغییر یافت");
setView("login");
} catch (err: any) {
toast.error(err.message || "خطا در تغییر رمز");
} finally {
setIsLoading(false);
}
};
return (
<AnimatePresence>
{isOpen && (
<>
{/* Backdrop */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-[60]"
onClick={onClose}
/>
{/* Modal content */}
<motion.div
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md bg-white rounded-[2.5rem] shadow-2xl z-[70] overflow-hidden border border-medical-gray-100 font-vazir"
dir="rtl"
>
{/* Top highlight bar */}
<div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-canina-blue to-indigo-500" />
<div className="p-8 relative">
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-6 left-6 p-2 rounded-full hover:bg-medical-gray-50 text-medical-gray-400 hover:text-medical-gray-600 transition-colors"
>
<X className="w-6 h-6" />
</button>
{/* Title Header */}
<div className="flex flex-col items-center text-center mb-8 pt-4">
<div className="w-20 h-20 bg-canina-blue/10 rounded-3xl flex items-center justify-center mb-4 shadow-inner">
<ShieldCheck className="w-10 h-10 text-canina-blue" />
</div>
<h2 className="text-3xl font-black text-medical-gray-900 mb-2 italic">
ورود به دنیای کانینا
</h2>
<p className="text-medical-gray-500 text-sm font-bold max-w-[280px]">
{view === "otp"
? `کد تایید ارسال شده به شماره ${toPersian(phoneNumber)} را وارد کنید`
: "برای دسترسی به پرونده سلامت و سفارشات وارد شوید"}
</p>
</div>
{/* Content Panel */}
<AnimatePresence mode="wait">
{view === "login" && (
<motion.form
key="login-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleLogin}
className="space-y-4"
>
<div className="space-y-2">
<label htmlFor="login-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input
id="login-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-left tracking-widest"
/>
</div>
<div className="space-y-2">
<label htmlFor="login-password" className="text-xs font-bold text-medical-gray-500">رمز عبور</label>
<input
id="login-password"
name="password"
type="password"
autoComplete="current-password"
placeholder="رمز عبور خود را وارد کنید"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left"
/>
</div>
<div className="flex items-center justify-between pt-2 flex-wrap gap-2">
<button
type="button"
onClick={() => setView("otp-phone")}
className="text-xs font-bold text-canina-blue hover:underline"
>
ورود با کد یکبار مصرف (OTP)
</button>
<div className="flex items-center gap-3">
<button
type="button"
onClick={() => { setPhoneNumber(""); setView("forgot-password"); }}
className="text-xs font-bold text-medical-gray-400 hover:text-canina-blue hover:underline"
>
فراموشی رمز
</button>
<button
type="button"
onClick={() => setView("register")}
className="text-xs font-bold text-canina-blue hover:underline"
>
حساب ندارید؟ ثبتنام
</button>
</div>
</div>
<div className="pt-3 border-t border-medical-gray-100">
<button
type="button"
onClick={() => setView("wholesale-request")}
className="w-full py-3 bg-amber-50 text-amber-900 border border-amber-200 rounded-xl text-xs font-black flex items-center justify-center gap-2 hover:bg-amber-100 transition-colors"
>
<Building2 className="w-4 h-4 text-amber-600" />
درخواست ثبتنام حساب خریدار عمده (پتشاپ / کلینیک)
</button>
</div>
<button
type="submit"
disabled={isLoading || !phoneNumber || !password}
className="w-full py-4 mt-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2 cursor-pointer"
>
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ورود به حساب</>}
</button>
</motion.form>
)}
{view === "wholesale-request" && (
<motion.form
key="wholesale-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={(e) => {
e.preventDefault();
toast.success("درخواست احراز هویت خریدار عمده با موفقیت ارسال شد و پس از بررسی ادمین تایید خواهد شد.");
setView("login");
}}
className="space-y-3"
>
<div className="p-3 bg-blue-50 border border-blue-200 rounded-xl text-xs text-canina-blue font-bold">
ثبت شماره نظام دامپزشکی یا پروانه کسب جهت دسترسی به قیمتها و سفارش حجمی الزامی است.
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-medical-gray-500">نام کلینیک / داروخانه / مجموعه *</label>
<input
type="text"
required
value={businessName}
onChange={e => setBusinessName(e.target.value)}
placeholder="مثال: کلینیک دامپزشکی رازی"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/>
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-medical-gray-500">شماره نظام دامپزشکی / پروانه کسب *</label>
<input
type="text"
required
value={medicalLicense}
onChange={e => setMedicalLicense(e.target.value)}
placeholder="مثال: ۹۸۷۶۵۴۳۲۱"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left"
/>
</div>
<div className="space-y-1">
<label className="text-xs font-bold text-medical-gray-500">موبایل مسئول *</label>
<input
type="tel"
required
maxLength={11}
value={phoneNumber}
onChange={e => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
placeholder="۰۹۱۲..."
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm text-left"
/>
</div>
<div
onClick={() => {
setDocumentUploaded(true);
toast.success("تصویر پروانه با موفقیت بارگذاری شد.");
}}
className={`p-4 border-2 border-dashed rounded-xl text-center cursor-pointer transition-all ${documentUploaded ? 'border-green-500 bg-green-50' : 'border-medical-gray-300 hover:border-canina-blue'}`}
>
{documentUploaded ? (
<div className="flex items-center justify-center gap-2 text-green-700 font-bold text-xs">
<CheckCircle2 className="w-4 h-4" />
تصویر مدرک بارگذاری شد
</div>
) : (
<div className="flex flex-col items-center gap-1 text-medical-gray-500 text-xs">
<Upload className="w-5 h-5 text-canina-blue" />
<span>بارگذاری تصویر کارت نظام / پروانه</span>
</div>
)}
</div>
<div className="flex justify-start pt-1">
<button
type="button"
onClick={() => setView("login")}
className="text-xs font-bold text-canina-blue hover:underline"
>
بازگشت به ورود
</button>
</div>
<button
type="submit"
disabled={!businessName || !medicalLicense || !phoneNumber}
className="w-full py-3 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all cursor-pointer"
>
ارسال درخواست بررسی به ادمین
</button>
</motion.form>
)}
{view === "register" && (
<motion.form
key="register-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleRegister}
className="space-y-3"
>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1">
<label htmlFor="reg-fname" className="text-xs font-bold text-medical-gray-500">نام</label>
<input
id="reg-fname"
name="given-name"
type="text"
autoComplete="given-name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/>
</div>
<div className="space-y-1">
<label htmlFor="reg-lname" className="text-xs font-bold text-medical-gray-500">نام خانوادگی</label>
<input
id="reg-lname"
name="family-name"
type="text"
autoComplete="family-name"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-sm"
/>
</div>
</div>
<div className="space-y-1">
<label htmlFor="reg-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل *</label>
<input
id="reg-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left tracking-widest text-sm"
/>
</div>
<div className="space-y-1">
<label htmlFor="reg-email" className="text-xs font-bold text-medical-gray-500">ایمیل (اختیاری)</label>
<input
id="reg-email"
name="email"
type="email"
autoComplete="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left text-sm"
/>
</div>
<div className="space-y-1">
<label htmlFor="reg-password" className="text-xs font-bold text-medical-gray-500">رمز عبور *</label>
<input
id="reg-password"
name="new-password"
type="password"
autoComplete="new-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-2 px-3 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left text-sm"
/>
</div>
<div className="flex justify-start pt-1">
<button
type="button"
onClick={() => setView("login")}
className="text-xs font-bold text-canina-blue hover:underline"
>
بازگشت به ورود
</button>
</div>
<button
type="submit"
disabled={isLoading || !phoneNumber || !password || !firstName || !lastName}
className="w-full py-3 mt-2 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all flex items-center justify-center gap-2 cursor-pointer"
>
{isLoading ? <RefreshCw className="w-4 h-4 animate-spin" /> : <>ثبتنام</>}
</button>
</motion.form>
)}
{view === "otp-phone" && (
<motion.form
key="otp-phone-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleSendOtp}
className="space-y-6"
>
<div className="space-y-2">
<div className="flex justify-between items-center">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">شماره موبایل</label>
<button
type="button"
onClick={() => setView("login")}
className="text-[10px] font-black text-canina-blue hover:underline"
>
برگشت به صفحه ورود
</button>
</div>
<div className="relative">
<Phone className="absolute right-4 top-1/2 -translate-y-1/2 w-5 h-5 text-medical-gray-400" />
<input
autoFocus
type="tel"
maxLength={11}
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-lg text-left tracking-widest"
/>
</div>
</div>
<button
type="submit"
disabled={isLoading || phoneNumber.length < 11}
className="w-full py-5 bg-canina-blue text-white rounded-2xl font-black text-xl hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-xl shadow-canina-blue/20 flex items-center justify-center gap-3 cursor-pointer"
>
{isLoading ? (
<RefreshCw className="w-6 h-6 animate-spin" />
) : (
<>
<LogIn className="w-6 h-6" />
ارسال کد تایید
</>
)}
</button>
</motion.form>
)}
{view === "otp" && (
<motion.form
key="otp-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleVerifyOtp}
className="space-y-6"
>
<div className="space-y-2">
<div className="flex justify-between items-center">
<label className="text-[10px] font-black text-medical-gray-400 uppercase tracking-widest">کد تایید پیامکی</label>
<button
type="button"
onClick={() => setView("otp-phone")}
className="text-[10px] font-black text-canina-blue flex items-center gap-1 hover:underline"
>
<ArrowRight className="w-3 h-3" />
تغییر شماره
</button>
</div>
<div className="relative">
<Key className="absolute right-4 top-1/2 -translate-y-1/2 w-5 h-5 text-medical-gray-400" />
<input
ref={otpInputRef}
type="text"
maxLength={5}
placeholder="کد ۵ رقمی"
value={otpCode}
onChange={(e) => setOtpCode(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pr-12 pl-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-2xl text-center tracking-[0.5em]"
/>
</div>
</div>
<div className="text-center">
{countdown > 0 ? (
<span className="text-xs font-bold text-medical-gray-400">
ارسال مجدد کد پس از {toPersian(countdown)} ثانیه
</span>
) : (
<button
type="button"
onClick={handleResendOtp}
disabled={isLoading}
className="text-xs font-black text-canina-blue hover:underline flex items-center gap-1.5 justify-center mx-auto"
>
<RefreshCw className="w-3.5 h-3.5" />
ارسال مجدد کد تایید
</button>
)}
</div>
<button
type="submit"
disabled={isLoading || otpCode.length < 5}
className="w-full py-5 bg-canina-blue text-white rounded-2xl font-black text-xl hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-xl shadow-canina-blue/20 flex items-center justify-center gap-3 cursor-pointer"
>
{isLoading ? (
<RefreshCw className="w-6 h-6 animate-spin" />
) : (
<>
<LogIn className="w-6 h-6" />
تایید کد و ورود
</>
)}
</button>
</motion.form>
)}
{/* Forgot Password - Enter Phone */}
{view === "forgot-password" && (
<motion.form
key="forgot-password-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotSendOtp}
className="space-y-4"
>
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 text-center">
<Lock className="w-8 h-8 text-amber-500 mx-auto mb-2" />
<p className="text-sm font-bold text-amber-700">برای بازیابی رمز عبور، کد تایید به شمارهتان ارسال میشود</p>
</div>
<div className="space-y-2">
<label htmlFor="forgot-phone" className="text-xs font-bold text-medical-gray-500">شماره موبایل</label>
<input
id="forgot-phone"
name="username"
type="tel"
maxLength={11}
autoComplete="username"
placeholder="مثال: ۰۹۱۲۳۴۵۶۷۸۹"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-bold text-left tracking-widest"
/>
</div>
<button
type="submit"
disabled={isLoading || phoneNumber.length < 11}
className="w-full py-4 bg-canina-blue text-white rounded-xl font-black hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2"
>
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>ارسال کد بازیابی</>}
</button>
<button type="button" onClick={() => setView("login")} className="w-full text-xs font-bold text-medical-gray-400 hover:text-canina-blue">بازگشت به ورود</button>
</motion.form>
)}
{/* Forgot Password - Enter OTP */}
{view === "forgot-otp" && (
<motion.form
key="forgot-otp-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleForgotVerifyOtp}
className="space-y-5"
>
<p className="text-sm font-bold text-medical-gray-600 text-center">کد ارسال شده به {toPersian(phoneNumber)} را وارد کنید</p>
<div className="relative">
<input
ref={otpInputRef}
id="forgot-otp-code"
name="one-time-code"
type="text"
autoComplete="one-time-code"
inputMode="numeric"
maxLength={5}
value={otpCode}
onChange={(e) => setOtpCode(e.target.value.replace(/[^0-9]/g, ''))}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-2xl py-4 pl-4 pr-4 focus:ring-2 focus:ring-canina-blue/20 outline-none font-black text-2xl text-center tracking-[0.5em]"
/>
</div>
{countdown > 0 ? (
<p className="text-xs font-bold text-medical-gray-400 text-center">ارسال مجدد پس از {toPersian(countdown)} ثانیه</p>
) : (
<button type="button" onClick={handleForgotSendOtp as any} disabled={isLoading} className="text-xs font-black text-canina-blue hover:underline flex items-center gap-1.5 justify-center mx-auto">
<RefreshCw className="w-3.5 h-3.5" />ارسال مجدد
</button>
)}
<button type="submit" disabled={isLoading || otpCode.length < 5} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2">
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تایید کد</>}
</button>
</motion.form>
)}
{/* Reset Password */}
{view === "reset-password" && (
<motion.form
key="reset-password-view"
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
onSubmit={handleResetPassword}
className="space-y-4"
>
<div className="bg-green-50 border border-green-200 rounded-2xl p-4 text-center">
<p className="text-sm font-bold text-green-700">هویت تایید شد. رمز جدید خود را وارد کنید</p>
</div>
<div className="space-y-2">
<label htmlFor="new-password" className="text-xs font-bold text-medical-gray-500">رمز جدید</label>
<input
id="new-password"
name="new-password"
type="password"
autoComplete="new-password"
placeholder="حداقل ۶ کاراکتر"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
disabled={isLoading}
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl py-3 px-4 focus:ring-2 focus:ring-canina-blue/20 outline-none text-left"
/>
</div>
<button type="submit" disabled={isLoading || newPassword.length < 6} className="w-full py-4 bg-canina-blue text-white rounded-xl font-black disabled:opacity-50 transition-all shadow-lg shadow-canina-blue/20 flex items-center justify-center gap-2">
{isLoading ? <RefreshCw className="w-5 h-5 animate-spin" /> : <>تغییر رمز عبور</>}
</button>
</motion.form>
)}
</AnimatePresence>
</div>
</motion.div>
</>
)}
</AnimatePresence>
);
}