mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 10:23:11 +00:00
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=.
98 lines
3.1 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|