import type { Decision } from "@umt/contracts"; import { ArrowRight, Bell, CheckCheck } from "lucide-react"; import { useState } from "react"; import { Link, useNavigate } from "react-router"; import { useAttention } from "~/api/attention.ts"; import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; import { consolePath } from "~/components/console/nav.ts"; import { recordPath } from "~/components/console/record-sheet.tsx"; import { SEVERITY } from "~/components/library/labels.ts"; import { Button } from "~/components/ui/button.tsx"; import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover.tsx"; import { formatRelative } from "~/lib/labels.ts"; import { cn } from "~/lib/utils.ts"; // 顶栏铃铛(2026-10-06 起只放两类):需要你批准的智能体操作(会话里的审批 / 方案选择、计划审批、原站页面等你接手) // 与系统健康问题(站点不可达、下载器离线、下载目录读不到容量、磁盘空间不足、媒体库目录离线,恢复后自动消失)。 // 整理问题在整理记录里处理,集数待确认在订阅详情 / 作品页集数映射里看,不进铃铛。 const RECENT = 6; const DOT = { info: "bg-muted-foreground/60", warning: "bg-warning", critical: "bg-destructive" } as const; /** 等你批准的事项点开去哪:智能体会话、下载、下载任务;其余去智能体页 */ export function approvalHref(d: Pick): string { if (d.subjectType === "agent_run" && d.subjectId) return consolePath("agent", d.subjectId); if (d.agentRunId) return consolePath("agent", d.agentRunId); if (d.subjectType === "acquisition_plan" && d.subjectId) return `${consolePath("downloads")}?plan=${encodeURIComponent(d.subjectId)}`; if (d.subjectType === "download_task" && d.subjectId) return recordPath("downloads", d.subjectId); return consolePath("agent"); } export const HEALTH_HREF = `${consolePath("status")}#attention`; function Group({ title, total, items, href, onGo, }: { title: string; total: number; items: Decision[]; href: (d: Decision) => string; onGo: () => void; }) { if (!total) return null; return (

{title} {total}

); } /** bare:放在顶栏的玻璃胶囊里时触发器不自带玻璃 */ export function InboxBell({ bare = false }: { bare?: boolean }) { const q = useAttention(); const [open, setOpen] = useState(false); const navigate = useNavigate(); const approvals = q.data?.approvals ?? { total: 0, items: [] }; const health = q.data?.health ?? { total: 0, items: [] }; const total = approvals.total + health.total; const label = total ? `需要你关注 ${total} 项` : "通知"; return (

通知

{q.isPending ? ( ) : q.isError ? ( ) : total === 0 ? ( ) : (
setOpen(false)} /> HEALTH_HREF} onGo={() => setOpen(false)} />
)}
); }