Files
SweetHome/app/register/owner/page.js
Claw AI 9cddee841b
All checks were successful
Build frontend / build (push) Successful in 38s
Add FullName field to owner and customer signup request payloads
2026-03-27 22:01:00 +00:00

523 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

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 { useState, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import Image from 'next/image';
import {
User, Mail, Phone, Lock, Eye, EyeOff, MessageCircle,
Camera, X, CheckCircle, XCircle, ArrowLeft, Building,
Loader2, Shield, KeyRound
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
import { addOwner, loginWithEmail, sendEmailOTP, verifyEmail } from '../../utils/api';
import AuthService from '../../services/AuthService';
import { OwnerType, OwnerTypeLabels } from '../../enums';
export default function OwnerRegisterPage() {
const router = useRouter();
const [step, setStep] = useState(1); // 1=form, 2=id images
const [showOtpModal, setShowOtpModal] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [formData, setFormData] = useState({
name: '',
email: '',
phone: '',
whatsapp: '',
password: '',
confirmPassword: '',
ownerType: OwnerType.PERSON,
agreeTerms: false
});
const [idImages, setIdImages] = useState({ front: null, back: null });
const [idImagePreviews, setIdImagePreviews] = useState({ front: '', back: '' });
const [otpCode, setOtpCode] = useState('');
const [errors, setErrors] = useState({});
const fileInputFrontRef = useRef(null);
const fileInputBackRef = useRef(null);
const handleImageUpload = (side, file) => {
if (!file) return;
if (!file.type.startsWith('image/')) {
toast.error('الرجاء اختيار صورة صالحة');
return;
}
if (file.size > 5 * 1024 * 1024) {
toast.error('حجم الصورة يجب أن يكون أقل من 5 ميجابايت');
return;
}
const reader = new FileReader();
reader.onloadend = () => {
setIdImagePreviews(prev => ({ ...prev, [side]: reader.result }));
};
reader.readAsDataURL(file);
setIdImages(prev => ({ ...prev, [side]: file }));
console.log('[OwnerRegister] Image uploaded:', side);
toast.success('تم رفع الصورة بنجاح', { style: { background: '#dcfce7', color: '#166534' } });
};
const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
const validatePhone = (phone) => /^(09|05)[0-9]{8}$/.test(phone);
const validateStep1 = () => {
const newErrors = {};
if (!formData.name) newErrors.name = 'الاسم الكامل مطلوب';
else if (formData.name.length < 3) newErrors.name = 'الاسم يجب أن يكون 3 أحرف على الأقل';
if (!formData.email) newErrors.email = 'البريد الإلكتروني مطلوب';
else if (!validateEmail(formData.email)) newErrors.email = 'البريد الإلكتروني غير صالح';
if (!formData.whatsapp) newErrors.whatsapp = 'رقم الواتساب مطلوب';
else if (!validatePhone(formData.whatsapp)) newErrors.whatsapp = 'رقم الواتساب غير صالح (يجب أن يبدأ 09 أو 05)';
if (formData.phone && !validatePhone(formData.phone)) newErrors.phone = 'رقم الهاتف غير صالح';
if (!formData.password) newErrors.password = 'كلمة المرور مطلوبة';
else if (formData.password.length < 6) newErrors.password = 'كلمة المرور يجب أن تكون 6 أحرف على الأقل';
if (formData.password !== formData.confirmPassword) newErrors.confirmPassword = 'كلمات المرور غير متطابقة';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const validateStep2 = () => {
const newErrors = {};
if (!idImages.front) newErrors.front = 'صورة الوجه الأمامي للهوية مطلوبة';
if (!idImages.back) newErrors.back = 'صورة الوجه الخلفي للهوية مطلوبة';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleNextStep = () => {
if (validateStep1()) {
console.log('[OwnerRegister] Step 1 valid, moving to step 2');
setStep(2);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else {
toast.error('يرجى تصحيح الأخطاء في النموذج');
}
};
// ─── Main signup handler ───
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateStep2()) {
toast.error('يرجى إكمال جميع الصور المطلوبة');
return;
}
if (!formData.agreeTerms) {
toast.error('يجب الموافقة على الشروط والأحكام');
return;
}
setIsLoading(true);
console.log('[OwnerRegister] Submitting owner registration...');
const payload = {
FullName: formData.name,
name: formData.name,
email: formData.email,
phoneNumber: formData.phone || '',
whatsAppNumber: formData.whatsapp,
password: formData.password,
ownerType: formData.ownerType,
};
try {
const res = await addOwner(payload);
console.log('[OwnerRegister] addOwner response:', res);
if (res.status === 200 || res.ok) {
const tempToken = res.data;
if (tempToken) {
AuthService.addToken(tempToken);
console.log('[OwnerRegister] Temp token stored for OTP');
}
const apiMessage = res.message || res.data?.message;
toast.success(apiMessage || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { duration: 4000 });
// Auto-login to trigger OTP
console.log('[OwnerRegister] Auto-login to send OTP...');
const loginRes = await loginWithEmail(formData.email, formData.password);
console.log('[OwnerRegister] login response:', loginRes);
if (loginRes.status === 206) {
const otpToken = loginRes.data;
if (otpToken) AuthService.addToken(otpToken);
const loginMsg = loginRes.message || loginRes.data?.message;
toast(loginMsg || 'تم إرسال رمز التحقق إلى بريدك الإلكتروني', { icon: '📧' });
setShowOtpModal(true);
} else if (loginRes.status === 200) {
const loginToken = loginRes.data;
if (loginToken) AuthService.addToken(loginToken);
toast.success(loginRes.message || 'تم تسجيل الدخول بنجاح!');
router.push('/');
}
} else {
const errMsg = res.message || res.data?.message || 'فشل في إنشاء الحساب';
console.error('[OwnerRegister] Registration failed:', errMsg);
toast.error(errMsg);
}
} catch (err) {
console.error('[OwnerRegister] Error:', err);
toast.error(err.message || 'حدث خطأ أثناء التسجيل');
} finally {
setIsLoading(false);
}
};
// ─── OTP verification handler ───
const handleVerifyOTP = async () => {
if (!otpCode || otpCode.length < 4) {
toast.error('يرجى إدخال رمز التحقق');
return;
}
setIsLoading(true);
console.log('[OwnerRegister] Verifying OTP:', otpCode);
try {
const res = await verifyEmail(otpCode);
console.log('[OwnerRegister] VerifyEmail response:', res);
if (res.status === 200) {
AuthService.deleteToken();
console.log('[OwnerRegister] Temp token removed after verification');
toast.success(res.message || 'تم التحقق من البريد الإلكتروني بنجاح!', { duration: 3000 });
setShowOtpModal(false);
setTimeout(() => router.push('/login'), 1500);
} else {
const errMsg = res.message || res.data?.message || 'رمز التحقق غير صحيح';
console.error('[OwnerRegister] Verification failed:', errMsg);
toast.error(errMsg);
}
} catch (err) {
console.error('[OwnerRegister] Verify error:', err);
toast.error(err.message || 'حدث خطأ أثناء التحقق');
} finally {
setIsLoading(false);
}
};
const handleResendOTP = async () => {
setIsLoading(true);
console.log('[OwnerRegister] Resending email OTP...');
try {
await sendEmailOTP();
toast.success('تم إرسال رمز تحقق جديد');
} catch (err) {
console.error('[OwnerRegister] Resend OTP error:', err);
toast.error('فشل في إرسال الرمز');
} finally {
setIsLoading(false);
}
};
const fadeInUp = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.5 }
};
const staggerContainer = {
animate: { transition: { staggerChildren: 0.1 } }
};
return (
<div className="min-h-screen bg-gradient-to-br from-gray-950 via-gray-900 to-gray-950 flex items-center justify-center p-4 relative overflow-hidden">
<Toaster position="top-center" reverseOrder={false} />
<div className="absolute inset-0 overflow-hidden">
{[...Array(20)].map((_, i) => (
<motion.div key={i} className="absolute rounded-full bg-amber-500/10"
style={{ left: `${Math.random() * 100}%`, top: `${Math.random() * 100}%`, width: Math.random() * 200 + 50, height: Math.random() * 200 + 50 }}
animate={{ x: [0, Math.random() * 100 - 50, 0], y: [0, Math.random() * 100 - 50, 0] }}
transition={{ duration: Math.random() * 15 + 15, repeat: Infinity, ease: "linear" }} />
))}
</div>
<motion.div initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.5 }}
className="relative z-10 w-full max-w-2xl">
{/* Progress */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<Link href="/auth/choose-role" className="flex items-center gap-2 text-gray-400 hover:text-white transition-colors group">
<motion.div whileHover={{ x: -5 }}><ArrowLeft className="w-4 h-4" /></motion.div>
<span>العودة</span>
</Link>
<span className="text-sm text-gray-400">خطوة {step} من 2</span>
</div>
<div className="flex gap-2">
{[1, 2].map((s) => (
<motion.div key={s} className={`h-2 flex-1 rounded-full ${step >= s ? 'bg-amber-500' : 'bg-gray-700'}`} animate={{ scaleX: step >= s ? 1 : 0.5 }} />
))}
</div>
</div>
<motion.div key={step} initial={{ opacity: 0, x: 20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -20 }} transition={{ duration: 0.3 }}
className="bg-white/5 backdrop-blur-xl rounded-3xl shadow-2xl border border-white/10 overflow-hidden">
<div className="bg-gradient-to-r from-amber-500 to-amber-600 p-8 text-center relative overflow-hidden">
<motion.div initial={{ scale: 0 }} animate={{ scale: 1 }} transition={{ delay: 0.2, type: "spring" }}
className="absolute -top-10 -right-10 w-40 h-40 bg-white/10 rounded-full" />
<motion.div initial={{ y: 20, opacity: 0 }} animate={{ y: 0, opacity: 1 }} className="relative z-10">
<motion.div animate={{ rotate: [0, 10, -10, 0] }} transition={{ duration: 2, repeat: Infinity }}
className="w-20 h-20 mx-auto mb-4 bg-white/20 rounded-2xl flex items-center justify-center backdrop-blur-sm">
<Building className="w-10 h-10 text-white" />
</motion.div>
<h1 className="text-3xl font-bold text-white mb-2">
{step === 1 ? 'معلومات المالك' : 'الوثائق الرسمية'}
</h1>
<p className="text-amber-100">
{step === 1 ? 'أدخل معلوماتك الأساسية' : 'يرجى رفع صور الهوية للتحقق'}
</p>
</motion.div>
</div>
<div className="p-8">
<motion.form variants={staggerContainer} initial="initial" animate="animate"
onSubmit={step === 1 ? (e) => { e.preventDefault(); handleNextStep(); } : handleSubmit}
className="space-y-6">
{/* ─── STEP 1: Form ─── */}
{step === 1 && (
<>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">الاسم الكامل <span className="text-red-500">*</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<User className={`w-5 h-5 ${errors.name ? 'text-red-500' : 'text-gray-400 group-focus-within:text-amber-500'}`} />
</div>
<input type="text" value={formData.name}
onChange={(e) => { setFormData({...formData, name: e.target.value}); setErrors({...errors, name: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.name ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل اسمك الكامل" />
</div>
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">البريد الإلكتروني <span className="text-red-500">*</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<Mail className={`w-5 h-5 ${errors.email ? 'text-red-500' : 'text-gray-400 group-focus-within:text-amber-500'}`} />
</div>
<input type="email" value={formData.email}
onChange={(e) => { setFormData({...formData, email: e.target.value}); setErrors({...errors, email: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.email ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل بريدك الإلكتروني" />
</div>
{errors.email && <p className="text-red-500 text-sm mt-1">{errors.email}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">رقم الهاتف <span className="text-gray-500">(اختياري)</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<Phone className="w-5 h-5 text-gray-400 group-focus-within:text-amber-500" />
</div>
<input type="tel" value={formData.phone}
onChange={(e) => { setFormData({...formData, phone: e.target.value}); setErrors({...errors, phone: null}); }}
className="w-full pr-12 pl-4 py-3 bg-white/5 border border-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all"
placeholder="أدخل رقم هاتفك (اختياري)" />
</div>
{errors.phone && <p className="text-red-500 text-sm mt-1">{errors.phone}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">رقم الواتساب <span className="text-red-500">*</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<MessageCircle className={`w-5 h-5 ${errors.whatsapp ? 'text-red-500' : 'text-gray-400 group-focus-within:text-amber-500'}`} />
</div>
<input type="tel" value={formData.whatsapp}
onChange={(e) => { setFormData({...formData, whatsapp: e.target.value}); setErrors({...errors, whatsapp: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.whatsapp ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل رقم الواتساب" />
</div>
{errors.whatsapp && <p className="text-red-500 text-sm mt-1">{errors.whatsapp}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">نوع المالك <span className="text-red-500">*</span></label>
<select value={formData.ownerType}
onChange={(e) => setFormData({...formData, ownerType: e.target.value})}
className="w-full py-3 px-4 bg-white/5 border border-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white appearance-none cursor-pointer">
{Object.entries(OwnerTypeLabels).map(([value, label]) => (
<option key={value} value={value} className="bg-gray-900 text-white">{label}</option>
))}
</select>
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">كلمة المرور <span className="text-red-500">*</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<Lock className={`w-5 h-5 ${errors.password ? 'text-red-500' : 'text-gray-400 group-focus-within:text-amber-500'}`} />
</div>
<input type={showPassword ? "text" : "password"} value={formData.password}
onChange={(e) => { setFormData({...formData, password: e.target.value}); setErrors({...errors, password: null}); }}
className={`w-full pr-12 pl-12 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.password ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل كلمة المرور" />
<button type="button" onClick={() => setShowPassword(!showPassword)} className="absolute inset-y-0 left-0 pl-3 flex items-center">
{showPassword ? <EyeOff className="w-5 h-5 text-gray-400" /> : <Eye className="w-5 h-5 text-gray-400" />}
</button>
</div>
{errors.password && <p className="text-red-500 text-sm mt-1">{errors.password}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">تأكيد كلمة المرور <span className="text-red-500">*</span></label>
<div className="relative group">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<Lock className={`w-5 h-5 ${errors.confirmPassword ? 'text-red-500' : 'text-gray-400 group-focus-within:text-amber-500'}`} />
</div>
<input type={showConfirmPassword ? "text" : "password"} value={formData.confirmPassword}
onChange={(e) => { setFormData({...formData, confirmPassword: e.target.value}); setErrors({...errors, confirmPassword: null}); }}
className={`w-full pr-12 pl-12 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.confirmPassword ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أعد إدخال كلمة المرور" />
<button type="button" onClick={() => setShowConfirmPassword(!showConfirmPassword)} className="absolute inset-y-0 left-0 pl-3 flex items-center">
{showConfirmPassword ? <EyeOff className="w-5 h-5 text-gray-400" /> : <Eye className="w-5 h-5 text-gray-400" />}
</button>
{formData.confirmPassword && (
<div className="absolute inset-y-0 left-12 flex items-center">
{formData.password === formData.confirmPassword ? <CheckCircle className="w-5 h-5 text-green-500" /> : <XCircle className="w-5 h-5 text-red-500" />}
</div>
)}
</div>
{errors.confirmPassword && <p className="text-red-500 text-sm mt-1">{errors.confirmPassword}</p>}
</motion.div>
</>
)}
{/* ─── STEP 2: ID Images ─── */}
{step === 2 && (
<>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">صورة الهوية - الوجه الأمامي <span className="text-red-500">*</span></label>
<div onClick={() => fileInputFrontRef.current?.click()}
className={`relative border-2 border-dashed rounded-xl p-6 text-center cursor-pointer transition-all ${idImagePreviews.front ? 'border-green-500 bg-green-500/10' : errors.front ? 'border-red-500 bg-red-500/10' : 'border-gray-700 hover:border-amber-500 hover:bg-white/5'}`}>
<input ref={fileInputFrontRef} type="file" accept="image/*" onChange={(e) => handleImageUpload('front', e.target.files?.[0])} className="hidden" />
{idImagePreviews.front ? (
<div className="relative">
<Image src={idImagePreviews.front} alt="Front ID" width={200} height={120} className="mx-auto rounded-lg object-cover" />
<button onClick={(e) => { e.stopPropagation(); setIdImages(prev => ({...prev, front: null})); setIdImagePreviews(prev => ({...prev, front: ''})); }}
className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 rounded-full flex items-center justify-center hover:bg-red-600">
<X className="w-4 h-4 text-white" />
</button>
</div>
) : (<><Camera className="w-12 h-12 text-gray-500 mx-auto mb-3" /><p className="text-gray-400">اضغط لرفع الصورة</p><p className="text-xs text-gray-500 mt-2">JPEG, PNG, JPG حتى 5MB</p></>)}
</div>
{errors.front && <p className="text-red-500 text-sm mt-1">{errors.front}</p>}
</motion.div>
<motion.div variants={fadeInUp}>
<label className="block text-sm font-medium text-gray-300 mb-2">صورة الهوية - الوجه الخلفي <span className="text-red-500">*</span></label>
<div onClick={() => fileInputBackRef.current?.click()}
className={`relative border-2 border-dashed rounded-xl p-6 text-center cursor-pointer transition-all ${idImagePreviews.back ? 'border-green-500 bg-green-500/10' : errors.back ? 'border-red-500 bg-red-500/10' : 'border-gray-700 hover:border-amber-500 hover:bg-white/5'}`}>
<input ref={fileInputBackRef} type="file" accept="image/*" onChange={(e) => handleImageUpload('back', e.target.files?.[0])} className="hidden" />
{idImagePreviews.back ? (
<div className="relative">
<Image src={idImagePreviews.back} alt="Back ID" width={200} height={120} className="mx-auto rounded-lg object-cover" />
<button onClick={(e) => { e.stopPropagation(); setIdImages(prev => ({...prev, back: null})); setIdImagePreviews(prev => ({...prev, back: ''})); }}
className="absolute -top-2 -right-2 w-6 h-6 bg-red-500 rounded-full flex items-center justify-center hover:bg-red-600">
<X className="w-4 h-4 text-white" />
</button>
</div>
) : (<><Camera className="w-12 h-12 text-gray-500 mx-auto mb-3" /><p className="text-gray-400">اضغط لرفع الصورة</p><p className="text-xs text-gray-500 mt-2">JPEG, PNG, JPG حتى 5MB</p></>)}
</div>
{errors.back && <p className="text-red-500 text-sm mt-1">{errors.back}</p>}
</motion.div>
<motion.div variants={fadeInUp} className="flex items-center gap-2">
<input type="checkbox" id="terms" checked={formData.agreeTerms}
onChange={(e) => setFormData({...formData, agreeTerms: e.target.checked})}
className="w-4 h-4 rounded border-gray-600 bg-white/5 text-amber-500 focus:ring-amber-500" required />
<label htmlFor="terms" className="text-sm text-gray-300">
أوافق على <Link href="/terms" className="text-amber-400 hover:text-amber-300">شروط الاستخدام</Link> و <Link href="/privacy" className="text-amber-400 hover:text-amber-300">سياسة الخصوصية</Link>
</label>
</motion.div>
</>
)}
{/* ─── Buttons ─── */}
<motion.div variants={fadeInUp} className="flex gap-3 pt-4">
{step === 1 ? (
<>
<button type="button" onClick={() => router.push('/auth/choose-role')}
className="flex-1 py-3 px-4 bg-white/5 border border-gray-700 rounded-xl text-gray-300 hover:bg-white/10 transition-colors">إلغاء</button>
<button type="submit"
className="flex-1 bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 rounded-xl font-medium hover:from-amber-600 hover:to-amber-700 transition-all">التالي</button>
</>
) : (
<>
<button type="button" onClick={() => setStep(1)}
className="flex-1 py-3 px-4 bg-white/5 border border-gray-700 rounded-xl text-gray-300 hover:bg-white/10 transition-colors">السابق</button>
<button type="submit" disabled={isLoading || !formData.agreeTerms}
className="flex-1 bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 rounded-xl font-medium hover:from-amber-600 hover:to-amber-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed">
{isLoading ? (<div className="flex items-center justify-center gap-2"><Loader2 className="w-5 h-5 animate-spin" /><span>جاري التسجيل...</span></div>) : 'إنشاء حساب'}
</button>
</>
)}
</motion.div>
</motion.form>
</div>
</motion.div>
</motion.div>
{/* ─── OTP Modal ─── */}
<AnimatePresence>
{showOtpModal && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center p-4 z-50">
<motion.div initial={{ scale: 0.9, y: 20 }} animate={{ scale: 1, y: 0 }} exit={{ scale: 0.9, y: 20 }}
className="bg-gray-900 border border-white/10 rounded-2xl w-full max-w-md p-6 shadow-2xl">
<div className="text-center mb-6">
<div className="w-16 h-16 bg-amber-500/20 rounded-full flex items-center justify-center mx-auto mb-3">
<Shield className="w-8 h-8 text-amber-500" />
</div>
<h2 className="text-xl font-bold text-white">التحقق من البريد</h2>
<p className="text-gray-400 text-sm mt-1">تم إرسال رمز التحقق إلى</p>
<p className="text-amber-400 font-medium text-sm">{formData.email}</p>
</div>
<div className="mb-6">
<label className="block text-sm font-medium text-gray-300 mb-2">رمز التحقق</label>
<div className="relative">
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<KeyRound className="w-5 h-5 text-gray-400" />
</div>
<input type="text" value={otpCode} maxLength={6}
onChange={(e) => setOtpCode(e.target.value)}
className="w-full pr-12 pl-4 py-3 bg-white/5 border border-gray-700 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 text-white text-center tracking-[0.5em] text-xl"
placeholder="------" />
</div>
</div>
<div className="flex gap-3">
<button onClick={handleVerifyOTP} disabled={isLoading || !otpCode}
className="flex-1 bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 rounded-xl font-medium hover:from-amber-600 hover:to-amber-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2">
{isLoading ? <><Loader2 className="w-5 h-5 animate-spin" /><span>جاري التحقق...</span></> : 'تحقق'}
</button>
</div>
<button onClick={handleResendOTP} disabled={isLoading}
className="w-full text-center text-amber-400 hover:text-amber-300 text-sm mt-3 disabled:opacity-50">
إعادة إرسال الرمز
</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}