shoc-frontend-new/src/components/notifications/notification-bell.tsx
Codex Review Integration dcf944d7dc SH-292: Notification Center bell panel and feed page
Header bell panel and /notifications page share one feed from GET /api/notifications: labeled sections with live counts, unread styling, session-only dismiss and clear all (acknowledge rows excluded), and deep links to the unassigned queue and a work order's tab via ?wo=&tab=.
2026-09-18 12:55:54 -03:00

98 lines
3.1 KiB
TypeScript

import { useState } from "react";
import { Box, Drawer } from "@mui/material";
import { Bell, X } from "lucide-react";
import { Text } from "@/components/ui/text";
import { NotificationFeedActions } from "./notification-feed-actions";
import { NotificationFeedList } from "./notification-feed-list";
import { useNotificationCenter } from "./use-notification-center";
export function NotificationBell() {
const [open, setOpen] = useState(false);
const center = useNotificationCenter(() => setOpen(false));
const unreadCount = center.unreadIds.size;
return (
<>
<Box
component="button"
type="button"
onClick={() => setOpen(true)}
title="Notifications"
aria-label={unreadCount > 0 ? `Notifications, ${unreadCount} unread` : "Notifications"}
sx={{
position: "relative",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
width: 38,
height: 38,
borderRadius: "6px",
border: "none",
cursor: "pointer",
overflow: "visible",
backgroundColor: "var(--color-header-surface)",
color: "var(--color-header-icon)",
"&:hover": { backgroundColor: "var(--color-header-surface-hover)" },
}}
>
<Bell size={18} aria-hidden />
{unreadCount > 0 && (
<span
aria-hidden
className="flex items-center justify-center"
style={{
position: "absolute",
top: 2,
right: 2,
minWidth: 16,
height: 16,
padding: "0 4px",
borderRadius: 9999,
background: "var(--destructive)",
color: "var(--destructive-foreground)",
fontSize: 10,
fontWeight: 600,
lineHeight: 1,
}}
>
{unreadCount}
</span>
)}
</Box>
<Drawer
anchor="right"
open={open}
onClose={() => setOpen(false)}
slotProps={{ paper: { sx: { width: 420, background: "var(--color-bg-surface)" } } }}
>
<div
className="flex shrink-0 items-center gap-2"
style={{ padding: "16px 20px", borderBottom: "1px solid var(--color-border)" }}
>
<Text
as="h2"
variant="title"
sx={{ flex: 1, fontSize: 18, fontWeight: 700, color: "var(--color-text-primary)" }}
>
Notifications
</Text>
<NotificationFeedActions center={center} />
<button
type="button"
onClick={() => setOpen(false)}
title="Close"
aria-label="Close notifications"
className="shrink-0 cursor-pointer transition-opacity hover:opacity-70"
style={{ color: "var(--color-text-muted)" }}
>
<X size={16} aria-hidden />
</button>
</div>
<div className="flex-1 overflow-y-auto" style={{ padding: "8px 20px 20px" }}>
<NotificationFeedList center={center} />
</div>
</Drawer>
</>
);
}