From 9b89e717641f7cf787f0b5a840281691c2b2e3dd Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Wed, 25 Feb 2026 06:45:49 +0530 Subject: [PATCH] feat: Implement notification API service, integrate unread count in header, and replace mock data on notification pages. --- src/app/(agent)/agent/notifications/page.tsx | 162 +++++++++--------- src/app/(user)/user/notifications/page.tsx | 152 ++++++++-------- src/components/layout/CommonHeader.tsx | 37 +++- .../providers/notification-provider.tsx | 3 + src/services/index.ts | 4 +- src/services/notifications-api.service.ts | 53 ++++++ 6 files changed, 252 insertions(+), 159 deletions(-) create mode 100644 src/services/notifications-api.service.ts diff --git a/src/app/(agent)/agent/notifications/page.tsx b/src/app/(agent)/agent/notifications/page.tsx index 30ec3ae..9cebac0 100644 --- a/src/app/(agent)/agent/notifications/page.tsx +++ b/src/app/(agent)/agent/notifications/page.tsx @@ -1,68 +1,11 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import Image from 'next/image'; import Link from 'next/link'; +import { notificationsApiService, type AppNotification } from '@/services/notifications-api.service'; -// Mock notification data - replace with actual API call when backend is ready -interface Notification { - id: string; - type: 'connection' | 'message' | 'system' | 'update' | 'request'; - title: string; - description: string; - timestamp: string; - read: boolean; - actionUrl?: string; - avatar?: string; -} - -const mockNotifications: Notification[] = [ - { - id: '1', - type: 'request', - title: 'New Connection Request', - description: 'Jane Doe wants to connect with you', - timestamp: '1 hour ago', - read: false, - actionUrl: '/agent/requests', - }, - { - id: '2', - type: 'message', - title: 'New Message', - description: 'You have a new message from Mike Wilson', - timestamp: '3 hours ago', - read: false, - actionUrl: '/agent/message', - }, - { - id: '3', - type: 'connection', - title: 'Connection Accepted', - description: 'Your connection with Sarah Johnson has been confirmed', - timestamp: '1 day ago', - read: true, - actionUrl: '/agent/connections', - }, - { - id: '4', - type: 'system', - title: 'Profile Verification', - description: 'Your profile has been verified successfully', - timestamp: '2 days ago', - read: true, - }, - { - id: '5', - type: 'update', - title: 'New Features Available', - description: 'Check out the latest updates to your dashboard', - timestamp: '5 days ago', - read: true, - }, -]; - -function getNotificationIcon(type: Notification['type']) { +function getNotificationIcon(type: AppNotification['type']) { switch (type) { case 'connection': return '/assets/icons/user-placeholder-icon.svg'; @@ -79,26 +22,69 @@ function getNotificationIcon(type: Notification['type']) { } } +function formatTimestamp(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + const diffDays = Math.floor(diffMs / 86400000); + + if (diffMins < 1) return 'Just now'; + if (diffMins < 60) return `${diffMins} min ago`; + if (diffHours < 24) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; + if (diffDays < 7) return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; + return date.toLocaleDateString(); +} + export default function AgentNotificationsPage() { - const [notifications, setNotifications] = useState(mockNotifications); + const [notifications, setNotifications] = useState([]); + const [unreadCount, setUnreadCount] = useState(0); + const [totalCount, setTotalCount] = useState(0); const [filter, setFilter] = useState<'all' | 'unread'>('all'); + const [isLoading, setIsLoading] = useState(true); - const filteredNotifications = filter === 'all' - ? notifications - : notifications.filter(n => !n.read); + const fetchNotifications = useCallback(async () => { + try { + const data = await notificationsApiService.getNotifications(filter, 1, 50); + setNotifications(data.notifications); + setUnreadCount(data.unreadCount); + setTotalCount(data.pagination.total); + } catch (err) { + console.error('Failed to fetch notifications:', err); + } finally { + setIsLoading(false); + } + }, [filter]); - const unreadCount = notifications.filter(n => !n.read).length; + useEffect(() => { + fetchNotifications(); + }, [fetchNotifications]); - const markAsRead = (id: string) => { - setNotifications(prev => - prev.map(n => n.id === id ? { ...n, read: true } : n) - ); + const markAsRead = async (id: string) => { + try { + await notificationsApiService.markAsRead(id); + setNotifications((prev) => + prev.map((n) => (n.id === id ? { ...n, read: true } : n)) + ); + setUnreadCount((prev) => Math.max(0, prev - 1)); + } catch (err) { + console.error('Failed to mark as read:', err); + } }; - const markAllAsRead = () => { - setNotifications(prev => prev.map(n => ({ ...n, read: true }))); + const markAllAsRead = async () => { + try { + await notificationsApiService.markAllAsRead(); + setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); + setUnreadCount(0); + } catch (err) { + console.error('Failed to mark all as read:', err); + } }; + const displayCount = filter === 'all' ? totalCount : unreadCount; + return (
{/* Header */} @@ -118,7 +104,9 @@ export default function AgentNotificationsPage() { Notifications

- {unreadCount > 0 ? `You have ${unreadCount} unread notification${unreadCount > 1 ? 's' : ''}` : 'All caught up!'} + {unreadCount > 0 + ? `You have ${unreadCount} unread notification${unreadCount > 1 ? 's' : ''}` + : 'All caught up!'}

@@ -143,7 +131,7 @@ export default function AgentNotificationsPage() { : 'bg-[#00293D]/10 text-[#00293D] hover:bg-[#00293D]/20' }`} > - All ({notifications.length}) + All ({totalCount}) {/* Profile Section with Greeting - Entire area clickable */} diff --git a/src/components/providers/notification-provider.tsx b/src/components/providers/notification-provider.tsx index 74cf357..26d547b 100644 --- a/src/components/providers/notification-provider.tsx +++ b/src/components/providers/notification-provider.tsx @@ -24,6 +24,9 @@ export function NotificationProvider() { setTimeout(() => { setToasts((prev) => prev.filter((t) => t.id !== id)); }, 5000); + + // Dispatch event so the header can refresh unread count + window.dispatchEvent(new Event("notification-received")); }, [] ); diff --git a/src/services/index.ts b/src/services/index.ts index 34f9872..94e35e4 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -55,5 +55,7 @@ export { messagesService } from './messages.service'; // CMS Service export { cmsService } from './cms.service'; -// Notification Service +// Notification Services export { notificationService } from './notification.service'; +export { notificationsApiService } from './notifications-api.service'; +export type { AppNotification } from './notifications-api.service'; diff --git a/src/services/notifications-api.service.ts b/src/services/notifications-api.service.ts new file mode 100644 index 0000000..ba5a874 --- /dev/null +++ b/src/services/notifications-api.service.ts @@ -0,0 +1,53 @@ +import api from './api'; + +export interface AppNotification { + id: string; + userId: string; + type: 'connection' | 'message' | 'system' | 'update' | 'request'; + title: string; + description: string; + read: boolean; + actionUrl?: string; + data?: Record; + createdAt: string; +} + +interface NotificationsResponse { + notifications: AppNotification[]; + unreadCount: number; + pagination: { + page: number; + limit: number; + total: number; + pages: number; + }; +} + +class NotificationsApiService { + async getNotifications( + filter: 'all' | 'unread' = 'all', + page = 1, + limit = 20, + ): Promise { + const response = await api.get('/notifications', { + params: { filter, page, limit }, + }); + return response.data?.data || response.data; + } + + async getUnreadCount(): Promise { + const response = await api.get('/notifications/unread-count'); + const data = response.data?.data || response.data; + return data.unreadCount; + } + + async markAsRead(notificationId: string): Promise { + await api.patch(`/notifications/${notificationId}/read`); + } + + async markAllAsRead(): Promise { + await api.patch('/notifications/read-all'); + } +} + +export const notificationsApiService = new NotificationsApiService();