feat: Implement notification API service, integrate unread count in header, and replace mock data on notification pages.

This commit is contained in:
pradeepkumar
2026-02-25 06:45:49 +05:30
parent a89c464f8d
commit 9b89e71764
6 changed files with 252 additions and 159 deletions

View File

@@ -1,60 +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';
title: string;
description: string;
timestamp: string;
read: boolean;
actionUrl?: string;
avatar?: string;
}
const mockNotifications: Notification[] = [
{
id: '1',
type: 'connection',
title: 'New Connection Request',
description: 'John Smith wants to connect with you',
timestamp: '2 hours ago',
read: false,
actionUrl: '/user/connections',
},
{
id: '2',
type: 'message',
title: 'New Message',
description: 'You have a new message from Sarah Johnson',
timestamp: '5 hours ago',
read: false,
actionUrl: '/user/message',
},
{
id: '3',
type: 'system',
title: 'Profile Update Reminder',
description: 'Complete your profile to get better recommendations',
timestamp: '1 day ago',
read: true,
actionUrl: '/user/settings',
},
{
id: '4',
type: 'update',
title: 'New Features Available',
description: 'Check out the latest updates to our platform',
timestamp: '3 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';
@@ -69,24 +20,65 @@ 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 UserNotificationsPage() {
const [notifications, setNotifications] = useState<Notification[]>(mockNotifications);
const [notifications, setNotifications] = useState<AppNotification[]>([]);
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);
}
};
return (
@@ -108,7 +100,9 @@ export default function UserNotificationsPage() {
Notifications
</h1>
<p className="font-serif text-[14px] text-[#00293D]/60">
{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!'}
</p>
</div>
</div>
@@ -133,7 +127,7 @@ export default function UserNotificationsPage() {
: 'bg-[#00293D]/10 text-[#00293D] hover:bg-[#00293D]/20'
}`}
>
All ({notifications.length})
All ({totalCount})
</button>
<button
onClick={() => setFilter('unread')}
@@ -150,7 +144,11 @@ export default function UserNotificationsPage() {
{/* Notifications List */}
<div className="border border-[#00293d]/10 rounded-[20px] bg-white overflow-hidden">
{filteredNotifications.length === 0 ? (
{isLoading ? (
<div className="flex items-center justify-center py-12">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#e58625]"></div>
</div>
) : notifications.length === 0 ? (
<div className="p-12 text-center">
<div className="w-16 h-16 rounded-full bg-[#00293D]/5 flex items-center justify-center mx-auto mb-4">
<Image
@@ -167,7 +165,7 @@ export default function UserNotificationsPage() {
</div>
) : (
<div className="divide-y divide-[#00293d]/10">
{filteredNotifications.map((notification) => (
{notifications.map((notification) => (
<div
key={notification.id}
className={`p-4 hover:bg-[#00293D]/5 transition-colors ${
@@ -176,9 +174,11 @@ export default function UserNotificationsPage() {
>
<div className="flex items-start gap-4">
{/* Icon */}
<div className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${
!notification.read ? 'bg-[#e58625]/20' : 'bg-[#00293D]/10'
}`}>
<div
className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${
!notification.read ? 'bg-[#e58625]/20' : 'bg-[#00293D]/10'
}`}
>
<Image
src={getNotificationIcon(notification.type)}
alt={notification.type}
@@ -191,9 +191,13 @@ export default function UserNotificationsPage() {
<div className="flex-1 min-w-0">
<div className="flex items-start justify-between gap-4">
<div>
<p className={`font-fractul text-[14px] leading-[17px] ${
!notification.read ? 'font-bold text-[#00293D]' : 'font-medium text-[#00293D]/80'
}`}>
<p
className={`font-fractul text-[14px] leading-[17px] ${
!notification.read
? 'font-bold text-[#00293D]'
: 'font-medium text-[#00293D]/80'
}`}
>
{notification.title}
</p>
<p className="font-serif text-[14px] text-[#00293D]/60 mt-1">
@@ -201,7 +205,7 @@ export default function UserNotificationsPage() {
</p>
</div>
<span className="font-serif text-[12px] text-[#00293D]/50 flex-shrink-0">
{notification.timestamp}
{formatTimestamp(notification.createdAt)}
</span>
</div>