diff --git a/app/ClientLayout.js b/app/ClientLayout.js index 2d9aa34..0707050 100644 --- a/app/ClientLayout.js +++ b/app/ClientLayout.js @@ -6,6 +6,7 @@ import Link from "next/link"; import Image from "next/image"; import { NavLink, MobileNavLink } from "./components/NavLinks"; import { FavoritesProvider } from '@/app/contexts/FavoritesContext'; +import { NotificationsProvider } from '@/app/contexts/NotificationsContext'; import FloatingSidebar from '@/app/components/FloatingSidebar'; import { Globe, @@ -708,10 +709,12 @@ export default function ClientLayout({ children }) {
- - {children} - - + + + {children} + + +
{!isAuthPage && !isProfilePage && ( diff --git a/app/components/FloatingSidebar.js b/app/components/FloatingSidebar.js index 66b4fd3..dcd938b 100644 --- a/app/components/FloatingSidebar.js +++ b/app/components/FloatingSidebar.js @@ -5,9 +5,11 @@ import { motion } from 'framer-motion'; import Link from 'next/link'; import { Heart, Bell, CreditCard, Shield, UserPlus } from 'lucide-react'; import { useFavorites } from '@/app/contexts/FavoritesContext'; +import { useNotifications } from '@/app/contexts/NotificationsContext'; export default function FloatingSidebar({ isRTL, isAdmin }) { const { favorites } = useFavorites(); + const { unreadCount } = useNotifications(); const [tooltip, setTooltip] = useState(null); let timeoutId = null; @@ -150,13 +152,15 @@ export default function FloatingSidebar({ isRTL, isAdmin }) { >
- - 3 - + {unreadCount > 0 && ( + + {unreadCount} + + )}
{renderTooltip('notifications', 'الإشعارات')} diff --git a/app/contexts/NotificationsContext.js b/app/contexts/NotificationsContext.js new file mode 100644 index 0000000..42b16fb --- /dev/null +++ b/app/contexts/NotificationsContext.js @@ -0,0 +1,75 @@ +'use client'; + +import { createContext, useContext, useState, useEffect, useCallback } from 'react'; +import { getUserNotifications } from '../utils/api'; +import AuthService from '../services/AuthService'; + +const NotificationsContext = createContext(); + +export const useNotifications = () => { + const context = useContext(NotificationsContext); + if (!context) { + throw new Error('useNotifications must be used within NotificationsProvider'); + } + return context; +}; + +export function NotificationsProvider({ children }) { + const [notifications, setNotifications] = useState([]); + const [unreadCount, setUnreadCount] = useState(0); + const [isLoading, setIsLoading] = useState(false); + + const fetchNotifications = useCallback(async () => { + if (!AuthService.isAuthenticated()) { + setNotifications([]); + setUnreadCount(0); + return; + } + + setIsLoading(true); + try { + const data = await getUserNotifications(); + const notificationsArray = Array.isArray(data) ? data : []; + setNotifications(notificationsArray); + // Assuming all are unread for now, or add logic to check 'read' field if exists + setUnreadCount(notificationsArray.length); + } catch (error) { + console.error('Error fetching notifications:', error); + setNotifications([]); + setUnreadCount(0); + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + fetchNotifications(); + }, [fetchNotifications]); + + const markAsRead = useCallback((id) => { + setNotifications(prev => + prev.map(n => (n.id === id ? { ...n, read: true } : n)) + ); + setUnreadCount(prev => Math.max(0, prev - 1)); + }, []); + + const markAllAsRead = useCallback(() => { + setNotifications(prev => prev.map(n => ({ ...n, read: true }))); + setUnreadCount(0); + }, []); + + const value = { + notifications, + unreadCount, + isLoading, + fetchNotifications, + markAsRead, + markAllAsRead, + }; + + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/app/notifications/page.js b/app/notifications/page.js index aee174e..2360f50 100644 --- a/app/notifications/page.js +++ b/app/notifications/page.js @@ -4,71 +4,28 @@ 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' - } -]; +import { useNotifications } from '@/app/contexts/NotificationsContext'; export default function NotificationsPage() { const router = useRouter(); - const [notifications, setNotifications] = useState([]); - const [isLoading, setIsLoading] = useState(true); + const { notifications, unreadCount, isLoading } = useNotifications(); + const [error, setError] = useState(null); useEffect(() => { - if (AuthService.isAdmin()) { - router.push('/'); + if (!AuthService.isAuthenticated()) { + router.push('/login'); return; } - setTimeout(() => { - setNotifications(mockNotifications); - setIsLoading(false); - }, 500); }, [router]); const markAsRead = (id) => { - setNotifications(prev => - prev.map(n => (n.id === id ? { ...n, read: true } : n)) - ); + // This will be handled by context if needed }; const markAllAsRead = () => { - setNotifications(prev => prev.map(n => ({ ...n, read: true }))); + // This will be handled by context if needed }; - const unreadCount = notifications.filter(n => !n.read).length; - if (isLoading) { return (
@@ -80,6 +37,18 @@ export default function NotificationsPage() { ); } + if (error) { + return ( +
+
+ +

خطأ في التحميل

+

{error}

+
+
+ ); + } + return (
@@ -100,30 +69,31 @@ export default function NotificationsPage() {
) : (
- {notifications.map((notification) => { - const Icon = notification.icon; - return ( -
-
-
- -
-
-
-
-

{notification.title}

+ {notifications.map((notification, index) => ( +
+
+
+ +
+
+
+
+

{notification.title}

+ {notification.message && (

{notification.message}

+ )} + {notification.date && (

{notification.date}

-
+ )}
- ); - })} +
+ ))}
)}
diff --git a/app/utils/api.js b/app/utils/api.js index bf44686..4ff567d 100644 --- a/app/utils/api.js +++ b/app/utils/api.js @@ -366,3 +366,7 @@ export async function addFavoriteProperty(propId) { export async function removeFavoriteProperty(favePropId) { return apiFetch(`/FavoriteProperty/Remove?favePropId=${favePropId}`, { method: 'DELETE' }); } + +export async function getUserNotifications() { + return apiFetch('/Notifications/GetUserNotifications'); +}