{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "content-calendar-matrix",
  "title": "Content Calendar Matrix",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/content-calendar-matrix/ContentCalendarMatrix.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport {\n  Calendar,\n  CalendarCheck,\n  CalendarDays,\n  CheckCircle2,\n  Clock,\n  FileText,\n  Layers,\n  Mail,\n  MoreHorizontal,\n  Pencil,\n  PenLine,\n  Plus,\n  Search,\n  Share2,\n  Trash2,\n  UserCheck,\n  Video,\n} from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Avatar, AvatarFallback } from '@/components/ui/avatar'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\nimport { Input } from '@/components/ui/input'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'\n\nexport type ContentChannel = 'all' | 'blog' | 'newsletter' | 'youtube' | 'social'\nexport type ContentStatus = 'published' | 'scheduled' | 'in_review' | 'draft'\n\nexport interface ContentItem {\n  id: string\n  title: string\n  channel: 'blog' | 'newsletter' | 'youtube' | 'social'\n  channelLabel: string\n  publishDate: string\n  publishTime: string\n  author: {\n    name: string\n    role: string\n    avatar: string\n  }\n  status: ContentStatus\n  statusLabel: string\n  keywords: string[]\n  format: string\n}\n\nexport interface ContentCalendarMatrixProps {\n  title?: string\n  subtitle?: string\n  className?: string\n}\n\nconst pacingKpis = [\n  {\n    id: 'planned',\n    title: 'Total Planned Content',\n    value: '24',\n    unit: 'Pieces this month',\n    note: '24 Pieces this month',\n    subtext: '+6 pieces vs previous month',\n    icon: Layers,\n    colorClass: 'text-primary bg-primary/10',\n  },\n  {\n    id: 'scheduled',\n    title: 'Scheduled for Publish',\n    value: '8',\n    unit: 'items ready',\n    note: '8 items ready',\n    subtext: 'Next drop: Sep 08 · 02:00 PM EST',\n    icon: CalendarCheck,\n    colorClass: 'text-info bg-info/10',\n  },\n  {\n    id: 'in-review',\n    title: 'In Review / Drafting',\n    value: '12',\n    unit: 'in progress',\n    note: '12 in progress',\n    subtext: '7 in editorial review · 5 in drafting',\n    icon: PenLine,\n    colorClass: 'text-warning bg-warning/10',\n  },\n  {\n    id: 'published',\n    title: 'Published this Month',\n    value: '4',\n    unit: 'live',\n    note: '4 live',\n    subtext: '100% on-schedule publishing rate',\n    icon: CheckCircle2,\n    colorClass: 'text-success bg-success/10',\n  },\n]\n\nconst channelConfig: Record<\n  'blog' | 'newsletter' | 'youtube' | 'social',\n  { label: string; badgeClass: string; icon: React.ComponentType<{ className?: string }> }\n> = {\n  blog: {\n    label: 'Blog',\n    badgeClass: 'bg-info/10 text-info border-info/20',\n    icon: FileText,\n  },\n  newsletter: {\n    label: 'Newsletter',\n    badgeClass: 'bg-chart-1/10 text-chart-1 border-chart-1/20',\n    icon: Mail,\n  },\n  youtube: {\n    label: 'YouTube',\n    badgeClass: 'bg-destructive/10 text-destructive border-destructive/20',\n    icon: Video,\n  },\n  social: {\n    label: 'Social',\n    badgeClass: 'bg-info/10 text-info border-info/20',\n    icon: Share2,\n  },\n}\n\nconst statusConfig: Record<ContentStatus, { label: string; badgeClass: string; dotClass: string }> = {\n  published: {\n    label: 'Published',\n    badgeClass: 'bg-success/10 text-success border-success/20',\n    dotClass: 'bg-success',\n  },\n  scheduled: {\n    label: 'Scheduled',\n    badgeClass: 'bg-info/10 text-info border-info/20',\n    dotClass: 'bg-info',\n  },\n  in_review: {\n    label: 'In Review',\n    badgeClass: 'bg-warning/10 text-warning border-warning/20',\n    dotClass: 'bg-warning',\n  },\n  draft: {\n    label: 'Draft',\n    badgeClass: 'bg-muted text-muted-foreground border-border',\n    dotClass: 'bg-muted-foreground',\n  },\n}\n\nconst contentItems: ContentItem[] = [\n  {\n    id: 'cnt-01',\n    title: 'Deep Dive: Headless Reka UI Primitives in Vue 3.5',\n    channel: 'blog',\n    channelLabel: 'Blog',\n    publishDate: 'Sep 04',\n    publishTime: '10:00 AM EST',\n    author: {\n      name: 'Sarah Chen',\n      role: 'Lead Developer Advocate',\n      avatar: 'SC',\n    },\n    status: 'published',\n    statusLabel: 'Published',\n    keywords: ['#vue3', '#reka-ui', '#frontend'],\n    format: 'Technical Guide',\n  },\n  {\n    id: 'cnt-02',\n    title: 'Building Zero-Runtime Design Systems with Tailwind CSS v4',\n    channel: 'youtube',\n    channelLabel: 'YouTube',\n    publishDate: 'Sep 08',\n    publishTime: '02:00 PM EST',\n    author: {\n      name: 'Marcus Vance',\n      role: 'Senior UI Engineer',\n      avatar: 'MV',\n    },\n    status: 'scheduled',\n    statusLabel: 'Scheduled',\n    keywords: ['#tailwindcss', '#design-systems', '#webdev'],\n    format: 'Video Tutorial',\n  },\n  {\n    id: 'cnt-03',\n    title: 'Frontend Weekly #142: Subgrid Mastery & CSS Anchor Positioning',\n    channel: 'newsletter',\n    channelLabel: 'Newsletter',\n    publishDate: 'Sep 12',\n    publishTime: '09:00 AM EST',\n    author: {\n      name: 'Elena Rostova',\n      role: 'Technical Editor',\n      avatar: 'ER',\n    },\n    status: 'scheduled',\n    statusLabel: 'Scheduled',\n    keywords: ['#css', '#newsletter', '#subgrid'],\n    format: 'Email Dispatch',\n  },\n  {\n    id: 'cnt-04',\n    title: 'Accessible Command Palettes & Keyboard Shortcuts Architecture',\n    channel: 'blog',\n    channelLabel: 'Blog',\n    publishDate: 'Sep 16',\n    publishTime: '11:30 AM EST',\n    author: {\n      name: 'David Kim',\n      role: 'Accessibility Specialist',\n      avatar: 'DK',\n    },\n    status: 'in_review',\n    statusLabel: 'In Review',\n    keywords: ['#a11y', '#cmdk', '#keyboard-nav'],\n    format: 'Deep Dive',\n  },\n  {\n    id: 'cnt-05',\n    title: 'Product Drop: 12 New Enterprise Blocks for Nuxt 4 & React',\n    channel: 'social',\n    channelLabel: 'Social',\n    publishDate: 'Sep 21',\n    publishTime: '04:00 PM EST',\n    author: {\n      name: 'Aisha Patel',\n      role: 'Product Marketing Lead',\n      avatar: 'AP',\n    },\n    status: 'in_review',\n    statusLabel: 'In Review',\n    keywords: ['#changelog', '#uipkge', '#react', '#vue'],\n    format: 'Social Campaign',\n  },\n  {\n    id: 'cnt-06',\n    title: 'Micro-Interactions & Spring Physics in Modern Web Applications',\n    channel: 'youtube',\n    channelLabel: 'YouTube',\n    publishDate: 'Sep 26',\n    publishTime: '01:15 PM EST',\n    author: {\n      name: 'Lucas Meyer',\n      role: 'Motion Designer',\n      avatar: 'LM',\n    },\n    status: 'draft',\n    statusLabel: 'Draft',\n    keywords: ['#animation', '#spring-physics', '#ui-motion'],\n    format: 'Interactive Workshop',\n  },\n]\n\nconst channelFilters: Array<{\n  value: ContentChannel\n  label: string\n  icon: React.ComponentType<{ className?: string }>\n}> = [\n  { value: 'all', label: 'All Channels', icon: Layers },\n  { value: 'blog', label: 'Blog', icon: FileText },\n  { value: 'newsletter', label: 'Newsletter', icon: Mail },\n  { value: 'youtube', label: 'YouTube', icon: Video },\n  { value: 'social', label: 'Social', icon: Share2 },\n]\n\nexport function ContentCalendarMatrix({\n  title = 'Editorial Content Calendar',\n  subtitle = 'Schedule, organize, and track cross-channel editorial pipelines and publishing milestones.',\n  className,\n}: ContentCalendarMatrixProps) {\n  const [selectedMonth, setSelectedMonth] = React.useState('2026-09')\n  const [selectedChannel, setSelectedChannel] = React.useState<ContentChannel>('all')\n  const [searchQuery, setSearchQuery] = React.useState('')\n\n  const filteredItems = React.useMemo(() => {\n    const query = searchQuery.trim().toLowerCase()\n    return contentItems.filter((item) => {\n      const matchesChannel = selectedChannel === 'all' || item.channel === selectedChannel\n      const matchesQuery =\n        !query ||\n        item.title.toLowerCase().includes(query) ||\n        item.author.name.toLowerCase().includes(query) ||\n        item.keywords.some((k) => k.toLowerCase().includes(query))\n      return matchesChannel && matchesQuery\n    })\n  }, [selectedChannel, searchQuery])\n\n  return (\n    <div data-slot=\"content-calendar-matrix\" className={cn('w-full space-y-6', className)}>\n      {/* Header Section */}\n      <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n        <div>\n          <div className=\"flex items-center gap-2\">\n            <h2 className=\"text-2xl font-bold tracking-tight\">{title}</h2>\n            <Badge variant=\"outline\" className=\"hidden sm:inline-flex\">\n              Editorial Desk\n            </Badge>\n          </div>\n          <p className=\"text-muted-foreground mt-1 text-sm\">{subtitle}</p>\n        </div>\n\n        <div className=\"flex flex-wrap items-center gap-3\">\n          {/* Month Selector */}\n          <Select value={selectedMonth} onValueChange={setSelectedMonth}>\n            <SelectTrigger className=\"w-[180px] shadow-xs\">\n              <CalendarDays className=\"text-muted-foreground mr-2 size-4\" />\n              <SelectValue placeholder=\"Select month\" />\n            </SelectTrigger>\n            <SelectContent>\n              <SelectItem value=\"2026-08\">August 2026</SelectItem>\n              <SelectItem value=\"2026-09\">September 2026</SelectItem>\n              <SelectItem value=\"2026-10\">October 2026</SelectItem>\n              <SelectItem value=\"2026-11\">November 2026</SelectItem>\n            </SelectContent>\n          </Select>\n\n          {/* New Content Item Primary Button */}\n          <Button className=\"gap-2 shadow-xs\">\n            <Plus className=\"size-4\" />\n            New Content Item\n          </Button>\n        </div>\n      </div>\n\n      {/* 4 Editorial Pacing KPI Cards */}\n      <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        {pacingKpis.map((kpi) => {\n          const Icon = kpi.icon\n          return (\n            <Card key={kpi.id} className=\"shadow-xs\">\n              <CardContent className=\"space-y-3 p-5\">\n                <div className=\"flex items-center justify-between\">\n                  <p className=\"text-muted-foreground text-sm font-medium\">{kpi.title}</p>\n                  <div className={cn('flex size-8 items-center justify-center rounded-md', kpi.colorClass)}>\n                    <Icon className=\"size-4\" />\n                  </div>\n                </div>\n\n                <div>\n                  <div className=\"flex items-baseline gap-1.5\">\n                    <span className=\"text-2xl font-bold tracking-tight tabular-nums\">{kpi.value}</span>\n                    <span className=\"text-muted-foreground text-xs font-medium\">{kpi.unit}</span>\n                  </div>\n                  <p className=\"text-muted-foreground mt-1 text-xs\">{kpi.subtext}</p>\n                </div>\n              </CardContent>\n            </Card>\n          )\n        })}\n      </div>\n\n      {/* Pipeline & Matrix Controls Bar */}\n      <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        {/* Channel Filters */}\n        <div className=\"flex flex-wrap items-center gap-1.5\">\n          {channelFilters.map((filter) => {\n            const Icon = filter.icon\n            return (\n              <Button\n                key={filter.value}\n                variant={selectedChannel === filter.value ? 'default' : 'outline'}\n                size=\"sm\"\n                className=\"h-8 gap-1.5 text-xs shadow-xs\"\n                onClick={() => setSelectedChannel(filter.value)}\n              >\n                <Icon className=\"size-3.5\" />\n                <span>{filter.label}</span>\n              </Button>\n            )\n          })}\n        </div>\n\n        {/* Keyword / Title Search Input */}\n        <div className=\"relative w-full sm:w-64\">\n          <Search className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2\" />\n          <Input\n            value={searchQuery}\n            onChange={(e) => setSearchQuery(e.target.value)}\n            type=\"text\"\n            placeholder=\"Filter by keyword or title...\"\n            className=\"h-8 pl-8 text-xs shadow-xs\"\n          />\n        </div>\n      </div>\n\n      {/* Content Publishing Pipeline Table */}\n      <div className=\"bg-card overflow-x-auto rounded-lg border shadow-xs\">\n        <Table>\n          <TableHeader>\n            <TableRow>\n              <TableHead className=\"w-[180px]\">Publish Date &amp; Time</TableHead>\n              <TableHead className=\"min-w-[280px]\">Content Title</TableHead>\n              <TableHead className=\"w-[120px]\">Channel</TableHead>\n              <TableHead className=\"min-w-[180px]\">Author</TableHead>\n              <TableHead className=\"w-[130px]\">Status</TableHead>\n              <TableHead className=\"min-w-[200px]\">SEO / Keywords</TableHead>\n              <TableHead className=\"w-12 text-right\">\n                <span className=\"sr-only\">Actions</span>\n              </TableHead>\n            </TableRow>\n          </TableHeader>\n          <TableBody>\n            {filteredItems.map((item) => {\n              const ChannelIcon = channelConfig[item.channel].icon\n              return (\n                <TableRow key={item.id} className=\"hover:bg-muted/50 transition-colors\">\n                  {/* Publish Date & Time */}\n                  <TableCell className=\"align-middle\">\n                    <div className=\"flex items-center gap-1.5\">\n                      <Clock className=\"text-muted-foreground size-3.5 shrink-0\" />\n                      <span className=\"text-xs font-medium tabular-nums\">\n                        {item.publishDate} · {item.publishTime}\n                      </span>\n                    </div>\n                  </TableCell>\n\n                  {/* Content Title */}\n                  <TableCell className=\"align-middle\">\n                    <div className=\"space-y-0.5\">\n                      <p className=\"text-foreground text-sm leading-snug font-semibold\">{item.title}</p>\n                      <p className=\"text-muted-foreground text-xs font-normal\">{item.format}</p>\n                    </div>\n                  </TableCell>\n\n                  {/* Channel Badge */}\n                  <TableCell className=\"align-middle\">\n                    <Badge\n                      variant=\"outline\"\n                      className={cn('gap-1 text-xs font-medium', channelConfig[item.channel].badgeClass)}\n                    >\n                      <ChannelIcon className=\"size-3\" />\n                      {channelConfig[item.channel].label}\n                    </Badge>\n                  </TableCell>\n\n                  {/* Author (Avatar + Name) */}\n                  <TableCell className=\"align-middle\">\n                    <div className=\"flex items-center gap-2.5\">\n                      <Avatar className=\"size-7\">\n                        <AvatarFallback className=\"bg-primary/10 text-primary text-xs font-semibold\">\n                          {item.author.avatar}\n                        </AvatarFallback>\n                      </Avatar>\n                      <div className=\"min-w-0\">\n                        <p className=\"text-foreground truncate text-xs font-medium\">{item.author.name}</p>\n                        <p className=\"text-muted-foreground truncate text-xs\">{item.author.role}</p>\n                      </div>\n                    </div>\n                  </TableCell>\n\n                  {/* Status Badge */}\n                  <TableCell className=\"align-middle\">\n                    <Badge\n                      variant=\"outline\"\n                      className={cn('gap-1.5 text-xs font-medium', statusConfig[item.status].badgeClass)}\n                    >\n                      <span className={cn('size-1.5 rounded-full', statusConfig[item.status].dotClass)} />\n                      {statusConfig[item.status].label}\n                    </Badge>\n                  </TableCell>\n\n                  {/* SEO / Target Keywords */}\n                  <TableCell className=\"align-middle\">\n                    <div className=\"flex flex-wrap gap-1\">\n                      {item.keywords.map((keyword) => (\n                        <span\n                          key={keyword}\n                          className=\"bg-muted/60 border-border/50 text-muted-foreground inline-flex items-center rounded-md border px-1.5 py-0.5 font-mono text-xs\"\n                        >\n                          {keyword}\n                        </span>\n                      ))}\n                    </div>\n                  </TableCell>\n\n                  {/* Actions Dropdown */}\n                  <TableCell className=\"text-right align-middle\">\n                    <DropdownMenu>\n                      <DropdownMenuTrigger asChild>\n                        <Button variant=\"ghost\" size=\"icon-sm\" className=\"text-muted-foreground size-8\">\n                          <MoreHorizontal className=\"size-4\" />\n                          <span className=\"sr-only\">Open actions for {item.title}</span>\n                        </Button>\n                      </DropdownMenuTrigger>\n                      <DropdownMenuContent align=\"end\" className=\"w-48\">\n                        <DropdownMenuLabel className=\"text-xs\">Manage Content</DropdownMenuLabel>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem className=\"gap-2 text-xs\">\n                          <Pencil className=\"size-3.5\" />\n                          <span>Edit item</span>\n                        </DropdownMenuItem>\n                        <DropdownMenuItem className=\"gap-2 text-xs\">\n                          <Calendar className=\"size-3.5\" />\n                          <span>Reschedule</span>\n                        </DropdownMenuItem>\n                        <DropdownMenuItem className=\"gap-2 text-xs\">\n                          <UserCheck className=\"size-3.5\" />\n                          <span>Assign reviewer</span>\n                        </DropdownMenuItem>\n                        <DropdownMenuSeparator />\n                        <DropdownMenuItem className=\"text-destructive focus:text-destructive gap-2 text-xs\">\n                          <Trash2 className=\"size-3.5\" />\n                          <span>Delete item</span>\n                        </DropdownMenuItem>\n                      </DropdownMenuContent>\n                    </DropdownMenu>\n                  </TableCell>\n                </TableRow>\n              )\n            })}\n\n            {filteredItems.length === 0 && (\n              <TableRow>\n                <TableCell colSpan={7} className=\"text-muted-foreground h-32 text-center text-sm\">\n                  No content items found matching the selected channel filter or search query.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n\n      {/* Publishing Matrix Footer */}\n      <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <p className=\"text-muted-foreground text-xs\">\n          Showing <span className=\"text-foreground font-semibold tabular-nums\">{filteredItems.length}</span> of{' '}\n          <span className=\"text-foreground font-semibold tabular-nums\">{contentItems.length}</span> scheduled content\n          pieces for <span className=\"text-foreground font-medium\">September 2026</span>\n        </p>\n        <div className=\"flex items-center gap-2\">\n          <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs shadow-xs\" disabled>\n            Previous\n          </Button>\n          <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs shadow-xs\">\n            Next\n          </Button>\n        </div>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/ContentCalendarMatrix.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/avatar.json",
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/dropdown-menu.json",
    "https://uipkge.dev/r/react/input.json",
    "https://uipkge.dev/r/react/select.json",
    "https://uipkge.dev/r/react/table.json"
  ],
  "description": "Editorial publishing calendar and content matrix with pacing KPI summary cards, channel filter tabs, month picker, publication status pipeline, author avatars, SEO keywords, and contextual action menus.",
  "categories": [
    "media",
    "app",
    "marketing"
  ]
}