File size: 2,966 Bytes
9dfccd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import * as Dialog from '@radix-ui/react-dialog'
import { useNotifications, type AppNotification } from '@/hooks/useNotifications'
import { cn } from '@/lib/utils'

const TYPE_ICON: Record<AppNotification['type'], string> = {
  query_answered:   'βœ“',
  escalation_spike: '⚠',
  breaking_change:  'πŸ”΄',
  data_sync_failed: 'βœ•',
  knowledge_gap:    'πŸ’‘',
}

interface Props {
  open:    boolean
  onClose: () => void
}

export function NotificationCenter({ open, onClose }: Props) {
  const { notifications, unreadCount, markRead, markAllRead } = useNotifications()

  return (
    <Dialog.Root open={open} onOpenChange={(o) => { if (!o) onClose() }}>
      <Dialog.Portal>
        <Dialog.Overlay className="fixed inset-0 z-40 bg-black/20" />
        <Dialog.Content className="fixed right-4 top-16 z-50 flex w-80 flex-col overflow-hidden rounded-xl border border-surface-subtle bg-white shadow-xl dark:bg-stone-900">
          <div className="flex items-center justify-between border-b border-surface-subtle px-4 py-3">
            <Dialog.Title className="text-sm font-semibold">
              Notifications {unreadCount > 0 && <span className="ml-1 text-brand">({unreadCount})</span>}
            </Dialog.Title>
            <div className="flex items-center gap-2">
              {unreadCount > 0 && (
                <button onClick={markAllRead} className="text-xs text-stone-400 hover:text-stone-600">
                  Mark all read
                </button>
              )}
              <Dialog.Close className="text-stone-400 hover:text-stone-600" aria-label="Close">βœ•</Dialog.Close>
            </div>
          </div>

          <div className="max-h-[480px] overflow-y-auto">
            {notifications.length === 0 && (
              <p className="px-4 py-8 text-center text-sm text-stone-400">No notifications yet</p>
            )}
            {notifications.map((n) => (
              <button
                key={n.id}
                onClick={() => markRead(n.id)}
                className={cn(
                  'flex w-full items-start gap-3 px-4 py-3 text-left text-sm transition-colors hover:bg-stone-50 dark:hover:bg-stone-800',
                  !n.read && 'bg-brand/5',
                )}
              >
                <span className="mt-0.5 shrink-0 text-base" aria-hidden>
                  {TYPE_ICON[n.type]}
                </span>
                <div className="min-w-0 flex-1">
                  <p className={cn('text-stone-700 dark:text-stone-300', !n.read && 'font-medium')}>
                    {n.message}
                  </p>
                  <p className="mt-0.5 text-xs text-stone-400">{n.timestamp}</p>
                </div>
                {!n.read && (
                  <span className="mt-1.5 h-2 w-2 shrink-0 rounded-full bg-brand" aria-hidden />
                )}
              </button>
            ))}
          </div>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}