From d22248248df03559178c4ff13bca1c6199d5e6c5 Mon Sep 17 00:00:00 2001 From: Rahaf Date: Mon, 30 Mar 2026 19:26:03 +0300 Subject: [PATCH] Added sidebar --- app/ClientLayout.js | 5 + app/components/FloatingSidebar.js | 165 ++++++++++++++++++++++++++++++ app/contexts/FavoritesContext.js | 53 ++++++++++ app/favorites/page.js | 146 ++++++++++++++++++++++++++ app/notifications/page.js | 132 ++++++++++++++++++++++++ app/payments/page.js | 96 +++++++++++++++++ 6 files changed, 597 insertions(+) create mode 100644 app/components/FloatingSidebar.js create mode 100644 app/contexts/FavoritesContext.js create mode 100644 app/favorites/page.js create mode 100644 app/notifications/page.js create mode 100644 app/payments/page.js diff --git a/app/ClientLayout.js b/app/ClientLayout.js index 760e4fe..4a74c66 100644 --- a/app/ClientLayout.js +++ b/app/ClientLayout.js @@ -5,6 +5,8 @@ import { useTranslation } from "react-i18next"; import Link from "next/link"; import Image from "next/image"; import { NavLink, MobileNavLink } from "./components/NavLinks"; +import { FavoritesProvider } from '@/app/contexts/FavoritesContext'; +import FloatingSidebar from '@/app/components/FloatingSidebar'; import { Globe, LogIn, @@ -705,7 +707,10 @@ export default function ClientLayout({ children }) {
+ {children} + {!isAdmin && } +
{!isAuthPage && !isProfilePage && ( diff --git a/app/components/FloatingSidebar.js b/app/components/FloatingSidebar.js new file mode 100644 index 0000000..2f4082f --- /dev/null +++ b/app/components/FloatingSidebar.js @@ -0,0 +1,165 @@ +'use client'; + +import { useState } from 'react'; +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import { Heart, Bell, CreditCard } from 'lucide-react'; +import { useFavorites } from '@/app/contexts/FavoritesContext'; + +export default function FloatingSidebar({ isRTL }) { + const { favorites } = useFavorites(); + const [tooltip, setTooltip] = useState(null); + let timeoutId = null; + + const showTooltip = (id) => { + timeoutId = setTimeout(() => { + setTooltip(id); + }, 300); + }; + + const hideTooltip = () => { + clearTimeout(timeoutId); + setTooltip(null); + }; + + const side = isRTL ? 'left' : 'right'; + const positionStyle = { + [side]: 0, + top: '50%', + transform: 'translateY(-50%)', + }; + + const cardVariants = { + initial: { opacity: 0, x: isRTL ? -20 : 20 }, + animate: { opacity: 1, x: 0, transition: { duration: 0.4, ease: 'easeOut' } }, + }; + + const buttonVariants = { + rest: { scale: 1, backgroundColor: 'rgba(255,255,255,0)' }, + hover: { scale: 1.05, backgroundColor: 'rgba(245,158,11,0.1)', transition: { duration: 0.2 } }, + tap: { scale: 0.95 }, + }; + + return ( + +
+ showTooltip('favorites')} + onMouseLeave={hideTooltip} + > + +
+ + {favorites.length > 0 && ( + + {favorites.length} + + )} +
+ + {tooltip === 'favorites' && ( +
+ + المفضلة + + +
+ )} +
+ showTooltip('notifications')} + onMouseLeave={hideTooltip} + > + +
+ + + 3 + +
+ + {tooltip === 'notifications' && ( +
+ + الإشعارات + + +
+ )} +
+ showTooltip('payments')} + onMouseLeave={hideTooltip} + > + + + + {tooltip === 'payments' && ( +
+ + المدفوعات + + +
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/app/contexts/FavoritesContext.js b/app/contexts/FavoritesContext.js new file mode 100644 index 0000000..e31c161 --- /dev/null +++ b/app/contexts/FavoritesContext.js @@ -0,0 +1,53 @@ +'use client'; + +import { createContext, useContext, useState, useEffect } from 'react'; + +const FavoritesContext = createContext(); + +export const useFavorites = () => { + const context = useContext(FavoritesContext); + if (!context) { + throw new Error('useFavorites must be used within FavoritesProvider'); + } + return context; +}; + +export const FavoritesProvider = ({ children }) => { + const [favorites, setFavorites] = useState([]); + + useEffect(() => { + const stored = localStorage.getItem('favorites'); + if (stored) { + try { + setFavorites(JSON.parse(stored)); + } catch (e) { + console.error('Failed to parse favorites', e); + } + } + }, []); + + useEffect(() => { + localStorage.setItem('favorites', JSON.stringify(favorites)); + }, [favorites]); + + const addFavorite = (property) => { + setFavorites(prev => { + if (prev.some(p => p.id === property.id)) return prev; + return [...prev, property]; + }); + }; + + const removeFavorite = (propertyId) => { + setFavorites(prev => prev.filter(p => p.id !== propertyId)); + }; + + const isFavorite = (propertyId) => { + return favorites.some(p => p.id === propertyId); + }; + + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/app/favorites/page.js b/app/favorites/page.js new file mode 100644 index 0000000..600c9dd --- /dev/null +++ b/app/favorites/page.js @@ -0,0 +1,146 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import Image from 'next/image'; +import { Heart, MapPin, Bed, Bath, Square, X, ImageIcon } from 'lucide-react'; +import { useFavorites } from '@/app/contexts/FavoritesContext'; +import AuthService from '@/app/services/AuthService'; + +export default function FavoritesPage() { + const router = useRouter(); + const { favorites, removeFavorite } = useFavorites(); + const [isLoading, setIsLoading] = useState(true); + const [isAdmin, setIsAdmin] = useState(false); + + useEffect(() => { + if (AuthService.isAdmin()) { + router.push('/'); + return; + } + setIsAdmin(AuthService.isAdmin()); + setIsLoading(false); + }, [router]); + + const formatCurrency = (amount) => { + return amount?.toLocaleString() + ' ل.س'; + }; + + if (isLoading) { + return ( +
+
+
+

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

+
+
+ ); + } + + return ( +
+
+
+

المفضلة

+

العقارات التي قمت بحفظها

+
+ + {favorites.length === 0 ? ( +
+ +

لا توجد عقارات في المفضلة

+

يمكنك إضافة العقارات التي تعجبك بالنقر على أيقونة القلب

+ + استعرض العقارات + +
+ ) : ( +
+ {favorites.map((property) => ( + +
+ {property.images && property.images[0] ? ( + {property.title} + ) : ( +
+ +
+ )} + +
+ +
+
+
+
+ + {property.type === 'apartment' ? 'شقة' : property.type === 'villa' ? 'فيلا' : 'بيت'} + +
+

{property.title}

+
+ + + {property.location.city}، {property.location.district} + +
+
+
+
{formatCurrency(property.price)}
+
/{property.priceUnit === 'daily' ? 'يوم' : 'شهر'}
+
+
+ +
+
+
+ + {property.bedrooms} +
+
+ + {property.bathrooms} +
+
+ + {property.area}م² +
+
+
+ + + عرض التفاصيل + +
+
+ ))} +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/app/notifications/page.js b/app/notifications/page.js new file mode 100644 index 0000000..aee174e --- /dev/null +++ b/app/notifications/page.js @@ -0,0 +1,132 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { Bell, CheckCircle, XCircle, Calendar, MessageCircle } from 'lucide-react'; +import AuthService from '@/app/services/AuthService'; + +const mockNotifications = [ + { + id: 1, + type: 'booking', + title: 'تأكيد الحجز', + message: 'تم تأكيد حجزك في فيلا المزة للفترة 10-15 مارس', + date: '2024-03-01', + read: false, + icon: CheckCircle, + color: 'text-green-600', + bgColor: 'bg-green-50' + }, + { + id: 2, + type: 'payment', + title: 'دفعة مستلمة', + message: 'تم استلام دفعة الإيجار بقيمة 500,000 ل.س', + date: '2024-02-28', + read: false, + icon: MessageCircle, + color: 'text-blue-600', + bgColor: 'bg-blue-50' + }, + { + id: 3, + type: 'reminder', + title: 'تذكير بالإيجار', + message: 'ينتهي عقد الإيجار خلال 3 أيام', + date: '2024-02-25', + read: true, + icon: Calendar, + color: 'text-amber-600', + bgColor: 'bg-amber-50' + } +]; + +export default function NotificationsPage() { + const router = useRouter(); + const [notifications, setNotifications] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + if (AuthService.isAdmin()) { + router.push('/'); + return; + } + setTimeout(() => { + setNotifications(mockNotifications); + setIsLoading(false); + }, 500); + }, [router]); + + const markAsRead = (id) => { + setNotifications(prev => + prev.map(n => (n.id === id ? { ...n, read: true } : n)) + ); + }; + + const markAllAsRead = () => { + setNotifications(prev => prev.map(n => ({ ...n, read: true }))); + }; + + const unreadCount = notifications.filter(n => !n.read).length; + + if (isLoading) { + return ( +
+
+
+

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

+
+
+ ); + } + + return ( +
+
+
+
+

الإشعارات

+

+ {unreadCount > 0 ? `لديك ${unreadCount} إشعار غير مقروء` : 'جميع الإشعارات مقروءة'} +

+
+
+ + {notifications.length === 0 ? ( +
+ +

لا توجد إشعارات

+

ستظهر هنا الإشعارات المتعلقة بحجوزاتك ومدفوعاتك

+
+ ) : ( +
+ {notifications.map((notification) => { + const Icon = notification.icon; + return ( +
+
+
+ +
+
+
+
+

{notification.title}

+

{notification.message}

+

{notification.date}

+
+
+
+
+
+ ); + })} +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/app/payments/page.js b/app/payments/page.js new file mode 100644 index 0000000..7b86ee2 --- /dev/null +++ b/app/payments/page.js @@ -0,0 +1,96 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { CreditCard, Download, Eye } from 'lucide-react'; +import AuthService from '@/app/services/AuthService'; +import Link from 'next/link'; + +const mockPayments = [ + { + id: 1, + property: 'فيلا فاخرة في المزة', + amount: 2500000, + date: '2024-03-10', + status: 'completed', + invoiceId: 'INV-001' + }, + { + id: 2, + property: 'شقة حديثة في الشهباء', + amount: 750000, + date: '2024-03-05', + status: 'completed', + invoiceId: 'INV-002' + } +]; + +export default function PaymentsPage() { + const router = useRouter(); + const [payments, setPayments] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + if (AuthService.isAdmin()) { + router.push('/'); + return; + } + setTimeout(() => { + setPayments(mockPayments); + setIsLoading(false); + }, 500); + }, [router]); + + const formatCurrency = (amount) => amount?.toLocaleString() + ' ل.س'; + + if (isLoading) { + return ( +
+
+
+

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

+
+
+ ); + } + + return ( +
+
+
+

المدفوعات

+

سجل المعاملات المالية والفواتير

+
+ + {payments.length === 0 ? ( +
+ +

لا توجد معاملات

+

ستظهر هنا مدفوعاتك للحجوزات

+
+ ) : ( +
+ {payments.map((payment) => ( +
+
+
+

{payment.property}

+

رقم الفاتورة: {payment.invoiceId}

+

{payment.date}

+
+
+
{formatCurrency(payment.amount)}
+ + مكتمل + +
+
+ +
+ ))} +
+ )} +
+
+ ); +} \ No newline at end of file