feat(topbar): aggregated notification bell #9
+18
-1
@@ -18,7 +18,7 @@ import Documents from "./scenes/Documents";
|
||||
import CommandBar from "./components/CommandBar";
|
||||
import Toaster from "./components/Toaster";
|
||||
import Console from "./components/Console";
|
||||
import { Cmd, Home, Layers, HistoryIcon, Pulse, Refresh, Branch, Cog, User, Sun, Moon, Bot } from "./components/icons";
|
||||
import { Cmd, Home, Layers, HistoryIcon, Pulse, Refresh, Branch, Cog, User, Sun, Moon, Bot, Bell } from "./components/icons";
|
||||
import { useBackendHealth } from "./lib/useBackendHealth";
|
||||
|
||||
|
||||
@@ -216,6 +216,23 @@ export default function App() {
|
||||
<Refresh size={12} /> Refresh
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className={`link-btn notif-btn${(chatUnreadCount + queueCount) > 0 ? " has-notif" : ""}`}
|
||||
onClick={() => setScene(chatUnreadCount > 0 ? "chat" : "approvals")}
|
||||
title={
|
||||
chatUnreadCount + queueCount === 0
|
||||
? "Nothing waiting for you"
|
||||
: `${chatUnreadCount > 0 ? `${chatUnreadCount} unread message${chatUnreadCount === 1 ? "" : "s"}` : ""}${chatUnreadCount > 0 && queueCount > 0 ? " · " : ""}${queueCount > 0 ? `${queueCount} item${queueCount === 1 ? "" : "s"} in your queue` : ""}. Click to open ${chatUnreadCount > 0 ? "chat" : "approvals"}.`
|
||||
}
|
||||
aria-label={`Notifications: ${chatUnreadCount} unread, ${queueCount} queued`}
|
||||
>
|
||||
<Bell size={13} />
|
||||
{(chatUnreadCount + queueCount) > 0 && (
|
||||
<span className="notif-dot" aria-hidden>
|
||||
{(chatUnreadCount + queueCount) > 99 ? "99+" : chatUnreadCount + queueCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
className={`link-btn${consoleOpen ? " is-on" : ""}`}
|
||||
onClick={() => setConsoleOpen(!consoleOpen)}
|
||||
|
||||
@@ -41,3 +41,4 @@ export const Layers = (p: P) => <Svg {...p}><path d="M12 3l9 5-9 5-9-5zM3 13l9 5
|
||||
export const HistoryIcon = (p: P) => <Svg {...p}><path d="M3 12a9 9 0 1 0 3-6.7L3 8M3 3v5h5M12 7v5l3 2" /></Svg>;
|
||||
export const Home = (p: P) => <Svg {...p}><path d="M4 11l8-7 8 7v9h-5v-6h-6v6H4z" /></Svg>;
|
||||
export const ChevronRight = (p: P) => <Svg {...p}><path d="M9 18l6-6-6-6" /></Svg>;
|
||||
export const Bell = (p: P) => <Svg {...p}><path d="M6 8a6 6 0 0 1 12 0c0 7 3 7 3 9H3c0-2 3-2 3-9M10 21a2 2 0 0 0 4 0" /></Svg>;
|
||||
|
||||
@@ -2328,3 +2328,24 @@ select.studio-input { background: var(--bp-paper); }
|
||||
font-family: inherit;
|
||||
}
|
||||
.wizard-form-input:disabled { opacity: 0.7; }
|
||||
|
||||
/* Aggregated topbar notification bell */
|
||||
.notif-btn { position: relative; display: inline-flex; align-items: center; gap: 4px; }
|
||||
.notif-btn.has-notif { color: var(--bp-amber, #d97a00); }
|
||||
.notif-dot {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: -4px;
|
||||
min-width: 14px;
|
||||
height: 14px;
|
||||
padding: 0 4px;
|
||||
border-radius: 7px;
|
||||
background: var(--bp-amber, #d97a00);
|
||||
color: #fff;
|
||||
font-family: var(--bp-mono, monospace);
|
||||
font-size: 9px;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user