From 1c8e888ea3d30d9e357621983dded7b290e41f82 Mon Sep 17 00:00:00 2001 From: Rahaf Date: Tue, 17 Mar 2026 20:36:59 +0300 Subject: [PATCH] Added Admin page, Login, forgot password, register and owner with profile --- app/ClientLayout.js | 651 +++++++++++++++++ app/admin/page.js | 2 + app/auth/choose-role/page.js | 232 ++++++ app/components/NavLinks.js | 1 + app/forgot-password/page.js | 129 ++++ app/layout.js | 276 +------ app/login/page.js | 424 +++++++++++ app/owner/properties/add/page.js | 1177 ++++++++++++++++++++++++++++++ app/owner/properties/page.js | 1022 ++++++++++++++++++++++++++ app/page.js | 473 +++++++----- app/profile/page.js | 641 ++++++++++++++++ app/register/owner/page.js | 709 ++++++++++++++++++ app/register/tenant/page.js | 438 +++++++++++ package-lock.json | 93 ++- package.json | 3 + 15 files changed, 5790 insertions(+), 481 deletions(-) create mode 100644 app/ClientLayout.js create mode 100644 app/auth/choose-role/page.js create mode 100644 app/forgot-password/page.js create mode 100644 app/login/page.js create mode 100644 app/owner/properties/add/page.js create mode 100644 app/owner/properties/page.js create mode 100644 app/profile/page.js create mode 100644 app/register/owner/page.js create mode 100644 app/register/tenant/page.js diff --git a/app/ClientLayout.js b/app/ClientLayout.js new file mode 100644 index 0000000..ef59356 --- /dev/null +++ b/app/ClientLayout.js @@ -0,0 +1,651 @@ +'use client'; + +import { usePathname } from 'next/navigation'; +import { useTranslation } from 'react-i18next'; +import Link from "next/link"; +import Image from "next/image"; +import { NavLink, MobileNavLink } from "./components/NavLinks"; +import { + Globe, + LogIn, + UserPlus, + UserCircle, + LogOut, + Calendar, + Building, + PlusCircle, + Heart, + MessageCircle, + Settings, + Edit, + Phone, + Mail, + MapPin, + Camera, + Shield, + Bell, + Home, + ChevronDown, + Menu, + X, + TrendingUp, + CalendarDays, + Clock, + Users, + DollarSign +} from 'lucide-react'; +import { useState, useEffect, useRef } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import './i18n/config'; + +export default function ClientLayout({ children }) { + const { t, i18n } = useTranslation(); + const pathname = usePathname(); + const [currentLanguage, setCurrentLanguage] = useState('en'); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [showUserMenu, setShowUserMenu] = useState(false); + const [user, setUser] = useState(null); + const [isMounted, setIsMounted] = useState(false); + const currentYear = new Date().getFullYear(); + const menuRef = useRef(null); + + useEffect(() => { + setIsMounted(true); + const savedLanguage = localStorage.getItem('language') || 'en'; + setCurrentLanguage(savedLanguage); + i18n.changeLanguage(savedLanguage); + + const storedUser = localStorage.getItem('user'); + if (storedUser) { + const userData = JSON.parse(storedUser); + console.log('User data loaded:', userData); + setUser(userData); + } + + if (savedLanguage === 'ar') { + document.documentElement.dir = 'rtl'; + document.documentElement.lang = 'ar'; + } else { + document.documentElement.dir = 'ltr'; + document.documentElement.lang = 'en'; + } + }, [i18n]); + + useEffect(() => { + const handleClickOutside = (event) => { + if (menuRef.current && !menuRef.current.contains(event.target)) { + setShowUserMenu(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const changeLanguage = (lng) => { + i18n.changeLanguage(lng); + setCurrentLanguage(lng); + localStorage.setItem('language', lng); + + if (lng === 'ar') { + document.documentElement.dir = 'rtl'; + document.documentElement.lang = 'ar'; + } else { + document.documentElement.dir = 'ltr'; + document.documentElement.lang = 'en'; + } + }; + + const toggleMobileMenu = () => { + setIsMobileMenuOpen(!isMobileMenuOpen); + }; + + const closeMobileMenu = () => { + setIsMobileMenuOpen(false); + }; + + const logout = () => { + localStorage.removeItem('user'); + setUser(null); + setShowUserMenu(false); + window.location.href = '/'; + }; + + const isAuthPage = ['/login', '/register', '/forgot-password', '/auth/choose-role'].includes(pathname); + + const isProfilePage = pathname === '/profile'; + + const isOwner = user?.role === 'owner'; + const isAdmin = user?.role === 'admin'; + + console.log('User role:', user?.role); + console.log('Is Admin:', isAdmin); + + const getUserInitial = () => { + if (user?.name) { + return user.name.charAt(0).toUpperCase(); + } + return ; + }; + + if (!isMounted) { + return ( +
+
+
+

جاري التحميل...

+
+
+ ); + } + + return ( + <> + {!isAuthPage && ( + + )} + +
+ {children} +
+ + {!isAuthPage && !isProfilePage && ( +
+
+
+
+
+
+ {t("logoAlt")} +
+ + {t("brandNamePart1")}{t("brandNamePart2")} + +
+

+ {t("footerDescription")} +

+
+
+

{t("quickLinks")}

+
    +
  • + + {t("home")} + +
  • +
  • + + {t("ourProducts")} + +
  • + {isAdmin && ( +
  • + + الإدارة + +
  • + )} +
+
+
+

{t("contactUs")}

+
    +
  • + + {t("phone")} +
  • +
  • + + {t("footerEmail")} +
  • +
+
+
+
+

© {currentYear} {t("copyright")}. {t("allRightsReserved")}

+
+
+
+ )} + + ); +} \ No newline at end of file diff --git a/app/admin/page.js b/app/admin/page.js index 93103be..088b2e0 100644 --- a/app/admin/page.js +++ b/app/admin/page.js @@ -1,3 +1,5 @@ +// app/admin/page.js (محدث) + 'use client'; import { motion } from 'framer-motion'; diff --git a/app/auth/choose-role/page.js b/app/auth/choose-role/page.js new file mode 100644 index 0000000..b27b832 --- /dev/null +++ b/app/auth/choose-role/page.js @@ -0,0 +1,232 @@ +'use client'; + +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import { Home, Building, ArrowLeft } from 'lucide-react'; + +export default function ChooseRolePage() { + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.2, + delayChildren: 0.3 + } + } + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { type: 'spring', stiffness: 100 } + } + }; + + const floatingAnimation = { + y: [0, -10, 0], + transition: { + duration: 3, + repeat: Infinity, + ease: "easeInOut" + } + }; + + return ( +
+
+ {[...Array(20)].map((_, i) => ( + + ))} +
+ + + + + + + + العودة للرئيسية + + + + + + مرحباً بك في SweetHome + +

+ اختر نوع الحساب المناسب لك +

+
+ +
+ + +
+ + +
+ + + + +

صاحب عقار

+

+ تريد عرض عقار للإيجار أو البيع؟ انضم كمالك عقار وابدأ الآن +

+ +
    +
  • + + إضافة عقارات غير محدودة +
  • +
  • + + إدارة الحجوزات بسهولة +
  • +
  • + + تواصل مباشر مع المستأجرين +
  • +
+ + + إنشاء حساب مالك + +
+
+ +
+ + + +
+ + +
+ + + + +

مستأجر / مشتري

+

+ تبحث عن عقار للإيجار أو الشراء؟ انضم كعميل وابحث عن منزل أحلامك +

+ +
    +
  • + + آلاف العقارات المتاحة +
  • +
  • + + بحث متقدم بفلاتر ذكية +
  • +
  • + + حجز وإستئجار فوري +
  • +
+ + + إنشاء حساب مستأجر + +
+
+ +
+
+ + + لديك حساب بالفعل؟{' '} + + تسجيل الدخول + + +
+
+ ); +} \ No newline at end of file diff --git a/app/components/NavLinks.js b/app/components/NavLinks.js index 803389a..2830a83 100644 --- a/app/components/NavLinks.js +++ b/app/components/NavLinks.js @@ -1,6 +1,7 @@ 'use client'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; +import { LogIn, UserPlus } from 'lucide-react'; export function NavLink({ href, children }) { const pathname = usePathname(); diff --git a/app/forgot-password/page.js b/app/forgot-password/page.js new file mode 100644 index 0000000..462bd10 --- /dev/null +++ b/app/forgot-password/page.js @@ -0,0 +1,129 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import { Mail, ArrowLeft, CheckCircle } from 'lucide-react'; + +export default function ForgotPasswordPage() { + const [email, setEmail] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [isSubmitted, setIsSubmitted] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsLoading(true); + + setTimeout(() => { + setIsLoading(false); + setIsSubmitted(true); + }, 1500); + }; + + return ( +
+
+
+
+
+ + + + + + العودة لتسجيل الدخول + + + +
+
+ + استعادة كلمة المرور + + + سنرسل لك رابط لإعادة تعيين كلمة المرور + +
+ +
+ {!isSubmitted ? ( +
+
+ +
+
+ +
+ setEmail(e.target.value)} + className="w-full pr-12 pl-4 py-3 bg-white/5 border border-gray-600 rounded-xl focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent text-white placeholder-gray-400 transition-all" + placeholder="أدخل بريدك الإلكتروني" + required + /> +
+
+ + +
+ ) : ( + +
+ +
+

تم الإرسال بنجاح!

+

+ تم إرسال رابط استعادة كلمة المرور إلى {email} +

+ + العودة لتسجيل الدخول + +
+ )} +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/layout.js b/app/layout.js index 8aa78be..82da771 100644 --- a/app/layout.js +++ b/app/layout.js @@ -1,15 +1,6 @@ -'use client'; - import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; -import "./i18n/config"; -import Link from "next/link"; -import Image from "next/image"; -import { NavLink, MobileNavLink } from "./components/NavLinks"; -import { useTranslation } from 'react-i18next'; -import { Globe } from 'lucide-react'; -import { useState, useEffect } from 'react'; -import { motion } from 'framer-motion'; +import ClientLayout from "./ClientLayout"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -21,264 +12,19 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); -export default function RootLayout({ children }) { - const { t, i18n } = useTranslation(); - const [currentLanguage, setCurrentLanguage] = useState('en'); - const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const currentYear = new Date().getFullYear(); - - const changeLanguage = (lng) => { - i18n.changeLanguage(lng); - setCurrentLanguage(lng); - localStorage.setItem('language', lng); - - if (lng === 'ar') { - document.documentElement.dir = 'rtl'; - document.documentElement.lang = 'ar'; - document.documentElement.classList.add('rtl'); - document.documentElement.classList.remove('ltr'); - } else { - document.documentElement.dir = 'ltr'; - document.documentElement.lang = 'en'; - document.documentElement.classList.add('ltr'); - document.documentElement.classList.remove('rtl'); - } - }; - - useEffect(() => { - const savedLanguage = localStorage.getItem('language') || 'en'; - const lng = savedLanguage; - setCurrentLanguage(lng); - i18n.changeLanguage(lng); - - if (lng === 'ar') { - document.documentElement.dir = 'rtl'; - document.documentElement.lang = 'ar'; - document.documentElement.classList.add('rtl'); - } else { - document.documentElement.dir = 'ltr'; - document.documentElement.lang = 'en'; - document.documentElement.classList.add('ltr'); - } - }, [i18n]); - - const toggleMobileMenu = () => { - setIsMobileMenuOpen(!isMobileMenuOpen); - }; - - const closeMobileMenu = () => { - setIsMobileMenuOpen(false); - }; +export const metadata = { + title: "SweetHome", + description: "Discover premium furniture and home decor", +}; +export default function Layout({ children }) { return ( - - - SweetHome - - - - - -
+ + + + {children} -
- -
-
-
-
-
-
- {t("logoAlt")} -
- - {t("brandNamePart1")}{t("brandNamePart2")} - -
-

- {t("footerDescription")} -

-
- -
-

{t("quickLinks")}

-
    -
  • - - {t("home")} - -
  • -
  • - - {t("ourProducts")} - -
  • -
  • - - {t("admin")} - -
  • -
-
- -
-

{t("contactUs")}

-
    -
  • - - - - {t("phone")} -
  • -
  • - - - - {t("footerEmail")} -
  • -
-
- -
-

{t("stayUpdated")}

-
- - -
-
-
- -
-

© {currentYear} {t("copyright")}. {t("allRightsReserved")}

-
-
-
+ ); diff --git a/app/login/page.js b/app/login/page.js new file mode 100644 index 0000000..f46c96c --- /dev/null +++ b/app/login/page.js @@ -0,0 +1,424 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import toast, { Toaster } from 'react-hot-toast'; +import Link from 'next/link'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import { + Mail, + Lock, + Eye, + EyeOff, + ArrowLeft, + LogIn, + CheckCircle, + Loader2, + Home, + Shield +} from 'lucide-react'; + +export default function LoginPage() { + const router = useRouter(); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isSuccess, setIsSuccess] = useState(false); + const [formData, setFormData] = useState({ + email: '', + password: '', + rememberMe: false + }); + const [errors, setErrors] = useState({}); + + const ADMIN_EMAIL = 'admin@gmail.com'; + const ADMIN_PASSWORD = '123'; + + const validateEmail = (email) => { + const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return re.test(email); + }; + + const validateForm = () => { + const newErrors = {}; + + if (!formData.email) { + newErrors.email = 'البريد الإلكتروني مطلوب'; + } else if (!validateEmail(formData.email)) { + newErrors.email = 'البريد الإلكتروني غير صالح'; + } + + if (!formData.password) { + newErrors.password = 'كلمة المرور مطلوبة'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!validateForm()) { + toast.error('يرجى تصحيح الأخطاء في النموذج', { + style: { background: '#fee2e2', color: '#991b1b' } + }); + return; + } + + setIsLoading(true); + + setTimeout(() => { + if (formData.email.toLowerCase() === ADMIN_EMAIL && formData.password === ADMIN_PASSWORD) { + setIsLoading(false); + setIsSuccess(true); + + toast.success('تم تسجيل الدخول كأدمن!', { + style: { background: '#dcfce7', color: '#166534' }, + duration: 3000 + }); + + localStorage.setItem('user', JSON.stringify({ + name: 'مدير النظام', + email: ADMIN_EMAIL, + role: 'admin', + avatar: 'أ' + })); + + setTimeout(() => { + router.push('/admin'); + }, 1500); + } else { + setIsLoading(false); + toast.error('بيانات الدخول غير صحيحة. حاول مع admin@gmail.com / 123', { + style: { background: '#fee2e2', color: '#991b1b' }, + duration: 4000 + }); + } + }, 1500); + }; + + const particles = Array.from({ length: 30 }, (_, i) => ({ + id: i, + x: Math.random() * 100, + y: Math.random() * 100, + size: Math.random() * 3 + 1, + duration: Math.random() * 15 + 10, + delay: Math.random() * 5 + })); + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2 + } + } + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { type: 'spring', stiffness: 100 } + } + }; + + return ( +
+ + +
+ {particles.map((particle) => ( + + ))} +
+ + + + + + + + + + + العودة للرئيسية + + + + +
+ + + + + + + +

SweetHome

+

مرحباً بعودتك!

+
+
+ +
+ + + +
+
+ +
+ { + setFormData({...formData, email: e.target.value}); + if (errors.email) setErrors({...errors, email: null}); + }} + className={`w-full pr-12 pl-4 py-4 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="أدخل بريدك الإلكتروني" + /> + {formData.email && validateEmail(formData.email) && ( + + + + )} +
+ {errors.email && ( + + {errors.email} + + )} +
+ + + +
+
+ +
+ { + setFormData({...formData, password: e.target.value}); + if (errors.password) setErrors({...errors, password: null}); + }} + className={`w-full pr-12 pl-12 py-4 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} + + )} +
+ + + + + + + نسيت كلمة المرور؟ + + + + + + + {isLoading ? ( + <> + + جاري تسجيل الدخول... + + ) : isSuccess ? ( + <> + + تم بنجاح! + + ) : ( + <> + + تسجيل الدخول + + )} + + +
+ + + ليس لديك حساب؟{' '} + + إنشاء حساب جديد + + +
+
+ + + بتسجيل الدخول، أنت توافق على{' '} + + شروط الاستخدام + + {' '}و{' '} + + سياسة الخصوصية + + +
+
+ ); +} \ No newline at end of file diff --git a/app/owner/properties/add/page.js b/app/owner/properties/add/page.js new file mode 100644 index 0000000..b0d4e19 --- /dev/null +++ b/app/owner/properties/add/page.js @@ -0,0 +1,1177 @@ +'use client'; + +import { useState, useRef, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import Image from 'next/image'; +import dynamic from 'next/dynamic'; +import 'leaflet/dist/leaflet.css'; +import { + ArrowLeft, + MapPin, + Camera, + X, + Home, + Building, + Bed, + Bath, + Square, + DollarSign, + Calendar, + Clock, + CheckCircle, + AlertCircle, + Info, + ChevronRight, + ChevronLeft, + Loader2, + Upload, + FileText, + Shield, + HelpCircle, + Search, + Navigation, + Wifi, + Zap, + Flame, + Droplets, + Cigarette, + Dog, + Music, + Star, + Sofa, + DoorOpen, + Warehouse, + Layers, + Plus, + Minus, + Save, + Wind, + Move +} from 'lucide-react'; +import toast, { Toaster } from 'react-hot-toast'; + +const MapContainer = dynamic(() => import('react-leaflet').then(mod => mod.MapContainer), { ssr: false }); +const TileLayer = dynamic(() => import('react-leaflet').then(mod => mod.TileLayer), { ssr: false }); +const Marker = dynamic(() => import('react-leaflet').then(mod => mod.Marker), { ssr: false }); +const Popup = dynamic(() => import('react-leaflet').then(mod => mod.Popup), { ssr: false }); +const useMapEvents = dynamic(() => import('react-leaflet').then(mod => mod.useMapEvents), { ssr: false }); + +function MapClickHandler({ onMapClick }) { + const map = useMapEvents({ + click: (e) => { + const { lat, lng } = e.latlng; + onMapClick([lat, lng]); + }, + }); + return null; +} + +export default function AddPropertyPage() { + const router = useRouter(); + + const [step, setStep] = useState(1); + const totalSteps = 4; + + const [formData, setFormData] = useState({ + propertyType: 'apartment', // apartment, villa, suite, room + + furnished: false, + + bedrooms: 1, + bathrooms: 1, + livingRooms: 1, + + services: { + electricity: false, + internet: false, + heating: false, + water: false, + airConditioning: false, + parking: false, + elevator: false + }, + + terms: { + noSmoking: false, + noPets: false, + noParties: false, + noAlcohol: false, + suitableForChildren: true, + suitableForElderly: true + }, + + offerType: 'daily', + + dailyPrice: '', + monthlyPrice: '', + salePrice: '', + + city: '', + district: '', + address: '', + lat: null, + lng: null, + + description: '', + + images: [] + }); + + const [imagePreviews, setImagePreviews] = useState([]); + + const [selectedLocation, setSelectedLocation] = useState(null); + const [mapCenter, setMapCenter] = useState([33.5138, 36.2765]); + const [searchQuery, setSearchQuery] = useState(''); + const [mapLoaded, setMapLoaded] = useState(false); + + const [errors, setErrors] = useState({}); + + const [isLoading, setIsLoading] = useState(false); + + const fileInputRef = useRef(null); + + const propertyTypes = [ + { id: 'apartment', label: 'شقة', icon: Building }, + { id: 'villa', label: 'فيلا', icon: Home }, + { id: 'suite', label: 'سويت', icon: Sofa }, + { id: 'room', label: 'غرفة ضمن شقة', icon: DoorOpen } + ]; + + const serviceList = [ + { id: 'electricity', label: 'كهرباء', icon: Zap }, + { id: 'internet', label: 'انترنت', icon: Wifi }, + { id: 'heating', label: 'تدفئة', icon: Flame }, + { id: 'water', label: 'ماء', icon: Droplets }, + { id: 'airConditioning', label: 'تكييف', icon: Wind }, + { id: 'parking', label: 'موقف سيارات', icon: Warehouse }, + { id: 'elevator', label: 'مصعد', icon: Layers } + ]; + + const termsList = [ + { id: 'noSmoking', label: 'ممنوع التدخين', icon: Cigarette }, + { id: 'noPets', label: 'ممنوع الحيوانات', icon: Dog }, + { id: 'noParties', label: 'عدم إقامة حفلات', icon: Music }, + { id: 'noAlcohol', label: 'ممنوع الكحول', icon: X }, + { id: 'suitableForChildren', label: 'مناسب للأطفال', icon: Star }, + { id: 'suitableForElderly', label: 'مناسب لكبار السن', icon: Star } + ]; + + const offerTypes = [ + { id: 'daily', label: 'إيجار يومي', icon: Clock }, + { id: 'monthly', label: 'إيجار شهري', icon: Calendar }, + { id: 'both', label: 'إيجار يومي وشهري', icon: Calendar }, + { id: 'sale', label: 'للبيع', icon: DollarSign } + ]; + + useEffect(() => { + if (typeof window !== 'undefined') { + const L = require('leaflet'); + delete L.Icon.Default.prototype._getIconUrl; + L.Icon.Default.mergeOptions({ + iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png', + iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png', + shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png', + }); + } + setMapLoaded(true); + }, []); + + const handleSearch = async () => { + if (!searchQuery) return; + + toast.loading('جاري البحث...', { id: 'search' }); + + try { + const response = await fetch( + `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(searchQuery)}&limit=1&accept-language=ar` + ); + const data = await response.json(); + + if (data && data.length > 0) { + const result = data[0]; + const lat = parseFloat(result.lat); + const lng = parseFloat(result.lon); + + setMapCenter([lat, lng]); + setMapZoom(18); + + const addressResponse = await fetch( + `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar` + ); + const addressData = await addressResponse.json(); + + setSelectedLocation({ + lat: lat, + lng: lng, + address: addressData.display_name || result.display_name + }); + + toast.success('تم العثور على الموقع', { id: 'search' }); + } else { + toast.error('لم يتم العثور على العنوان', { id: 'search' }); + } + } catch (error) { + console.error('خطأ في البحث:', error); + toast.error('حدث خطأ في البحث', { id: 'search' }); + } + }; + + const handleGeolocation = () => { + if (!navigator.geolocation) { + toast.error('المتصفح لا يدعم تحديد الموقع'); + return; + } + + toast.loading('جاري تحديد موقعك...', { id: 'geolocation' }); + + navigator.geolocation.getCurrentPosition( + async (position) => { + const { latitude, longitude } = position.coords; + setMapCenter([latitude, longitude]); + setMapZoom(18); + + try { + const response = await fetch( + `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latitude}&lon=${longitude}&accept-language=ar` + ); + const data = await response.json(); + + setSelectedLocation({ + lat: latitude, + lng: longitude, + address: data.display_name || 'موقعك الحالي' + }); + + toast.success('تم تحديد موقعك', { id: 'geolocation' }); + } catch (error) { + setSelectedLocation({ + lat: latitude, + lng: longitude, + address: 'موقعك الحالي' + }); + toast.success('تم تحديد موقعك', { id: 'geolocation' }); + } + }, + (error) => { + toast.error('فشل في تحديد الموقع', { id: 'geolocation' }); + } + ); + }; + +const handleMapClick = async (coords) => { + try { + const [lat, lng] = coords; + + toast.loading('جاري تحديد الموقع...', { id: 'location' }); + + const response = await fetch( + `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=ar` + ); + const data = await response.json(); + + setSelectedLocation({ + lat: lat, + lng: lng, + address: data.display_name || 'موقع محدد' + }); + + setMapZoom(18); + toast.success('تم تحديد الموقع بنجاح!', { id: 'location' }); + + } catch (error) { + console.error('خطأ في تحديد الموقع:', error); + const [lat, lng] = coords; + setSelectedLocation({ + lat: lat, + lng: lng, + address: 'موقع محدد' + }); + setMapZoom(18); + toast.success('تم تحديد الموقع', { id: 'location' }); + } +}; + const confirmLocation = () => { + if (selectedLocation) { + setFormData({ + ...formData, + lat: selectedLocation.lat, + lng: selectedLocation.lng, + address: selectedLocation.address + }); + + toast.success('تم تأكيد الموقع بنجاح'); + } + }; + + const resetLocation = () => { + setSelectedLocation(null); + setFormData({ + ...formData, + lat: null, + lng: null, + address: '' + }); + setMapZoom(15); + toast.info('تم إلغاء تحديد الموقع'); + }; + + const handleImageUpload = (files) => { + const newImages = Array.from(files); + + if (formData.images.length + newImages.length > 10) { + toast.error('يمكنك رفع 10 صور كحد أقصى'); + return; + } + + newImages.forEach(file => { + if (!file.type.startsWith('image/')) { + toast.error('الرجاء اختيار صور صالحة فقط'); + return; + } + + if (file.size > 5 * 1024 * 1024) { + toast.error('حجم الصورة يجب أن يكون أقل من 5 ميجابايت'); + return; + } + + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreviews(prev => [...prev, reader.result]); + }; + reader.readAsDataURL(file); + + setFormData({ + ...formData, + images: [...formData.images, file] + }); + }); + }; + + const removeImage = (index) => { + const newImages = [...formData.images]; + newImages.splice(index, 1); + + const newPreviews = [...imagePreviews]; + newPreviews.splice(index, 1); + + setFormData({ + ...formData, + images: newImages + }); + setImagePreviews(newPreviews); + }; + + const toggleService = (serviceId) => { + setFormData({ + ...formData, + services: { + ...formData.services, + [serviceId]: !formData.services[serviceId] + } + }); + }; + + const toggleTerm = (termId) => { + setFormData({ + ...formData, + terms: { + ...formData.terms, + [termId]: !formData.terms[termId] + } + }); + }; + + const incrementBedrooms = () => { + setFormData({ + ...formData, + bedrooms: formData.bedrooms + 1 + }); + }; + + const decrementBedrooms = () => { + if (formData.bedrooms > 1) { + setFormData({ + ...formData, + bedrooms: formData.bedrooms - 1 + }); + } + }; + + const incrementBathrooms = () => { + setFormData({ + ...formData, + bathrooms: formData.bathrooms + 1 + }); + }; + + const decrementBathrooms = () => { + if (formData.bathrooms > 1) { + setFormData({ + ...formData, + bathrooms: formData.bathrooms - 1 + }); + } + }; + + const incrementLivingRooms = () => { + setFormData({ + ...formData, + livingRooms: formData.livingRooms + 1 + }); + }; + + const decrementLivingRooms = () => { + if (formData.livingRooms > 1) { + setFormData({ + ...formData, + livingRooms: formData.livingRooms - 1 + }); + } + }; + + const validateStep = () => { + const newErrors = {}; + + switch(step) { + case 1: + if (!formData.propertyType) { + newErrors.propertyType = 'نوع العقار مطلوب'; + } + break; + + case 2: + if (!formData.bedrooms) { + newErrors.bedrooms = 'عدد الغرف مطلوب'; + } + if (!formData.bathrooms) { + newErrors.bathrooms = 'عدد الحمامات مطلوب'; + } + if (!formData.livingRooms) { + newErrors.livingRooms = 'عدد الصالونات مطلوب'; + } + break; + + case 3: + if (formData.offerType === 'daily' && !formData.dailyPrice) { + newErrors.dailyPrice = 'السعر اليومي مطلوب'; + } + if (formData.offerType === 'monthly' && !formData.monthlyPrice) { + newErrors.monthlyPrice = 'السعر الشهري مطلوب'; + } + if (formData.offerType === 'both') { + if (!formData.dailyPrice) newErrors.dailyPrice = 'السعر اليومي مطلوب'; + if (!formData.monthlyPrice) newErrors.monthlyPrice = 'السعر الشهري مطلوب'; + } + if (formData.offerType === 'sale' && !formData.salePrice) { + newErrors.salePrice = 'سعر البيع مطلوب'; + } + break; + + case 4: + if (!formData.lat || !formData.lng) { + newErrors.location = 'الرجاء تحديد موقع العقار على الخريطة'; + } + if (formData.images.length === 0) { + newErrors.images = 'يجب رفع صورة واحدة على الأقل'; + } + break; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleNext = () => { + if (validateStep()) { + setStep(step + 1); + window.scrollTo({ top: 0, behavior: 'smooth' }); + } + }; + + const handleBack = () => { + setStep(step - 1); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + + const handleSubmit = async () => { + if (!validateStep()) return; + + setIsLoading(true); + + setTimeout(() => { + console.log('Property Data:', formData); + setIsLoading(false); + toast.success('تم إضافة العقار بنجاح!'); + + setTimeout(() => { + router.push('/owner/properties'); + }, 1500); + }, 2000); + }; + + const fadeInUp = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.5 } + }; + +function MapClickHandler({ onMapClick }) { + const map = useMapEvents({ + dblclick: (e) => { + const { lat, lng } = e.latlng; + onMapClick([lat, lng]); + }, + }); + return null; +} + return ( +
+ + +
+
+
+ + + العودة للعقارات + +
+ خطوة {step} من {totalSteps} +
+
+ +
+ {[1, 2, 3, 4].map((s) => ( + + ))} +
+ +
+ معلومات العقار + التفاصيل والخدمات + السعر + الموقع والصور +
+
+ + + {step === 1 && ( + +
+
+ +
+

معلومات العقار

+

اختر نوع العقار والحالة

+
+ +
+ +
+ {propertyTypes.map((type) => { + const Icon = type.icon; + return ( + + ); + })} +
+ {errors.propertyType && ( +

{errors.propertyType}

+ )} +
+ +
+ +
+ + +
+
+ +
+ +