feat(operator): notification drawer behind the topbar bell
Right-anchored slide-in inbox triggered by the bell button. Backend is a follow-up — for now this is a visual + behavior shell with mock fixtures, same pattern as INCIDENT / FLAGS / OP_AUDIT. - data/fixtures.ts: new NotificationItem type + 6 seed rows from the design (DMARC, invitation, invoice, SAML, ticket reply, failed sign-in) - useNotifications composable: isOpen + items + unreadCount + markRead + markAllRead. Items deep-clone the fixture on first import so toggling unread doesn't mutate the shared seed. - NotificationDrawer component: Teleport + scrim + slide animation, header/list/footer. Each row shows tone-tinted icon tile + title + description + timestamp + left-rail unread dot. Click a row to mark read; click Mark all read or Preferences in the footer. - OpTopbar: bell now opens the drawer and only shows .icon-btn-dot when unreadCount > 0. - Layout mounts <NotificationDrawer /> alongside the other floating components. Dismissal: backdrop click, Escape, X, and route-change watcher (so Preferences → /settings closes the drawer cleanly).
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
// Shared notification-drawer state. The bell in the topbar opens the drawer
|
||||
// via `open()`; the drawer reads from `items` and toggles `unread` via
|
||||
// `markRead` / `markAllRead`. `unreadCount` powers the red dot on the bell.
|
||||
//
|
||||
// All state is in-memory and re-seeds from the NOTIFICATIONS fixture on each
|
||||
// full reload — there's no backend yet. When the real notifications source
|
||||
// lands (see follow-ups in NEXT-STEPS.md), swap `items` for a useFetch.
|
||||
|
||||
import { NOTIFICATIONS, type NotificationItem } from '~/data/fixtures'
|
||||
|
||||
const isOpen = ref(false)
|
||||
const items = ref<NotificationItem[]>(NOTIFICATIONS.map((n) => ({ ...n })))
|
||||
|
||||
const unreadCount = computed(() => items.value.filter((n) => n.unread).length)
|
||||
|
||||
export const useNotifications = () => ({
|
||||
isOpen,
|
||||
items,
|
||||
unreadCount,
|
||||
open: () => {
|
||||
isOpen.value = true
|
||||
},
|
||||
close: () => {
|
||||
isOpen.value = false
|
||||
},
|
||||
markRead: (id: string) => {
|
||||
const i = items.value.find((n) => n.id === id)
|
||||
if (i) i.unread = false
|
||||
},
|
||||
markAllRead: () => {
|
||||
for (const n of items.value) n.unread = false
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user