'use client'; import { useState, useEffect, useRef } from 'react'; import { Bell, Clock, CheckCircle2, PlusCircle, MessageSquare, Flame } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { ScrollArea } from '@/components/ui/scroll-area'; interface ActivityItem { id: string; actor: string; action: string; entityType: string; entityId: string; changes: Record | null; workspaceId: string; createdAt: string; } const actionIcons: Record = { created: , completed: , logged: , commented: , }; function formatTimeAgo(dateStr: string): string { const now = Date.now(); const then = new Date(dateStr).getTime(); const diffMs = now - then; const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return 'just now'; if (diffMin < 60) return `${diffMin}m ago`; const diffH = Math.floor(diffMin / 60); if (diffH < 24) return `${diffH}h ago`; const diffD = Math.floor(diffH / 24); if (diffD < 7) return `${diffD}d ago`; return new Date(dateStr).toLocaleDateString(); } function actionLabel(item: ActivityItem): string { const entity = item.entityType.charAt(0).toUpperCase() + item.entityType.slice(1); switch (item.action) { case 'created': return `${entity} created`; case 'completed': return `${entity} completed`; case 'logged': return `${entity} logged`; case 'commented': return `Comment on ${entity}`; default: return `${item.action} ${item.entityType}`; } } export function NotificationBell() { const [domainId, setDomainId] = useState(null); const [activities, setActivities] = useState([]); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const fetchedRef = useRef(false); // Auto-detect first domain useEffect(() => { fetch('/api/domains?sort=sort_order') .then((res) => res.json()) .then((data) => { const items = data.items || []; if (items.length > 0) setDomainId(items[0].id); }) .catch(() => {}); }, []); useEffect(() => { if (!open || fetchedRef.current) return; fetchActivities(); }, [open, domainId]); async function fetchActivities() { if (!domainId) return; setLoading(true); fetchedRef.current = true; try { const res = await fetch(`/api/domains/${domainId}/activity?limit=10`); if (!res.ok) throw new Error(); const data = await res.json(); setActivities(data.items || []); } catch { // Silently fail — non-critical UI } finally { setLoading(false); } } return (
Activity {activities.length > 0 && ( {activities.length} items )}
{loading ? (
Loading...
) : activities.length === 0 ? (
No recent activity
) : (
{activities.map((item) => (
{actionIcons[item.action] || }

{actionLabel(item)}

{formatTimeAgo(item.createdAt)}

))}
)}
); }