EmberClone/apps/web/src/components/NotificationBell.tsx

89 lines
3.0 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Bell } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { api } from '@emberclone/shared';
import type { AuditLog } from '@emberclone/shared';
export default function NotificationBell() {
const [isOpen, setIsOpen] = useState(false);
const [unreadCount, setUnreadCount] = useState(0);
const { data: logs, isLoading } = useQuery({
queryKey: ['audit-log', 'notifications'],
queryFn: () => api.listAuditLog({ userId: 'me', limit: 10 }),
});
useEffect(() => {
if (logs) {
const lastSeen = localStorage.getItem('lastSeenAuditAt');
if (!lastSeen) {
setUnreadCount(logs.length);
return;
}
const lastSeenDate = new Date(lastSeen).getTime();
const unread = logs.filter((log) => {
const logDate = new Date(log.createdAt).getTime();
return logDate > lastSeenDate;
}).length;
setUnreadCount(unread);
}
}, [logs]);
const handleOpen = () => {
setIsOpen(!isOpen);
if (!isOpen) {
localStorage.setItem('lastSeenAuditAt', new Date().toISOString());
setUnreadCount(0);
}
};
return (
<div className="relative">
<button
onClick={handleOpen}
className="relative p-2 text-slate-600 hover:text-slate-900 transition-colors rounded-full hover:bg-slate-100"
>
<Bell size={20} />
{unreadCount > 0 && (
<span className="absolute top-1.5 right-1.5 flex h-4 w-4 items-center justify-center rounded-full bg-red-500 text-[10px] font-medium text-white ring-2 ring-white">
{unreadCount > 9 ? '9+' : unreadCount}
</span>
)}
</button>
{isOpen && (
<>
<div
className="fixed inset-0 z-10"
onClick={() => setIsOpen(false)}
/>
<div className="absolute right-0 mt-2 w-80 overflow-hidden rounded-lg border border-slate-200 bg-white shadow-lg z-20">
<div className="border-b border-slate-100 bg-slate-50 px-4 py-2 text-sm font-semibold text-slate-700">
Notifications
</div>
<div className="max-h-96 overflow-y-auto">
{isLoading ? (
<div className="p-4 text-center text-sm text-slate-500">Loading...</div>
) : logs && logs.length > 0 ? (
<div className="divide-y divide-slate-100">
{logs.map((log) => (
<div key={log.id} className="p-3 text-sm hover:bg-slate-50 cursor-default">
<div className="font-medium text-slate-900">{log.action}</div>
<div className="text-xs text-slate-500 mt-1">
{new Date(log.createdAt).toLocaleString()}
</div>
</div>
))}
</div>
) : (
<div className="p-4 text-center text-sm text-slate-500">No notifications</div>
)}
</div>
</div>
</>
)}
</div>
);
}