{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "integration-card",
  "type": "registry:component",
  "title": "Integration Card",
  "description": "Show connected services with status, actions, and simple integration management.",
  "dependencies": [
    "framer-motion",
    "lucide-react",
    "react-icons"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "components/watermelon/integration-card.tsx",
      "type": "registry:component",
      "content": "import React, { useState, useRef, useEffect } from 'react';\nimport { motion, AnimatePresence } from 'framer-motion';\nimport {\n  Search,\n  ChevronDown,\n  ChevronLeft,\n  ChevronRight,\n  Check,\n} from 'lucide-react';\nimport { IoClose } from 'react-icons/io5';\nimport { cn } from '@/lib/utils';\n\n/* ---------- Types ---------- */\nexport interface IntegrationItem {\n  id: string;\n  name: string;\n  entities: string;\n  description: string;\n  tags: string[];\n  triggers: number;\n  actions: number;\n  available: boolean;\n  icon: React.ReactNode;\n}\n\ninterface IntegrationsCardProps {\n  items: IntegrationItem[];\n  title: string;\n}\n\n/* ---------- Sub-components ---------- */\nconst FilterButton: React.FC<{\n  label: string;\n  active?: boolean;\n  onClick: () => void;\n  selected?: string;\n}> = ({ label, active, onClick, selected }) => (\n  <button\n    onClick={onClick}\n    className={cn(\n      'relative flex shrink-0 items-center gap-1.5 rounded-lg border px-3 py-1.5 text-[11px] transition active:scale-95',\n      active\n        ? 'border-zinc-300 bg-zinc-100 text-zinc-900 dark:border-[#3a3a3a] dark:bg-[#1a1a1a] dark:text-white'\n        : 'border-zinc-200 bg-zinc-100/50 text-zinc-500 hover:text-zinc-900 dark:border-[#2a2a2a] dark:bg-[#141414] dark:text-[#a3a3a3] dark:hover:text-white',\n    )}\n  >\n    {selected || label}\n    <ChevronDown\n      size={12}\n      className={cn(\n        'transition-transform duration-300',\n        active ? 'rotate-180' : '',\n      )}\n    />\n  </button>\n);\n\nconst IntegrationCard: React.FC<{ item: IntegrationItem }> = ({ item }) => {\n  return (\n    <motion.div\n      layout\n      initial={{ opacity: 0, y: 6 }}\n      animate={{ opacity: 1, y: 0 }}\n      className=\"flex gap-4 border-b border-zinc-100 px-5 py-4 transition-colors last:rounded-b-[14px] last:border-b-[1.6px] hover:bg-zinc-50 dark:border-[#1f1f1f] dark:hover:bg-[#141414]\"\n    >\n      {/* Icon */}\n      <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-zinc-200 bg-zinc-50 dark:border-[#2a2a2a] dark:bg-[#0f0f0f]\">\n        {item.icon}\n      </div>\n\n      {/* Content */}\n      <div className=\"min-w-0 flex-1\">\n        <div className=\"flex items-start justify-between gap-2\">\n          <div className=\"truncate\">\n            <h3 className=\"truncate text-[14px] font-medium text-zinc-900 dark:text-[#EEEEEE]\">\n              {item.name}\n            </h3>\n            <p className=\"mt-0.5 truncate text-[10px] tracking-wider text-zinc-400 uppercase dark:text-[#6b6b6b]\">\n              {item.entities}\n            </p>\n          </div>\n\n          {item.available && (\n            <span className=\"shrink-0 rounded-full border border-green-200 bg-green-50 px-2 py-0.5 pt-1 text-[9px] font-bold text-green-600 dark:border-[#1AA420]/70 dark:bg-[#142E17] dark:text-[#1bb022]\">\n              AVAILABLE\n            </span>\n          )}\n        </div>\n\n        <p className=\"mt-2 max-w-full text-[12px] leading-relaxed text-zinc-500 dark:text-[#8a8a8a]\">\n          {item.description}\n        </p>\n\n        <div className=\"mt-3 flex flex-col justify-between gap-2 sm:flex-row sm:items-center\">\n          <div className=\"flex flex-wrap gap-1.5\">\n            {item.tags.map((tag) => (\n              <span\n                key={tag}\n                className=\"rounded-full border border-zinc-200 bg-zinc-100 px-2 py-0.5 text-[10px] text-zinc-500 dark:border-[#2a2a2a] dark:bg-[#1a1a1a] dark:text-[#9a9a9a]\"\n              >\n                {tag}\n              </span>\n            ))}\n          </div>\n\n          <span className=\"text-[10px] whitespace-nowrap text-zinc-400 dark:text-[#6b6b6b]\">\n            {item.triggers} TRIGGERS / {item.actions} ACTIONS\n          </span>\n        </div>\n      </div>\n    </motion.div>\n  );\n};\n\n/* ---------- Main ---------- */\nexport const IntegrationsCard: React.FC<IntegrationsCardProps> = ({\n  items,\n  title,\n}) => {\n  const [activePopover, setActivePopover] = useState<\n    'type' | 'useCase' | 'more' | null\n  >(null);\n  const [selectedType, setSelectedType] = useState('All types');\n  const [selectedUseCase, setSelectedUseCase] = useState('All use cases');\n  const [searchQuery, setSearchQuery] = useState('');\n  const [currentPage, setCurrentPage] = useState(1);\n  const itemsPerPage = 6;\n\n  const popoverRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    const handleClickOutside = (event: MouseEvent) => {\n      if (\n        popoverRef.current &&\n        !popoverRef.current.contains(event.target as Node)\n      ) {\n        setActivePopover(null);\n      }\n    };\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => document.removeEventListener('mousedown', handleClickOutside);\n  }, []);\n\n  // Filter Logic\n  const filteredItems = items.filter((item) => {\n    const matchesSearch =\n      item.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n      item.description.toLowerCase().includes(searchQuery.toLowerCase());\n    const matchesType =\n      selectedType === 'All types' || item.tags.includes(selectedType);\n    const matchesUseCase =\n      selectedUseCase === 'All use cases' ||\n      item.entities.includes(selectedUseCase.toUpperCase());\n    return matchesSearch && matchesType && matchesUseCase;\n  });\n\n  // Pagination Logic\n  const totalPages = Math.ceil(filteredItems.length / itemsPerPage);\n  const paginatedItems = filteredItems.slice(\n    (currentPage - 1) * itemsPerPage,\n    currentPage * itemsPerPage,\n  );\n\n  const typeOptions = ['All types', 'Marketplace', 'Internal', 'Third-party'];\n  const useCaseOptions = [\n    'All use cases',\n    'Productivity',\n    'Marketing',\n    'Development',\n    'Sales',\n  ];\n\n  return (\n    <div className=\"text-foreground flex w-full flex-col items-center bg-transparent px-4 py-8\">\n      <div className=\"w-full max-w-145 overflow-hidden rounded-[22px] border border-zinc-200 bg-white shadow-2xl transition-all duration-300 dark:border-[#1f1f1f] dark:bg-[#101010]\">\n        {/* Header */}\n        <header className=\"flex items-center justify-between px-5 py-4\">\n          <h2 className=\"text-sm font-semibold tracking-tight text-zinc-900 dark:text-white\">\n            {title}\n          </h2>\n          <button\n            title=\"close\"\n            className=\"flex h-8 w-8 items-center justify-center rounded-full text-zinc-400 transition-all hover:bg-zinc-100 hover:text-zinc-900 active:scale-90 dark:text-[#6b6b6b] dark:hover:bg-white/5 dark:hover:text-white\"\n          >\n            <IoClose size={18} />\n          </button>\n        </header>\n\n        {/* Filters & Search */}\n        <div className=\"relative flex flex-col items-stretch gap-3 rounded-t-[14px] border-t border-b border-zinc-100 bg-zinc-50 px-5 py-4 md:flex-row md:items-center dark:border-[#1f1f1f] dark:bg-[#171717]\">\n          <div className=\"no-scrollbar flex items-center gap-2 overflow-x-auto pb-1 md:pb-0\">\n            <FilterButton\n              label=\"All types\"\n              selected={selectedType !== 'All types' ? selectedType : undefined}\n              active={activePopover === 'type'}\n              onClick={() =>\n                setActivePopover(activePopover === 'type' ? null : 'type')\n              }\n            />\n            <FilterButton\n              label=\"All use cases\"\n              selected={\n                selectedUseCase !== 'All use cases'\n                  ? selectedUseCase\n                  : undefined\n              }\n              active={activePopover === 'useCase'}\n              onClick={() =>\n                setActivePopover(activePopover === 'useCase' ? null : 'useCase')\n              }\n            />\n            <FilterButton\n              label=\"More\"\n              active={activePopover === 'more'}\n              onClick={() =>\n                setActivePopover(activePopover === 'more' ? null : 'more')\n              }\n            />\n          </div>\n\n          <div className=\"hidden flex-1 md:block\" />\n\n          <div className=\"group relative w-full md:w-56\">\n            <Search\n              size={14}\n              className=\"absolute top-1/2 left-3 -translate-y-1/2 text-zinc-400 transition-colors group-focus-within:text-zinc-900 dark:text-[#6b6b6b] dark:group-focus-within:text-white\"\n            />\n            <input\n              value={searchQuery}\n              onChange={(e) => {\n                setSearchQuery(e.target.value);\n                setCurrentPage(1);\n              }}\n              placeholder=\"Search for an app...\"\n              className=\"w-full rounded-lg border border-zinc-200 bg-white py-1.5 pr-4 pl-9 text-[12px] text-zinc-900 placeholder-zinc-400 transition-all focus:border-zinc-400 focus:outline-none dark:border-[#2a2a2a] dark:bg-[#121212] dark:text-white dark:placeholder-[#6b6b6b] dark:focus:border-[#3a3a3a]\"\n            />\n          </div>\n\n          {/* Global Popover */}\n          <AnimatePresence>\n            {activePopover && (\n              <motion.div\n                ref={popoverRef}\n                initial={{ opacity: 0, y: 10, scale: 0.95 }}\n                animate={{ opacity: 1, y: 4 }}\n                exit={{ opacity: 0, y: 10, scale: 0.95 }}\n                className=\"absolute top-full right-5 left-5 z-50 w-auto overflow-hidden rounded-xl border border-zinc-200 bg-white p-1.5 shadow-2xl md:right-auto md:left-5 md:min-w-48 dark:border-white/10 dark:bg-[#171717]\"\n              >\n                {activePopover === 'type' &&\n                  typeOptions.map((opt) => (\n                    <button\n                      key={opt}\n                      onClick={() => {\n                        setSelectedType(opt);\n                        setActivePopover(null);\n                        setCurrentPage(1);\n                      }}\n                      className=\"flex w-full items-center justify-between gap-3 rounded-lg px-3 py-2 text-left text-[12px] font-medium text-zinc-700 transition-colors hover:bg-zinc-50 dark:text-[#EEEEEE] dark:hover:bg-white/5\"\n                    >\n                      {opt}\n                      {selectedType === opt && (\n                        <Check\n                          size={12}\n                          className=\"text-zinc-900 dark:text-white\"\n                        />\n                      )}\n                    </button>\n                  ))}\n                {activePopover === 'useCase' &&\n                  useCaseOptions.map((opt) => (\n                    <button\n                      key={opt}\n                      onClick={() => {\n                        setSelectedUseCase(opt);\n                        setActivePopover(null);\n                        setCurrentPage(1);\n                      }}\n                      className=\"flex w-full items-center justify-between gap-3 rounded-lg px-3 py-2 text-left text-[12px] font-medium text-zinc-700 transition-colors hover:bg-zinc-50 dark:text-[#EEEEEE] dark:hover:bg-white/5\"\n                    >\n                      {opt}\n                      {selectedUseCase === opt && (\n                        <Check\n                          size={12}\n                          className=\"text-zinc-900 dark:text-white\"\n                        />\n                      )}\n                    </button>\n                  ))}\n                {activePopover === 'more' && (\n                  <div className=\"space-y-1 p-2\">\n                    <p className=\"px-2 pb-2 text-[9px] font-bold tracking-wider text-zinc-400 uppercase dark:text-[#555]\">\n                      Advanced\n                    </p>\n                    <button className=\"flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[11px] text-zinc-600 transition-colors hover:bg-zinc-50 dark:text-[#888] dark:hover:bg-white/5\">\n                      Sort by Name\n                    </button>\n                    <button className=\"flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[11px] text-zinc-600 transition-colors hover:bg-zinc-50 dark:text-[#888] dark:hover:bg-white/5\">\n                      Sort by Recent\n                    </button>\n                    <button className=\"flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-[11px] font-medium text-red-500 transition-colors hover:bg-zinc-50 dark:hover:bg-white/5\">\n                      Reset Filters\n                    </button>\n                  </div>\n                )}\n              </motion.div>\n            )}\n          </AnimatePresence>\n        </div>\n\n        {/* List */}\n        <div className=\"no-scrollbar max-h-130 overflow-y-auto bg-white dark:bg-[#171717]\">\n          <AnimatePresence mode=\"popLayout\">\n            {paginatedItems.map((item) => (\n              <IntegrationCard key={item.id} item={item} />\n            ))}\n            {paginatedItems.length === 0 && (\n              <motion.div\n                initial={{ opacity: 0 }}\n                animate={{ opacity: 1 }}\n                className=\"space-y-3 py-24 text-center\"\n              >\n                <div className=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full border border-zinc-100 bg-zinc-50 dark:border-white/10 dark:bg-white/5\">\n                  <Search\n                    size={20}\n                    className=\"text-zinc-400 dark:text-[#555]\"\n                  />\n                </div>\n                <p className=\"text-[14px] font-medium tracking-tight text-zinc-900 dark:text-white\">\n                  No integrations found\n                </p>\n                <p className=\"text-[12px] text-zinc-400 dark:text-[#6b6b6b]\">\n                  Try adjusting your search or filters\n                </p>\n                <button\n                  onClick={() => {\n                    setSearchQuery('');\n                    setSelectedType('All types');\n                    setSelectedUseCase('All use cases');\n                  }}\n                  className=\"text-[12px] font-bold text-zinc-900 hover:underline dark:text-white\"\n                >\n                  Clear all filters\n                </button>\n              </motion.div>\n            )}\n          </AnimatePresence>\n        </div>\n\n        {/* Footer */}\n        <footer className=\"flex flex-col items-center justify-between gap-4 border-t border-zinc-100 bg-zinc-50/30 px-6 py-4 text-[11px] text-zinc-400 sm:flex-row dark:border-[#1f1f1f] dark:bg-transparent dark:text-[#6b6b6b]\">\n          <span className=\"font-medium\">\n            {filteredItems.length > 0\n              ? (currentPage - 1) * itemsPerPage + 1\n              : 0}{' '}\n            – {Math.min(currentPage * itemsPerPage, filteredItems.length)} of{' '}\n            {filteredItems.length} apps\n          </span>\n\n          <div className=\"flex items-center gap-1\">\n            <button\n              disabled={currentPage === 1}\n              onClick={() => setCurrentPage((prev) => prev - 1)}\n              className=\"rounded-lg p-1.5 transition-all hover:bg-zinc-100 disabled:opacity-30 dark:hover:bg-white/5\"\n            >\n              <ChevronLeft size={16} />\n            </button>\n            <div className=\"flex items-center gap-1.5 px-2\">\n              {[...Array(totalPages)].map((_, i) => {\n                const pageNum = i + 1;\n                if (\n                  totalPages > 5 &&\n                  pageNum > 2 &&\n                  pageNum < totalPages - 1 &&\n                  Math.abs(pageNum - currentPage) > 1\n                ) {\n                  if (pageNum === 3 || pageNum === totalPages - 2)\n                    return (\n                      <span key={pageNum} className=\"px-1 opacity-50\">\n                        …\n                      </span>\n                    );\n                  return null;\n                }\n                return (\n                  <button\n                    key={pageNum}\n                    onClick={() => setCurrentPage(pageNum)}\n                    className={cn(\n                      'flex h-7 w-7 items-center justify-center rounded-lg text-[11px] font-bold transition-all',\n                      currentPage === pageNum\n                        ? 'border border-orange-200 bg-orange-50 text-orange-600 shadow-sm dark:border-[#3a1f14] dark:bg-[#2a160e] dark:text-[#f97316]'\n                        : 'text-zinc-500 hover:bg-zinc-100 dark:text-[#6b6b6b] dark:hover:bg-white/5',\n                    )}\n                  >\n                    {pageNum}\n                  </button>\n                );\n              })}\n            </div>\n            <button\n              disabled={currentPage === totalPages || totalPages === 0}\n              onClick={() => setCurrentPage((prev) => prev + 1)}\n              className=\"rounded-lg p-1.5 transition-all hover:bg-zinc-100 disabled:opacity-30 dark:hover:bg-white/5\"\n            >\n              <ChevronRight size={16} />\n            </button>\n          </div>\n        </footer>\n      </div>\n    </div>\n  );\n};\n"
    }
  ]
}
