{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "knowledge-base-hub",
  "title": "Knowledge Base Hub",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/knowledge-base-hub/knowledge-base-hub.component.ts",
      "content": "// Self-serve help center and customer documentation hub with hero search, quick topic pills,\n// category cards grid, trending articles, and community support banner. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  ArrowRight,\n  ArrowUpRight,\n  Blocks,\n  BookOpen,\n  Clock,\n  Code,\n  CreditCard,\n  Eye,\n  HelpCircle,\n  LucideAngularModule,\n  MessageSquare,\n  Rocket,\n  Search,\n  Shield,\n  Users,\n  X,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\n\ninterface Category {\n  id: string\n  title: string\n  description: string\n  articlesCount: number\n  icon: unknown\n  featuredTopics: string[]\n}\n\ninterface Article {\n  id: string\n  title: string\n  description: string\n  category: string\n  readTime: string\n  viewCount: string\n}\n\nconst defaultCategories: Category[] = [\n  {\n    id: 'getting-started',\n    title: 'Getting Started',\n    icon: Rocket,\n    articlesCount: 14,\n    description: 'Quick start guides, installation walkthroughs, and core architecture concepts.',\n    featuredTopics: ['Quickstart Guide', 'Project Setup', 'First Deployment'],\n  },\n  {\n    id: 'account-billing',\n    title: 'Account & Billing',\n    icon: CreditCard,\n    articlesCount: 22,\n    description: 'Subscription plans, payment methods, invoice management, and seat licensing.',\n    featuredTopics: ['Upgrade Plan', 'Payment Methods', 'Invoices'],\n  },\n  {\n    id: 'api-sdks',\n    title: 'API & Developer SDKs',\n    icon: Code,\n    articlesCount: 35,\n    description: 'REST & GraphQL endpoints, SDK reference libraries, auth tokens, and rate limits.',\n    featuredTopics: ['Authentication', 'Rate Limits', 'SDK Libraries'],\n  },\n  {\n    id: 'security-compliance',\n    title: 'Security & Compliance',\n    icon: Shield,\n    articlesCount: 18,\n    description: 'SOC 2 certification, SSO/SAML configuration, audit logs, and data encryption.',\n    featuredTopics: ['SAML SSO', 'Audit Logs', 'Data Encryption'],\n  },\n  {\n    id: 'integrations-webhooks',\n    title: 'Integrations & Webhooks',\n    icon: Blocks,\n    articlesCount: 28,\n    description: 'Connecting third-party apps, custom webhooks, payload schemas, and event retries.',\n    featuredTopics: ['Webhooks Setup', 'Slack App', 'Retry Policies'],\n  },\n  {\n    id: 'troubleshooting-faq',\n    title: 'Troubleshooting & FAQ',\n    icon: HelpCircle,\n    articlesCount: 40,\n    description: 'Common error codes, debugging workflows, known limits, and frequent questions.',\n    featuredTopics: ['Error Codes', 'Performance', 'Account Recovery'],\n  },\n]\n\nconst defaultArticles: Article[] = [\n  {\n    id: 'art-1',\n    title: 'Setting up SAML 2.0 Single Sign-On (SSO)',\n    description: 'Step-by-step instructions for configuring Okta, Azure AD, and Google Workspace SSO.',\n    category: 'Security',\n    readTime: '6 min read',\n    viewCount: '14.2k views',\n  },\n  {\n    id: 'art-2',\n    title: 'Migrating from v1 REST API to v2 GraphQL',\n    description: 'Breaking changes, schema transformations, and backward-compatibility guidelines.',\n    category: 'API & SDKs',\n    readTime: '8 min read',\n    viewCount: '11.8k views',\n  },\n  {\n    id: 'art-3',\n    title: 'Configuring Custom Domains & SSL Certificates',\n    description: 'DNS record configuration, automatic TLS provisioning, and apex domain routing.',\n    category: 'Getting Started',\n    readTime: '4 min read',\n    viewCount: '9.5k views',\n  },\n  {\n    id: 'art-4',\n    title: 'Webhook Signatures & Replay Attack Prevention',\n    description: 'Verify HMAC-SHA256 signatures and implement idempotency keys for event handling.',\n    category: 'Integrations',\n    readTime: '5 min read',\n    viewCount: '8.9k views',\n  },\n  {\n    id: 'art-5',\n    title: 'Managing Team Roles & Granular Permissions',\n    description: 'Assigning RBAC policies, custom permission sets, and project-level scopes.',\n    category: 'Account',\n    readTime: '5 min read',\n    viewCount: '7.4k views',\n  },\n  {\n    id: 'art-6',\n    title: 'Debugging 429 Rate Limit Errors & Backoff Strategies',\n    description: 'Understanding quota tiers, exponential backoff algorithms, and jitter implementations.',\n    category: 'Troubleshooting',\n    readTime: '7 min read',\n    viewCount: '12.1k views',\n  },\n]\n\nconst quickTopics = ['Authentication', 'Billing', 'Webhooks', 'Custom Domains']\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-knowledge-base-hub, [ui-knowledge-base-hub]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n  ],\n  template: `\n    <div data-slot=\"knowledge-base-hub\" [class]=\"rootClass\">\n      <!-- Hero Header -->\n      <section class=\"mx-auto max-w-3xl space-y-6 text-center\">\n        <div class=\"space-y-3\">\n          <span ui-badge variant=\"secondary\" class=\"gap-1.5 px-3 py-1 font-medium\">\n            <lucide-icon [img]=\"BookOpen\" class=\"text-primary size-3.5\" />\n            Help Center & Documentation\n          </span>\n          <h1 class=\"text-3xl font-bold tracking-tight sm:text-4xl lg:text-5xl\">How can we help you today?</h1>\n          <p class=\"text-muted-foreground mx-auto max-w-xl text-base sm:text-lg\">\n            Search our knowledge base for guides, API references, troubleshooting tips, and common workflows.\n          </p>\n        </div>\n\n        <div class=\"relative mx-auto max-w-2xl\">\n          <lucide-icon\n            [img]=\"Search\"\n            class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-4 size-5 -translate-y-1/2\"\n          />\n          <ui-input\n            [value]=\"searchQuery()\"\n            (valueChange)=\"searchQuery.set($event)\"\n            type=\"search\"\n            placeholder=\"Search for answers, guides, and error codes...\"\n            class=\"bg-card h-12 rounded-xl pr-14 pl-11 text-base shadow-xs\"\n          />\n          <div class=\"pointer-events-none absolute top-1/2 right-3 -translate-y-1/2\">\n            <kbd\n              class=\"bg-muted text-muted-foreground border-border hidden h-6 items-center rounded border px-2 font-mono text-xs select-none sm:inline-flex\"\n            >\n              ⌘K\n            </kbd>\n          </div>\n        </div>\n\n        <div class=\"flex flex-wrap items-center justify-center gap-2\">\n          <span class=\"text-muted-foreground text-xs font-medium\">Quick topics:</span>\n          @for (topic of topics; track topic) {\n            <button type=\"button\" [class]=\"topicPillClass(topic)\" (click)=\"selectTopic(topic)\">\n              {{ topic }}\n            </button>\n          }\n          @if (searchQuery()) {\n            <button\n              aria-label=\"Clear search\"\n              type=\"button\"\n              class=\"text-muted-foreground hover:text-foreground inline-flex items-center gap-1 px-1.5 py-1 text-xs font-medium transition-colors\"\n              (click)=\"searchQuery.set('')\"\n            >\n              <lucide-icon [img]=\"X\" class=\"size-3\" />\n              Clear search\n            </button>\n          }\n        </div>\n      </section>\n\n      <!-- Empty Search State -->\n      @if (filteredCategories().length === 0 && filteredArticles().length === 0) {\n        <div class=\"border-border bg-card/40 rounded-xl border border-dashed p-10 text-center\">\n          <div class=\"bg-muted text-muted-foreground mx-auto flex size-12 items-center justify-center rounded-full\">\n            <lucide-icon [img]=\"Search\" class=\"size-6\" />\n          </div>\n          <h3 class=\"mt-4 text-base font-semibold\">No matching articles found</h3>\n          <p class=\"text-muted-foreground mt-1 text-sm\">\n            No guides or categories matched &ldquo;{{ searchQuery() }}&rdquo;. Try searching for another topic.\n          </p>\n          <button\n            ui-button\n            aria-label=\"Clear search\"\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"mt-4\"\n            (click)=\"searchQuery.set('')\"\n          >\n            Reset search\n          </button>\n        </div>\n      }\n\n      <!-- Category Cards Grid -->\n      @if (filteredCategories().length > 0) {\n        <section class=\"space-y-4\">\n          <div class=\"flex items-center justify-between\">\n            <h2 class=\"text-xl font-semibold tracking-tight sm:text-2xl\">Browse by Category</h2>\n            <span class=\"text-muted-foreground text-xs tabular-nums\">{{ filteredCategories().length }} categories</span>\n          </div>\n\n          <div class=\"grid gap-5 sm:grid-cols-2 lg:grid-cols-3\">\n            @for (category of filteredCategories(); track category.id) {\n              <div\n                ui-card\n                class=\"group/card hover:border-ring/50 relative flex flex-col justify-between overflow-hidden transition-colors duration-200 hover:shadow-sm\"\n              >\n                <div ui-card-header class=\"pb-3\">\n                  <div class=\"flex items-start justify-between gap-3\">\n                    <div class=\"bg-primary/10 text-primary flex size-11 items-center justify-center rounded-lg\">\n                      <lucide-icon [img]=\"categoryIcon(category)\" class=\"size-5\" />\n                    </div>\n                    <span ui-badge variant=\"secondary\" class=\"text-xs font-normal\">\n                      {{ category.articlesCount }} articles\n                    </span>\n                  </div>\n                  <h3 ui-card-title class=\"mt-3 text-lg font-semibold tracking-tight\">{{ category.title }}</h3>\n                  <p ui-card-description class=\"text-muted-foreground text-sm leading-relaxed\">\n                    {{ category.description }}\n                  </p>\n                </div>\n                <div ui-card-content class=\"pt-0\">\n                  <div class=\"border-border flex flex-wrap gap-1.5 border-t pt-3\">\n                    @for (topic of category.featuredTopics; track topic) {\n                      <span class=\"bg-muted/60 text-muted-foreground rounded-md px-2 py-0.5 text-xs font-medium\">\n                        {{ topic }}\n                      </span>\n                    }\n                  </div>\n                </div>\n                <div ui-card-footer class=\"border-border bg-muted/20 flex items-center justify-between border-t py-3\">\n                  <a\n                    href=\"#\"\n                    class=\"text-primary group-hover/card:text-primary/80 flex min-h-6 items-center gap-1 text-xs font-medium transition-colors\"\n                  >\n                    <span>Explore category</span>\n                    <lucide-icon\n                      [img]=\"ArrowRight\"\n                      class=\"size-3.5 transition-transform duration-200 group-hover/card:translate-x-1\"\n                    />\n                  </a>\n                </div>\n              </div>\n            }\n          </div>\n        </section>\n      }\n\n      <!-- Popular & Trending Articles Section -->\n      @if (filteredArticles().length > 0) {\n        <section class=\"space-y-4\">\n          <div class=\"flex flex-col justify-between gap-2 sm:flex-row sm:items-end\">\n            <div>\n              <h2 class=\"text-xl font-semibold tracking-tight sm:text-2xl\">Popular & Trending Articles</h2>\n              <p class=\"text-muted-foreground mt-1 text-sm\">\n                Frequently read guides and developer documentation this week.\n              </p>\n            </div>\n            <a\n              href=\"#\"\n              class=\"text-primary hover:text-primary/80 inline-flex min-h-6 items-center gap-1 text-xs font-medium transition-colors\"\n            >\n              <span>View all documentation</span>\n              <lucide-icon [img]=\"ArrowRight\" class=\"size-3.5\" />\n            </a>\n          </div>\n\n          <div class=\"grid gap-4 md:grid-cols-2\">\n            @for (article of filteredArticles(); track article.id) {\n              <div\n                ui-card\n                class=\"group/article hover:border-ring/50 relative flex flex-col justify-between p-5 transition-colors duration-200 hover:shadow-sm\"\n              >\n                <div class=\"space-y-2.5\">\n                  <div class=\"flex items-center justify-between gap-2\">\n                    <span ui-badge variant=\"outline\" class=\"text-xs font-medium\">{{ article.category }}</span>\n                    <div class=\"text-muted-foreground flex items-center gap-3 text-xs\">\n                      <span class=\"inline-flex items-center gap-1\">\n                        <lucide-icon [img]=\"Clock\" class=\"size-3.5\" />\n                        {{ article.readTime }}\n                      </span>\n                      <span class=\"inline-flex items-center gap-1\">\n                        <lucide-icon [img]=\"Eye\" class=\"size-3.5\" />\n                        {{ article.viewCount }}\n                      </span>\n                    </div>\n                  </div>\n                  <div>\n                    <a\n                      href=\"#\"\n                      class=\"text-foreground group-hover/article:text-primary flex items-start justify-between gap-2 text-base font-semibold tracking-tight transition-colors\"\n                    >\n                      <span>{{ article.title }}</span>\n                      <lucide-icon\n                        [img]=\"ArrowUpRight\"\n                        class=\"text-muted-foreground group-hover/article:text-primary size-4 shrink-0 transition-transform duration-200 group-hover/article:translate-x-0.5 group-hover/article:-translate-y-0.5\"\n                      />\n                    </a>\n                    <p class=\"text-muted-foreground mt-1 line-clamp-2 text-sm leading-relaxed\">\n                      {{ article.description }}\n                    </p>\n                  </div>\n                </div>\n              </div>\n            }\n          </div>\n        </section>\n      }\n\n      <!-- Community & Live Support Footer Banner -->\n      <div ui-card class=\"border-border bg-card/60 overflow-hidden shadow-xs\">\n        <div\n          ui-card-content\n          class=\"flex flex-col items-center justify-between gap-6 p-8 text-center sm:p-10 md:flex-row md:text-left\"\n        >\n          <div class=\"max-w-xl space-y-2\">\n            <h3 class=\"text-xl font-semibold tracking-tight sm:text-2xl\">\n              Can&apos;t find what you&apos;re looking for?\n            </h3>\n            <p class=\"text-muted-foreground text-sm leading-relaxed\">\n              Our support engineers are available 24/7 to help resolve technical issues, or join our Discord community\n              to connect with other developers.\n            </p>\n          </div>\n          <div class=\"flex shrink-0 flex-wrap items-center justify-center gap-3\">\n            <button ui-button class=\"gap-2\">\n              <lucide-icon [img]=\"MessageSquare\" class=\"size-4\" />\n              <span>Contact Support</span>\n            </button>\n            <button ui-button variant=\"outline\" class=\"gap-2\">\n              <lucide-icon [img]=\"Users\" class=\"size-4\" />\n              <span>Join Discord Community</span>\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiKnowledgeBaseHubComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly ArrowUpRight = ArrowUpRight\n  protected readonly BookOpen = BookOpen\n  protected readonly Clock = Clock\n  protected readonly Eye = Eye\n  protected readonly MessageSquare = MessageSquare\n  protected readonly Search = Search\n  protected readonly Users = Users\n  protected readonly X = X\n\n  protected readonly topics = quickTopics\n\n  @Input() set initialSearch(v: string) {\n    this.searchQuery.set(v ?? '')\n  }\n  @Input('class') className?: string\n\n  readonly categories = signal<Category[]>([...defaultCategories])\n  readonly articles = signal<Article[]>([...defaultArticles])\n  readonly searchQuery = signal('')\n\n  readonly filteredCategories = computed(() => {\n    const q = this.searchQuery().trim().toLowerCase()\n    if (!q) return this.categories()\n    return this.categories().filter(\n      (c) =>\n        c.title.toLowerCase().includes(q) ||\n        c.description.toLowerCase().includes(q) ||\n        c.featuredTopics.some((t) => t.toLowerCase().includes(q)),\n    )\n  })\n\n  readonly filteredArticles = computed(() => {\n    const q = this.searchQuery().trim().toLowerCase()\n    if (!q) return this.articles()\n    return this.articles().filter(\n      (a) =>\n        a.title.toLowerCase().includes(q) ||\n        a.description.toLowerCase().includes(q) ||\n        a.category.toLowerCase().includes(q),\n    )\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-12 py-6', this.className)\n  }\n\n  categoryIcon(category: Category): unknown {\n    return category.icon\n  }\n\n  topicPillClass(topic: string): string {\n    const active = this.searchQuery() === topic\n    return cn(\n      'focus-visible:ring-ring inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none',\n      active\n        ? 'bg-primary text-primary-foreground border-primary'\n        : 'bg-muted/50 text-muted-foreground hover:bg-muted hover:text-foreground border-transparent',\n    )\n  }\n\n  selectTopic(topic: string): void {\n    this.searchQuery.set(this.searchQuery() === topic ? '' : topic)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/knowledge-base-hub/knowledge-base-hub.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/knowledge-base-hub/index.ts",
      "content": "export { UiKnowledgeBaseHubComponent } from './knowledge-base-hub.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/knowledge-base-hub/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json"
  ],
  "description": "Self-serve help center and customer documentation hub with hero search, quick topic pills, category cards grid, trending articles, and community support banner.",
  "categories": [
    "media",
    "support",
    "marketing"
  ]
}