{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dashboard-store",
  "title": "Modern Store Dashboard",
  "description": "",
  "dependencies": [
    "framer-motion",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/default/blocks/Dashboard/dashboard-finance.tsx",
      "content": "\"use client\";\r\n\r\n/**\r\n * Dashboard2 — fully interactive version\r\n * ────────────────────────────────────────────────────────────────────────\r\n * Every control actually does something:\r\n *   - Sidebar nav (\"Dashboard\", \"Product\", \"Order\", \"Customer\", \"Message\",\r\n *     \"Email\", \"Automation\", \"Analytics\", \"Integration\") swaps the dataset,\r\n *     the stats row, and the table columns shown in the main panel.\r\n *   - Search box filters the current table's rows live.\r\n *   - Filter dropdown restricts rows by status; Sort dropdown re-orders rows;\r\n *     column headers are clickable to sort (asc/desc toggle).\r\n *   - \"Show Statistics\" toggle shows/hides the stats row.\r\n *   - Checkboxes drive a real selection state -> bulk action bar appears,\r\n *     \"Apply Code\" / \"Edit Info\" / \"Delete\" mutate the in-memory dataset.\r\n *   - \"+ Add New Product\" opens an inline quick-add row.\r\n *   - \"Export\" downloads the current (filtered) table as a CSV file.\r\n *   - \"Customize\" / \"Customize Widget\" toggle which stat cards / columns\r\n *     are visible.\r\n *   - Pagination buttons/page-size/go-to-page all actually page the data.\r\n *   - A small toast system confirms every action.\r\n *   - A light/dark toggle switches the whole dashboard's theme (Tailwind\r\n *     `dark:` class strategy, scoped to the dashboard root — no need for\r\n *     the `<html>` tag to carry the class).\r\n *\r\n * Font: Geist (next/font/google), set once on the root wrapper.\r\n * Colors: BagUI's neutral/gray system (bagui.vercel.app) — gray-50→900,\r\n * black primary actions, soft-pill status colors. Primary black surfaces\r\n * invert to white in dark mode to keep the strict black/white/gray language.\r\n *\r\n * Dependencies: npm install framer-motion lucide-react\r\n *\r\n * Fix note: the crash (\"Cannot read properties of undefined (reading 'rows')\")\r\n * happened because `active` could hold a nav label with no matching key in\r\n * the `datasets` map (e.g. a sidebar item added without a matching entry in\r\n * buildDatasets()), so `datasets[active]` was `undefined`. `dataset` now\r\n * falls back to the first available dataset instead of crashing, and logs a\r\n * dev-only warning so the mismatch is easy to spot and fix at the source.\r\n */\r\n\r\nimport { useMemo, useState, useEffect, useRef } from \"react\";\r\nimport { motion, AnimatePresence, type Variants } from \"framer-motion\";\r\nimport {\r\n  Search,\r\n  LayoutGrid,\r\n  Package,\r\n  ShoppingCart,\r\n  Users,\r\n  MessageSquare,\r\n  Mail,\r\n  Zap,\r\n  BarChart3,\r\n  Plug,\r\n  HelpCircle,\r\n  MessageCircle,\r\n  Settings,\r\n  ChevronDown,\r\n  ChevronLeft,\r\n  ChevronRight,\r\n  ChevronsUpDown,\r\n  ChevronUp,\r\n  MoreHorizontal,\r\n  Filter,\r\n  ArrowUpDown,\r\n  Share2,\r\n  Bell,\r\n  Plus,\r\n  Download,\r\n  SlidersHorizontal,\r\n  Star,\r\n  X,\r\n  Info,\r\n  Check,\r\n  ArrowUpRight,\r\n  ArrowDownRight,\r\n  Sparkles,\r\n  CheckCircle2,\r\n  Trash2,\r\n  Sun,\r\n  Moon,\r\n  Crown,\r\n} from \"lucide-react\";\r\nimport Image from \"next/image\";\r\n\r\n// ─── Font (applied once, inherited everywhere) ─────────────────────────────\r\nconst font = {\r\n  fontFamily: \"var(--font-geist-sans), Geist, Inter, system-ui, sans-serif\",\r\n} as const;\r\n\r\n// ─── Motion variants ────────────────────────────────────────────────────────\r\nconst stagger: Variants = {\r\n  hidden: {},\r\n  visible: { transition: { staggerChildren: 0.04, delayChildren: 0.04 } },\r\n};\r\nconst fadeUp: Variants = {\r\n  hidden: { opacity: 0, y: 10 },\r\n  visible: { opacity: 1, y: 0, transition: { duration: 0.3, ease: \"easeOut\" } },\r\n};\r\n\r\n// ─── Shared types ───────────────────────────────────────────────────────────\r\ntype Status =\r\n  | \"In Stock\"\r\n  | \"Out of Stock\"\r\n  | \"Restock\"\r\n  | \"Active\"\r\n  | \"Pending\"\r\n  | \"Completed\"\r\n  | \"Cancelled\"\r\n  | \"Read\"\r\n  | \"Unread\"\r\n  | \"Published\"\r\n  | \"Draft\"\r\n  | \"New\"\r\n  | \"In Review\"\r\n  | \"Resolved\";\r\n\r\ntype Row = {\r\n  id: string;\r\n  cells: Record<string, string | number>;\r\n  status: Status;\r\n  rating?: number;\r\n};\r\n\r\ntype ColumnDef = {\r\n  key: string;\r\n  label: string;\r\n  align?: \"left\" | \"right\";\r\n  numeric?: boolean;\r\n};\r\n\r\ntype Dataset = {\r\n  key: string;\r\n  title: string;\r\n  addLabel: string;\r\n  columns: ColumnDef[];\r\n  stats: { label: string; value: string; delta: string }[];\r\n  rows: Row[];\r\n};\r\n\r\n// ─── Icons per nav item ─────────────────────────────────────────────────────\r\ntype NavItem = {\r\n  label: string;\r\n  icon: React.ComponentType<{ size?: number; className?: string }>;\r\n};\r\n\r\nconst MAIN_MENU: NavItem[] = [\r\n  { label: \"Dashboard\", icon: LayoutGrid },\r\n  { label: \"Product\", icon: Package },\r\n  { label: \"Order\", icon: ShoppingCart },\r\n  { label: \"Customer\", icon: Users },\r\n  { label: \"Message\", icon: MessageSquare },\r\n];\r\nconst TOOLS_MENU: NavItem[] = [\r\n  { label: \"Email\", icon: Mail },\r\n  { label: \"Automation\", icon: Zap },\r\n  { label: \"Analytics\", icon: BarChart3 },\r\n  { label: \"Integration\", icon: Plug },\r\n];\r\n\r\nconst BOTTOM_MENU: NavItem[] = [\r\n  { label: \"Help center\", icon: HelpCircle },\r\n  { label: \"Feedback\", icon: MessageCircle },\r\n  { label: \"Settings\", icon: Settings },\r\n];\r\n\r\nconst WORKSPACES = [\r\n  { label: \"Campaign\", count: 5, color: \"bg-gray-900 dark:bg-neutral-100\" },\r\n  { label: \"Product Plan\", count: 4, color: \"bg-gray-400 dark:bg-neutral-600\" },\r\n];\r\n\r\n// Strictly black / white / gray. Status is communicated through the pill's\r\n// shade (tone) plus an optional check mark — never through hue. In dark\r\n// mode, \"solid\" pills invert (white chip / dark text) to keep the same\r\n// strict black-white-gray language instead of turning muddy.\r\ntype Tone = \"solid\" | \"subtle\" | \"outline\";\r\nconst STATUS_META: Record<string, { tone: Tone }> = {\r\n  \"In Stock\": { tone: \"solid\" },\r\n  \"Out of Stock\": { tone: \"outline\" },\r\n  Restock: { tone: \"subtle\" },\r\n  Active: { tone: \"solid\" },\r\n  Pending: { tone: \"subtle\" },\r\n  Completed: { tone: \"solid\" },\r\n  Cancelled: { tone: \"outline\" },\r\n  Read: { tone: \"outline\" },\r\n  Unread: { tone: \"solid\" },\r\n  Published: { tone: \"solid\" },\r\n  Draft: { tone: \"subtle\" },\r\n  New: { tone: \"solid\" },\r\n  \"In Review\": { tone: \"subtle\" },\r\n  Resolved: { tone: \"solid\" },\r\n};\r\nconst TONE_CLASSES: Record<Tone, string> = {\r\n  solid: \"bg-gray-900 text-white dark:bg-neutral-50 dark:text-neutral-900\",\r\n  subtle:\r\n    \"bg-gray-100 text-gray-600 border border-gray-200 dark:bg-neutral-800 dark:text-neutral-300 dark:border-neutral-700\",\r\n  outline:\r\n    \"bg-white text-gray-400 border border-gray-300 dark:bg-neutral-900 dark:text-neutral-500 dark:border-neutral-700\",\r\n};\r\n\r\n// Team avatars — Dicebear \"notionists\" set.\r\nconst AVATARS = [\r\n  {\r\n    id: 1,\r\n    src: \"https://api.dicebear.com/9.x/notionists/svg?seed=JK&backgroundColor=b6e3f4\",\r\n  },\r\n  {\r\n    id: 2,\r\n    src: \"https://api.dicebear.com/9.x/notionists/svg?seed=AM&backgroundColor=c0aede\",\r\n  },\r\n  {\r\n    id: 3,\r\n    src: \"https://api.dicebear.com/9.x/notionists/svg?seed=SL&backgroundColor=ffdfbf\",\r\n  },\r\n  {\r\n    id: 4,\r\n    src: \"https://api.dicebear.com/9.x/notionists/svg?seed=TR&backgroundColor=d1d4f9\",\r\n  },\r\n  {\r\n    id: 5,\r\n    src: \"https://api.dicebear.com/9.x/notionists/svg?seed=PW&backgroundColor=ffd5dc\",\r\n  },\r\n];\r\n\r\n// ─── Datasets (one per nav item — this is what makes the sidebar \"dynamic\") ─\r\nfunction buildDatasets(): Record<string, Dataset> {\r\n  return {\r\n    Dashboard: {\r\n      key: \"Dashboard\",\r\n      title: \"Dashboard\",\r\n      addLabel: \"Add Widget\",\r\n      columns: [\r\n        { key: \"name\", label: \"Metric\" },\r\n        { key: \"value\", label: \"Value\" },\r\n        { key: \"change\", label: \"Change\" },\r\n        { key: \"period\", label: \"Period\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Total Revenue\", value: \"$48,290\", delta: \"+ 12%\" },\r\n        { label: \"Active Users\", value: \"3,204\", delta: \"+ 4%\" },\r\n        { label: \"Conversion Rate\", value: \"3.8%\", delta: \"+ 0.6%\" },\r\n        { label: \"Churn Rate\", value: \"1.2%\", delta: \"+ 0.2%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"d1\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Monthly Recurring Revenue\",\r\n            value: \"$48,290\",\r\n            change: \"+12%\",\r\n            period: \"This month\",\r\n          },\r\n        },\r\n        {\r\n          id: \"d2\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"New Signups\",\r\n            value: \"312\",\r\n            change: \"+18%\",\r\n            period: \"This month\",\r\n          },\r\n        },\r\n        {\r\n          id: \"d3\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Support Tickets\",\r\n            value: \"27\",\r\n            change: \"-5%\",\r\n            period: \"This week\",\r\n          },\r\n        },\r\n        {\r\n          id: \"d4\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"Deployments\",\r\n            value: \"9\",\r\n            change: \"+2\",\r\n            period: \"This week\",\r\n          },\r\n        },\r\n        {\r\n          id: \"d5\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Server Uptime\",\r\n            value: \"99.98%\",\r\n            change: \"+0.01%\",\r\n            period: \"This month\",\r\n          },\r\n        },\r\n        {\r\n          id: \"d6\",\r\n          status: \"Cancelled\",\r\n          cells: {\r\n            name: \"Failed Payments\",\r\n            value: \"4\",\r\n            change: \"-2\",\r\n            period: \"This month\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Product: {\r\n      key: \"Product\",\r\n      title: \"Product\",\r\n      addLabel: \"Add New Product\",\r\n      columns: [\r\n        { key: \"name\", label: \"Product\" },\r\n        { key: \"price\", label: \"Price\", numeric: true },\r\n        { key: \"sales\", label: \"Sales\" },\r\n        { key: \"revenue\", label: \"Revenue\", numeric: true },\r\n        { key: \"stock\", label: \"Stock\", numeric: true },\r\n        { key: \"status\", label: \"Status\" },\r\n        { key: \"rating\", label: \"Rating\", numeric: true },\r\n      ],\r\n      stats: [\r\n        { label: \"Total Product\", value: \"250\", delta: \"+ 3 product\" },\r\n        { label: \"Product Revenue\", value: \"$15,490\", delta: \"+ 9%\" },\r\n        { label: \"Product Sold\", value: \"2,355\", delta: \"+ 7%\" },\r\n        { label: \"Avg. Monthly Sales\", value: \"890\", delta: \"+ 6%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"p1\",\r\n          status: \"In Stock\",\r\n          rating: 5.0,\r\n          cells: {\r\n            name: \"Orbit T-Shirt #10 - White\",\r\n            price: 1.35,\r\n            sales: \"471 pcs\",\r\n            revenue: 635.85,\r\n            stock: 100,\r\n          },\r\n        },\r\n        {\r\n          id: \"p2\",\r\n          status: \"Out of Stock\",\r\n          rating: 5.0,\r\n          cells: {\r\n            name: \"Orbit T-Shirt #10 - Black\",\r\n            price: 1.35,\r\n            sales: \"402 pcs\",\r\n            revenue: 544.0,\r\n            stock: 20,\r\n          },\r\n        },\r\n        {\r\n          id: \"p3\",\r\n          status: \"Restock\",\r\n          rating: 4.9,\r\n          cells: {\r\n            name: \"Orbit T-Shirt #19 - White\",\r\n            price: 1.35,\r\n            sales: \"455 pcs\",\r\n            revenue: 645.25,\r\n            stock: 20,\r\n          },\r\n        },\r\n        {\r\n          id: \"p4\",\r\n          status: \"In Stock\",\r\n          rating: 4.8,\r\n          cells: {\r\n            name: \"SmartHome Hub\",\r\n            price: 150,\r\n            sales: \"7 pcs\",\r\n            revenue: 1050.0,\r\n            stock: 12,\r\n          },\r\n        },\r\n        {\r\n          id: \"p5\",\r\n          status: \"Out of Stock\",\r\n          rating: 4.8,\r\n          cells: {\r\n            name: \"UltraSound Wireless Earbuds\",\r\n            price: 200,\r\n            sales: \"5 pcs\",\r\n            revenue: 1000.0,\r\n            stock: 0,\r\n          },\r\n        },\r\n        {\r\n          id: \"p6\",\r\n          status: \"Restock\",\r\n          rating: 4.7,\r\n          cells: {\r\n            name: \"ProVision 4K Monitor\",\r\n            price: 400.25,\r\n            sales: \"1 pcs\",\r\n            revenue: 400.25,\r\n            stock: 3,\r\n          },\r\n        },\r\n        {\r\n          id: \"p7\",\r\n          status: \"In Stock\",\r\n          rating: 4.7,\r\n          cells: {\r\n            name: \"Orbit Retro Wave Shirt\",\r\n            price: 1.35,\r\n            sales: \"120 pcs\",\r\n            revenue: 162.4,\r\n            stock: 0,\r\n          },\r\n        },\r\n        {\r\n          id: \"p8\",\r\n          status: \"Out of Stock\",\r\n          rating: 4.9,\r\n          cells: {\r\n            name: \"Orbit Graphic Art T-Shirt\",\r\n            price: 1.35,\r\n            sales: \"200 pcs\",\r\n            revenue: 270.15,\r\n            stock: 0,\r\n          },\r\n        },\r\n        {\r\n          id: \"p9\",\r\n          status: \"In Stock\",\r\n          rating: 4.8,\r\n          cells: {\r\n            name: \"Orbit Classic Fit Crewneck\",\r\n            price: 28.5,\r\n            sales: \"130 pcs\",\r\n            revenue: 3705.25,\r\n            stock: 11,\r\n          },\r\n        },\r\n        {\r\n          id: \"p10\",\r\n          status: \"In Stock\",\r\n          rating: 4.8,\r\n          cells: {\r\n            name: \"EchoWave Bluetooth Speaker\",\r\n            price: 55.5,\r\n            sales: \"10 pcs\",\r\n            revenue: 555.0,\r\n            stock: 20,\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Order: {\r\n      key: \"Order\",\r\n      title: \"Order\",\r\n      addLabel: \"Create Order\",\r\n      columns: [\r\n        { key: \"name\", label: \"Order\" },\r\n        { key: \"customer\", label: \"Customer\" },\r\n        { key: \"items\", label: \"Items\" },\r\n        { key: \"total\", label: \"Total\", numeric: true },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Total Orders\", value: \"1,204\", delta: \"+ 5%\" },\r\n        { label: \"Order Revenue\", value: \"$62,140\", delta: \"+ 11%\" },\r\n        { label: \"Pending Orders\", value: \"38\", delta: \"+ 2\" },\r\n        { label: \"Avg. Order Value\", value: \"$51.60\", delta: \"+ 3%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"o1\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"#ORD-1042\",\r\n            customer: \"Alice Mensah\",\r\n            items: \"3 items\",\r\n            total: 89.5,\r\n          },\r\n        },\r\n        {\r\n          id: \"o2\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"#ORD-1043\",\r\n            customer: \"Alex Chen\",\r\n            items: \"1 item\",\r\n            total: 24.0,\r\n          },\r\n        },\r\n        {\r\n          id: \"o3\",\r\n          status: \"Cancelled\",\r\n          cells: {\r\n            name: \"#ORD-1044\",\r\n            customer: \"Isabella Green\",\r\n            items: \"2 items\",\r\n            total: 61.2,\r\n          },\r\n        },\r\n        {\r\n          id: \"o4\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"#ORD-1045\",\r\n            customer: \"Victoria Stone\",\r\n            items: \"5 items\",\r\n            total: 154.9,\r\n          },\r\n        },\r\n        {\r\n          id: \"o5\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"#ORD-1046\",\r\n            customer: \"Marc Dupont\",\r\n            items: \"1 item\",\r\n            total: 18.75,\r\n          },\r\n        },\r\n        {\r\n          id: \"o6\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"#ORD-1047\",\r\n            customer: \"Sarah Kim\",\r\n            items: \"4 items\",\r\n            total: 132.4,\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Customer: {\r\n      key: \"Customer\",\r\n      title: \"Customer\",\r\n      addLabel: \"Add Customer\",\r\n      columns: [\r\n        { key: \"name\", label: \"Customer\" },\r\n        { key: \"email\", label: \"Email\" },\r\n        { key: \"orders\", label: \"Orders\", numeric: true },\r\n        { key: \"spent\", label: \"Total Spent\", numeric: true },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Total Customers\", value: \"5,830\", delta: \"+ 2%\" },\r\n        { label: \"New Customers\", value: \"142\", delta: \"+ 8%\" },\r\n        { label: \"Returning Rate\", value: \"64%\", delta: \"+ 3%\" },\r\n        { label: \"Avg. Lifetime Value\", value: \"$212\", delta: \"+ 5%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"c1\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Alice Mensah\",\r\n            email: \"alice@mail.com\",\r\n            orders: 12,\r\n            spent: 640.5,\r\n          },\r\n        },\r\n        {\r\n          id: \"c2\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Alex Chen\",\r\n            email: \"alex@mail.com\",\r\n            orders: 4,\r\n            spent: 120.0,\r\n          },\r\n        },\r\n        {\r\n          id: \"c3\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Isabella Green\",\r\n            email: \"isabella@mail.com\",\r\n            orders: 1,\r\n            spent: 42.0,\r\n          },\r\n        },\r\n        {\r\n          id: \"c4\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Victoria Stone\",\r\n            email: \"victoria@mail.com\",\r\n            orders: 21,\r\n            spent: 1290.75,\r\n          },\r\n        },\r\n        {\r\n          id: \"c5\",\r\n          status: \"Cancelled\",\r\n          cells: {\r\n            name: \"Marc Dupont\",\r\n            email: \"marc@mail.com\",\r\n            orders: 2,\r\n            spent: 58.4,\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Message: {\r\n      key: \"Message\",\r\n      title: \"Message\",\r\n      addLabel: \"New Message\",\r\n      columns: [\r\n        { key: \"name\", label: \"From\" },\r\n        { key: \"subject\", label: \"Subject\" },\r\n        { key: \"preview\", label: \"Preview\" },\r\n        { key: \"time\", label: \"Time\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Unread\", value: \"12\", delta: \"+ 3\" },\r\n        { label: \"Total Messages\", value: \"486\", delta: \"+ 21\" },\r\n        { label: \"Avg. Response Time\", value: \"2.4h\", delta: \"- 0.3h\" },\r\n        { label: \"Resolved Today\", value: \"18\", delta: \"+ 6\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"m1\",\r\n          status: \"Unread\",\r\n          cells: {\r\n            name: \"Alice Mensah\",\r\n            subject: \"Order delay\",\r\n            preview: \"Hi, my order #1042 is late...\",\r\n            time: \"2m\",\r\n          },\r\n        },\r\n        {\r\n          id: \"m2\",\r\n          status: \"Read\",\r\n          cells: {\r\n            name: \"Alex Chen\",\r\n            subject: \"Refund request\",\r\n            preview: \"Can I get a refund for...\",\r\n            time: \"15m\",\r\n          },\r\n        },\r\n        {\r\n          id: \"m3\",\r\n          status: \"Unread\",\r\n          cells: {\r\n            name: \"Isabella Green\",\r\n            subject: \"Product question\",\r\n            preview: \"Does this come in blue?\",\r\n            time: \"1h\",\r\n          },\r\n        },\r\n        {\r\n          id: \"m4\",\r\n          status: \"Read\",\r\n          cells: {\r\n            name: \"Victoria Stone\",\r\n            subject: \"Thank you!\",\r\n            preview: \"Just wanted to say thanks...\",\r\n            time: \"3h\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Email: {\r\n      key: \"Email\",\r\n      title: \"Email\",\r\n      addLabel: \"New Campaign\",\r\n      columns: [\r\n        { key: \"name\", label: \"Campaign\" },\r\n        { key: \"recipients\", label: \"Recipients\", numeric: true },\r\n        { key: \"openRate\", label: \"Open Rate\" },\r\n        { key: \"clickRate\", label: \"Click Rate\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Campaigns Sent\", value: \"34\", delta: \"+ 4\" },\r\n        { label: \"Avg. Open Rate\", value: \"42%\", delta: \"+ 3%\" },\r\n        { label: \"Avg. Click Rate\", value: \"8.6%\", delta: \"+ 1.1%\" },\r\n        { label: \"Unsubscribes\", value: \"6\", delta: \"- 2\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"e1\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"Spring Launch\",\r\n            recipients: 4200,\r\n            openRate: \"44%\",\r\n            clickRate: \"9.1%\",\r\n          },\r\n        },\r\n        {\r\n          id: \"e2\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Weekly Digest\",\r\n            recipients: 3800,\r\n            openRate: \"39%\",\r\n            clickRate: \"7.4%\",\r\n          },\r\n        },\r\n        {\r\n          id: \"e3\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Re-engagement\",\r\n            recipients: 1500,\r\n            openRate: \"-\",\r\n            clickRate: \"-\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Automation: {\r\n      key: \"Automation\",\r\n      title: \"Automation\",\r\n      addLabel: \"New Workflow\",\r\n      columns: [\r\n        { key: \"name\", label: \"Workflow\" },\r\n        { key: \"trigger\", label: \"Trigger\" },\r\n        { key: \"runs\", label: \"Runs\", numeric: true },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Active Workflows\", value: \"18\", delta: \"+ 2\" },\r\n        { label: \"Runs Today\", value: \"1,204\", delta: \"+ 9%\" },\r\n        { label: \"Success Rate\", value: \"98.4%\", delta: \"+ 0.4%\" },\r\n        { label: \"Time Saved\", value: \"36h\", delta: \"+ 5h\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"a1\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Welcome Email Series\",\r\n            trigger: \"New signup\",\r\n            runs: 312,\r\n          },\r\n        },\r\n        {\r\n          id: \"a2\",\r\n          status: \"Active\",\r\n          cells: { name: \"Abandoned Cart\", trigger: \"Cart idle 1h\", runs: 214 },\r\n        },\r\n        {\r\n          id: \"a3\",\r\n          status: \"Pending\",\r\n          cells: { name: \"Win-back Flow\", trigger: \"No login 30d\", runs: 0 },\r\n        },\r\n      ],\r\n    },\r\n    Analytics: {\r\n      key: \"Analytics\",\r\n      title: \"Analytics\",\r\n      addLabel: \"New Report\",\r\n      columns: [\r\n        { key: \"name\", label: \"Report\" },\r\n        { key: \"metric\", label: \"Metric\" },\r\n        { key: \"value\", label: \"Value\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Sessions\", value: \"24,890\", delta: \"+ 6%\" },\r\n        { label: \"Bounce Rate\", value: \"38%\", delta: \"- 2%\" },\r\n        { label: \"Avg. Session\", value: \"3m 42s\", delta: \"+ 12s\" },\r\n        { label: \"Page Views\", value: \"88,410\", delta: \"+ 9%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"an1\",\r\n          status: \"Completed\",\r\n          cells: {\r\n            name: \"Weekly Traffic\",\r\n            metric: \"Sessions\",\r\n            value: \"24,890\",\r\n          },\r\n        },\r\n        {\r\n          id: \"an2\",\r\n          status: \"Completed\",\r\n          cells: { name: \"Funnel Report\", metric: \"Conversion\", value: \"3.8%\" },\r\n        },\r\n        {\r\n          id: \"an3\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Cohort Retention\",\r\n            metric: \"D30 retention\",\r\n            value: \"22%\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Integration: {\r\n      key: \"Integration\",\r\n      title: \"Integration\",\r\n      addLabel: \"Add Integration\",\r\n      columns: [\r\n        { key: \"name\", label: \"Integration\" },\r\n        { key: \"category\", label: \"Category\" },\r\n        { key: \"syncedAt\", label: \"Last Synced\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Connected Apps\", value: \"9\", delta: \"+ 1\" },\r\n        { label: \"Synced Records\", value: \"142,300\", delta: \"+ 4%\" },\r\n        { label: \"Sync Errors\", value: \"2\", delta: \"- 1\" },\r\n        { label: \"Avg. Sync Time\", value: \"48s\", delta: \"- 4s\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"i1\",\r\n          status: \"Active\",\r\n          cells: { name: \"Stripe\", category: \"Payments\", syncedAt: \"2m ago\" },\r\n        },\r\n        {\r\n          id: \"i2\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Slack\",\r\n            category: \"Notifications\",\r\n            syncedAt: \"10m ago\",\r\n          },\r\n        },\r\n        {\r\n          id: \"i3\",\r\n          status: \"Pending\",\r\n          cells: { name: \"HubSpot\", category: \"CRM\", syncedAt: \"Never\" },\r\n        },\r\n      ],\r\n    },\r\n    Settings: {\r\n      key: \"Settings\",\r\n      title: \"Settings\",\r\n      addLabel: \"Add Setting\",\r\n      columns: [\r\n        { key: \"name\", label: \"Setting\" },\r\n        { key: \"value\", label: \"Value\" },\r\n        { key: \"category\", label: \"Category\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Plan\", value: \"Free Plan\", delta: \"+ upgrade\" },\r\n        { label: \"Team Members\", value: \"4\", delta: \"+ 1\" },\r\n        { label: \"Storage Used\", value: \"2.1GB / 5GB\", delta: \"+ 0.3GB\" },\r\n        { label: \"API Calls (mo)\", value: \"1,204\", delta: \"+ 9%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"s1\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Two-Factor Authentication\",\r\n            value: \"Enabled\",\r\n            category: \"Security\",\r\n          },\r\n        },\r\n        {\r\n          id: \"s2\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Email Notifications\",\r\n            value: \"Daily digest\",\r\n            category: \"Notifications\",\r\n          },\r\n        },\r\n        {\r\n          id: \"s3\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"Dark Mode\",\r\n            value: \"System default\",\r\n            category: \"Appearance\",\r\n          },\r\n        },\r\n        {\r\n          id: \"s4\",\r\n          status: \"Cancelled\",\r\n          cells: {\r\n            name: \"SSO (SAML)\",\r\n            value: \"Not configured\",\r\n            category: \"Security\",\r\n          },\r\n        },\r\n        {\r\n          id: \"s5\",\r\n          status: \"Active\",\r\n          cells: {\r\n            name: \"API Access\",\r\n            value: \"3 keys active\",\r\n            category: \"Developer\",\r\n          },\r\n        },\r\n        {\r\n          id: \"s6\",\r\n          status: \"Pending\",\r\n          cells: {\r\n            name: \"Billing Plan\",\r\n            value: \"Free Plan\",\r\n            category: \"Billing\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    \"Help center\": {\r\n      key: \"Help center\",\r\n      title: \"Help Center\",\r\n      addLabel: \"New Article\",\r\n      columns: [\r\n        { key: \"name\", label: \"Article\" },\r\n        { key: \"category\", label: \"Category\" },\r\n        { key: \"views\", label: \"Views\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Open Tickets\", value: \"7\", delta: \"- 2\" },\r\n        { label: \"Avg. Resolution Time\", value: \"3.2h\", delta: \"- 0.4h\" },\r\n        { label: \"Articles Published\", value: \"86\", delta: \"+ 4\" },\r\n        { label: \"Satisfaction Score\", value: \"96%\", delta: \"+ 1%\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"h1\",\r\n          status: \"Published\",\r\n          cells: {\r\n            name: \"Getting started with Orbit\",\r\n            category: \"Onboarding\",\r\n            views: \"4,210\",\r\n          },\r\n        },\r\n        {\r\n          id: \"h2\",\r\n          status: \"Published\",\r\n          cells: {\r\n            name: \"How to export your data\",\r\n            category: \"Data\",\r\n            views: \"1,880\",\r\n          },\r\n        },\r\n        {\r\n          id: \"h3\",\r\n          status: \"Draft\",\r\n          cells: { name: \"Setting up SSO\", category: \"Security\", views: \"—\" },\r\n        },\r\n        {\r\n          id: \"h4\",\r\n          status: \"Published\",\r\n          cells: {\r\n            name: \"Managing team roles\",\r\n            category: \"Team\",\r\n            views: \"962\",\r\n          },\r\n        },\r\n        {\r\n          id: \"h5\",\r\n          status: \"Draft\",\r\n          cells: {\r\n            name: \"Webhook reference\",\r\n            category: \"Developer\",\r\n            views: \"—\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n    Feedback: {\r\n      key: \"Feedback\",\r\n      title: \"Feedback\",\r\n      addLabel: \"New Feedback\",\r\n      columns: [\r\n        { key: \"name\", label: \"From\" },\r\n        { key: \"summary\", label: \"Summary\" },\r\n        { key: \"type\", label: \"Type\" },\r\n        { key: \"status\", label: \"Status\" },\r\n      ],\r\n      stats: [\r\n        { label: \"Total Feedback\", value: \"312\", delta: \"+ 18\" },\r\n        { label: \"New This Week\", value: \"24\", delta: \"+ 6\" },\r\n        { label: \"Avg. Rating\", value: \"4.6/5\", delta: \"+ 0.1\" },\r\n        { label: \"Resolved\", value: \"268\", delta: \"+ 12\" },\r\n      ],\r\n      rows: [\r\n        {\r\n          id: \"f1\",\r\n          status: \"New\",\r\n          cells: {\r\n            name: \"Alice Mensah\",\r\n            summary: \"Would love a dark mode toggle\",\r\n            type: \"Feature\",\r\n          },\r\n        },\r\n        {\r\n          id: \"f2\",\r\n          status: \"In Review\",\r\n          cells: {\r\n            name: \"Alex Chen\",\r\n            summary: \"Export button is slow on large tables\",\r\n            type: \"Bug\",\r\n          },\r\n        },\r\n        {\r\n          id: \"f3\",\r\n          status: \"Resolved\",\r\n          cells: {\r\n            name: \"Isabella Green\",\r\n            summary: \"Great support response time!\",\r\n            type: \"Praise\",\r\n          },\r\n        },\r\n        {\r\n          id: \"f4\",\r\n          status: \"New\",\r\n          cells: {\r\n            name: \"Marc Dupont\",\r\n            summary: \"Add bulk CSV import\",\r\n            type: \"Feature\",\r\n          },\r\n        },\r\n        {\r\n          id: \"f5\",\r\n          status: \"In Review\",\r\n          cells: {\r\n            name: \"Sarah Kim\",\r\n            summary: \"Pagination resets after edit\",\r\n            type: \"Bug\",\r\n          },\r\n        },\r\n      ],\r\n    },\r\n  };\r\n}\r\n\r\n// ─── Toasts ─────────────────────────────────────────────────────────────────\r\ntype Toast = { id: number; message: string };\r\n\r\nfunction useToasts() {\r\n  const [toasts, setToasts] = useState<Toast[]>([]);\r\n  const idRef = useRef(0);\r\n  const push = (message: string) => {\r\n    const id = ++idRef.current;\r\n    setToasts((t) => [...t, { id, message }]);\r\n    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 2400);\r\n  };\r\n  return { toasts, push };\r\n}\r\n\r\nfunction ToastStack({ toasts }: { toasts: Toast[] }) {\r\n  return (\r\n    <div className=\"pointer-events-none absolute bottom-5 right-5 z-30 flex flex-col gap-2\">\r\n      <AnimatePresence>\r\n        {toasts.map((t) => (\r\n          <motion.div\r\n            key={t.id}\r\n            initial={{ opacity: 0, y: 10, scale: 0.96 }}\r\n            animate={{ opacity: 1, y: 0, scale: 1 }}\r\n            exit={{ opacity: 0, y: 10, scale: 0.96 }}\r\n            transition={{ duration: 0.2 }}\r\n            className=\"flex items-center gap-2 rounded-lg bg-gray-900 px-3.5 py-2.5 text-sm font-medium text-white shadow-lg dark:border dark:border-neutral-700 dark:bg-neutral-800\"\r\n          >\r\n            <CheckCircle2 size={15} className=\"text-green-300\" />\r\n            {t.message}\r\n          </motion.div>\r\n        ))}\r\n      </AnimatePresence>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Small building blocks ──────────────────────────────────────────────────\r\nfunction Toggle({\r\n  checked,\r\n  onChange,\r\n}: {\r\n  checked: boolean;\r\n  onChange: (v: boolean) => void;\r\n}) {\r\n  return (\r\n    <button\r\n      role=\"switch\"\r\n      aria-checked={checked}\r\n      onClick={() => onChange(!checked)}\r\n      className={`relative flex h-5 w-9 shrink-0 items-center rounded-full transition-colors duration-200 cursor-pointer ${\r\n        checked\r\n          ? \"bg-gray-900 dark:bg-neutral-50\"\r\n          : \"bg-gray-200 dark:bg-neutral-700\"\r\n      }`}\r\n    >\r\n      <motion.span\r\n        layout\r\n        transition={{ type: \"spring\", stiffness: 500, damping: 30 }}\r\n        className=\"h-4 w-4 rounded-full bg-white shadow-sm dark:bg-neutral-900\"\r\n        style={{ marginLeft: checked ? \"18px\" : \"2px\" }}\r\n      />\r\n    </button>\r\n  );\r\n}\r\n\r\nfunction Checkbox({\r\n  checked,\r\n  onChange,\r\n}: {\r\n  checked: boolean;\r\n  onChange: () => void;\r\n}) {\r\n  return (\r\n    <button\r\n      onClick={onChange}\r\n      aria-pressed={checked}\r\n      className={`flex h-4 w-4 shrink-0 items-center justify-center rounded border transition-colors duration-150 cursor-pointer ${\r\n        checked\r\n          ? \"border-gray-900 bg-gray-900 dark:border-neutral-100 dark:bg-neutral-100\"\r\n          : \"border-gray-300 bg-white hover:border-gray-400 dark:border-neutral-600 dark:bg-neutral-900 dark:hover:border-neutral-500\"\r\n      }`}\r\n    >\r\n      {checked && (\r\n        <Check\r\n          size={11}\r\n          className=\"text-white dark:text-neutral-900\"\r\n          strokeWidth={3}\r\n        />\r\n      )}\r\n    </button>\r\n  );\r\n}\r\n\r\nfunction StatusBadge({ status }: { status: string }) {\r\n  const meta = STATUS_META[status] ?? { tone: \"subtle\" as Tone };\r\n  return (\r\n    <span\r\n      className={`inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-medium ${TONE_CLASSES[meta.tone]}`}\r\n    >\r\n      {meta.tone === \"solid\" && <Check size={11} strokeWidth={3} />}\r\n      {status}\r\n    </span>\r\n  );\r\n}\r\n\r\nfunction RatingStars({ rating }: { rating: number }) {\r\n  return (\r\n    <div className=\"flex items-center gap-1\">\r\n      <Star\r\n        size={13}\r\n        className=\"fill-amber-400 text-amber-400 dark:fill-amber-300 dark:text-amber-300\"\r\n      />\r\n      <span className=\"text-sm font-medium text-gray-700 dark:text-neutral-300\">\r\n        {rating.toFixed(1)}\r\n      </span>\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction Dropdown({\r\n  label,\r\n  icon: Icon,\r\n  options,\r\n  value,\r\n  onChange,\r\n}: {\r\n  label: string;\r\n  icon: React.ComponentType<{ size?: number; className?: string }>;\r\n  options: string[];\r\n  value: string;\r\n  onChange: (v: string) => void;\r\n}) {\r\n  const [open, setOpen] = useState(false);\r\n  const ref = useRef<HTMLDivElement>(null);\r\n\r\n  useEffect(() => {\r\n    const onDoc = (e: MouseEvent) => {\r\n      if (ref.current && !ref.current.contains(e.target as Node))\r\n        setOpen(false);\r\n    };\r\n    document.addEventListener(\"mousedown\", onDoc);\r\n    return () => document.removeEventListener(\"mousedown\", onDoc);\r\n  }, []);\r\n\r\n  return (\r\n    <div className=\"relative\" ref={ref}>\r\n      <button\r\n        onClick={() => setOpen((o) => !o)}\r\n        className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 dark:text-neutral-300 dark:hover:bg-neutral-800 cursor-pointer ${\r\n          value !== \"All\" && value !== \"Default\"\r\n            ? \"bg-gray-100 text-gray-900 dark:bg-neutral-800 dark:text-neutral-50\"\r\n            : \"\"\r\n        }`}\r\n      >\r\n        <Icon size={14} className=\"text-gray-400 dark:text-neutral-500\" />\r\n        {value === \"All\" || value === \"Default\" ? label : value}\r\n        <ChevronDown\r\n          size={14}\r\n          className=\"text-gray-400 dark:text-neutral-500\"\r\n        />\r\n      </button>\r\n      <AnimatePresence>\r\n        {open && (\r\n          <motion.div\r\n            initial={{ opacity: 0, y: -6, scale: 0.98 }}\r\n            animate={{ opacity: 1, y: 0, scale: 1 }}\r\n            exit={{ opacity: 0, y: -6, scale: 0.98 }}\r\n            transition={{ duration: 0.15 }}\r\n            className=\"absolute left-0 top-full z-20 mt-1.5 w-44 overflow-hidden rounded-lg border border-gray-100 bg-white p-1 shadow-lg dark:border-neutral-700 dark:bg-neutral-900\"\r\n          >\r\n            {options.map((o) => (\r\n              <button\r\n                key={o}\r\n                onClick={() => {\r\n                  onChange(o);\r\n                  setOpen(false);\r\n                }}\r\n                className={`flex w-full items-center justify-between rounded-md px-2.5 py-1.5 text-left text-sm hover:bg-gray-50 dark:hover:bg-neutral-800 ${\r\n                  value === o\r\n                    ? \"font-medium text-gray-900 dark:text-neutral-50\"\r\n                    : \"text-gray-500 dark:text-neutral-400\"\r\n                }`}\r\n              >\r\n                {o}\r\n                {value === o && <Check size={13} />}\r\n              </button>\r\n            ))}\r\n          </motion.div>\r\n        )}\r\n      </AnimatePresence>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Sidebar ────────────────────────────────────────────────────────────────\r\nfunction NavLink({\r\n  item,\r\n  active,\r\n  onClick,\r\n}: {\r\n  item: NavItem;\r\n  active: boolean;\r\n  onClick: () => void;\r\n}) {\r\n  const Icon = item.icon;\r\n  return (\r\n    <motion.button\r\n      variants={fadeUp}\r\n      onClick={onClick}\r\n      className={`relative flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition-colors duration-150 cursor-pointer ${\r\n        active\r\n          ? \"bg-gray-100 text-gray-900 dark:bg-neutral-800 dark:text-neutral-50\"\r\n          : \"text-gray-500 hover:bg-gray-50 hover:text-gray-900 dark:text-neutral-400 dark:hover:bg-neutral-800/60 dark:hover:text-neutral-50\"\r\n      }`}\r\n    >\r\n      {active && (\r\n        <motion.span\r\n          layoutId=\"nav-active-bar\"\r\n          className=\"absolute left-0 top-1/2 h-4 w-0.5 -translate-y-1/2 rounded-full bg-gray-900 dark:bg-neutral-50\"\r\n        />\r\n      )}\r\n      <Icon\r\n        size={16}\r\n        className={\r\n          active\r\n            ? \"text-gray-900 dark:text-neutral-50\"\r\n            : \"text-gray-400 dark:text-neutral-500\"\r\n        }\r\n      />\r\n      {item.label}\r\n    </motion.button>\r\n  );\r\n}\r\n\r\nfunction SectionLabel({ children }: { children: React.ReactNode }) {\r\n  return (\r\n    <p className=\"px-3 pb-1.5 pt-4 text-[11px] font-semibold uppercase tracking-wider text-gray-400 dark:text-neutral-500\">\r\n      {children}\r\n    </p>\r\n  );\r\n}\r\n\r\nfunction Sidebar({\r\n  active,\r\n  setActive,\r\n  search,\r\n  setSearch,\r\n  onUpgrade,\r\n  onLearnMore,\r\n}: {\r\n  active: string;\r\n  setActive: (v: string) => void;\r\n  search: string;\r\n  setSearch: (v: string) => void;\r\n  onUpgrade: () => void;\r\n  onLearnMore: () => void;\r\n}) {\r\n  return (\r\n    <motion.aside\r\n      initial={{ opacity: 0, x: -12 }}\r\n      animate={{ opacity: 1, x: 0 }}\r\n      transition={{ duration: 0.35, ease: \"easeOut\" }}\r\n      className=\"flex h-full w-64 shrink-0 flex-col border-r border-gray-100 bg-white dark:border-neutral-800 dark:bg-black\"\r\n    >\r\n      <div className=\"flex items-center justify-between p-4\">\r\n        <button className=\"flex min-w-0 items-center gap-3 rounded-lg px-1.5 py-1 transition-colors hover:bg-gray-50 dark:hover:bg-neutral-800\">\r\n          <div className=\"relative h-9 w-9 shrink-0 overflow-hidden rounded-lg\">\r\n            <Image\r\n              src=\"/logoR.png\" // Remplace par le chemin de ton logo\r\n              alt=\"BagUI\"\r\n              fill\r\n              className=\"object-contain\"\r\n            />\r\n          </div>\r\n\r\n          <div className=\"min-w-0 text-left\">\r\n            <p className=\"truncate text-sm font-semibold text-gray-900 dark:text-neutral-50\">\r\n              Bag\\UI\r\n            </p>\r\n            <p className=\"truncate text-xs text-gray-500 dark:text-neutral-400\">\r\n              Open Source UI Blocks\r\n            </p>\r\n          </div>\r\n        </button>\r\n      </div>\r\n\r\n      <div className=\"px-4 pb-2\">\r\n        <div className=\"relative\">\r\n          <Search\r\n            size={14}\r\n            className=\"absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 dark:text-neutral-500\"\r\n          />\r\n          <input\r\n            value={search}\r\n            onChange={(e) => setSearch(e.target.value)}\r\n            placeholder=\"Search\"\r\n            className=\"w-full rounded-lg bg-gray-100 py-2 pl-8 pr-9 text-sm text-gray-700 outline-none placeholder:text-gray-400 dark:bg-neutral-800/60 dark:text-neutral-200 dark:placeholder:text-neutral-500\"\r\n          />\r\n          {search ? (\r\n            <button\r\n              onClick={() => setSearch(\"\")}\r\n              className=\"absolute right-2.5 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:text-neutral-500 dark:hover:text-neutral-300\"\r\n            >\r\n              <X size={13} />\r\n            </button>\r\n          ) : (\r\n            <span className=\"absolute right-2.5 top-1/2 -translate-y-1/2 rounded border border-gray-200 bg-white px-1.5 py-0.5 text-[10px] font-medium text-gray-400 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-500\">\r\n              ⌘K\r\n            </span>\r\n          )}\r\n        </div>\r\n      </div>\r\n\r\n      <motion.nav\r\n        variants={stagger}\r\n        initial=\"hidden\"\r\n        animate=\"visible\"\r\n        className=\"flex-1 overflow-y-auto px-3 pb-4\"\r\n      >\r\n        <SectionLabel>Main Menu</SectionLabel>\r\n        <div className=\"flex flex-col gap-0.5\">\r\n          {MAIN_MENU.map((item) => (\r\n            <NavLink\r\n              key={item.label}\r\n              item={item}\r\n              active={active === item.label}\r\n              onClick={() => setActive(item.label)}\r\n            />\r\n          ))}\r\n        </div>\r\n\r\n        <SectionLabel>Tools</SectionLabel>\r\n        <div className=\"flex flex-col gap-0.5\">\r\n          {TOOLS_MENU.map((item) => (\r\n            <NavLink\r\n              key={item.label}\r\n              item={item}\r\n              active={active === item.label}\r\n              onClick={() => setActive(item.label)}\r\n            />\r\n          ))}\r\n        </div>\r\n\r\n        <SectionLabel>Workspace</SectionLabel>\r\n        <div className=\"flex flex-col gap-0.5\">\r\n          {WORKSPACES.map((w) => (\r\n            <motion.button\r\n              key={w.label}\r\n              variants={fadeUp}\r\n              className=\"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50 hover:text-gray-900 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-50\"\r\n            >\r\n              <span className={`h-2 w-2 rounded-full ${w.color}`} />\r\n              <span className=\"flex-1 text-left\">{w.label}</span>\r\n              <span className=\"rounded-full bg-gray-100 px-1.5 py-0.5 text-[11px] font-semibold text-gray-500 dark:bg-neutral-800 dark:text-neutral-400\">\r\n                {w.count}\r\n              </span>\r\n            </motion.button>\r\n          ))}\r\n        </div>\r\n      </motion.nav>\r\n\r\n      <div className=\"flex flex-col gap-0.5 border-t border-gray-100 px-3 py-4 dark:border-neutral-800\">\r\n        {BOTTOM_MENU.map((item) => (\r\n          <NavLink\r\n            key={item.label}\r\n            item={item}\r\n            active={active === item.label}\r\n            onClick={() => setActive(item.label)}\r\n          />\r\n        ))}\r\n\r\n        <div className=\"mt-2 rounded-2xl border border-gray-200 bg-white p-3.5 dark:border-neutral-800 dark:bg-neutral-900\">\r\n          <p className=\"mb-1 flex items-center gap-1.5 text-sm font-semibold text-gray-900 dark:text-neutral-50\">\r\n            Upgrade Pro\r\n            <Sparkles\r\n              size={13}\r\n              className=\"text-gray-400 dark:text-neutral-500\"\r\n            />\r\n          </p>\r\n          <p className=\"mb-3 text-xs leading-snug text-gray-500 dark:text-neutral-400\">\r\n            Higher productivity with better organization\r\n          </p>\r\n          <div className=\"flex items-center gap-2\">\r\n            <motion.button\r\n              whileHover={{ scale: 1.02 }}\r\n              whileTap={{ scale: 0.98 }}\r\n              onClick={onUpgrade}\r\n              className=\"flex flex-1 items-center justify-center gap-1.5 rounded-full bg-gray-900 py-1.5 text-xs font-semibold text-white hover:bg-gray-800 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-200 cursor-pointer\"\r\n            >\r\n              <Crown size={12} />\r\n              Upgrade\r\n            </motion.button>\r\n            <button\r\n              onClick={onLearnMore}\r\n              className=\"flex-1 rounded-full border border-gray-200 py-1.5 text-xs font-medium text-gray-600 hover:bg-gray-50 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800 cursor-pointer\"\r\n            >\r\n              Learn more\r\n            </button>\r\n          </div>\r\n        </div>\r\n      </div>\r\n    </motion.aside>\r\n  );\r\n}\r\n\r\n// ─── Top bar ────────────────────────────────────────────────────────────────\r\nfunction AvatarStack() {\r\n  return (\r\n    <div className=\"flex items-center -space-x-2\">\r\n      {AVATARS.map((a) => (\r\n        <img\r\n          key={a.id}\r\n          src={a.src}\r\n          alt=\"Team member avatar\"\r\n          className=\"h-8 w-8 rounded-full border-2 border-white bg-gray-100 object-cover dark:border-black dark:bg-neutral-800\"\r\n        />\r\n      ))}\r\n    </div>\r\n  );\r\n}\r\n\r\nfunction ThemeToggle({\r\n  darkMode,\r\n  setDarkMode,\r\n}: {\r\n  darkMode: boolean;\r\n  setDarkMode: (v: boolean) => void;\r\n}) {\r\n  return (\r\n    <motion.div\r\n      layout\r\n      className=\"flex items-center gap-1.5 rounded-lg px-2 py-1.5\"\r\n      transition={{\r\n        layout: { duration: 0.25 },\r\n      }}\r\n    >\r\n      <motion.button\r\n        type=\"button\"\r\n        onClick={() => setDarkMode(false)}\r\n        aria-label=\"Switch to light mode\"\r\n        whileTap={{ scale: 0.85 }}\r\n        whileHover={{ scale: 1.15 }}\r\n        className={`flex h-4 w-4 cursor-pointer items-center justify-center ${\r\n          darkMode ? \"text-gray-500 hover:text-gray-300\" : \"text-gray-900\"\r\n        }`}\r\n      >\r\n        <AnimatePresence mode=\"wait\">\r\n          {!darkMode && (\r\n            <motion.div\r\n              initial={{ opacity: 0, rotate: -90, scale: 0.5 }}\r\n              animate={{ opacity: 1, rotate: 0, scale: 1 }}\r\n              exit={{ opacity: 0, rotate: 90, scale: 0.5 }}\r\n              transition={{ duration: 0.2 }}\r\n            >\r\n              <Sun size={14} />\r\n            </motion.div>\r\n          )}\r\n        </AnimatePresence>\r\n      </motion.button>\r\n\r\n      <Toggle checked={darkMode} onChange={setDarkMode} />\r\n\r\n      <motion.button\r\n        type=\"button\"\r\n        onClick={() => setDarkMode(true)}\r\n        aria-label=\"Switch to dark mode\"\r\n        whileTap={{ scale: 0.85 }}\r\n        whileHover={{ scale: 1.15 }}\r\n        className={`flex h-4 w-4 cursor-pointer items-center justify-center ${\r\n          darkMode ? \"text-gray-50\" : \"text-gray-300 hover:text-gray-500\"\r\n        }`}\r\n      >\r\n        <AnimatePresence mode=\"wait\">\r\n          {darkMode && (\r\n            <motion.div\r\n              initial={{ opacity: 0, rotate: 90, scale: 0.5 }}\r\n              animate={{ opacity: 1, rotate: 0, scale: 1 }}\r\n              exit={{ opacity: 0, rotate: -90, scale: 0.5 }}\r\n              transition={{ duration: 0.2 }}\r\n            >\r\n              <Moon size={14} />\r\n            </motion.div>\r\n          )}\r\n        </AnimatePresence>\r\n      </motion.button>\r\n    </motion.div>\r\n  );\r\n}\r\n\r\nfunction TopBar({\r\n  title,\r\n  onShare,\r\n  onCustomizeWidget,\r\n  notifCount,\r\n  onBell,\r\n  darkMode,\r\n  setDarkMode,\r\n}: {\r\n  title: string;\r\n  onShare: () => void;\r\n  onCustomizeWidget: () => void;\r\n  notifCount: number;\r\n  onBell: () => void;\r\n  darkMode: boolean;\r\n  setDarkMode: (v: boolean) => void;\r\n}) {\r\n  return (\r\n    <div className=\"flex items-center justify-between border-b border-gray-100 px-6 py-4 dark:border-neutral-800\">\r\n      <h1 className=\"text-xl font-semibold text-gray-900 dark:text-neutral-50\">\r\n        {title}\r\n      </h1>\r\n      <div className=\"flex items-center gap-3\">\r\n        <ThemeToggle darkMode={darkMode} setDarkMode={setDarkMode} />\r\n        <button\r\n          onClick={onShare}\r\n          className=\"flex h-9 w-9 items-center justify-center rounded-lg text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:text-neutral-500 dark:hover:bg-neutral-800 dark:hover:text-neutral-300 cursor-pointer\"\r\n        >\r\n          <Share2 size={16} />\r\n        </button>\r\n        <button\r\n          onClick={onBell}\r\n          className=\"relative flex h-9 w-9 items-center justify-center rounded-lg text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:text-neutral-500 dark:hover:bg-neutral-800 dark:hover:text-neutral-300 cursor-pointer\"\r\n        >\r\n          <Bell size={20} />\r\n          {notifCount > 0 && (\r\n            <span className=\"absolute right-1.5 top-1.5 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-red-500 text-[9px] font-semibold text-white dark:bg-red-500 dark:text-white\">\r\n              {notifCount}\r\n            </span>\r\n          )}\r\n        </button>\r\n        <AvatarStack />\r\n        <span className=\"h-6 w-px bg-gray-200 dark:bg-neutral-700\" />\r\n        <motion.button\r\n          whileHover={{ scale: 1.02 }}\r\n          whileTap={{ scale: 0.98 }}\r\n          onClick={onCustomizeWidget}\r\n          className=\"flex items-center gap-1.5 rounded-lg bg-gray-900 px-3.5 py-2 text-sm font-medium text-white hover:bg-gray-800 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-200 cursor-pointer\"\r\n        >\r\n          <SlidersHorizontal size={14} />\r\n          Customize Widget\r\n        </motion.button>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Toolbar ────────────────────────────────────────────────────────────────\r\nfunction Toolbar({\r\n  statusFilter,\r\n  setStatusFilter,\r\n  sortOption,\r\n  setSortOption,\r\n  statusOptions,\r\n  showStats,\r\n  setShowStats,\r\n  onCustomize,\r\n  onExport,\r\n  onAdd,\r\n  addLabel,\r\n}: {\r\n  statusFilter: string;\r\n  setStatusFilter: (v: string) => void;\r\n  sortOption: string;\r\n  setSortOption: (v: string) => void;\r\n  statusOptions: string[];\r\n  showStats: boolean;\r\n  setShowStats: (v: boolean) => void;\r\n  onCustomize: () => void;\r\n  onExport: () => void;\r\n  onAdd: () => void;\r\n  addLabel: string;\r\n}) {\r\n  return (\r\n    <div className=\"flex flex-wrap items-center justify-between gap-3 px-6 pt-5\">\r\n      <div className=\"flex flex-wrap items-center gap-2\">\r\n        <button className=\"flex items-center gap-1.5 rounded-lg border border-gray-200 px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800\">\r\n          <LayoutGrid\r\n            size={14}\r\n            className=\"text-gray-400 dark:text-neutral-500\"\r\n          />\r\n          Table View\r\n          <ChevronDown\r\n            size={14}\r\n            className=\"text-gray-400 dark:text-neutral-500\"\r\n          />\r\n        </button>\r\n\r\n        <Dropdown\r\n          label=\"Filter\"\r\n          icon={Filter}\r\n          options={statusOptions}\r\n          value={statusFilter}\r\n          onChange={setStatusFilter}\r\n        />\r\n        <Dropdown\r\n          label=\"Sort\"\r\n          icon={ArrowUpDown}\r\n          options={[\"Default\", \"Name (A-Z)\", \"Name (Z-A)\"]}\r\n          value={sortOption}\r\n          onChange={setSortOption}\r\n        />\r\n\r\n        <div className=\"ml-1 flex items-center gap-2 rounded-lg px-3 py-1.5\">\r\n          <span className=\"text-sm font-medium text-gray-600 dark:text-neutral-300\">\r\n            Show Statistics\r\n          </span>\r\n          <Toggle checked={showStats} onChange={setShowStats} />\r\n        </div>\r\n      </div>\r\n\r\n      <div className=\"flex items-center gap-2\">\r\n        <button\r\n          onClick={onCustomize}\r\n          className=\"flex items-center gap-1.5 rounded-lg border border-gray-200 px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800 cursor-pointer\"\r\n        >\r\n          <SlidersHorizontal\r\n            size={14}\r\n            className=\"text-gray-400 dark:text-neutral-500\"\r\n          />\r\n          Customize\r\n        </button>\r\n        <button\r\n          onClick={onExport}\r\n          className=\"flex items-center gap-1.5 rounded-lg border border-gray-200 px-3 py-1.5 text-sm font-medium text-gray-600 hover:bg-gray-50 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800 cursor-pointer\"\r\n        >\r\n          <Download size={14} className=\"text-gray-400 dark:text-neutral-500\" />\r\n          Export\r\n        </button>\r\n        <motion.button\r\n          whileHover={{ scale: 1.02 }}\r\n          whileTap={{ scale: 0.98 }}\r\n          onClick={onAdd}\r\n          className=\"flex items-center gap-1.5 rounded-lg bg-gray-900 px-3.5 py-1.5 text-sm font-medium text-white hover:bg-gray-800 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-200 cursor-pointer\"\r\n        >\r\n          <Plus size={14} />\r\n          {addLabel}\r\n        </motion.button>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Stats ──────────────────────────────────────────────────────────────────\r\nfunction StatsRow({\r\n  stats,\r\n  visible,\r\n  focused,\r\n  setFocused,\r\n}: {\r\n  stats: { label: string; value: string; delta: string }[];\r\n  visible: boolean[];\r\n  focused: string | null;\r\n  setFocused: (v: string | null) => void;\r\n}) {\r\n  return (\r\n    <motion.div\r\n      variants={stagger}\r\n      initial=\"hidden\"\r\n      animate=\"visible\"\r\n      className=\"grid grid-cols-1 gap-4 px-6 pt-5 sm:grid-cols-2 lg:grid-cols-4\"\r\n    >\r\n      {stats.map((s, i) =>\r\n        visible[i] ? (\r\n          <motion.button\r\n            key={s.label}\r\n            variants={fadeUp}\r\n            onClick={() => setFocused(focused === s.label ? null : s.label)}\r\n            className={`rounded-xl border p-4 text-left transition-colors duration-150 ${\r\n              focused === s.label\r\n                ? \"border-gray-900 bg-gray-50 dark:border-neutral-100 dark:bg-neutral-800\"\r\n                : \"border-gray-100 bg-white hover:border-gray-200 dark:border-neutral-800 dark:bg-neutral-900 dark:hover:border-neutral-700\"\r\n            }`}\r\n          >\r\n            <div className=\"flex items-center gap-1.5 text-xs font-medium text-gray-400 dark:text-neutral-500\">\r\n              {s.label}\r\n              <Info size={12} />\r\n            </div>\r\n            <div className=\"mt-2 text-2xl font-semibold text-gray-900 dark:text-neutral-50\">\r\n              {s.value}\r\n            </div>\r\n            <div className=\"mt-1.5 flex items-center gap-1 text-xs text-gray-400 dark:text-neutral-500\">\r\n              vs last month\r\n              <span\r\n                className={`inline-flex items-center gap-0.5 rounded-full border px-1.5 py-0.5 font-medium ${\r\n                  s.delta.trim().startsWith(\"-\")\r\n                    ? \"border-red-200 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/40 dark:text-red-400\"\r\n                    : \"border-green-200 bg-green-50 text-green-700 dark:border-green-900/50 dark:bg-green-950/40 dark:text-green-400\"\r\n                }`}\r\n              >\r\n                {s.delta.trim().startsWith(\"-\") ? (\r\n                  <ArrowDownRight size={11} />\r\n                ) : (\r\n                  <ArrowUpRight size={11} />\r\n                )}\r\n                {s.delta}\r\n              </span>\r\n            </div>\r\n          </motion.button>\r\n        ) : null,\r\n      )}\r\n    </motion.div>\r\n  );\r\n}\r\n\r\nfunction CustomizePopover({\r\n  columns,\r\n  visibleCols,\r\n  toggleCol,\r\n  onClose,\r\n}: {\r\n  columns: string[];\r\n  visibleCols: Record<string, boolean>;\r\n  toggleCol: (c: string) => void;\r\n  onClose: () => void;\r\n}) {\r\n  const ref = useRef<HTMLDivElement>(null);\r\n  useEffect(() => {\r\n    const onDoc = (e: MouseEvent) => {\r\n      if (ref.current && !ref.current.contains(e.target as Node)) onClose();\r\n    };\r\n    document.addEventListener(\"mousedown\", onDoc);\r\n    return () => document.removeEventListener(\"mousedown\", onDoc);\r\n  }, [onClose]);\r\n\r\n  return (\r\n    <motion.div\r\n      ref={ref}\r\n      initial={{ opacity: 0, y: -6, scale: 0.98 }}\r\n      animate={{ opacity: 1, y: 0, scale: 1 }}\r\n      exit={{ opacity: 0, y: -6, scale: 0.98 }}\r\n      className=\"absolute right-6 top-16 z-20 w-56 rounded-lg border border-gray-100 bg-white p-2 shadow-lg dark:border-neutral-700 dark:bg-neutral-900\"\r\n    >\r\n      <p className=\"px-2 pb-1.5 pt-1 text-[11px] font-semibold uppercase tracking-wider text-gray-400 dark:text-neutral-500\">\r\n        Visible columns\r\n      </p>\r\n      {columns.map((c) => {\r\n        const checked = visibleCols[c] !== false;\r\n        return (\r\n          <div\r\n            key={c}\r\n            role=\"button\"\r\n            tabIndex={0}\r\n            onClick={() => toggleCol(c)}\r\n            onKeyDown={(e) =>\r\n              (e.key === \"Enter\" || e.key === \" \") && toggleCol(c)\r\n            }\r\n            className=\"flex w-full cursor-pointer items-center justify-between rounded-md px-2 py-1.5 text-left text-sm text-gray-600 hover:bg-gray-50 dark:text-neutral-300 dark:hover:bg-neutral-800\"\r\n          >\r\n            {c}\r\n            <span\r\n              aria-hidden\r\n              className={`flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\r\n                checked\r\n                  ? \"border-gray-900 bg-gray-900 dark:border-neutral-100 dark:bg-neutral-100\"\r\n                  : \"border-gray-300 bg-white dark:border-neutral-600 dark:bg-neutral-900\"\r\n              }`}\r\n            >\r\n              {checked && (\r\n                <Check\r\n                  size={11}\r\n                  className=\"text-white dark:text-neutral-900\"\r\n                  strokeWidth={3}\r\n                />\r\n              )}\r\n            </span>\r\n          </div>\r\n        );\r\n      })}\r\n    </motion.div>\r\n  );\r\n}\r\n\r\n// ─── Table ──────────────────────────────────────────────────────────────────\r\nfunction ProductTable({\r\n  columns,\r\n  rows,\r\n  visibleCols,\r\n  selected,\r\n  toggleRow,\r\n  toggleAll,\r\n  sortKey,\r\n  sortDir,\r\n  onSortColumn,\r\n  editingId,\r\n  editValue,\r\n  setEditValue,\r\n  onCommitEdit,\r\n}: {\r\n  columns: ColumnDef[];\r\n  rows: Row[];\r\n  visibleCols: Record<string, boolean>;\r\n  selected: Set<string>;\r\n  toggleRow: (id: string) => void;\r\n  toggleAll: () => void;\r\n  sortKey: string | null;\r\n  sortDir: \"asc\" | \"desc\";\r\n  onSortColumn: (key: string) => void;\r\n  editingId: string | null;\r\n  editValue: string;\r\n  setEditValue: (v: string) => void;\r\n  onCommitEdit: () => void;\r\n}) {\r\n  const cols = columns.filter((c) => visibleCols[c.label] !== false);\r\n  const allChecked = rows.length > 0 && selected.size === rows.length;\r\n\r\n  return (\r\n    <div className=\"overflow-hidden overflow-x-auto rounded-xl border border-gray-100 dark:border-neutral-800\">\r\n      <table className=\"w-full min-w-[820px] border-collapse text-left\">\r\n        <thead>\r\n          <tr className=\"border-b border-gray-100 bg-gray-50/60 dark:border-neutral-800 dark:bg-neutral-900/40\">\r\n            <th className=\"w-10 py-3 pl-4\">\r\n              <Checkbox checked={allChecked} onChange={toggleAll} />\r\n            </th>\r\n            {cols.map((c) => (\r\n              <th\r\n                key={c.key}\r\n                className=\"py-3 pr-4 text-xs font-medium uppercase tracking-wide text-gray-400 dark:text-neutral-500\"\r\n              >\r\n                <button\r\n                  onClick={() => onSortColumn(c.key)}\r\n                  className=\"flex items-center gap-1 hover:text-gray-600 dark:hover:text-neutral-300\"\r\n                >\r\n                  {c.label}\r\n                  {sortKey === c.key ? (\r\n                    sortDir === \"asc\" ? (\r\n                      <ChevronUp size={12} />\r\n                    ) : (\r\n                      <ChevronDown size={12} />\r\n                    )\r\n                  ) : (\r\n                    <ChevronsUpDown size={11} className=\"opacity-40\" />\r\n                  )}\r\n                </button>\r\n              </th>\r\n            ))}\r\n            <th className=\"w-10 py-3 pr-4 text-right\">\r\n              <button className=\"flex h-6 w-6 items-center justify-center rounded-md text-gray-400 hover:bg-gray-100 dark:text-neutral-500 dark:hover:bg-neutral-800\">\r\n                <Plus size={13} />\r\n              </button>\r\n            </th>\r\n          </tr>\r\n        </thead>\r\n        <motion.tbody variants={stagger} initial=\"hidden\" animate=\"visible\">\r\n          {rows.length === 0 && (\r\n            <tr>\r\n              <td\r\n                colSpan={cols.length + 2}\r\n                className=\"px-4 py-10 text-center text-sm text-gray-400 dark:text-neutral-500\"\r\n              >\r\n                No results match your search/filter.\r\n              </td>\r\n            </tr>\r\n          )}\r\n          {rows.map((r) => {\r\n            const isSelected = selected.has(r.id);\r\n            const isEditing = editingId === r.id;\r\n            return (\r\n              <motion.tr\r\n                key={r.id}\r\n                layout\r\n                variants={fadeUp}\r\n                className={`border-b border-gray-50 text-sm transition-colors duration-150 dark:border-neutral-800/60 cursor-pointer ${\r\n                  isSelected\r\n                    ? \"border-l-4 border-l-gray-900 bg-gray-50 dark:border-l-neutral-50 dark:bg-neutral-800/50\"\r\n                    : \"hover:bg-gray-50/60 dark:hover:bg-neutral-800/40\"\r\n                }`}\r\n              >\r\n                <td className=\"py-3 pl-4\">\r\n                  <Checkbox\r\n                    checked={isSelected}\r\n                    onChange={() => toggleRow(r.id)}\r\n                  />\r\n                </td>\r\n                {cols.map((c, i) => {\r\n                  if (c.key === \"status\") {\r\n                    return (\r\n                      <td key={c.key} className=\"py-3 pr-4\">\r\n                        <StatusBadge status={r.status} />\r\n                      </td>\r\n                    );\r\n                  }\r\n                  if (c.key === \"rating\" && r.rating !== undefined) {\r\n                    return (\r\n                      <td key={c.key} className=\"py-3 pr-4\">\r\n                        <RatingStars rating={r.rating} />\r\n                      </td>\r\n                    );\r\n                  }\r\n                  const raw = r.cells[c.key];\r\n                  const isMoney = [\r\n                    \"price\",\r\n                    \"revenue\",\r\n                    \"spent\",\r\n                    \"total\",\r\n                  ].includes(c.key);\r\n                  const display =\r\n                    typeof raw === \"number\" && isMoney\r\n                      ? `$${raw.toFixed(2)}`\r\n                      : (raw ?? \"—\");\r\n                  if (i === 0 && isEditing) {\r\n                    return (\r\n                      <td key={c.key} className=\"py-2 pr-4\">\r\n                        <input\r\n                          autoFocus\r\n                          value={editValue}\r\n                          onChange={(e) => setEditValue(e.target.value)}\r\n                          onKeyDown={(e) => e.key === \"Enter\" && onCommitEdit()}\r\n                          onBlur={onCommitEdit}\r\n                          className=\"w-full rounded-md border border-gray-300 bg-white px-2 py-1 text-sm text-gray-900 outline-none focus:border-gray-500 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100 dark:focus:border-neutral-400\"\r\n                        />\r\n                      </td>\r\n                    );\r\n                  }\r\n                  return (\r\n                    <td\r\n                      key={c.key}\r\n                      className={`py-3 pr-4 ${\r\n                        i === 0\r\n                          ? \"font-medium text-gray-800 dark:text-neutral-200\"\r\n                          : \"text-gray-500 dark:text-neutral-400\"\r\n                      }`}\r\n                    >\r\n                      {display}\r\n                    </td>\r\n                  );\r\n                })}\r\n                <td />\r\n              </motion.tr>\r\n            );\r\n          })}\r\n        </motion.tbody>\r\n      </table>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Bulk action bar ────────────────────────────────────────────────────────\r\nfunction BulkActionBar({\r\n  count,\r\n  onClear,\r\n  onApplyCode,\r\n  onEditInfo,\r\n  onDelete,\r\n}: {\r\n  count: number;\r\n  onClear: () => void;\r\n  onApplyCode: () => void;\r\n  onEditInfo: () => void;\r\n  onDelete: () => void;\r\n}) {\r\n  return (\r\n    <AnimatePresence>\r\n      {count > 0 && (\r\n        <motion.div\r\n          initial={{ opacity: 0, y: 16 }}\r\n          animate={{ opacity: 1, y: 0 }}\r\n          exit={{ opacity: 0, y: 16 }}\r\n          transition={{ duration: 0.2, ease: \"easeOut\" }}\r\n          className=\"sticky bottom-4 z-10 mx-auto flex w-fit items-center gap-3 rounded-full border border-gray-200 bg-white px-4 py-2 shadow-lg dark:border-neutral-700 dark:bg-neutral-900\"\r\n        >\r\n          <span className=\"text-sm font-medium text-gray-700 dark:text-neutral-200\">\r\n            {count} Selected\r\n          </span>\r\n          <span className=\"h-4 w-px bg-gray-200 dark:bg-neutral-700\" />\r\n          <button\r\n            onClick={onApplyCode}\r\n            className=\"text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-neutral-400 dark:hover:text-neutral-50\"\r\n          >\r\n            Apply Code\r\n          </button>\r\n          <button\r\n            onClick={onEditInfo}\r\n            className=\"text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-neutral-400 dark:hover:text-neutral-50\"\r\n          >\r\n            Edit Info\r\n          </button>\r\n          <button\r\n            onClick={onDelete}\r\n            className=\"flex items-center gap-1 text-sm font-medium text-red-500 transition-colors hover:text-red-600 dark:text-red-400 dark:hover:text-red-300\"\r\n          >\r\n            <Trash2 size={13} />\r\n            Delete\r\n          </button>\r\n          <button className=\"flex h-6 w-6 items-center justify-center rounded-md text-gray-400 hover:bg-gray-100 dark:text-neutral-500 dark:hover:bg-neutral-800\">\r\n            <MoreHorizontal size={15} />\r\n          </button>\r\n          <button\r\n            onClick={onClear}\r\n            className=\"flex h-6 w-6 items-center justify-center rounded-md text-gray-400 hover:bg-gray-100 dark:text-neutral-500 dark:hover:bg-neutral-800\"\r\n          >\r\n            <X size={15} />\r\n          </button>\r\n        </motion.div>\r\n      )}\r\n    </AnimatePresence>\r\n  );\r\n}\r\n\r\n// ─── Pagination ─────────────────────────────────────────────────────────────\r\nfunction Pagination({\r\n  page,\r\n  setPage,\r\n  pageSize,\r\n  setPageSize,\r\n  totalRows,\r\n}: {\r\n  page: number;\r\n  setPage: (p: number) => void;\r\n  pageSize: number;\r\n  setPageSize: (n: number) => void;\r\n  totalRows: number;\r\n}) {\r\n  const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));\r\n  const [goTo, setGoTo] = useState(\"\");\r\n  const [sizeOpen, setSizeOpen] = useState(false);\r\n\r\n  const pageList: (number | string)[] = [];\r\n  for (let p = 1; p <= totalPages; p++) {\r\n    if (p === 1 || p === totalPages || Math.abs(p - page) <= 1)\r\n      pageList.push(p);\r\n    else if (pageList[pageList.length - 1] !== \"…\") pageList.push(\"…\");\r\n  }\r\n\r\n  return (\r\n    <div className=\"flex flex-wrap items-center justify-between gap-3 px-1 pt-4 text-sm text-gray-500 dark:text-neutral-400\">\r\n      <div className=\"relative flex items-center gap-2\">\r\n        Showing per page\r\n        <button\r\n          onClick={() => setSizeOpen((o) => !o)}\r\n          className=\"flex items-center gap-1 rounded-lg border border-gray-200 px-2.5 py-1 font-medium text-gray-600 hover:bg-gray-50 dark:border-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-800\"\r\n        >\r\n          {pageSize}\r\n          <ChevronsUpDown\r\n            size={13}\r\n            className=\"text-gray-400 dark:text-neutral-500\"\r\n          />\r\n        </button>\r\n        <AnimatePresence>\r\n          {sizeOpen && (\r\n            <motion.div\r\n              initial={{ opacity: 0, y: -4 }}\r\n              animate={{ opacity: 1, y: 0 }}\r\n              exit={{ opacity: 0, y: -4 }}\r\n              className=\"absolute bottom-full left-24 mb-1 w-20 overflow-hidden rounded-lg border border-gray-100 bg-white p-1 shadow-lg dark:border-neutral-700 dark:bg-neutral-900\"\r\n            >\r\n              {[5, 10, 20].map((n) => (\r\n                <button\r\n                  key={n}\r\n                  onClick={() => {\r\n                    setPageSize(n);\r\n                    setPage(1);\r\n                    setSizeOpen(false);\r\n                  }}\r\n                  className=\"block w-full rounded-md px-2 py-1 text-left hover:bg-gray-50 dark:hover:bg-neutral-800\"\r\n                >\r\n                  {n}\r\n                </button>\r\n              ))}\r\n            </motion.div>\r\n          )}\r\n        </AnimatePresence>\r\n      </div>\r\n\r\n      <div className=\"flex items-center gap-1\">\r\n        <button\r\n          onClick={() => setPage(Math.max(1, page - 1))}\r\n          disabled={page === 1}\r\n          className=\"flex h-7 w-7 items-center justify-center rounded-md text-gray-400 hover:bg-gray-100 disabled:opacity-40 dark:text-neutral-500 dark:hover:bg-neutral-800\"\r\n        >\r\n          <ChevronLeft size={15} />\r\n        </button>\r\n        {pageList.map((p, i) =>\r\n          typeof p === \"number\" ? (\r\n            <button\r\n              key={i}\r\n              onClick={() => setPage(p)}\r\n              className={`flex h-7 w-7 items-center justify-center rounded-md text-sm font-medium transition-colors ${\r\n                page === p\r\n                  ? \"bg-gray-900 text-white dark:bg-neutral-50 dark:text-neutral-900\"\r\n                  : \"text-gray-500 hover:bg-gray-100 dark:text-neutral-400 dark:hover:bg-neutral-800\"\r\n              }`}\r\n            >\r\n              {p}\r\n            </button>\r\n          ) : (\r\n            <span key={i} className=\"px-1 text-gray-300 dark:text-neutral-600\">\r\n              {p}\r\n            </span>\r\n          ),\r\n        )}\r\n        <button\r\n          onClick={() => setPage(Math.min(totalPages, page + 1))}\r\n          disabled={page === totalPages}\r\n          className=\"flex h-7 w-7 items-center justify-center rounded-md text-gray-400 hover:bg-gray-100 disabled:opacity-40 dark:text-neutral-500 dark:hover:bg-neutral-800\"\r\n        >\r\n          <ChevronRight size={15} />\r\n        </button>\r\n      </div>\r\n\r\n      <div className=\"flex items-center gap-2\">\r\n        Go to page\r\n        <input\r\n          value={goTo}\r\n          onChange={(e) => setGoTo(e.target.value.replace(/\\D/g, \"\"))}\r\n          className=\"w-12 rounded-lg border border-gray-200 bg-white px-2 py-1 text-center text-gray-700 outline-none focus:border-gray-400 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-200 dark:focus:border-neutral-500\"\r\n        />\r\n        <button\r\n          onClick={() => {\r\n            const n = parseInt(goTo, 10);\r\n            if (n >= 1 && n <= totalPages) setPage(n);\r\n            setGoTo(\"\");\r\n          }}\r\n          className=\"flex items-center gap-1 rounded-lg px-2.5 py-1 font-medium text-gray-600 hover:bg-gray-100 dark:text-neutral-300 dark:hover:bg-neutral-800\"\r\n        >\r\n          Go\r\n          <ChevronRight size={13} />\r\n        </button>\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n\r\n// ─── Main export ────────────────────────────────────────────────────────────\r\nexport default function Dashboard2() {\r\n  const [datasets, setDatasets] = useState<Record<string, Dataset>>(() =>\r\n    buildDatasets(),\r\n  );\r\n  const [active, setActive] = useState(\"Product\");\r\n  const [darkMode, setDarkMode] = useState(false);\r\n  const [showStats, setShowStats] = useState(true);\r\n  const [search, setSearch] = useState(\"\");\r\n  const [statusFilter, setStatusFilter] = useState(\"All\");\r\n  const [sortOption, setSortOption] = useState(\"Default\");\r\n  const [sortKey, setSortKey] = useState<string | null>(null);\r\n  const [sortDir, setSortDir] = useState<\"asc\" | \"desc\">(\"asc\");\r\n  const [selected, setSelected] = useState<Set<string>>(new Set([\"p2\", \"p4\"]));\r\n  const [page, setPage] = useState(1);\r\n  const [pageSize, setPageSize] = useState(10);\r\n  const [visibleStats, setVisibleStats] = useState<Record<string, boolean[]>>(\r\n    {},\r\n  );\r\n  const [visibleCols, setVisibleCols] = useState<Record<string, boolean>>({});\r\n  const [customizeOpen, setCustomizeOpen] = useState(false);\r\n  const [focusedStat, setFocusedStat] = useState<string | null>(null);\r\n  const [notifCount, setNotifCount] = useState(3);\r\n  const [editingId, setEditingId] = useState<string | null>(null);\r\n  const [editValue, setEditValue] = useState(\"\");\r\n  const { toasts, push } = useToasts();\r\n\r\n  // `active` is normally guaranteed to match a key in `datasets` (every nav\r\n  // item's label has a matching entry from buildDatasets()). This fallback\r\n  // is what actually prevents the crash if the two ever drift apart — e.g. a\r\n  // sidebar item gets added/renamed without a matching dataset entry.\r\n  const dataset = datasets[active] ?? Object.values(datasets)[0];\r\n\r\n  if (!datasets[active] && process.env.NODE_ENV !== \"production\") {\r\n    // eslint-disable-next-line no-console\r\n    console.warn(\r\n      `[Dashboard2] No dataset found for nav item \"${active}\" — falling back to \"${dataset.key}\". Add a matching entry in buildDatasets().`,\r\n    );\r\n  }\r\n\r\n  // reset per-view transient state whenever the nav switches\r\n  useEffect(() => {\r\n    setSearch(\"\");\r\n    setStatusFilter(\"All\");\r\n    setSortOption(\"Default\");\r\n    setSortKey(null);\r\n    setSelected(new Set());\r\n    setPage(1);\r\n    setFocusedStat(null);\r\n    setCustomizeOpen(false);\r\n    setVisibleCols({});\r\n  }, [active]);\r\n\r\n  const statusOptions = useMemo(\r\n    () => [\"All\", ...Array.from(new Set(dataset.rows.map((r) => r.status)))],\r\n    [dataset],\r\n  );\r\n  const statsVisibility = visibleStats[active] ?? dataset.stats.map(() => true);\r\n\r\n  const filteredRows = useMemo(() => {\r\n    let rows = dataset.rows;\r\n    if (search.trim()) {\r\n      const q = search.toLowerCase();\r\n      rows = rows.filter(\r\n        (r) =>\r\n          Object.values(r.cells).some((v) =>\r\n            String(v).toLowerCase().includes(q),\r\n          ) || r.status.toLowerCase().includes(q),\r\n      );\r\n    }\r\n    if (statusFilter !== \"All\")\r\n      rows = rows.filter((r) => r.status === statusFilter);\r\n\r\n    const nameKey = dataset.columns[0]?.key ?? \"name\";\r\n    if (sortOption === \"Name (A-Z)\")\r\n      rows = [...rows].sort((a, b) =>\r\n        String(a.cells[nameKey]).localeCompare(String(b.cells[nameKey])),\r\n      );\r\n    if (sortOption === \"Name (Z-A)\")\r\n      rows = [...rows].sort((a, b) =>\r\n        String(b.cells[nameKey]).localeCompare(String(a.cells[nameKey])),\r\n      );\r\n\r\n    if (sortKey) {\r\n      rows = [...rows].sort((a, b) => {\r\n        const av =\r\n          sortKey === \"status\"\r\n            ? a.status\r\n            : sortKey === \"rating\"\r\n              ? (a.rating ?? 0)\r\n              : a.cells[sortKey];\r\n        const bv =\r\n          sortKey === \"status\"\r\n            ? b.status\r\n            : sortKey === \"rating\"\r\n              ? (b.rating ?? 0)\r\n              : b.cells[sortKey];\r\n        const cmp =\r\n          typeof av === \"number\" && typeof bv === \"number\"\r\n            ? av - bv\r\n            : String(av).localeCompare(String(bv));\r\n        return sortDir === \"asc\" ? cmp : -cmp;\r\n      });\r\n    }\r\n    return rows;\r\n  }, [dataset, search, statusFilter, sortOption, sortKey, sortDir]);\r\n\r\n  const pagedRows = filteredRows.slice((page - 1) * pageSize, page * pageSize);\r\n\r\n  const toggleRow = (id: string) =>\r\n    setSelected((prev) => {\r\n      const next = new Set(prev);\r\n      next.has(id) ? next.delete(id) : next.add(id);\r\n      return next;\r\n    });\r\n\r\n  const toggleAll = () =>\r\n    setSelected((prev) =>\r\n      prev.size === pagedRows.length\r\n        ? new Set()\r\n        : new Set(pagedRows.map((r) => r.id)),\r\n    );\r\n\r\n  const onSortColumn = (key: string) => {\r\n    if (sortKey === key) setSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\r\n    else {\r\n      setSortKey(key);\r\n      setSortDir(\"asc\");\r\n    }\r\n    setSortOption(\"Default\");\r\n  };\r\n\r\n  const handleAdd = () => {\r\n    const nameKey = dataset.columns[0].key;\r\n    const newId = `${active}-${Date.now()}`;\r\n    const newRow: Row = {\r\n      id: newId,\r\n      status: (statusOptions[1] ?? \"Active\") as Status,\r\n      rating: dataset.columns.some((c) => c.key === \"rating\") ? 5.0 : undefined,\r\n      cells: Object.fromEntries(\r\n        dataset.columns\r\n          .filter((c) => c.key !== \"status\" && c.key !== \"rating\")\r\n          .map((c) => [\r\n            c.key,\r\n            c.key === nameKey ? \"New entry\" : c.numeric ? 0 : \"—\",\r\n          ]),\r\n      ),\r\n    };\r\n    setDatasets((prev) => ({\r\n      ...prev,\r\n      [active]: { ...prev[active], rows: [newRow, ...prev[active].rows] },\r\n    }));\r\n    setEditingId(newId);\r\n    setEditValue(\"New entry\");\r\n    setPage(1);\r\n    push(`${dataset.addLabel} — row added`);\r\n  };\r\n\r\n  const commitEdit = () => {\r\n    if (!editingId) return;\r\n    const nameKey = dataset.columns[0].key;\r\n    setDatasets((prev) => ({\r\n      ...prev,\r\n      [active]: {\r\n        ...prev[active],\r\n        rows: prev[active].rows.map((r) =>\r\n          r.id === editingId\r\n            ? {\r\n                ...r,\r\n                cells: { ...r.cells, [nameKey]: editValue || \"Untitled\" },\r\n              }\r\n            : r,\r\n        ),\r\n      },\r\n    }));\r\n    setEditingId(null);\r\n  };\r\n\r\n  const handleDelete = () => {\r\n    setDatasets((prev) => ({\r\n      ...prev,\r\n      [active]: {\r\n        ...prev[active],\r\n        rows: prev[active].rows.filter((r) => !selected.has(r.id)),\r\n      },\r\n    }));\r\n    push(`${selected.size} row(s) deleted`);\r\n    setSelected(new Set());\r\n  };\r\n\r\n  const handleApplyCode = () =>\r\n    push(`Promo code applied to ${selected.size} row(s)`);\r\n\r\n  const handleEditInfo = () => {\r\n    const firstId = Array.from(selected)[0];\r\n    const row = dataset.rows.find((r) => r.id === firstId);\r\n    if (row) {\r\n      setEditingId(firstId);\r\n      setEditValue(String(row.cells[dataset.columns[0].key]));\r\n    }\r\n    push(\"Editing first selected row\");\r\n  };\r\n\r\n  const handleExport = () => {\r\n    const cols = dataset.columns.filter((c) => visibleCols[c.label] !== false);\r\n    const header = [...cols.map((c) => c.label)].join(\",\");\r\n    const lines = filteredRows.map((r) =>\r\n      cols\r\n        .map((c) =>\r\n          c.key === \"status\"\r\n            ? r.status\r\n            : c.key === \"rating\"\r\n              ? (r.rating ?? \"\")\r\n              : (r.cells[c.key] ?? \"\"),\r\n        )\r\n        .join(\",\"),\r\n    );\r\n    const csv = [header, ...lines].join(\"\\n\");\r\n    try {\r\n      const blob = new Blob([csv], { type: \"text/csv;charset=utf-8;\" });\r\n      const url = URL.createObjectURL(blob);\r\n      const a = document.createElement(\"a\");\r\n      a.href = url;\r\n      a.download = `${active.toLowerCase()}-export.csv`;\r\n      a.click();\r\n      URL.revokeObjectURL(url);\r\n    } catch {\r\n      // ignore outside the browser\r\n    }\r\n    push(`Exported ${filteredRows.length} rows as CSV`);\r\n  };\r\n\r\n  const toggleCol = (label: string) =>\r\n    setVisibleCols((prev) => ({\r\n      ...prev,\r\n      [label]: prev[label] === false ? true : false,\r\n    }));\r\n\r\n  return (\r\n    <div className={darkMode ? \"dark\" : \"\"}>\r\n      <div\r\n        style={font}\r\n        className=\"relative flex h-screen w-full overflow-hidden bg-white shadow-sm dark:border-neutral-800 dark:bg-black\"\r\n      >\r\n        <Sidebar\r\n          active={active}\r\n          setActive={setActive}\r\n          search={search}\r\n          setSearch={setSearch}\r\n          onUpgrade={() => push(\"Redirecting to billing…\")}\r\n          onLearnMore={() => push(\"Opening plan details…\")}\r\n        />\r\n\r\n        <main className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\r\n          <TopBar\r\n            title={dataset.title}\r\n            onShare={() => push(\"Share link copied\")}\r\n            onCustomizeWidget={() => setCustomizeOpen((o) => !o)}\r\n            notifCount={notifCount}\r\n            onBell={() => {\r\n              setNotifCount(0);\r\n              push(\"Notifications cleared\");\r\n            }}\r\n            darkMode={darkMode}\r\n            setDarkMode={setDarkMode}\r\n          />\r\n          <div className=\"relative flex-1 overflow-y-auto pb-4\">\r\n            <Toolbar\r\n              statusFilter={statusFilter}\r\n              setStatusFilter={setStatusFilter}\r\n              sortOption={sortOption}\r\n              setSortOption={(v) => {\r\n                setSortOption(v);\r\n                setSortKey(null);\r\n              }}\r\n              statusOptions={statusOptions}\r\n              showStats={showStats}\r\n              setShowStats={setShowStats}\r\n              onCustomize={() => setCustomizeOpen((o) => !o)}\r\n              onExport={handleExport}\r\n              onAdd={handleAdd}\r\n              addLabel={dataset.addLabel}\r\n            />\r\n\r\n            <AnimatePresence>\r\n              {customizeOpen && (\r\n                <CustomizePopover\r\n                  columns={dataset.columns.map((c) => c.label)}\r\n                  visibleCols={visibleCols}\r\n                  toggleCol={toggleCol}\r\n                  onClose={() => setCustomizeOpen(false)}\r\n                />\r\n              )}\r\n            </AnimatePresence>\r\n\r\n            {showStats && (\r\n              <StatsRow\r\n                stats={dataset.stats}\r\n                visible={statsVisibility}\r\n                focused={focusedStat}\r\n                setFocused={setFocusedStat}\r\n              />\r\n            )}\r\n\r\n            <div className=\"px-6 pt-5\">\r\n              <ProductTable\r\n                columns={dataset.columns}\r\n                rows={pagedRows}\r\n                visibleCols={visibleCols}\r\n                selected={selected}\r\n                toggleRow={toggleRow}\r\n                toggleAll={toggleAll}\r\n                sortKey={sortKey}\r\n                sortDir={sortDir}\r\n                onSortColumn={onSortColumn}\r\n                editingId={editingId}\r\n                editValue={editValue}\r\n                setEditValue={setEditValue}\r\n                onCommitEdit={commitEdit}\r\n              />\r\n              <BulkActionBar\r\n                count={selected.size}\r\n                onClear={() => setSelected(new Set())}\r\n                onApplyCode={handleApplyCode}\r\n                onEditInfo={handleEditInfo}\r\n                onDelete={handleDelete}\r\n              />\r\n              <Pagination\r\n                page={page}\r\n                setPage={setPage}\r\n                pageSize={pageSize}\r\n                setPageSize={setPageSize}\r\n                totalRows={filteredRows.length}\r\n              />\r\n            </div>\r\n          </div>\r\n        </main>\r\n\r\n        <ToastStack toasts={toasts} />\r\n      </div>\r\n    </div>\r\n  );\r\n}\r\n",
      "type": "registry:block",
      "target": "components/Dashboard/dashboard-finance.tsx"
    }
  ],
  "type": "registry:block"
}