Files
SweetHome/app/register/owner/page.js
Claw AI eff0b41b78
All checks were successful
Build frontend / build (push) Successful in 57s
Add enums, AuthService, and integrate backend registration endpoints
- Add separate enum files: BuildingType, PropertyStatus, BookingStatus, CommissionType, IdentityType, UserRole, City, LoginMethod, OwnerType, CustomerType
- Add AuthService (addToken/getToken/deleteToken)
- Update api.js: use AuthService, add Owner/Add and Customer/Add endpoints
- Update login page to use AuthService for token storage
- Rewrite owner register: 3-step flow with OwnerType dropdown, backend integration, OTP verification
- Rewrite tenant register: 2-step flow with CustomerType dropdown, backend integration, OTP verification
- Update homepage and property detail to use enums instead of hardcoded maps
- Update AddPropertyForm to import from enums directly
- Add console logs and status toasts linked to API response messages
2026-03-27 18:03:12 +00:00

580 lines
32 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, 3=OTP
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 = {
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) {
// ── Store temp token for OTP flow ──
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) {
// OTP sent — move to OTP step
const otpToken = loginRes.data;
if (otpToken) {
AuthService.addToken(otpToken);
console.log('[OwnerRegister] OTP token stored');
}
const loginMsg = loginRes.message || loginRes.data?.message;
toast(loginMsg || 'تم إرسال رمز التحقق إلى بريدك الإلكتروني', { icon: '📧' });
setStep(3);
} else if (loginRes.status === 200) {
// Direct login success (no OTP needed)
const loginToken = loginRes.data;
if (loginToken) {
AuthService.addToken(loginToken);
}
toast.success(loginRes.message || 'تم تسجيل الدخول بنجاح!');
router.push('/');
}
} else {
// Registration failed
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) {
// ── Verified! Remove temp token, redirect to login ──
AuthService.deleteToken();
console.log('[OwnerRegister] Temp token removed after verification');
toast.success(res.message || 'تم التحقق من البريد الإلكتروني بنجاح!', {
duration: 3000,
});
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);
}
};
// ─── Resend OTP ───
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} />
{/* Background blobs */}
<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 bar */}
<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} من 3</span>
</div>
<div className="flex gap-2">
{[1, 2, 3].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"
>
{/* Header */}
<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"
>
{step === 3 ? <KeyRound className="w-10 h-10 text-white" /> : <Building className="w-10 h-10 text-white" />}
</motion.div>
<h1 className="text-3xl font-bold text-white mb-2">
{step === 1 ? 'معلومات المالك' : step === 2 ? 'الوثائق الرسمية' : 'التحقق من البريد'}
</h1>
<p className="text-amber-100">
{step === 1 ? 'أدخل معلوماتك الأساسية' : step === 2 ? 'يرجى رفع صور الهوية للتحقق' : 'أدخل رمز التحقق المرسل إلى بريدك'}
</p>
</motion.div>
</div>
<div className="p-8">
<motion.form
variants={staggerContainer}
initial="initial"
animate="animate"
onSubmit={step === 1 ? handleNextStep : step === 2 ? handleSubmit : (e) => { e.preventDefault(); handleVerifyOTP(); }}
className="space-y-6"
>
{/* ─── STEP 1: Form fields ─── */}
{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>
{/* Owner Type */}
<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>
<p className="text-xs text-gray-500 mt-1">المحدد: {OwnerTypeLabels[formData.ownerType]}</p>
<p className="text-xs text-gray-600">[Console] ownerType = {formData.ownerType}</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-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 hover:text-gray-300" /> : <Eye className="w-5 h-5 text-gray-400 hover:text-gray-300" />}
</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 hover:text-gray-300" /> : <Eye className="w-5 h-5 text-gray-400 hover:text-gray-300" />}
</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 focus:ring-offset-0" 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>
</>
)}
{/* ─── STEP 3: OTP ─── */}
{step === 3 && (
<motion.div variants={fadeInUp} className="space-y-6">
<div className="bg-amber-500/10 border border-amber-500/20 rounded-xl p-4 text-center">
<Shield className="w-10 h-10 text-amber-500 mx-auto mb-2" />
<p className="text-gray-300 text-sm">تم إرسال رمز التحقق إلى</p>
<p className="text-amber-400 font-medium">{formData.email}</p>
</div>
<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">
<KeyRound className="w-5 h-5 text-gray-400 group-focus-within:text-amber-500" />
</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 focus:border-transparent text-white placeholder-gray-500 text-center tracking-[0.5em] text-xl transition-all"
placeholder="------" />
</div>
</div>
<button type="button" onClick={handleResendOTP} disabled={isLoading}
className="w-full text-center text-amber-400 hover:text-amber-300 text-sm transition-colors disabled:opacity-50">
إعادة إرسال الرمز
</button>
</motion.div>
)}
{/* ─── Navigation 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="button" onClick={handleNextStep}
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>
</>
)}
{step === 2 && (
<>
<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>
</>
)}
{step === 3 && (
<button type="submit" disabled={isLoading || !otpCode}
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>
</div>
);
}