diff --git a/app/register/owner/page.js b/app/register/owner/page.js index 0944672..fd72d96 100644 --- a/app/register/owner/page.js +++ b/app/register/owner/page.js @@ -17,7 +17,8 @@ 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 [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); @@ -134,7 +135,6 @@ export default function OwnerRegisterPage() { 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); @@ -142,36 +142,26 @@ export default function OwnerRegisterPage() { } const apiMessage = res.message || res.data?.message; - toast.success(apiMessage || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { - duration: 4000, - }); + toast.success(apiMessage || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { duration: 4000 }); - // ── Auto-login to trigger OTP ── + // 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'); - } + if (otpToken) AuthService.addToken(otpToken); const loginMsg = loginRes.message || loginRes.data?.message; toast(loginMsg || 'تم إرسال رمز التحقق إلى بريدك الإلكتروني', { icon: '📧' }); - setStep(3); + setShowOtpModal(true); } else if (loginRes.status === 200) { - // Direct login success (no OTP needed) const loginToken = loginRes.data; - if (loginToken) { - AuthService.addToken(loginToken); - } + 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); @@ -199,17 +189,11 @@ export default function OwnerRegisterPage() { 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); + 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); @@ -223,11 +207,9 @@ export default function OwnerRegisterPage() { } }; - // ─── Resend OTP ─── const handleResendOTP = async () => { setIsLoading(true); console.log('[OwnerRegister] Resending email OTP...'); - try { await sendEmailOTP(); toast.success('تم إرسال رمز تحقق جديد'); @@ -253,82 +235,58 @@ export default function OwnerRegisterPage() {
- {/* Background blobs */}
{[...Array(20)].map((_, i) => ( - + transition={{ duration: Math.random() * 15 + 15, repeat: Infinity, ease: "linear" }} /> ))}
- - {/* Progress bar */} + + {/* Progress */}
العودة - خطوة {step} من 3 + خطوة {step} من 2
- {[1, 2, 3].map((s) => ( + {[1, 2].map((s) => ( = s ? 'bg-amber-500' : 'bg-gray-700'}`} animate={{ scaleX: step >= s ? 1 : 0.5 }} /> ))}
- - {/* Header */} +
- - {step === 3 ? : } + +

- {step === 1 ? 'معلومات المالك' : step === 2 ? 'الوثائق الرسمية' : 'التحقق من البريد'} + {step === 1 ? 'معلومات المالك' : 'الوثائق الرسمية'}

- {step === 1 ? 'أدخل معلوماتك الأساسية' : step === 2 ? 'يرجى رفع صور الهوية للتحقق' : 'أدخل رمز التحقق المرسل إلى بريدك'} + {step === 1 ? 'أدخل معلوماتك الأساسية' : 'يرجى رفع صور الهوية للتحقق'}

- { e.preventDefault(); handleVerifyOTP(); }} - className="space-y-6" - > - {/* ─── STEP 1: Form fields ─── */} + { e.preventDefault(); handleNextStep(); } : handleSubmit} + className="space-y-6"> + + {/* ─── STEP 1: Form ─── */} {step === 1 && ( <> @@ -387,20 +345,15 @@ export default function OwnerRegisterPage() { {errors.whatsapp &&

{errors.whatsapp}

}
- {/* Owner Type */} - -

المحدد: {OwnerTypeLabels[formData.ownerType]}

-

[Console] ownerType = {formData.ownerType}

@@ -414,7 +367,7 @@ export default function OwnerRegisterPage() { 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="أدخل كلمة المرور" />
{errors.password &&

{errors.password}

} @@ -431,7 +384,7 @@ export default function OwnerRegisterPage() { 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="أعد إدخال كلمة المرور" /> {formData.confirmPassword && (
@@ -460,13 +413,7 @@ export default function OwnerRegisterPage() {
- ) : ( - <> - -

اضغط لرفع الصورة

-

JPEG, PNG, JPG • حتى 5MB

- - )} + ) : (<>

اضغط لرفع الصورة

JPEG, PNG, JPG • حتى 5MB

)}
{errors.front &&

{errors.front}

} @@ -484,13 +431,7 @@ export default function OwnerRegisterPage() { - ) : ( - <> - -

اضغط لرفع الصورة

-

JPEG, PNG, JPG • حتى 5MB

- - )} + ) : (<>

اضغط لرفع الصورة

JPEG, PNG, JPG • حتى 5MB

)} {errors.back &&

{errors.back}

} @@ -498,7 +439,7 @@ export default function OwnerRegisterPage() { 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 /> + className="w-4 h-4 rounded border-gray-600 bg-white/5 text-amber-500 focus:ring-amber-500" required /> @@ -506,74 +447,75 @@ export default function OwnerRegisterPage() { )} - {/* ─── STEP 3: OTP ─── */} - {step === 3 && ( - -
- -

تم إرسال رمز التحقق إلى

-

{formData.email}

-
- -
- -
-
- -
- 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="------" /> -
-
- - -
- )} - - {/* ─── Navigation Buttons ─── */} + {/* ─── Buttons ─── */} - {step === 1 && ( + {step === 1 ? ( <> - - )} - {step === 2 && ( + ) : ( <> )} - {step === 3 && ( - - )}
+ + {/* ─── OTP Modal ─── */} + + {showOtpModal && ( + + +
+
+ +
+

التحقق من البريد

+

تم إرسال رمز التحقق إلى

+

{formData.email}

+
+ +
+ +
+
+ +
+ 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="------" /> +
+
+ +
+ +
+ + +
+
+ )} +
); } diff --git a/app/register/tenant/page.js b/app/register/tenant/page.js index 82f4c39..366e4ac 100644 --- a/app/register/tenant/page.js +++ b/app/register/tenant/page.js @@ -1,13 +1,14 @@ 'use client'; -import { useState } from 'react'; -import { motion } from 'framer-motion'; +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, CheckCircle, XCircle, ArrowLeft, Home, Loader2, - Shield, KeyRound + Shield, KeyRound, Camera, X } from 'lucide-react'; import toast, { Toaster } from 'react-hot-toast'; import { addCustomer, loginWithEmail, sendEmailOTP, verifyEmail } from '../../utils/api'; @@ -16,7 +17,8 @@ import { CustomerType, CustomerTypeLabels } from '../../enums'; export default function TenantRegisterPage() { const router = useRouter(); - const [step, setStep] = useState(1); // 1=form, 2=OTP + 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); @@ -31,13 +33,38 @@ export default function TenantRegisterPage() { 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('[CustomerRegister] 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 validateForm = () => { + const validateStep1 = () => { const newErrors = {}; if (!formData.name) newErrors.name = 'الاسم الكامل مطلوب'; else if (formData.name.length < 3) newErrors.name = 'الاسم يجب أن يكون 3 أحرف على الأقل'; @@ -57,12 +84,30 @@ export default function TenantRegisterPage() { 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('[CustomerRegister] 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 (!validateForm()) { - toast.error('يرجى تصحيح الأخطاء في النموذج'); + if (!validateStep2()) { + toast.error('يرجى إكمال جميع الصور المطلوبة'); return; } if (!formData.agreeTerms) { @@ -86,7 +131,6 @@ export default function TenantRegisterPage() { console.log('[CustomerRegister] addCustomer response:', res); if (res.status === 200 || res.ok) { - // ── Store temp token for OTP flow ── const tempToken = res.data; if (tempToken) { AuthService.addToken(tempToken); @@ -94,36 +138,26 @@ export default function TenantRegisterPage() { } const apiMessage = res.message || res.data?.message; - toast.success(apiMessage || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { - duration: 4000, - }); + toast.success(apiMessage || 'تم إنشاء الحساب! يرجى التحقق من بريدك الإلكتروني', { duration: 4000 }); - // ── Auto-login to trigger OTP ── + // Auto-login to trigger OTP console.log('[CustomerRegister] Auto-login to send OTP...'); const loginRes = await loginWithEmail(formData.email, formData.password); console.log('[CustomerRegister] login response:', loginRes); if (loginRes.status === 206) { - // OTP sent — move to OTP step const otpToken = loginRes.data; - if (otpToken) { - AuthService.addToken(otpToken); - console.log('[CustomerRegister] OTP token stored'); - } + if (otpToken) AuthService.addToken(otpToken); const loginMsg = loginRes.message || loginRes.data?.message; toast(loginMsg || 'تم إرسال رمز التحقق إلى بريدك الإلكتروني', { icon: '📧' }); - setStep(2); + setShowOtpModal(true); } else if (loginRes.status === 200) { - // Direct login success (no OTP needed) const loginToken = loginRes.data; - if (loginToken) { - AuthService.addToken(loginToken); - } + if (loginToken) AuthService.addToken(loginToken); toast.success(loginRes.message || 'تم تسجيل الدخول بنجاح!'); router.push('/'); } } else { - // Registration failed const errMsg = res.message || res.data?.message || 'فشل في إنشاء الحساب'; console.error('[CustomerRegister] Registration failed:', errMsg); toast.error(errMsg); @@ -151,17 +185,11 @@ export default function TenantRegisterPage() { console.log('[CustomerRegister] VerifyEmail response:', res); if (res.status === 200) { - // ── Verified! Remove temp token, redirect to login ── AuthService.deleteToken(); console.log('[CustomerRegister] Temp token removed after verification'); - - toast.success(res.message || 'تم التحقق من البريد الإلكتروني بنجاح!', { - duration: 3000, - }); - - setTimeout(() => { - router.push('/login'); - }, 1500); + toast.success(res.message || 'تم التحقق من البريد الإلكتروني بنجاح!', { duration: 3000 }); + setShowOtpModal(false); + setTimeout(() => router.push('/login'), 1500); } else { const errMsg = res.message || res.data?.message || 'رمز التحقق غير صحيح'; console.error('[CustomerRegister] Verification failed:', errMsg); @@ -175,11 +203,9 @@ export default function TenantRegisterPage() { } }; - // ─── Resend OTP ─── const handleResendOTP = async () => { setIsLoading(true); console.log('[CustomerRegister] Resending email OTP...'); - try { await sendEmailOTP(); toast.success('تم إرسال رمز تحقق جديد'); @@ -205,30 +231,19 @@ export default function TenantRegisterPage() {
- {/* Background blobs */}
{[...Array(20)].map((_, i) => ( - + transition={{ duration: Math.random() * 15 + 15, repeat: Infinity, ease: "linear" }} /> ))}
- - {/* Back link */} - + + {/* Back */} + العودة @@ -236,43 +251,36 @@ export default function TenantRegisterPage() { {/* Progress */} -
+
{[1, 2].map((s) => ( = s ? 'bg-blue-500' : 'bg-gray-700'}`} animate={{ scaleX: step >= s ? 1 : 0.5 }} /> ))}
- {/* Header */}
- - {step === 2 ? : } + +

- {step === 1 ? 'إنشاء حساب مستأجر' : 'التحقق من البريد'} + {step === 1 ? 'إنشاء حساب مستأجر' : 'الوثائق الرسمية'}

- {step === 1 ? 'انضم إلينا وابحث عن منزل أحلامك' : 'أدخل رمز التحقق المرسل إلى بريدك'} + {step === 1 ? 'انضم إلينا وابحث عن منزل أحلامك' : 'يرجى رفع صور الهوية للتحقق'}

- { e.preventDefault(); handleVerifyOTP(); }} - className="space-y-6" - > - {/* ─── STEP 1: Form fields ─── */} + { e.preventDefault(); handleNextStep(); } : handleSubmit} + className="space-y-6"> + + {/* ─── STEP 1: Form ─── */} {step === 1 && ( <> @@ -317,20 +325,15 @@ export default function TenantRegisterPage() { {errors.phone &&

{errors.phone}

}
- {/* Customer Type */} - -

المحدد: {CustomerTypeLabels[formData.customerType]}

-

[Console] customerType = {formData.customerType}

@@ -344,7 +347,7 @@ export default function TenantRegisterPage() { className={`w-full pr-12 pl-12 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.password ? 'border-red-500' : 'border-gray-700'}`} placeholder="أدخل كلمة المرور" />
{errors.password &&

{errors.password}

} @@ -361,7 +364,7 @@ export default function TenantRegisterPage() { className={`w-full pr-12 pl-12 py-3 bg-white/5 border rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${errors.confirmPassword ? 'border-red-500' : 'border-gray-700'}`} placeholder="أعد إدخال كلمة المرور" /> {formData.confirmPassword && (
@@ -371,11 +374,52 @@ export default function TenantRegisterPage() {
{errors.confirmPassword &&

{errors.confirmPassword}

} + + )} + + {/* ─── STEP 2: ID Images ─── */} + {step === 2 && ( + <> + + +
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-blue-500 hover:bg-white/5'}`}> + handleImageUpload('front', e.target.files?.[0])} className="hidden" /> + {idImagePreviews.front ? ( +
+ Front ID + +
+ ) : (<>

اضغط لرفع الصورة

JPEG, PNG, JPG • حتى 5MB

)} +
+ {errors.front &&

{errors.front}

} +
+ + + +
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-blue-500 hover:bg-white/5'}`}> + handleImageUpload('back', e.target.files?.[0])} className="hidden" /> + {idImagePreviews.back ? ( +
+ Back ID + +
+ ) : (<>

اضغط لرفع الصورة

JPEG, PNG, JPG • حتى 5MB

)} +
+ {errors.back &&

{errors.back}

} +
setFormData({...formData, agreeTerms: e.target.checked})} - className="w-4 h-4 rounded border-gray-600 bg-white/5 text-blue-500 focus:ring-blue-500 focus:ring-offset-0" required /> + className="w-4 h-4 rounded border-gray-600 bg-white/5 text-blue-500 focus:ring-blue-500" required /> @@ -383,61 +427,25 @@ export default function TenantRegisterPage() { )} - {/* ─── STEP 2: OTP ─── */} - {step === 2 && ( - -
- -

تم إرسال رمز التحقق إلى

-

{formData.email}

-
- -
- -
-
- -
- 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-blue-500 focus:border-transparent text-white placeholder-gray-500 text-center tracking-[0.5em] text-xl transition-all" - placeholder="------" /> -
-
- - -
- )} - - {/* ─── Navigation Buttons ─── */} + {/* ─── Buttons ─── */} - {step === 1 && ( + {step === 1 ? ( <> - + + ) : ( + <> + + )} - {step === 2 && ( - - )} {step === 1 && ( @@ -450,6 +458,51 @@ export default function TenantRegisterPage() {
+ + {/* ─── OTP Modal ─── */} + + {showOtpModal && ( + + +
+
+ +
+

التحقق من البريد

+

تم إرسال رمز التحقق إلى

+

{formData.email}

+
+ +
+ +
+
+ +
+ 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-blue-500 text-white text-center tracking-[0.5em] text-xl" + placeholder="------" /> +
+
+ +
+ +
+ + +
+
+ )} +
); }