{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "notification-3",
  "dependencies": [
    "lucide-react",
    "react-icons"
  ],
  "registryDependencies": [
    "avatar",
    "badge",
    "button",
    "card",
    "scroll-area",
    "separator",
    "utils"
  ],
  "files": [
    {
      "path": "src/components/watermelon-ui/notification-3.tsx",
      "content": "import React, { useState } from \"react\";\nimport {\n  RiNotification3Fill,\n  RiCheckFill,\n  RiFileTextFill,\n  RiSettings4Fill,\n  RiArrowRightSFill,\n  RiCircleFill,\n  RiCloseFill,\n} from \"react-icons/ri\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  Card,\n  CardContent,\n  CardFooter,\n  CardHeader,\n} from \"@/components/ui/card\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport { Button } from \"@/components/ui/button\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { Separator } from \"@/components/ui/separator\";\n\nexport type ActivityKind =\n  | \"merge\"\n  | \"deploy\"\n  | \"comment\"\n  | \"invite\"\n  | \"review\"\n  | \"report\";\n\nexport type ActivityTab = \"all\" | \"activity\" | \"tasks\" | \"digest\";\n\nexport interface ActivityActor {\n  name: string;\n  initials: string;\n  avatar?: string;\n}\n\nexport interface ActivityAttachment {\n  label: string;\n  meta: string;\n}\n\nexport interface ActivityItem {\n  id: string;\n  kind: ActivityKind;\n  actor: ActivityActor;\n  workspace: string;\n  summary: string;\n  timestamp: string;\n  isNew: boolean;\n  isTask: boolean;\n  isDigest: boolean;\n  attachment?: ActivityAttachment;\n  actions?: { label: string; variant: \"default\" | \"outline\" }[];\n}\n\nexport interface Notification3Props {\n  title?: string;\n  items?: ActivityItem[];\n  className?: string;\n}\n\nconst KindColorMap: Record<ActivityKind, string> = {\n  merge: \"bg-primary/10 text-primary\",\n  deploy: \"bg-primary/10 text-primary\",\n  comment: \"bg-secondary text-secondary-foreground\",\n  invite: \"bg-muted text-muted-foreground\",\n  review: \"bg-muted text-muted-foreground\",\n  report: \"bg-destructive/10 text-destructive\",\n};\n\nconst KindLabelMap: Record<ActivityKind, string> = {\n  merge: \"Merged\",\n  deploy: \"Deploy\",\n  comment: \"Comment\",\n  invite: \"Invite\",\n  review: \"Review\",\n  report: \"Report\",\n};\n\nconst defaultItems: ActivityItem[] = [\n  {\n    id: \"1\",\n    kind: \"deploy\",\n    actor: { name: \"CI Pipeline\", initials: \"CI\" },\n    workspace: \"Atlas Platform\",\n    summary:\n      \"Release v4.2.0 deployed to production — 0 errors, 98% test coverage.\",\n    timestamp: \"Just now\",\n    isNew: true,\n    isTask: false,\n    isDigest: false,\n  },\n  {\n    id: \"2\",\n    kind: \"review\",\n    actor: {\n      name: \"Nadia Osei\",\n      initials: \"NO\",\n      avatar: \"https://assets.watermelon.sh/wm_emma.png\",\n    },\n    workspace: \"Design System\",\n    summary: \"Requested your review on the updated component token schema.\",\n    timestamp: \"12m ago\",\n    isNew: true,\n    isTask: true,\n    isDigest: false,\n    actions: [\n      { label: \"Decline\", variant: \"outline\" },\n      { label: \"Review\", variant: \"default\" },\n    ],\n  },\n  {\n    id: \"3\",\n    kind: \"merge\",\n    actor: {\n      name: \"Ravi Menon\",\n      initials: \"RM\",\n      avatar: \"https://assets.watermelon.sh/wm_alex.png\",\n    },\n    workspace: \"Atlas Platform\",\n    summary: \"PR #389 — Unified auth middleware merged into main.\",\n    timestamp: \"1h ago\",\n    isNew: true,\n    isTask: false,\n    isDigest: false,\n    attachment: { label: \"auth-middleware.ts\", meta: \"3 files changed\" },\n  },\n  {\n    id: \"4\",\n    kind: \"comment\",\n    actor: {\n      name: \"Sara Lin\",\n      initials: \"SL\",\n      avatar: \"https://assets.watermelon.sh/wm_mia.png\",\n    },\n    workspace: \"Content Hub\",\n    summary: '\"Looks great — the new skeleton loader feels much snappier! 🚀\"',\n    timestamp: \"3h ago\",\n    isNew: false,\n    isTask: false,\n    isDigest: true,\n  },\n  {\n    id: \"5\",\n    kind: \"invite\",\n    actor: {\n      name: \"Dev Ops\",\n      initials: \"DO\",\n    },\n    workspace: \"Infrastructure\",\n    summary: \"Kaito Nakamura has been added to the Infrastructure workspace.\",\n    timestamp: \"Yesterday\",\n    isNew: false,\n    isTask: false,\n    isDigest: true,\n  },\n  {\n    id: \"6\",\n    kind: \"report\",\n    actor: { name: \"Monitor\", initials: \"MN\" },\n    workspace: \"Atlas Platform\",\n    summary:\n      \"Weekly digest: 142 events, 4 deploys, 12 PRs merged, 0 incidents.\",\n    timestamp: \"Yesterday\",\n    isNew: false,\n    isTask: false,\n    isDigest: true,\n    attachment: { label: \"weekly-report.pdf\", meta: \"1.2 MB\" },\n  },\n];\n\nfunction TabPill({\n  label,\n  count,\n  active,\n  onClick,\n}: {\n  label: string;\n  count?: number;\n  active: boolean;\n  onClick: () => void;\n}) {\n  return (\n    <button\n      onClick={onClick}\n      className={cn(\n        \"flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold transition-colors\",\n        active\n          ? \"bg-primary text-primary-foreground shadow-[inset_0_2px_4px_0_rgba(255,255,255,0.5),inset_0_-2px_4px_0_rgba(0,0,0,0.1)] dark:shadow-[inset_0_-2px_4px_0_rgba(0,0,0,0.1),inset_0_2px_4px_0_rgba(255,255,255,0.08)]\"\n          : \"text-muted-foreground hover:bg-muted hover:text-foreground\",\n      )}\n    >\n      {label}\n      {count !== undefined && count > 0 && (\n        <span\n          className={cn(\n            \"flex h-4 min-w-[16px] items-center justify-center rounded-full text-[9px] font-bold tabular-nums\",\n            active\n              ? \"bg-primary-foreground/50 text-primary-foreground\"\n              : \"bg-muted text-muted-foreground\",\n          )}\n        >\n          {count}\n        </span>\n      )}\n    </button>\n  );\n}\n\nfunction AttachmentChip({ attachment }: { attachment: ActivityAttachment }) {\n  return (\n    <div className=\"border-border bg-muted/40 mt-2.5 inline-flex items-center gap-1.5 rounded-lg border px-2.5 py-1.5\">\n      <RiFileTextFill className=\"text-muted-foreground h-3.5 w-3.5 shrink-0\" />\n      <span className=\"text-foreground text-xs font-medium\">\n        {attachment.label}\n      </span>\n      <span className=\"text-muted-foreground text-xs\">· {attachment.meta}</span>\n    </div>\n  );\n}\n\nfunction ActivityRow({\n  item,\n  onDismiss,\n}: {\n  item: ActivityItem;\n  onDismiss: (id: string) => void;\n}) {\n  const iconColor = KindColorMap[item.kind];\n\n  return (\n    <div\n      className={cn(\n        \"group relative flex gap-3 px-4 py-3 transition-colors duration-150\",\n        \"hover:bg-muted/40\",\n      )}\n    >\n      <div className=\"relative shrink-0\">\n        <Avatar className=\"h-9 w-9\">\n          <AvatarImage src={item.actor.avatar} alt={item.actor.name} />\n          <AvatarFallback className=\"bg-muted text-muted-foreground text-xs font-bold\">\n            {item.actor.initials}\n          </AvatarFallback>\n        </Avatar>\n      </div>\n\n      <div className=\"min-w-0 flex-1\">\n        <div className=\"flex items-start justify-between gap-2\">\n          <div className=\"flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-0.5\">\n            <span className=\"text-foreground text-sm font-semibold\">\n              {item.actor.name}\n            </span>\n            <Badge\n              variant=\"outline\"\n              className={cn(\n                \"h-4 shrink-0 rounded-full border-0 px-1.5 text-[10px] font-semibold\",\n                iconColor,\n              )}\n            >\n              {KindLabelMap[item.kind]}\n            </Badge>\n          </div>\n          <div className=\"flex shrink-0 items-center gap-1.5\">\n            <span className=\"text-muted-foreground/60 text-xs tabular-nums\">\n              {item.timestamp}\n            </span>\n            <button\n              onClick={() => onDismiss(item.id)}\n              className=\"text-muted-foreground/40 hover:text-muted-foreground rounded p-0.5 opacity-0 transition-opacity group-hover:opacity-100\"\n              aria-label=\"Dismiss\"\n            >\n              <RiCloseFill className=\"h-3.5 w-3.5\" />\n            </button>\n          </div>\n        </div>\n\n        <p className=\"text-muted-foreground mt-0.5 text-xs font-medium\">\n          {item.workspace}\n        </p>\n\n        <p className=\"text-foreground/80 mt-1 text-sm leading-snug\">\n          {item.summary}\n        </p>\n\n        {item.attachment && <AttachmentChip attachment={item.attachment} />}\n\n        {item.actions && item.actions.length > 0 && (\n          <div className=\"mt-2.5 flex items-center gap-2\">\n            {item.actions.map((action) => (\n              <Button\n                key={action.label}\n                variant={action.variant}\n                size=\"sm\"\n                className=\"h-7 rounded-full px-3 text-xs font-semibold shadow-[inset_0_2px_4px_0_rgba(255,255,255,0.5),inset_0_-2px_4px_0_rgba(0,0,0,0.1)] dark:shadow-[inset_0_-2px_4px_0_rgba(0,0,0,0.1),inset_0_2px_4px_0_rgba(255,255,255,0.08)]\"\n              >\n                {action.label}\n              </Button>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport default function Notification3({\n  title = \"Activity\",\n  items = defaultItems,\n  className,\n}: Notification3Props) {\n  const [activeTab, setActiveTab] = useState<ActivityTab>(\"all\");\n  const [dismissed, setDismissed] = useState<Set<string>>(new Set());\n\n  const handleDismiss = (id: string) => {\n    setDismissed((prev) => new Set(prev).add(id));\n  };\n\n  const visible = items.filter((i) => !dismissed.has(i.id));\n  const newCount = visible.filter((i) => i.isNew).length;\n  const taskCount = visible.filter((i) => i.isTask).length;\n  const digestCount = visible.filter((i) => i.isDigest).length;\n\n  const filtered =\n    activeTab === \"all\"\n      ? visible\n      : activeTab === \"activity\"\n        ? visible.filter((i) => i.isNew)\n        : activeTab === \"tasks\"\n          ? visible.filter((i) => i.isTask)\n          : visible.filter((i) => i.isDigest);\n\n  const tabs: { id: ActivityTab; label: string; count?: number }[] = [\n    { id: \"all\", label: \"All\", count: visible.length },\n    { id: \"activity\", label: \"New\", count: newCount },\n    { id: \"tasks\", label: \"Tasks\", count: taskCount },\n    { id: \"digest\", label: \"Digest\", count: digestCount },\n  ];\n\n  return (\n    <section\n      className={cn(\n        \"bg-background flex items-center justify-center\",\n        className,\n      )}\n    >\n      <Card className=\"w-full max-w-2xl gap-0 overflow-hidden rounded-3xl p-0 shadow-xs\">\n        <CardHeader className=\"px-4 py-2\">\n          <div className=\"flex items-center justify-between\">\n            <div className=\"flex items-center gap-2\">\n              <div className=\"bg-primary/10 flex h-8 w-8 items-center justify-center rounded-xl\">\n                <RiNotification3Fill className=\"text-primary h-4 w-4\" />\n              </div>\n              <div>\n                <h2 className=\"text-foreground text-base font-bold tracking-tight\">\n                  {title}\n                </h2>\n                <p className=\"text-muted-foreground text-[11px] font-medium\">\n                  {newCount > 0 ? `${newCount} new` : \"All caught up\"} ·{\" \"}\n                  {visible.length} total\n                </p>\n              </div>\n            </div>\n            <Button\n              variant=\"ghost\"\n              size=\"sm\"\n              className=\"text-primary hover:bg-primary/5 h-8 gap-1 rounded-xl px-3 text-xs font-semibold\"\n              onClick={() =>\n                setDismissed(\n                  new Set(items.filter((i) => i.isNew).map((i) => i.id)),\n                )\n              }\n            >\n              <RiCheckFill className=\"h-3.5 w-3.5\" />\n              Clear new\n            </Button>\n          </div>\n\n          <div className=\"mt-1 flex items-center gap-1\">\n            {tabs.map((tab) => (\n              <TabPill\n                key={tab.id}\n                label={tab.label}\n                count={tab.count}\n                active={activeTab === tab.id}\n                onClick={() => setActiveTab(tab.id)}\n              />\n            ))}\n          </div>\n        </CardHeader>\n        <CardContent className=\"p-0\">\n          <ScrollArea className=\"h-[340px] w-full\">\n            {filtered.length === 0 ? (\n              <div className=\"flex h-64 flex-col items-center justify-center gap-3\">\n                <div className=\"bg-muted flex h-12 w-12 items-center justify-center rounded-full\">\n                  <RiNotification3Fill className=\"text-muted-foreground h-5 w-5 opacity-40\" />\n                </div>\n                <p className=\"text-muted-foreground text-sm font-medium\">\n                  Nothing here\n                </p>\n              </div>\n            ) : (\n              <div>\n                {filtered.map((item, idx) => (\n                  <React.Fragment key={item.id}>\n                    <ActivityRow item={item} onDismiss={handleDismiss} />\n                    {idx < filtered.length - 1 && (\n                      <Separator className=\"mx-4 w-auto\" />\n                    )}\n                  </React.Fragment>\n                ))}\n              </div>\n            )}\n          </ScrollArea>\n        </CardContent>\n\n        <CardFooter className=\"border-border border-t px-5 py-3\">\n          <div className=\"flex w-full items-center justify-between\">\n            <div className=\"text-muted-foreground flex items-center gap-1 text-xs\">\n              <RiCircleFill className=\"text-primary h-1.5 w-1.5\" />\n              <span>{newCount > 0 ? `${newCount} unread` : \"Up to date\"}</span>\n            </div>\n            <Button\n              variant=\"ghost\"\n              size=\"sm\"\n              className=\"text-muted-foreground hover:text-foreground h-7 gap-0.5 rounded-lg px-2 text-xs font-medium\"\n            >\n              <RiSettings4Fill className=\"h-3.5 w-3.5\" />\n              Manage\n              <RiArrowRightSFill className=\"h-3.5 w-3.5\" />\n            </Button>\n          </div>\n        </CardFooter>\n      </Card>\n    </section>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/notification-3.tsx"
    }
  ],
  "type": "registry:ui"
}
