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 && (
+
+ )}
+ >
+ );
+}
\ 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 ? (
+
+ ) : (
+
+
+
+
+ تم الإرسال بنجاح!
+
+ تم إرسال رابط استعادة كلمة المرور إلى {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}
-
-
-
+
);
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}
+ )}
+
+
+
+
+
+
+
+
+ )}
+
+ {step === 2 && (
+
+
+
+
+
+
تفاصيل العقار
+
أدخل التفاصيل والخدمات المتاحة
+
+
+
+
+
+
+
+
+ {formData.bedrooms}
+
+
+
+ {errors.bedrooms && (
+
{errors.bedrooms}
+ )}
+
+
+
+
+
+
+
+ {formData.bathrooms}
+
+
+
+ {errors.bathrooms && (
+
{errors.bathrooms}
+ )}
+
+
+
+
+
+
+
+ {formData.livingRooms}
+
+
+
+ {errors.livingRooms && (
+
{errors.livingRooms}
+ )}
+
+
+
+
+
الخدمات المتوفرة
+
+ {serviceList.map((service) => {
+ const Icon = service.icon;
+ return (
+
+ );
+ })}
+
+
+
+
+
شروط استخدام العقار
+
+ {termsList.map((term) => {
+ const Icon = term.icon;
+ return (
+
+ );
+ })}
+
+
+
+ )}
+
+ {step === 3 && (
+
+
+
+
+
+
نوع العرض والسعر
+
اختر نوع العرض وحدد السعر المناسب
+
+
+
+
+
+ {offerTypes.map((type) => {
+ const Icon = type.icon;
+ return (
+
+ );
+ })}
+
+
+
+
+ {(formData.offerType === 'daily' || formData.offerType === 'both') && (
+
+
+
+
+
+ setFormData({...formData, dailyPrice: e.target.value})}
+ className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
+ errors.dailyPrice ? 'border-red-500' : 'border-gray-300'
+ }`}
+ placeholder="مثال: 50000"
+ />
+
+ {errors.dailyPrice && (
+
{errors.dailyPrice}
+ )}
+
+
+ )}
+
+ {(formData.offerType === 'monthly' || formData.offerType === 'both') && (
+
+
+
+
+
+ setFormData({...formData, monthlyPrice: e.target.value})}
+ className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
+ errors.monthlyPrice ? 'border-red-500' : 'border-gray-300'
+ }`}
+ placeholder="مثال: 1000000"
+ />
+
+ {errors.monthlyPrice && (
+
{errors.monthlyPrice}
+ )}
+
+
+ )}
+
+ {formData.offerType === 'sale' && (
+
+
+
+
+
+ setFormData({...formData, salePrice: e.target.value})}
+ className={`w-full pr-12 pl-4 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 ${
+ errors.salePrice ? 'border-red-500' : 'border-gray-300'
+ }`}
+ placeholder="أدخل السعر المطلوب"
+ />
+
+ {errors.salePrice && (
+
{errors.salePrice}
+ )}
+
+
+ )}
+
+
+ )}
+
+{step === 4 && (
+
+
+
+
+
+
الموقع والصور
+
حدد موقع العقار وأضف الصور
+
+
+
+
حدد موقع العقار على الخريطة
+
+
+
+ setSearchQuery(e.target.value)}
+ onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
+ placeholder="ابحث عن عنوان..."
+ className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-amber-500 pr-12"
+ />
+
+
+
+
+
+
+ {mapLoaded && (
+
+
+
+
+
+ {selectedLocation && (
+ {
+ const marker = e.target;
+ const position = marker.getLatLng();
+
+ try {
+ const response = await fetch(
+ `https://nominatim.openstreetmap.org/reverse?format=json&lat=${position.lat}&lon=${position.lng}&accept-language=ar`
+ );
+ const data = await response.json();
+
+ setSelectedLocation({
+ lat: position.lat,
+ lng: position.lng,
+ address: data.display_name || 'موقع محدد'
+ });
+ } catch (error) {
+ setSelectedLocation({
+ lat: position.lat,
+ lng: position.lng,
+ address: 'موقع محدد'
+ });
+ }
+ }
+ }}
+ >
+
+
+
موقع العقار
+
{selectedLocation.address}
+
+
+
+ )}
+
+ )}
+
+
+
+ {selectedLocation && !formData.lat && (
+
+ )}
+
+ {formData.lat && (
+
+
+
+ تم تأكيد الموقع:
+
+
{formData.address}
+
+ )}
+
+ {errors.location && (
+
{errors.location}
+ )}
+
+
+
+
صور العقار
+
+
fileInputRef.current?.click()}
+ className={`border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-all ${
+ errors.images ? 'border-red-500 bg-red-50' : 'border-gray-300 hover:border-amber-500 hover:bg-amber-50'
+ }`}
+ >
+
handleImageUpload(e.target.files)}
+ className="hidden"
+ />
+
+
+
اضغط لرفع الصور
+
+ JPEG, PNG, JPG • حتى 5MB • 800x600 بكسل • حد أقصى 10 صور
+
+
+
+ {errors.images && (
+
{errors.images}
+ )}
+
+ {imagePreviews.length > 0 && (
+
+ {imagePreviews.map((preview, index) => (
+
+
+
+
+ ))}
+
+ )}
+
+
+)}
+
+
+ {step > 1 && (
+
+ )}
+
+ {step < totalSteps ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/owner/properties/page.js b/app/owner/properties/page.js
new file mode 100644
index 0000000..58eb998
--- /dev/null
+++ b/app/owner/properties/page.js
@@ -0,0 +1,1022 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { useRouter } from 'next/navigation';
+import Link from 'next/link';
+import {
+ PlusCircle,
+ Building,
+ Home,
+ DollarSign,
+ MapPin,
+ Bed,
+ Bath,
+ Square,
+ Edit,
+ Trash2,
+ Eye,
+ Calendar,
+ TrendingUp,
+ Users,
+ FileText,
+ Image as ImageIcon,
+ CheckCircle,
+ XCircle,
+ AlertCircle,
+ ChevronRight,
+ ChevronLeft,
+ Loader2,
+ Clock,
+ Wifi,
+ Zap,
+ Flame,
+ Droplets,
+ Cigarette,
+ Dog,
+ Music,
+ Sofa,
+ DoorOpen,
+ Warehouse,
+ Layers,
+ Wind,
+ Pencil,
+ Save,
+ X
+} from 'lucide-react';
+import toast, { Toaster } from 'react-hot-toast';
+
+const DeleteConfirmationModal = ({ isOpen, onClose, onConfirm, propertyTitle }) => {
+ if (!isOpen) return null;
+
+ return (
+
+ e.stopPropagation()}
+ >
+
+
+
تأكيد الحذف
+
+ هل أنت متأكد من حذف العقار: "{propertyTitle}"؟
+
+
هذا الإجراء لا يمكن التراجع عنه
+
+
+
+
+
+
+
+
+ );
+};
+
+const PropertyViewModal = ({ isOpen, onClose, property }) => {
+ if (!isOpen || !property) return null;
+
+ const getServiceIcon = (serviceId) => {
+ const icons = {
+ electricity: Zap,
+ internet: Wifi,
+ heating: Flame,
+ water: Droplets,
+ airConditioning: Wind,
+ parking: Warehouse,
+ elevator: Layers
+ };
+ const Icon = icons[serviceId];
+ return Icon ? : null;
+ };
+
+ return (
+
+ e.stopPropagation()}
+ >
+
+
+
{property.title}
+
تم الإضافة: {new Date(property.createdAt).toLocaleDateString('ar-SA')}
+
+
+
+
+
+ {property.images && property.images.length > 0 && (
+
+
صور العقار
+
+ {property.images.map((image, index) => (
+
+

+
+ ))}
+
+
+ )}
+
+
+
+
معلومات أساسية
+
+
+ نوع العقار:
+
+ {property.propertyType === 'apartment' ? 'شقة' :
+ property.propertyType === 'villa' ? 'فيلا' :
+ property.propertyType === 'suite' ? 'سويت' : 'غرفة ضمن شقة'}
+
+
+
+ الحالة:
+
+ {property.furnished ? 'مفروش' : 'غير مفروش'}
+
+
+
+ حالة العقار:
+
+ {property.status === 'available' ? 'متاح' : 'مؤجر'}
+
+
+ {property.description && (
+
+ الوصف:
+ {property.description}
+
+ )}
+
+
+
+
+
التفاصيل
+
+
+
+ {property.bedrooms}
+ غرف
+
+
+
+ {property.bathrooms}
+ حمامات
+
+
+
+ {property.area}
+ م²
+
+ {property.livingRooms && (
+
+
+ {property.livingRooms}
+ صالونات
+
+ )}
+
+
+
+
+
+
+
+ الموقع
+
+
+ {property.address || 'لم يتم تحديد العنوان'}
+ {property.city && `، ${property.city}`}
+ {property.district && `، ${property.district}`}
+
+
+
+ {property.services && Object.values(property.services).some(v => v) && (
+
+
الخدمات المتوفرة
+
+ {Object.entries(property.services).map(([key, value]) => {
+ if (!value) return null;
+ const Icon = getServiceIcon(key);
+ return (
+
+ {Icon}
+ {key === 'electricity' && 'كهرباء'}
+ {key === 'internet' && 'انترنت'}
+ {key === 'heating' && 'تدفئة'}
+ {key === 'water' && 'ماء'}
+ {key === 'airConditioning' && 'تكييف'}
+ {key === 'parking' && 'موقف سيارات'}
+ {key === 'elevator' && 'مصعد'}
+
+ );
+ })}
+
+
+ )}
+
+ {property.terms && Object.values(property.terms).some(v => v) && (
+
+
شروط الاستخدام
+
+ {Object.entries(property.terms).map(([key, value]) => {
+ if (!value) return null;
+ return (
+
+ {key === 'noSmoking' && ' ممنوع التدخين'}
+ {key === 'noPets' && ' ممنوع الحيوانات'}
+ {key === 'noParties' && ' ممنوع الحفلات'}
+ {key === 'noAlcohol' && ' ممنوع الكحول'}
+ {key === 'suitableForChildren' && ' مناسب للأطفال'}
+ {key === 'suitableForElderly' && ' مناسب لكبار السن'}
+
+ );
+ })}
+
+
+ )}
+
+
+
معلومات السعر
+ {property.purpose === 'rent' ? (
+
+ {property.dailyPrice && (
+
+ السعر اليومي:
+ {Number(property.dailyPrice).toLocaleString()} ل.س
+
+ )}
+ {property.monthlyPrice && (
+
+ السعر الشهري:
+ {Number(property.monthlyPrice).toLocaleString()} ل.س
+
+ )}
+
+ نوع الإيجار: {
+ property.rentType === 'daily' ? 'يومي' :
+ property.rentType === 'monthly' ? 'شهري' : 'يومي وشهري'
+ }
+
+
+ ) : (
+
+ سعر البيع:
+ {Number(property.salePrice).toLocaleString()} ل.س
+
+ )}
+
+
+
+
+ );
+};
+
+const PropertyEditModal = ({ isOpen, onClose, property, onSave }) => {
+ const [formData, setFormData] = useState({ ...property });
+ const [isSaving, setIsSaving] = useState(false);
+ const [editingField, setEditingField] = useState(null);
+ const [tempValues, setTempValues] = useState({});
+
+ const sections = [
+ {
+ title: 'معلومات أساسية',
+ fields: [
+ { id: 'title', label: 'عنوان العقار', type: 'text' },
+ { id: 'description', label: 'الوصف', type: 'textarea' },
+ {
+ id: 'propertyType',
+ label: 'نوع العقار',
+ type: 'select',
+ options: [
+ { value: 'apartment', label: 'شقة' },
+ { value: 'villa', label: 'فيلا' },
+ { value: 'suite', label: 'سويت' },
+ { value: 'room', label: 'غرفة ضمن شقة' }
+ ],
+
+ },
+ {
+ id: 'furnished',
+ label: 'حالة العقار',
+ type: 'radio',
+ options: [
+ { value: true, label: 'مفروش' },
+ { value: false, label: 'غير مفروش' }
+ ],
+
+ }
+ ]
+ },
+ {
+ title: 'التفاصيل',
+ fields: [
+ { id: 'bedrooms', label: 'عدد الغرف', type: 'number' },
+ { id: 'bathrooms', label: 'عدد الحمامات', type: 'number' },
+ { id: 'livingRooms', label: 'عدد الصالونات', type: 'number' },
+ { id: 'area', label: 'المساحة (م²)', type: 'number' }
+ ]
+ },
+ {
+ title: 'الموقع',
+ fields: [
+ { id: 'address', label: 'العنوان الكامل', type: 'text' },
+ { id: 'city', label: 'المدينة', type: 'text' },
+ { id: 'district', label: 'الحي', type: 'text' }
+ ]
+ },
+ {
+ title: 'السعر',
+ fields: formData?.purpose === 'rent' ? [
+ { id: 'dailyPrice', label: 'السعر اليومي', type: 'number' },
+ { id: 'monthlyPrice', label: 'السعر الشهري', type: 'number' },
+ {
+ id: 'rentType',
+ label: 'نوع الإيجار',
+ type: 'select',
+ options: [
+ { value: 'daily', label: 'يومي' },
+ { value: 'monthly', label: 'شهري' },
+ { value: 'both', label: 'يومي وشهري' }
+ ],
+ }
+ ] : [
+ { id: 'salePrice', label: 'سعر البيع', type: 'number' }
+ ]
+ }
+ ];
+
+ const startEditing = (fieldId) => {
+ setEditingField(fieldId);
+ setTempValues({ ...tempValues, [fieldId]: formData[fieldId] });
+ };
+
+ const cancelEditing = () => {
+ setEditingField(null);
+ setTempValues({});
+ };
+
+ const saveField = (fieldId) => {
+ setFormData({
+ ...formData,
+ [fieldId]: tempValues[fieldId]
+ });
+ setEditingField(null);
+ setTempValues({});
+ const fieldLabel = sections.flatMap(s => s.fields).find(f => f.id === fieldId)?.label;
+ toast.success(`تم تحديث ${fieldLabel}`);
+ };
+
+ const handleTempChange = (fieldId, value) => {
+ setTempValues({ ...tempValues, [fieldId]: value });
+ };
+
+ const handleSave = () => {
+ setIsSaving(true);
+
+ setTimeout(() => {
+ onSave(formData);
+ setIsSaving(false);
+ onClose();
+ toast.success('تم تحديث العقار بنجاح');
+ }, 1000);
+ };
+
+ if (!isOpen || !property) return null;
+
+ return (
+
+ e.stopPropagation()}
+ >
+
+
+
تعديل العقار
+
يمكنك تعديل أي حقل بالضغط على أيقونة القلم
+
+
+
+
+
+ {formData.images && formData.images.length > 0 && (
+
+
+
صور العقار
+
+
+
+ {formData.images.map((image, index) => (
+
+

+
+
+ ))}
+
+
+ )}
+
+ {sections.map((section, sectionIndex) => (
+
+
{section.title}
+
+ {section.fields.map((field) => (
+
+
+
+ {editingField !== field.id && (
+
+ )}
+
+
+ {editingField === field.id ? (
+
+ {field.type === 'textarea' ? (
+
+ ) : (
+
+ {field.type === 'select' ? (
+ field.options.find(opt => opt.value === formData[field.id])?.label || formData[field.id]
+ ) : field.type === 'radio' ? (
+ formData[field.id] ? 'مفروش' : 'غير مفروش'
+ ) : field.id.includes('Price') ? (
+ formData[field.id] ? `${Number(formData[field.id]).toLocaleString()} ل.س` : '—'
+ ) : (
+ formData[field.id] || '—'
+ )}
+
+ )}
+
+ ))}
+
+
+ ))}
+
+
+
الخدمات
+
+ {Object.entries(formData.services || {}).map(([key, value]) => {
+ const serviceLabels = {
+ electricity: 'كهرباء',
+ internet: 'انترنت',
+ heating: 'تدفئة',
+ water: 'ماء',
+ airConditioning: 'تكييف',
+ parking: 'موقف سيارات',
+ elevator: 'مصعد'
+ };
+ return (
+
+ );
+ })}
+
+
+
+
+
شروط الاستخدام
+
+ {Object.entries(formData.terms || {}).map(([key, value]) => {
+ const termLabels = {
+ noSmoking: ' ممنوع التدخين',
+ noPets: ' ممنوع الحيوانات',
+ noParties: ' ممنوع الحفلات',
+ noAlcohol: ' ممنوع الكحول',
+ suitableForChildren: ' مناسب للأطفال',
+ suitableForElderly: ' مناسب لكبار السن'
+ };
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default function OwnerPropertiesPage() {
+ const router = useRouter();
+ const [user, setUser] = useState(null);
+ const [properties, setProperties] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [showAddMenu, setShowAddMenu] = useState(false);
+
+ const [deleteModal, setDeleteModal] = useState({ isOpen: false, property: null });
+ const [viewModal, setViewModal] = useState({ isOpen: false, property: null });
+ const [editModal, setEditModal] = useState({ isOpen: false, property: null });
+
+ useEffect(() => {
+ const storedUser = localStorage.getItem('user');
+ if (storedUser) {
+ const userData = JSON.parse(storedUser);
+ if (userData.role !== 'owner') {
+ router.push('/');
+ } else {
+ setUser(userData);
+ loadProperties();
+ }
+ } else {
+ router.push('/auth/choose-role');
+ }
+ }, [router]);
+
+ const loadProperties = () => {
+ const storedProperties = localStorage.getItem('ownerProperties');
+ if (storedProperties) {
+ setProperties(JSON.parse(storedProperties));
+ } else {
+ const mockProperties = [
+ {
+ id: 1,
+ title: 'فيلا فاخرة في المزة',
+ propertyType: 'villa',
+ purpose: 'rent',
+ rentType: 'both',
+ dailyPrice: 500000,
+ monthlyPrice: 15000000,
+ location: 'دمشق، المزة',
+ bedrooms: 5,
+ bathrooms: 4,
+ area: 450,
+ livingRooms: 3,
+ status: 'available',
+ images: ['/villa1.jpg'],
+ createdAt: new Date().toISOString(),
+ furnished: true,
+ description: 'فيلا فاخرة مع حديقة خاصة ومسبح',
+ address: 'شارع المزة - فيلات غربية',
+ city: 'دمشق',
+ district: 'المزة',
+ services: {
+ electricity: true,
+ internet: true,
+ heating: true,
+ water: true,
+ airConditioning: true,
+ parking: true,
+ elevator: false
+ },
+ terms: {
+ noSmoking: true,
+ noPets: false,
+ noParties: true,
+ noAlcohol: false,
+ suitableForChildren: true,
+ suitableForElderly: true
+ }
+ }
+ ];
+ setProperties(mockProperties);
+ localStorage.setItem('ownerProperties', JSON.stringify(mockProperties));
+ }
+ setIsLoading(false);
+ };
+
+ const updatePropertiesInStorage = (newProperties) => {
+ setProperties(newProperties);
+ localStorage.setItem('ownerProperties', JSON.stringify(newProperties));
+ };
+
+ const handleDelete = () => {
+ if (deleteModal.property) {
+ const newProperties = properties.filter(p => p.id !== deleteModal.property.id);
+ updatePropertiesInStorage(newProperties);
+ setDeleteModal({ isOpen: false, property: null });
+ toast.success('تم حذف العقار بنجاح');
+ }
+ };
+
+ const handleSaveEdit = (updatedProperty) => {
+ const newProperties = properties.map(p =>
+ p.id === updatedProperty.id ? updatedProperty : p
+ );
+ updatePropertiesInStorage(newProperties);
+ setEditModal({ isOpen: false, property: null });
+ };
+
+ const fadeInUp = {
+ initial: { opacity: 0, y: 20 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.5 }
+ };
+
+ if (isLoading) {
+ return (
+
+
+
+
جاري تحميل عقاراتك...
+
+
+ );
+ }
+
+ return (
+
+
+
+
setDeleteModal({ isOpen: false, property: null })}
+ onConfirm={handleDelete}
+ propertyTitle={deleteModal.property?.title}
+ />
+
+ setViewModal({ isOpen: false, property: null })}
+ property={viewModal.property}
+ />
+
+ setEditModal({ isOpen: false, property: null })}
+ property={editModal.property}
+ onSave={handleSaveEdit}
+ />
+
+
+
+
+
عقاراتي
+
مرحباً {user?.name}، لديك {properties.length} عقار
+
+
+
+
setShowAddMenu(!showAddMenu)}
+ className="bg-gradient-to-r from-amber-500 to-amber-600 text-white px-6 py-3 rounded-xl font-medium flex items-center gap-2 shadow-lg hover:shadow-xl transition-all"
+ whileHover={{ scale: 1.05 }}
+ whileTap={{ scale: 0.95 }}
+ >
+
+ إضافة عقار جديد
+
+
+
+ {showAddMenu && (
+
+
+
setShowAddMenu(false)}
+ >
+
+
+
+
+
عقار للإيجار
+
أضف عقار متاح للإيجار
+
+
+
+
setShowAddMenu(false)}
+ >
+
+
+
+
+
عقار للبيع
+
أضف عقار متاح للبيع
+
+
+
+
+ )}
+
+
+
+
+ {properties.length === 0 ? (
+
+
+
+
+ لا توجد عقارات بعد
+ ابدأ بإضافة أول عقار لك الآن
+
+
+ إضافة عقار جديد
+
+
+ ) : (
+
+ {properties.map((property, index) => (
+
+
+ {property.images && property.images.length > 0 ? (
+

+ ) : (
+
+
+
+ )}
+
+
+ {property.status === 'available' ? 'متاح' : 'مؤجر'}
+
+
+
+
+
+
{property.title}
+
+
+
+ {property.address || property.location || 'موقع غير محدد'}
+
+
+
+
+
+ {property.bedrooms}
+
+
+
+ {property.bathrooms}
+
+
+
+ {property.area}م²
+
+
+
+
+
+ {property.purpose === 'rent' ? (
+
+ {property.dailyPrice && (
+
+ {Number(property.dailyPrice).toLocaleString()} ل.س
+
+ )}
+
+ {property.rentType === 'daily' ? '/يوم' :
+ property.rentType === 'monthly' ? '/شهر' :
+ property.dailyPrice && property.monthlyPrice ? '/يوم وشهر' : ''}
+
+
+ ) : (
+
+
+ {Number(property.salePrice).toLocaleString()} ل.س
+
+ للبيع
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/page.js b/app/page.js
index 50c6939..6bde01b 100644
--- a/app/page.js
+++ b/app/page.js
@@ -16,11 +16,20 @@ import {
ChevronDown,
Shield,
Award,
- Sparkles
+ Sparkles,
+ UserCircle,
+ LogOut,
+ Calendar,
+ Building,
+ PlusCircle,
+ Heart,
+ MessageCircle
} from 'lucide-react';
import './i18n/config';
import HeroSearch from './components/home/HeroSearch';
import PropertyMap from './components/home/PropertyMap';
+import Link from 'next/link';
+import Image from 'next/image';
export default function HomePage() {
const { t } = useTranslation();
@@ -29,6 +38,32 @@ export default function HomePage() {
const [showMap, setShowMap] = useState(false);
const [filteredProperties, setFilteredProperties] = useState([]);
const [isScrolling, setIsScrolling] = useState(false);
+ const [user, setUser] = useState(null);
+ const [showUserMenu, setShowUserMenu] = useState(false);
+ const menuRef = useRef(null);
+
+ useEffect(() => {
+ const storedUser = localStorage.getItem('user');
+ if (storedUser) {
+ setUser(JSON.parse(storedUser));
+ }
+ }, []);
+
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (menuRef.current && !menuRef.current.contains(event.target)) {
+ setShowUserMenu(false);
+ }
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
+ }, []);
+
+ const logout = () => {
+ localStorage.removeItem('user');
+ setUser(null);
+ setShowUserMenu(false);
+ };
const [allProperties] = useState([
{
@@ -258,6 +293,15 @@ export default function HomePage() {
});
};
+ const getUserInitial = () => {
+ if (user?.name) {
+ return user.name.charAt(0).toUpperCase();
+ }
+ return null;
+ };
+
+ const isOwner = user?.role === 'owner';
+
return (
@@ -319,10 +363,35 @@ export default function HomePage() {
{t("heroSubtitle")}
-
+
+ {!isOwner && }
+
+ {isOwner && (
+
+
+ مرحباً {user?.name}!
+
+
+ يمكنك إدارة عقاراتك من خلال لوحة التحكم الخاصة بك
+
+ {/*
+
+ إدارة عقاراتي
+ */}
+
+ )}
- {!showMap && (
+
+ {!showMap && !isOwner && (
)}
-
- {showMap && (
-
- {isScrolling && (
-
- )}
-
-
-
-
- {filteredProperties.length > 0 ? 'نتائج البحث' : 'لا توجد نتائج'}
-
-
-
- بحث جديد
-
-
+ {!isOwner && (
+
+ {showMap && (
+
+ {isScrolling && (
+
+ )}
- {filteredProperties.length > 0 ? (
-
- تم العثور على {filteredProperties.length} عقار يطابق معايير البحث
-
- ) : (
-
- لا توجد عقارات تطابق معايير البحث. جرب تغيير الفلاتر.
-
- )}
-
-
- {filteredProperties.length > 0 ? (
-
- ) : (
-
-
-
لا توجد نتائج
-
حاول تغيير معايير البحث
-
- )}
-
- {filteredProperties.length > 0 && searchFilters && (
+
-
-
المدينة:
-
- {searchFilters.city === 'all' ? 'جميع المدن' : searchFilters.city}
-
+
+
+ {filteredProperties.length > 0 ? 'نتائج البحث' : 'لا توجد نتائج'}
+
+
+
+ بحث جديد
+
-
- نوع العقار:
-
- {searchFilters.propertyType === 'all' ? 'الكل' :
- searchFilters.propertyType === 'apartment' ? 'شقة' :
- searchFilters.propertyType === 'villa' ? 'فيلا' : 'بيت'}
-
-
-
- نطاق السعر:
-
- {searchFilters.priceRange === 'all' ? 'جميع الأسعار' :
- searchFilters.priceRange === '0-500' ? 'أقل من 50$' :
- searchFilters.priceRange === '500-1000' ? '50$ - 100$' :
- searchFilters.priceRange === '1000-2000' ? '100$ - 200$' :
- searchFilters.priceRange === '2000-3000' ? '200$ - 300$' : 'أكثر من 300$'}
-
-
-
- )}
-
-
- )}
-
-
-
-
-
- لماذا نحن؟
-
-
- {t("whyChooseUsTitle")}
-
-
- {t("whyChooseUsSubtitle")}
-
-
-
-
-
-
-
+ {filteredProperties.length > 0 ? (
+
+ تم العثور على {filteredProperties.length} عقار يطابق معايير البحث
+
+ ) : (
+
+ لا توجد عقارات تطابق معايير البحث. جرب تغيير الفلاتر.
+
+ )}
+
+
+
+ {filteredProperties.length > 0 ? (
+
+ ) : (
+
+
+
لا توجد نتائج
+
حاول تغيير معايير البحث
+
+ )}
+
+
+ {filteredProperties.length > 0 && searchFilters && (
+
+
+ المدينة:
+
+ {searchFilters.city === 'all' ? 'جميع المدن' : searchFilters.city}
+
+
+
+ نوع العقار:
+
+ {searchFilters.propertyType === 'all' ? 'الكل' :
+ searchFilters.propertyType === 'apartment' ? 'شقة' :
+ searchFilters.propertyType === 'villa' ? 'فيلا' : 'بيت'}
+
+
+
+ نطاق السعر:
+
+ {searchFilters.priceRange === 'all' ? 'جميع الأسعار' :
+ searchFilters.priceRange === '0-500' ? 'أقل من 50$' :
+ searchFilters.priceRange === '500-1000' ? '50$ - 100$' :
+ searchFilters.priceRange === '1000-2000' ? '100$ - 200$' :
+ searchFilters.priceRange === '2000-3000' ? '200$ - 300$' : 'أكثر من 300$'}
+
+
+
+ )}
+
+
+ )}
+
+ )}
+
+
+
+
+
+ لماذا نحن؟
+
+
+ {t("whyChooseUsTitle")}
+
+
+ {t("whyChooseUsSubtitle")}
+
+
+
+
+
+
+
+
+
+
+ {t("feature1Title")}
+
+
+
+
+ {t("feature1Description")}
+
+
+
+
+
+
+
+
+
+ {t("feature2Title")}
+
+
+
+
+ {t("feature2Description")}
+
+
+
+
+
+
+
+
+
+ {t("feature3Title")}
+
+
+
+
+ {t("feature3Description")}
+
+
-
- {t("feature1Title")}
-
-
-
- {t("feature1Description")}
-
-
-
-
-
-
-
-
- {t("feature2Title")}
-
-
-
-
- {t("feature2Description")}
-
-
-
-
-
-
-
-
- {t("feature3Title")}
-
-
-
-
- {t("feature3Description")}
-
-
-
-
-
+
);
}
\ No newline at end of file
diff --git a/app/profile/page.js b/app/profile/page.js
new file mode 100644
index 0000000..94043f6
--- /dev/null
+++ b/app/profile/page.js
@@ -0,0 +1,641 @@
+'use client';
+
+import { useState, useEffect, 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,
+ MessageCircle,
+ Camera,
+ Save,
+ X,
+ CheckCircle,
+ AlertCircle,
+ ArrowLeft,
+ Building,
+ Home,
+ Calendar,
+ MapPin,
+ Edit,
+ Loader2,
+ Upload,
+ Check,
+ Pencil
+} from 'lucide-react';
+import toast, { Toaster } from 'react-hot-toast';
+
+export default function ProfilePage() {
+ const router = useRouter();
+ const [user, setUser] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [isSaving, setIsSaving] = useState(false);
+
+ const [editingField, setEditingField] = useState(null);
+
+ const [formData, setFormData] = useState({
+ name: '',
+ email: '',
+ phone: '',
+ whatsapp: '',
+ bio: '',
+ location: '',
+ joinedDate: ''
+ });
+
+ const [tempValues, setTempValues] = useState({});
+ const [avatar, setAvatar] = useState(null);
+ const [avatarPreview, setAvatarPreview] = useState('');
+ const [errors, setErrors] = useState({});
+
+ const fileInputRef = useRef(null);
+ const inputRefs = {
+ name: useRef(null),
+ email: useRef(null),
+ phone: useRef(null),
+ whatsapp: useRef(null),
+ location: useRef(null),
+ bio: useRef(null)
+ };
+
+ useEffect(() => {
+ const storedUser = localStorage.getItem('user');
+ if (storedUser) {
+ const userData = JSON.parse(storedUser);
+ setUser(userData);
+
+ const savedProfile = localStorage.getItem('userProfile');
+ let profileData;
+
+ if (savedProfile) {
+ profileData = JSON.parse(savedProfile);
+ } else {
+ profileData = {
+ name: userData.name || '',
+ email: userData.email || '',
+ phone: '',
+ whatsapp: '',
+ bio: '',
+ location: '',
+ joinedDate: new Date().toLocaleDateString('ar-SA', { month: 'long', year: 'numeric' })
+ };
+ }
+
+ setFormData(profileData);
+ setTempValues(profileData);
+
+ const savedAvatar = localStorage.getItem('userAvatar');
+ if (savedAvatar) {
+ setAvatarPreview(savedAvatar);
+ }
+
+ setIsLoading(false);
+ } else {
+ router.push('/login');
+ }
+ }, [router]);
+
+ useEffect(() => {
+ if (editingField && inputRefs[editingField]?.current) {
+ inputRefs[editingField].current.focus();
+ }
+ }, [editingField]);
+
+ const handleImageUpload = (file) => {
+ if (!file) return;
+
+ if (!file.type.startsWith('image/')) {
+ toast.error('الرجاء اختيار صورة صالحة');
+ return;
+ }
+
+ if (file.size > 2 * 1024 * 1024) {
+ toast.error('حجم الصورة يجب أن يكون أقل من 2 ميجابايت');
+ return;
+ }
+
+ const reader = new FileReader();
+ reader.onloadend = () => {
+ setAvatarPreview(reader.result);
+ localStorage.setItem('userAvatar', reader.result);
+ toast.success('تم تغيير الصورة بنجاح');
+ };
+ reader.readAsDataURL(file);
+ };
+
+ const validateField = (field, value) => {
+ if (field === 'email' && value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
+ return 'البريد الإلكتروني غير صالح';
+ }
+ if ((field === 'phone' || field === 'whatsapp') && value && !/^(09|05)[0-9]{8}$/.test(value)) {
+ return 'رقم الهاتف غير صالح (يجب أن يبدأ 09 أو 05)';
+ }
+ if (field === 'name' && !value?.trim()) {
+ return 'الاسم مطلوب';
+ }
+ return null;
+ };
+
+ const startEditing = (field) => {
+ setEditingField(field);
+ setTempValues(prev => ({ ...prev, [field]: formData[field] }));
+ setErrors(prev => ({ ...prev, [field]: null }));
+ };
+
+ const cancelEditing = () => {
+ setEditingField(null);
+ setTempValues({});
+ setErrors({});
+ };
+
+ const saveField = (field) => {
+ const value = tempValues[field];
+ const error = validateField(field, value);
+
+ if (error) {
+ setErrors(prev => ({ ...prev, [field]: error }));
+ return;
+ }
+
+ const updatedData = { ...formData, [field]: value };
+ setFormData(updatedData);
+
+ localStorage.setItem('userProfile', JSON.stringify(updatedData));
+
+ if (field === 'name') {
+ const updatedUser = { ...user, name: value };
+ localStorage.setItem('user', JSON.stringify(updatedUser));
+ setUser(updatedUser);
+ }
+
+ setEditingField(null);
+ setTempValues({});
+ toast.success(`تم تحديث ${getFieldLabel(field)} بنجاح`);
+ };
+
+ const handleKeyDown = (e, field) => {
+ if (e.key === 'Enter') {
+ saveField(field);
+ } else if (e.key === 'Escape') {
+ cancelEditing();
+ }
+ };
+
+ const getFieldLabel = (field) => {
+ const labels = {
+ name: 'الاسم',
+ email: 'البريد الإلكتروني',
+ phone: 'رقم الهاتف',
+ whatsapp: 'رقم الواتساب',
+ location: 'الموقع',
+ bio: 'نبذة عني'
+ };
+ return labels[field] || field;
+ };
+
+ const getFieldIcon = (field) => {
+ const icons = {
+ name: User,
+ email: Mail,
+ phone: Phone,
+ whatsapp: MessageCircle,
+ location: MapPin,
+ bio: User
+ };
+ const Icon = icons[field];
+ return Icon ? : null;
+ };
+
+ const fadeInUp = {
+ initial: { opacity: 0, y: 20 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.5 }
+ };
+
+ if (isLoading) {
+ return (
+
+
+
+
جاري تحميل الملف الشخصي...
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ العودة للرئيسية
+
+
+
+
+
+
+
+
+
+
+
+
fileInputRef.current?.click()}>
+ {avatarPreview ? (
+

+ ) : (
+ formData.name?.charAt(0).toUpperCase() || 'U'
+ )}
+
+
+
+
+
handleImageUpload(e.target.files?.[0])}
+ className="hidden"
+ />
+
+
+
+
+
+ {editingField === 'name' ? (
+
+ setTempValues({...tempValues, name: e.target.value})}
+ onKeyDown={(e) => handleKeyDown(e, 'name')}
+ className="text-3xl font-bold text-center border-b-2 border-amber-500 outline-none px-2 py-1 w-64"
+ placeholder="الاسم الكامل"
+ />
+
+
+
+ ) : (
+ <>
+
{formData.name}
+
+ >
+ )}
+
+ {errors.name && (
+
{errors.name}
+ )}
+
+
+
+ {editingField === 'location' ? (
+
+ setTempValues({...tempValues, location: e.target.value})}
+ onKeyDown={(e) => handleKeyDown(e, 'location')}
+ className="border-b border-amber-500 outline-none px-2 py-1"
+ placeholder="الموقع"
+ />
+
+
+
+ ) : (
+ <>
+
{formData.location || 'الموقع غير محدد'}
+
+ >
+ )}
+
+
+
+
+ عضو منذ {formData.joinedDate}
+
+
+
+
+
+
+
+ {editingField !== 'email' && (
+
+ )}
+
+
+ {editingField === 'email' ? (
+
+
+ setTempValues({...tempValues, email: e.target.value})}
+ onKeyDown={(e) => handleKeyDown(e, 'email')}
+ className="flex-1 px-3 py-2 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-transparent"
+ placeholder="example@domain.com"
+ />
+
+
+
+ {errors.email && (
+
{errors.email}
+ )}
+
+ ) : (
+
+
+ {formData.email}
+
+ )}
+
+
+
+
+
+ {editingField !== 'phone' && (
+
+ )}
+
+
+ {editingField === 'phone' ? (
+
+
+ setTempValues({...tempValues, phone: e.target.value})}
+ onKeyDown={(e) => handleKeyDown(e, 'phone')}
+ className="flex-1 px-3 py-2 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-transparent"
+ placeholder="09XXXXXXXX"
+ />
+
+
+
+ {errors.phone && (
+
{errors.phone}
+ )}
+
+ ) : (
+
+
+
{formData.phone || 'غير محدد'}
+
+ )}
+
+
+
+
+
+ {editingField !== 'whatsapp' && (
+
+ )}
+
+
+ {editingField === 'whatsapp' ? (
+
+
+ setTempValues({...tempValues, whatsapp: e.target.value})}
+ onKeyDown={(e) => handleKeyDown(e, 'whatsapp')}
+ className="flex-1 px-3 py-2 border rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-transparent"
+ placeholder="09XXXXXXXX"
+ />
+
+
+
+ {errors.whatsapp && (
+
{errors.whatsapp}
+ )}
+
+ ) : (
+
+
+ {formData.whatsapp || 'غير محدد'}
+
+ )}
+
+
+
+
+
+ {user?.role === 'owner' ? (
+ <>
+
+ مالك عقار
+ >
+ ) : (
+ <>
+
+ مستأجر
+ >
+ )}
+
+
+
+
+
+
+
+ {editingField !== 'bio' && (
+
+ )}
+
+
+ {editingField === 'bio' ? (
+
+ ) : (
+
+ {formData.bio || 'لا توجد نبذة تعريفية بعد'}
+
+ )}
+
+
+ {user?.role === 'owner' && (
+
+ )}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/register/owner/page.js b/app/register/owner/page.js
new file mode 100644
index 0000000..cd1ff02
--- /dev/null
+++ b/app/register/owner/page.js
@@ -0,0 +1,709 @@
+'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 {
+ User,
+ Mail,
+ Phone,
+ Lock,
+ Eye,
+ EyeOff,
+ MessageCircle,
+ Camera,
+ Upload,
+ X,
+ CheckCircle,
+ XCircle,
+ AlertCircle,
+ ArrowLeft,
+ Building,
+ Loader2,
+ Home
+} from 'lucide-react';
+import toast, { Toaster } from 'react-hot-toast';
+
+export default function OwnerRegisterPage() {
+ const router = useRouter();
+ const [step, setStep] = useState(1);
+ 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: '',
+ agreeTerms: false
+ });
+ const [idImages, setIdImages] = useState({
+ front: null,
+ back: null
+ });
+ const [idImagePreviews, setIdImagePreviews] = useState({
+ front: '',
+ back: ''
+ });
+ 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
+ }));
+
+ toast.success(`تم رفع الصورة بنجاح`, {
+ style: { background: '#dcfce7', color: '#166534' }
+ });
+ };
+
+ const validateEmail = (email) => {
+ const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return re.test(email);
+ };
+
+ const validatePhone = (phone) => {
+ const re = /^(09|05)[0-9]{8}$/;
+ return re.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()) {
+ setStep(2);
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ } else {
+ toast.error('يرجى تصحيح الأخطاء في النموذج');
+ }
+ };
+
+ const handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (!validateStep2()) {
+ toast.error('يرجى إكمال جميع الصور المطلوبة');
+ return;
+ }
+
+ if (!formData.agreeTerms) {
+ toast.error('يجب الموافقة على الشروط والأحكام');
+ return;
+ }
+
+ setIsLoading(true);
+
+ setTimeout(() => {
+ setIsLoading(false);
+ toast.success('تم إنشاء الحساب بنجاح!', {
+ style: { background: '#dcfce7', color: '#166534' },
+ duration: 3000
+ });
+
+ localStorage.setItem('user', JSON.stringify({
+ name: formData.name,
+ email: formData.email,
+ role: 'owner',
+ avatar: formData.name.charAt(0).toUpperCase()
+ }));
+
+ setTimeout(() => {
+ router.push('/');
+ }, 1500);
+ }, 2000);
+ };
+
+ const fadeInUp = {
+ initial: { opacity: 0, y: 20 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.5 }
+ };
+
+ const staggerContainer = {
+ animate: {
+ transition: {
+ staggerChildren: 0.1
+ }
+ }
+ };
+
+ return (
+
+
+
+
+ {[...Array(20)].map((_, i) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
العودة
+
+
+ خطوة {step} من 2
+
+
+
+
+ = 1 ? 'bg-amber-500' : 'bg-gray-700'
+ }`}
+ animate={{ scaleX: step >= 1 ? 1 : 0.5 }}
+ />
+ = 2 ? 'bg-amber-500' : 'bg-gray-700'
+ }`}
+ animate={{ scaleX: step >= 2 ? 1 : 0.5 }}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ {step === 1 ? 'معلومات المالك' : 'الوثائق الرسمية'}
+
+
+ {step === 1
+ ? 'أدخل معلوماتك الأساسية للتواصل'
+ : 'يرجى رفع صور الهوية الشخصية للتحقق'}
+
+
+
+
+
+
+ {step === 1 ? (
+ <>
+
+
+
+
+
+
+
{
+ 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="أدخل اسمك الكامل"
+ />
+
+ {errors.name && (
+ {errors.name}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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="أدخل بريدك الإلكتروني"
+ />
+
+ {errors.email && (
+ {errors.email}
+ )}
+
+
+
+
+
+
+
{
+ 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="أدخل رقم هاتفك (اختياري)"
+ />
+
+ {errors.phone && (
+ {errors.phone}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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="أدخل رقم الواتساب"
+ />
+
+ {errors.whatsapp && (
+ {errors.whatsapp}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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="أدخل كلمة المرور"
+ />
+
+
+ {errors.password && (
+ {errors.password}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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="أعد إدخال كلمة المرور"
+ />
+
+ {formData.confirmPassword && (
+
+ {formData.password === formData.confirmPassword ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ {errors.confirmPassword && (
+ {errors.confirmPassword}
+ )}
+
+ >
+ ) : (
+ <>
+
+
+ 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'
+ }`}
+ >
+
handleImageUpload('front', e.target.files?.[0])}
+ className="hidden"
+ />
+
+ {idImagePreviews.front ? (
+
+
+
+
+ ) : (
+ <>
+
+
اضغط لرفع الصورة
+
+ JPEG, PNG, JPG • حتى 5MB • 800x600 بكسل
+
+ >
+ )}
+
+ {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-amber-500 hover:bg-white/5'
+ }`}
+ >
+
handleImageUpload('back', e.target.files?.[0])}
+ className="hidden"
+ />
+
+ {idImagePreviews.back ? (
+
+
+
+
+ ) : (
+ <>
+
+
اضغط لرفع الصورة
+
+ JPEG, PNG, JPG • حتى 5MB • 800x600 بكسل
+
+ >
+ )}
+
+ {errors.back && (
+ {errors.back}
+ )}
+
+
+
+ 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
+ />
+
+
+ >
+ )}
+
+
+ {step === 1 ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/register/tenant/page.js b/app/register/tenant/page.js
new file mode 100644
index 0000000..3b1ab8f
--- /dev/null
+++ b/app/register/tenant/page.js
@@ -0,0 +1,438 @@
+'use client';
+
+import { useState } from 'react';
+import { motion } from 'framer-motion';
+import { useRouter } from 'next/navigation';
+import Link from 'next/link';
+import {
+ User,
+ Mail,
+ Phone,
+ Lock,
+ Eye,
+ EyeOff,
+ CheckCircle,
+ XCircle,
+ ArrowLeft,
+ Home,
+ Loader2
+} from 'lucide-react';
+import toast, { Toaster } from 'react-hot-toast';
+
+export default function TenantRegisterPage() {
+ const router = useRouter();
+ const [showPassword, setShowPassword] = useState(false);
+ const [showConfirmPassword, setShowConfirmPassword] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [formData, setFormData] = useState({
+ name: '',
+ email: '',
+ phone: '',
+ password: '',
+ confirmPassword: '',
+ agreeTerms: false
+ });
+ const [errors, setErrors] = useState({});
+
+ const validateEmail = (email) => {
+ const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return re.test(email);
+ };
+
+ const validatePhone = (phone) => {
+ const re = /^(09|05)[0-9]{8}$/;
+ return re.test(phone);
+ };
+
+ const validateForm = () => {
+ 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.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 handleSubmit = async (e) => {
+ e.preventDefault();
+
+ if (!validateForm()) {
+ toast.error('يرجى تصحيح الأخطاء في النموذج');
+ return;
+ }
+
+ if (!formData.agreeTerms) {
+ toast.error('يجب الموافقة على الشروط والأحكام');
+ return;
+ }
+
+ setIsLoading(true);
+
+ setTimeout(() => {
+ setIsLoading(false);
+ toast.success('تم إنشاء الحساب بنجاح!', {
+ style: { background: '#dcfce7', color: '#166534' },
+ duration: 3000
+ });
+
+ localStorage.setItem('user', JSON.stringify({
+ name: formData.name,
+ email: formData.email,
+ role: 'tenant',
+ avatar: formData.name.charAt(0).toUpperCase()
+ }));
+
+ setTimeout(() => {
+ router.push('/');
+ }, 1500);
+ }, 2000);
+ };
+
+ const fadeInUp = {
+ initial: { opacity: 0, y: 20 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.5 }
+ };
+
+ const staggerContainer = {
+ animate: {
+ transition: {
+ staggerChildren: 0.1
+ }
+ }
+ };
+
+ return (
+
+
+
+
+ {[...Array(20)].map((_, i) => (
+
+ ))}
+
+
+
+
+
+
+
+
+ العودة
+
+
+
+
+
+
+
+
+
+
+
+ إنشاء حساب مستأجر
+ انضم إلينا وابحث عن منزل أحلامك
+
+
+
+
+
+
+
+
+
+
+
+
{
+ 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-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${
+ errors.name ? 'border-red-500' : 'border-gray-700'
+ }`}
+ placeholder="أدخل اسمك الكامل"
+ />
+
+ {errors.name && (
+ {errors.name}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${
+ errors.email ? 'border-red-500' : 'border-gray-700'
+ }`}
+ placeholder="أدخل بريدك الإلكتروني"
+ />
+
+ {errors.email && (
+ {errors.email}
+ )}
+
+
+
+
+
+
+
{
+ 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-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${
+ errors.phone ? 'border-red-500' : 'border-gray-700'
+ }`}
+ placeholder="أدخل رقم هاتفك"
+ />
+
+ {errors.phone && (
+ {errors.phone}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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-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}
+ )}
+
+
+
+
+
+
+
+
+
{
+ 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-blue-500 focus:border-transparent text-white placeholder-gray-500 transition-all ${
+ errors.confirmPassword ? 'border-red-500' : 'border-gray-700'
+ }`}
+ placeholder="أعد إدخال كلمة المرور"
+ />
+
+ {formData.confirmPassword && (
+
+ {formData.password === formData.confirmPassword ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+ {errors.confirmPassword && (
+ {errors.confirmPassword}
+ )}
+
+
+
+ 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
+ />
+
+
+
+
+ {isLoading ? (
+
+
+ جاري إنشاء الحساب...
+
+ ) : (
+ 'إنشاء حساب'
+ )}
+
+
+
+ لديك حساب بالفعل؟{' '}
+
+ تسجيل الدخول
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 46c6cde..af8ae1b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "sweethome",
"version": "0.1.0",
"dependencies": {
+ "@pbe/react-yandex-maps": "^1.2.5",
"flowbite": "^4.0.1",
"flowbite-react": "^0.12.16",
"framer-motion": "^12.29.2",
@@ -18,7 +19,9 @@
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3",
+ "react-hot-toast": "^2.6.0",
"react-i18next": "^16.5.4",
+ "react-intersection-observer": "^10.0.3",
"react-leaflet": "^5.0.0"
},
"devDependencies": {
@@ -46,7 +49,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -56,7 +59,7 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -75,7 +78,7 @@
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz",
"integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -843,6 +846,21 @@
"node": ">= 8"
}
},
+ "node_modules/@pbe/react-yandex-maps": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@pbe/react-yandex-maps/-/react-yandex-maps-1.2.5.tgz",
+ "integrity": "sha512-cBojin5e1fPx9XVCAqHQJsCnHGMeBNsP0TrNfpWCrPFfxb30ye+JgcGr2mn767Gbr1d+RufBLRiUcX2kaiAwjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/yandex-maps": "2.1.29"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "react": "^16.x || ^17.x || ^18.x"
+ }
+ },
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@@ -1187,6 +1205,12 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
"license": "MIT"
},
+ "node_modules/@types/yandex-maps": {
+ "version": "2.1.29",
+ "resolved": "https://registry.npmjs.org/@types/yandex-maps/-/yandex-maps-2.1.29.tgz",
+ "integrity": "sha512-nuibRWj3RU/9KXlCzTrRtDE+n6V9l7NbT9JakicqZ5OXIdwyb6blvV2Uwn6lB58WYm3DSUDP2I2AWlnWMc8z2w==",
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/project-service": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz",
@@ -1341,7 +1365,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz",
"integrity": "sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==",
- "devOptional": true,
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.26.0"
@@ -1484,6 +1508,12 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
"node_modules/daisyui": {
"version": "5.5.14",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.14.tgz",
@@ -1763,6 +1793,15 @@
"node": ">= 6"
}
},
+ "node_modules/goober": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz",
+ "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "csstype": "^3.0.10"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2463,6 +2502,23 @@
"react": "^19.2.3"
}
},
+ "node_modules/react-hot-toast": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz",
+ "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.3",
+ "goober": "^2.1.16"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-i18next": {
"version": "16.5.4",
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz",
@@ -2490,6 +2546,21 @@
}
}
},
+ "node_modules/react-intersection-observer": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-10.0.3.tgz",
+ "integrity": "sha512-luICLMbs0zxTO/70Zy7K5jOXkABPEVSAF8T3FdZUlctsrIaPLmx8TZe2SSA+CY2HGWfz2INyNTnp82pxNNsShA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/react-leaflet": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
@@ -2785,20 +2856,6 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
- "node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "license": "Apache-2.0",
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
diff --git a/package.json b/package.json
index 564fd5f..205f974 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"start": "next start"
},
"dependencies": {
+ "@pbe/react-yandex-maps": "^1.2.5",
"flowbite": "^4.0.1",
"flowbite-react": "^0.12.16",
"framer-motion": "^12.29.2",
@@ -18,7 +19,9 @@
"next": "16.1.6",
"react": "19.2.3",
"react-dom": "19.2.3",
+ "react-hot-toast": "^2.6.0",
"react-i18next": "^16.5.4",
+ "react-intersection-observer": "^10.0.3",
"react-leaflet": "^5.0.0"
},
"devDependencies": {