{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "integrations-search-directory",
  "title": "Integrations — Search Directory",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-react/blocks/integrations-search-directory/IntegrationsSearchDirectory.tsx",
      "content": "'use client'\n\nimport { useMemo, useState } from 'react'\nimport { ArrowUpRight, Search } from 'lucide-react'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Input } from '@/components/ui/input'\nimport { Separator } from '@/components/ui/separator'\n\nconst integrations = [\n  {\n    mark: 'SF',\n    name: 'Snowflake',\n    category: 'Warehouses',\n    blurb: 'Query live tables with a read-only role.',\n    auth: 'Key pair',\n  },\n  {\n    mark: 'BQ',\n    name: 'BigQuery',\n    category: 'Warehouses',\n    blurb: 'Service-account access, per-dataset scope.',\n    auth: 'Service account',\n  },\n  {\n    mark: 'RS',\n    name: 'Redshift',\n    category: 'Warehouses',\n    blurb: 'Serverless and provisioned clusters.',\n    auth: 'IAM role',\n  },\n  {\n    mark: 'OK',\n    name: 'Okta',\n    category: 'Identity',\n    blurb: 'Group membership drives row-level scope.',\n    auth: 'SAML + SCIM',\n  },\n  {\n    mark: 'EI',\n    name: 'Entra ID',\n    category: 'Identity',\n    blurb: 'Directory groups sync on a schedule.',\n    auth: 'SAML + SCIM',\n  },\n  {\n    mark: 'SL',\n    name: 'Slack',\n    category: 'Alerting',\n    blurb: 'Route alerts to the metric owner’s channel.',\n    auth: 'OAuth',\n  },\n  {\n    mark: 'PD',\n    name: 'PagerDuty',\n    category: 'Alerting',\n    blurb: 'Page on freshness breaches, not on noise.',\n    auth: 'API key',\n  },\n  {\n    mark: 'DT',\n    name: 'dbt',\n    category: 'Modelling',\n    blurb: 'Import models as certified metric sources.',\n    auth: 'API token',\n  },\n  {\n    mark: 'AF',\n    name: 'Airflow',\n    category: 'Modelling',\n    blurb: 'Trigger materialisation from your DAGs.',\n    auth: 'API token',\n  },\n]\n\nexport function IntegrationsSearchDirectory() {\n  const [query, setQuery] = useState('')\n\n  const groups = useMemo(() => {\n    const q = query.trim().toLowerCase()\n    const matches = q\n      ? integrations.filter((i) => `${i.name} ${i.category} ${i.blurb} ${i.auth}`.toLowerCase().includes(q))\n      : integrations\n    const map = new Map<string, typeof integrations>()\n    for (const integration of matches) {\n      const list = map.get(integration.category)\n      if (list) list.push(integration)\n      else map.set(integration.category, [integration])\n    }\n    return [...map.entries()]\n  }, [query])\n\n  return (\n    <section data-slot=\"integrations-search-directory\" className=\"bg-background\">\n      <div className=\"mx-auto max-w-4xl px-6 py-20 lg:py-28\">\n        <div className=\"flex flex-wrap items-end justify-between gap-6\">\n          <div>\n            <Badge variant=\"secondary\">Directory</Badge>\n            <h2 className=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">Find your stack</h2>\n          </div>\n\n          <div className=\"relative w-full sm:w-72\">\n            <Search\n              className=\"text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2\"\n              aria-hidden=\"true\"\n            />\n            <Input\n              value={query}\n              onChange={(event) => setQuery(event.target.value)}\n              className=\"pl-9\"\n              placeholder=\"Search integrations…\"\n              aria-label=\"Search integrations\"\n            />\n          </div>\n        </div>\n\n        <Card className=\"mt-8\">\n          <CardContent className=\"p-2\">\n            {!groups.length && (\n              <div className=\"px-4 py-16 text-center\">\n                <p className=\"text-sm font-medium\">No integration matches “{query}”.</p>\n                <p className=\"text-muted-foreground mt-1 text-xs\">\n                  We ship new connectors most releases — tell us which one you need.\n                </p>\n                <Button variant=\"outline\" size=\"sm\" className=\"mt-4\">\n                  Request an integration\n                </Button>\n              </div>\n            )}\n\n            {groups.map(([category, list], index) => (\n              <div key={category}>\n                <p className=\"text-muted-foreground px-4 pt-4 pb-2 font-mono text-xs tracking-[0.14em] uppercase\">\n                  {category}\n                </p>\n                <ul>\n                  {list.map((integration) => (\n                    <li key={integration.name}>\n                      <a\n                        href=\"#\"\n                        className=\"hover:bg-muted focus-visible:ring-ring group flex items-center gap-4 rounded-lg px-4 py-3 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                      >\n                        <span\n                          className=\"border-border text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-md border font-mono text-xs font-semibold\"\n                          aria-hidden=\"true\"\n                        >\n                          {integration.mark}\n                        </span>\n                        <span className=\"min-w-0 flex-1\">\n                          <span className=\"block text-sm font-medium\">{integration.name}</span>\n                          <span className=\"text-muted-foreground block truncate text-xs\">{integration.blurb}</span>\n                        </span>\n                        <Badge variant=\"outline\" className=\"hidden shrink-0 sm:inline-flex\">\n                          {integration.auth}\n                        </Badge>\n                        <ArrowUpRight\n                          className=\"text-muted-foreground size-4 shrink-0 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5\"\n                          aria-hidden=\"true\"\n                        />\n                      </a>\n                    </li>\n                  ))}\n                </ul>\n                {index < groups.length - 1 && <Separator className=\"my-2\" />}\n              </div>\n            ))}\n          </CardContent>\n        </Card>\n      </div>\n    </section>\n  )\n}\n",
      "type": "registry:block",
      "target": "~/components/blocks/IntegrationsSearchDirectory.tsx"
    }
  ],
  "dependencies": [
    "lucide-react"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/react/badge.json",
    "https://uipkge.dev/r/react/button.json",
    "https://uipkge.dev/r/react/card.json",
    "https://uipkge.dev/r/react/input.json",
    "https://uipkge.dev/r/react/separator.json"
  ],
  "description": "Searchable integration directory: a live filter field above category-grouped rows carrying a monogram, blurb, and auth type, with an empty state when nothing matches.",
  "categories": [
    "marketing"
  ]
}