Files
SweetHome/app/register/agent/page.js
mouazkh 00ccf5f262
All checks were successful
Build frontend / build (push) Successful in 55s
the best in the west is mouaz
2026-05-25 21:27:39 +03:00

649 lines
39 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.

// app/register/agent/page.js
'use client';
import { useState, useRef, useMemo } 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, MapPin, FileText, BadgeCheck, Briefcase
} from 'lucide-react';
import toast, { Toaster } from 'react-hot-toast';
import { registerRealEstateAgent, loginWithEmail, sendEmailOTP, verifyEmail } from '../../utils/api';
import AuthService from '../../services/AuthService';
export default function AgentRegisterPage() {
const router = useRouter();
const [step, setStep] = useState(1);
const [showOtpModal, setShowOtpModal] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
phone: '',
whatsapp: '',
nationalNumber: '',
password: '',
confirmPassword: '',
agencyAddress: '',
licenseNumber: '',
agreeTerms: false
});
const [idImages, setIdImages] = useState({ front: null, back: null, license: null });
const [idImagePreviews, setIdImagePreviews] = useState({ front: '', back: '', license: '' });
const [otpCode, setOtpCode] = useState('');
const [errors, setErrors] = useState({});
const fileInputFrontRef = useRef(null);
const fileInputBackRef = useRef(null);
const fileInputLicenseRef = useRef(null);
const fileInputLicenseStep3Ref = 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 }));
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.firstName) newErrors.firstName = 'الاسم الأول مطلوب';
if (!formData.lastName) newErrors.lastName = 'اسم العائلة مطلوب';
if (!formData.email) newErrors.email = 'البريد الإلكتروني مطلوب';
else if (!validateEmail(formData.email)) newErrors.email = 'البريد الإلكتروني غير صالح';
if (!formData.phone) newErrors.phone = 'رقم الهاتف مطلوب';
else if (!validatePhone(formData.phone)) newErrors.phone = 'رقم الهاتف غير صالح (يجب أن يبدأ 09 أو 05)';
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 (!formData.nationalNumber) newErrors.nationalNumber = 'الرقم الوطني مطلوب';
if (!formData.whatsapp) newErrors.whatsapp = 'رقم الواتساب مطلوب';
else if (!validatePhone(formData.whatsapp)) newErrors.whatsapp = 'رقم الواتساب غير صالح (يجب أن يبدأ 09 أو 05)';
if (!idImages.front) newErrors.front = 'صورة الوجه الأمامي للهوية مطلوبة';
if (!idImages.back) newErrors.back = 'صورة الوجه الخلفي للهوية مطلوبة';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const validateStep3 = () => {
const newErrors = {};
if (!formData.agencyAddress) newErrors.agencyAddress = 'عنوان الوكالة مطلوب';
if (!formData.licenseNumber) newErrors.licenseNumber = 'رقم الترخيص مطلوب';
if (!idImages.license) newErrors.license = 'صورة الترخيص مطلوبة';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleNextStep = () => {
if (step === 1 && validateStep1()) {
setStep(2);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else if (step === 2 && validateStep2()) {
setStep(3);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else if (step === 3 && validateStep3()) {
setStep(4);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else {
toast.error('يرجى تصحيح الأخطاء في النموذج');
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formData.agreeTerms) {
toast.error('يجب الموافقة على الشروط والأحكام');
return;
}
setIsLoading(true);
try {
const fd = new FormData();
fd.append('FirstName', formData.firstName);
fd.append('LastName', formData.lastName);
fd.append('Email', formData.email);
fd.append('PhoneNumber', formData.phone);
fd.append('WhatsAppNumber', formData.whatsapp);
fd.append('NationalNumber', formData.nationalNumber);
fd.append('Password', formData.password);
fd.append('AgencyAddress', formData.agencyAddress);
fd.append('LicenseNumber', formData.licenseNumber);
fd.append('Language', '0');
if (idImages.front) fd.append('FrontIdCarImagePath', idImages.front);
if (idImages.back) fd.append('RearIdCarImagePath', idImages.back);
if (idImages.license) fd.append('LicenseImagePath', idImages.license);
const res = await registerRealEstateAgent(fd);
if (res.ok || res.status === 200) {
const tempToken = res.data;
if (tempToken) AuthService.addToken(tempToken);
toast.success(res.message || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { duration: 4000 });
const loginRes = await loginWithEmail(formData.email, formData.password);
if (loginRes.status === 206) {
const otpToken = loginRes.data;
if (otpToken) AuthService.addToken(otpToken);
toast(loginRes.message || 'تم إرسال رمز التحقق إلى بريدك الإلكتروني', { icon: '📧' });
setShowOtpModal(true);
} else if (loginRes.status === 200) {
const loginToken = loginRes.data;
if (loginToken) AuthService.addToken(loginToken);
toast.success(loginRes.message || 'تم تسجيل الدخول بنجاح!');
router.push('/');
} else {
toast.success('تم إنشاء الحساب بنجاح! يرجى تسجيل الدخول');
setTimeout(() => router.push('/login'), 1500);
}
} else {
toast.error(res.message || res.data?.message || 'فشل في إنشاء الحساب');
}
} catch (err) {
toast.error(err.message || 'حدث خطأ أثناء التسجيل');
} finally {
setIsLoading(false);
}
};
const handleVerifyOTP = async () => {
if (!otpCode || otpCode.length < 4) {
toast.error('يرجى إدخال رمز التحقق');
return;
}
setIsLoading(true);
try {
const res = await verifyEmail(otpCode);
if (res.status === 200) {
AuthService.deleteToken();
toast.success(res.message || 'تم التحقق من البريد الإلكتروني بنجاح!');
setShowOtpModal(false);
setTimeout(() => router.push('/login'), 1500);
} else {
toast.error(res.message || res.data?.message || 'رمز التحقق غير صحيح');
}
} catch (err) {
toast.error(err.message || 'حدث خطأ أثناء التحقق');
} finally {
setIsLoading(false);
}
};
const handleResendOTP = async () => {
setIsLoading(true);
try {
await sendEmailOTP();
toast.success('تم إرسال رمز تحقق جديد');
} catch (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 } }
};
const backgroundElements = useMemo(() => {
const circles = [
{ style: { top: '20%', right: '20%', width: '256px', height: '256px' }, className: 'bg-purple-500/5' },
{ style: { bottom: '20%', left: '20%', width: '320px', height: '320px' }, className: 'bg-amber-500/5' },
{ style: { top: '50%', left: '50%', width: '384px', height: '384px', transform: 'translate(-50%, -50%)' }, className: 'bg-purple-500/5' },
];
const dots = Array.from({ length: 10 }).map((_, i) => ({
left: `${5 + i * 10}%`,
top: `${10 + (i * 7) % 80}%`,
size: `${80 + (i % 5) * 15}px`
}));
return (
<>
{circles.map((circle, i) => (
<div key={`circle-${i}`} className={`absolute rounded-full ${circle.className}`} style={circle.style} />
))}
{dots.map((dot, i) => (
<div key={`dot-${i}`} className="absolute rounded-full bg-purple-500/10" style={{ left: dot.left, top: dot.top, width: dot.size, height: dot.size }} />
))}
</>
);
}, []);
const stepTitles = ['المعلومات الأساسية', 'معلومات الهوية', 'معلومات الوكالة', 'تأكيد التسجيل'];
const stepDescriptions = ['أدخل معلوماتك الأساسية', 'يرجى رفع صور الهوية للتحقق', 'أدخل بيانات الوكالة والترخيص', 'راجع معلوماتك قبل الإرسال'];
const renderStepFields = () => {
switch (step) {
case 1:
return (
<>
<motion.div variants={fadeInUp} className="grid grid-cols-2 gap-3">
<div>
<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.firstName ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-500'}`} />
</div>
<input type="text" value={formData.firstName}
onChange={(e) => { setFormData({...formData, firstName: e.target.value}); setErrors({...errors, firstName: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.firstName ? 'border-red-500' : 'border-gray-700'}`}
placeholder="الاسم الأول" />
</div>
{errors.firstName && <p className="text-red-500 text-sm mt-1">{errors.firstName}</p>}
</div>
<div>
<label className="block text-sm font-medium text-gray-300 mb-2">اسم العائلة <span className="text-red-500">*</span></label>
<input type="text" value={formData.lastName}
onChange={(e) => { setFormData({...formData, lastName: e.target.value}); setErrors({...errors, lastName: null}); }}
className={`w-full px-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.lastName ? 'border-red-500' : 'border-gray-700'}`}
placeholder="اسم العائلة" />
{errors.lastName && <p className="text-red-500 text-sm mt-1">{errors.lastName}</p>}
</div>
</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-purple-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-purple-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-red-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 ${errors.phone ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-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 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.phone ? 'border-red-500' : 'border-gray-700'}`}
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">
<Lock className={`w-5 h-5 ${errors.password ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-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-purple-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-purple-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-purple-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>
</>
);
case 2:
return (
<>
<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.nationalNumber ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-500'}`} />
</div>
<input type="text" value={formData.nationalNumber}
onChange={(e) => { setFormData({...formData, nationalNumber: e.target.value}); setErrors({...errors, nationalNumber: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.nationalNumber ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل الرقم الوطني" />
</div>
{errors.nationalNumber && <p className="text-red-500 text-sm mt-1">{errors.nationalNumber}</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-purple-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-purple-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>
<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-purple-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-purple-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>
</>
);
case 3:
return (
<>
<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">
<MapPin className={`w-5 h-5 ${errors.agencyAddress ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-500'}`} />
</div>
<input type="text" value={formData.agencyAddress}
onChange={(e) => { setFormData({...formData, agencyAddress: e.target.value}); setErrors({...errors, agencyAddress: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.agencyAddress ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل عنوان الوكالة" />
</div>
{errors.agencyAddress && <p className="text-red-500 text-sm mt-1">{errors.agencyAddress}</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">
<BadgeCheck className={`w-5 h-5 ${errors.licenseNumber ? 'text-red-500' : 'text-gray-400 group-focus-within:text-purple-500'}`} />
</div>
<input type="text" value={formData.licenseNumber}
onChange={(e) => { setFormData({...formData, licenseNumber: e.target.value}); setErrors({...errors, licenseNumber: null}); }}
className={`w-full pr-12 pl-4 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.licenseNumber ? 'border-red-500' : 'border-gray-700'}`}
placeholder="أدخل رقم الترخيص العقاري" />
</div>
{errors.licenseNumber && <p className="text-red-500 text-sm mt-1">{errors.licenseNumber}</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={() => fileInputLicenseStep3Ref.current?.click()}
className={`relative border-2 border-dashed rounded-xl p-6 text-center cursor-pointer transition-all ${idImagePreviews.license ? 'border-green-500 bg-green-500/10' : errors.license ? 'border-red-500 bg-red-500/10' : 'border-gray-700 hover:border-purple-500 hover:bg-white/5'}`}>
<input ref={fileInputLicenseStep3Ref} type="file" accept="image/*" onChange={(e) => handleImageUpload('license', e.target.files?.[0])} className="hidden" />
{idImagePreviews.license ? (
<div className="relative">
<Image src={idImagePreviews.license} alt="License" width={200} height={120} className="mx-auto rounded-lg object-cover" />
<button onClick={(e) => { e.stopPropagation(); setIdImages(prev => ({...prev, license: null})); setIdImagePreviews(prev => ({...prev, license: ''})); }}
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.license && <p className="text-red-500 text-sm mt-1">{errors.license}</p>}
</motion.div>
</>
);
case 4:
return (
<motion.div variants={fadeInUp} className="space-y-4">
<div className="bg-white/5 rounded-xl p-4 border border-gray-700">
<h3 className="text-lg font-semibold text-white mb-3 flex items-center gap-2">
<User className="w-5 h-5 text-purple-400" />
المعلومات الأساسية
</h3>
<div className="grid grid-cols-2 gap-3 text-sm">
<div><span className="text-gray-400">الاسم الأول:</span> <span className="text-white">{formData.firstName}</span></div>
<div><span className="text-gray-400">اسم العائلة:</span> <span className="text-white">{formData.lastName}</span></div>
<div><span className="text-gray-400">البريد الإلكتروني:</span> <span className="text-white">{formData.email}</span></div>
<div><span className="text-gray-400">رقم الهاتف:</span> <span className="text-white">{formData.phone}</span></div>
</div>
</div>
<div className="bg-white/5 rounded-xl p-4 border border-gray-700">
<h3 className="text-lg font-semibold text-white mb-3 flex items-center gap-2">
<FileText className="w-5 h-5 text-purple-400" />
معلومات الهوية
</h3>
<div className="grid grid-cols-2 gap-3 text-sm">
<div><span className="text-gray-400">الرقم الوطني:</span> <span className="text-white">{formData.nationalNumber}</span></div>
<div><span className="text-gray-400">رقم الواتساب:</span> <span className="text-white">{formData.whatsapp}</span></div>
</div>
<div className="flex gap-3 mt-3">
{idImagePreviews.front && (
<div className="text-center">
<Image src={idImagePreviews.front} alt="Front ID" width={100} height={60} className="rounded-lg object-cover mx-auto" />
<p className="text-xs text-gray-400 mt-1">الوجه الأمامي</p>
</div>
)}
{idImagePreviews.back && (
<div className="text-center">
<Image src={idImagePreviews.back} alt="Back ID" width={100} height={60} className="rounded-lg object-cover mx-auto" />
<p className="text-xs text-gray-400 mt-1">الوجه الخلفي</p>
</div>
)}
</div>
</div>
<div className="bg-white/5 rounded-xl p-4 border border-gray-700">
<h3 className="text-lg font-semibold text-white mb-3 flex items-center gap-2">
<Briefcase className="w-5 h-5 text-purple-400" />
معلومات الوكالة
</h3>
<div className="grid grid-cols-2 gap-3 text-sm">
<div><span className="text-gray-400">عنوان الوكالة:</span> <span className="text-white">{formData.agencyAddress}</span></div>
<div><span className="text-gray-400">رقم الترخيص:</span> <span className="text-white">{formData.licenseNumber}</span></div>
</div>
{idImagePreviews.license && (
<div className="text-center mt-3">
<Image src={idImagePreviews.license} alt="License" width={100} height={60} className="rounded-lg object-cover mx-auto" />
<p className="text-xs text-gray-400 mt-1">صورة الترخيص</p>
</div>
)}
</div>
<motion.div variants={fadeInUp} className="flex items-center gap-2 pt-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-purple-500 focus:ring-purple-500" required />
<label htmlFor="terms" className="text-sm text-gray-300">
أوافق على <Link href="/terms" className="text-purple-400 hover:text-purple-300">شروط الاستخدام</Link> و <Link href="/privacy" className="text-purple-400 hover:text-purple-300">سياسة الخصوصية</Link>
</label>
</motion.div>
</motion.div>
);
default:
return null;
}
};
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">{backgroundElements}</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">
<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} من 4</span>
</div>
<div className="flex gap-2">
{[1, 2, 3, 4].map((s) => (
<motion.div key={s} className={`h-2 flex-1 rounded-full ${step >= s ? 'bg-purple-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-purple-500 to-purple-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">
<Briefcase className="w-10 h-10 text-white" />
</motion.div>
<h1 className="text-3xl font-bold text-white mb-2">{stepTitles[step - 1]}</h1>
<p className="text-purple-100">{stepDescriptions[step - 1]}</p>
</motion.div>
</div>
<div className="p-8">
<motion.form variants={staggerContainer} initial="initial" animate="animate"
onSubmit={step < 4 ? (e) => { e.preventDefault(); handleNextStep(); } : handleSubmit}
className="space-y-6">
{renderStepFields()}
<motion.div variants={fadeInUp} className="flex gap-3 pt-4">
{step > 1 && (
<button type="button" onClick={() => { setStep(step - 1); window.scrollTo({ top: 0, behavior: 'smooth' }); }}
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>
)}
{step < 4 ? (
<button type="submit" className="flex-1 bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 px-4 rounded-xl font-medium hover:from-purple-600 hover:to-purple-700 transition-all">
التالي
</button>
) : (
<button type="submit" disabled={isLoading || !formData.agreeTerms}
className="flex-1 bg-gradient-to-r from-purple-500 to-purple-600 text-white py-3 px-4 rounded-xl font-medium hover:from-purple-600 hover:to-purple-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>
<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-purple-500/20 rounded-full flex items-center justify-center mx-auto mb-3">
<Shield className="w-8 h-8 text-purple-500" />
</div>
<h2 className="text-xl font-bold text-white">التحقق من البريد</h2>
<p className="text-gray-400 text-sm mt-1">تم إرسال رمز التحقق إلى</p>
<p className="text-purple-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-purple-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-purple-500 to-purple-600 text-white py-3 rounded-xl font-medium hover:from-purple-600 hover:to-purple-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-purple-400 hover:text-purple-300 text-sm mt-3 disabled:opacity-50">
إعادة إرسال الرمز
</button>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}