feat: add real-time notification system with Socket.IO client

Implements the frontend notification client for TEC-2217:

1. notifications-api.ts — API client for list, unread-count,
   markAsRead, markAllAsRead endpoints
2. notifications-store.ts — Zustand store for notification state
   (recent list, unread count, dropdown open state)
3. use-socket-notifications.ts — Socket.IO hook that connects with
   httpOnly cookie auth, listens for notification:new events,
   auto-reconnects, and syncs unread count on (re)connect
4. notification-bell.tsx — Bell icon with unread badge + dropdown
   showing 10 most recent notifications with time-ago formatting,
   mark-as-read on click, mark-all-as-read, and "Xem tất cả" link
5. notifications-provider.tsx — Provider wired into locale layout
   (inside AuthProvider) to initialize Socket.IO connection
6. Dashboard header — NotificationBell placed before LanguageSwitcher

Added socket.io-client dependency.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-16 02:24:21 +07:00
parent 3a5d2ca9c1
commit 4400d0c123
9 changed files with 851 additions and 13 deletions

View File

@@ -22,6 +22,7 @@ import {
import { usePathname } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useState } from 'react';
import { NotificationBell } from '@/components/notifications/notification-bell';
import { useTheme } from '@/components/providers/theme-provider';
import { Button } from '@/components/ui/button';
import { LanguageSwitcher } from '@/components/ui/language-switcher';
@@ -247,6 +248,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
{user.fullName}
</span>
)}
<NotificationBell />
<LanguageSwitcher />
<Button
variant="ghost"

View File

@@ -4,6 +4,7 @@ import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, getTranslations } from 'next-intl/server';
import { AuthProvider } from '@/components/providers/auth-provider';
import { NotificationsProvider } from '@/components/providers/notifications-provider';
import { QueryProvider } from '@/components/providers/query-provider';
import { ThemeProvider } from '@/components/providers/theme-provider';
import { WebVitals } from '@/components/providers/web-vitals';
@@ -122,8 +123,10 @@ export default async function LocaleLayout({
<ThemeProvider>
<QueryProvider>
<AuthProvider>
<WebVitals />
{children}
<NotificationsProvider>
<WebVitals />
{children}
</NotificationsProvider>
</AuthProvider>
</QueryProvider>
</ThemeProvider>