Files
NextChair/src/features/notifications/components/notification-bell.tsx
T
2026-09-05 09:32:05 +01:00

71 lines
2.7 KiB
TypeScript

"use client";
import Link from "next/link";
import { Bell } from "lucide-react";
import { usePathname } from "next/navigation";
/** Pages that render their own inline bell (see InlineNotificationBell) as part of a proper header — the shell's own floating bell steps aside there instead of sitting on top of it. */
const PAGES_WITH_INLINE_BELL = [
"/staff",
"/owner/calendar",
"/staff/current",
"/staff/clients",
"/staff/settings",
"/appointments",
"/loyalty",
"/settings",
"/owner",
];
function badge(unreadCount: number) {
if (unreadCount === 0) return null;
return (
<span className="bg-danger absolute -top-1 -right-1 flex h-5 min-w-5 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white">
{unreadCount > 9 ? "9+" : unreadCount}
</span>
);
}
/**
* Fixed top-right on every screen inside the shell that renders it (see
* StaffShell and (customer)/layout.tsx) — unread count is fetched
* server-side by the layout and passed in, not polled client-side, so it
* only ever reflects "as of this page load/navigation". Real-time badge
* updates without a reload would need a persistent connection this phase
* doesn't build (see docs/phase-7-summary.md, "Known limitations").
*
* Steps aside (renders nothing) on pages that build their own proper
* header — a fixed top-right bell has nowhere sensible to sit once a page
* already has its own top-right control (e.g. Calendar's "New" button) —
* see InlineNotificationBell below, used in exactly those pages' headers.
*/
export function NotificationBell({ href, unreadCount }: { href: string; unreadCount: number }) {
const pathname = usePathname();
if (PAGES_WITH_INLINE_BELL.includes(pathname)) return null;
return (
<Link
href={href}
aria-label={unreadCount > 0 ? `Notifications, ${unreadCount} unread` : "Notifications"}
className="fixed top-4 right-4 z-40 flex h-10 w-10 items-center justify-center rounded-full border border-line bg-white shadow-sm"
>
<Bell className="h-4 w-4" aria-hidden="true" />
{badge(unreadCount)}
</Link>
);
}
/** Same look as the fixed bell above, but a normal in-flow element — for a page's own TopNav `leftAction`, not the shell's floating corner. */
export function InlineNotificationBell({ href, unreadCount }: { href: string; unreadCount: number }) {
return (
<Link
href={href}
aria-label={unreadCount > 0 ? `Notifications, ${unreadCount} unread` : "Notifications"}
className="relative flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-line"
>
<Bell className="h-4 w-4" aria-hidden="true" />
{badge(unreadCount)}
</Link>
);
}