{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "support-ticket-detail",
  "title": "Support Ticket Detail",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/support-ticket-detail/support-ticket-detail.component.ts",
      "content": "// Edit this file to change the ticket, the thread, the canned macros or the copy.\n// Everything is local state: replies and internal notes are appended to the thread,\n// \"Resolve Ticket\" / \"Escalate\" / \"Send & Mark Solved\" set the status select.\nimport { Component, DestroyRef, inject, signal, ChangeDetectionStrategy } from '@angular/core'\nimport {\n  AlertCircle,\n  AlertTriangle,\n  ArrowUpRight,\n  Bold,\n  CheckCheck,\n  CheckCircle2,\n  ChevronRight,\n  Clock,\n  Code,\n  Copy,\n  Download,\n  Eye,\n  FileCode2,\n  Globe,\n  ImageIcon,\n  Italic,\n  Link2,\n  Lock,\n  LucideAngularModule,\n  MessageSquare,\n  Paperclip,\n  Send,\n  ShieldAlert,\n  Tag,\n  X,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport { UiTabsComponent, UiTabsListComponent, UiTabsTriggerComponent } from '@/ui/tabs/tabs.component'\nimport { UiTextareaComponent } from '@/ui/textarea/textarea.component'\n\nexport type SupportTicketComposerTab = 'public' | 'internal'\n\nexport interface SupportTicketThreadMessage {\n  id: string\n  authorName: string\n  authorRole: string\n  avatarText: string\n  badgeText: string\n  badgeVariant?: 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning' | 'info'\n  timestamp: string\n  isInternal: boolean\n  content: string\n}\n\nexport interface SupportTicketMacro {\n  id: string\n  name: string\n  text: string\n}\n\nconst cannedMacros: SupportTicketMacro[] = [\n  {\n    id: 'ssl-guide',\n    name: 'SSL Guide',\n    text: `Hi Sarah,\\n\\nTo resolve the ACME challenge issue, please ensure your DNS provider includes the following CAA record:\\n\\n0 issue \"letsencrypt.org\"\\n\\nOnce added, DNS propagation typically takes 2-5 minutes, after which you can re-run verification in your domain dashboard.`,\n  },\n  {\n    id: 'request-logs',\n    name: 'Request logs',\n    text: `Hi Sarah,\\n\\nCould you please provide the full HAR export and debug network logs from your browser console during the SSL verification attempt? This will help us trace the challenge request.`,\n  },\n  {\n    id: 'refund-confirmation',\n    name: 'Refund confirmation',\n    text: `Hi Sarah,\\n\\nWe have processed the billing adjustment for your dedicated SSL add-on. You will see the credit reflected on your next statement.`,\n  },\n]\n\nconst errorLog = `[2026-10-24T09:12:44Z] [SSL: CERTIFICATE_VERIFY_FAILED] ACME challenge failed for _acme-challenge.api.acme-corp.io\n[2026-10-24T09:13:58Z] DNS TXT record validation timed out after 600s. Response status: 400 Bad Request\n[2026-10-24T09:14:02Z] Detail: CAA record restriction on 'acme-corp.io' prevented Let's Encrypt issuance.`\n\n/**\n * Support ticket workbench (React `SupportTicketDetail` 1:1): ticket header with status select\n * and SLA pill, the conversation thread (customer message with error log + attachment, an\n * internal note, an agent reply, then anything sent from the composer), the public-reply /\n * internal-note composer with canned macros and a formatting toolbar, and the customer,\n * ticket-attribute and SLA sidebar cards.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-support-ticket-detail, [ui-support-ticket-detail]',\n  standalone: true,\n  host: { class: 'block' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSeparatorComponent,\n    UiTabsComponent,\n    UiTabsListComponent,\n    UiTabsTriggerComponent,\n    UiTextareaComponent,\n  ],\n  template: `\n    <div data-slot=\"support-ticket-detail\" class=\"bg-background text-foreground w-full space-y-6\">\n      <!-- Ticket Header -->\n      <header class=\"bg-card rounded-xl border p-5 shadow-xs sm:p-6\">\n        <div class=\"flex flex-col gap-4\">\n          <!-- Top Breadcrumb & Actions Row -->\n          <div class=\"flex flex-wrap items-center justify-between gap-3\">\n            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n              <span>Support</span>\n              <lucide-icon [img]=\"ChevronRight\" class=\"size-3.5\" />\n              <span>Tickets</span>\n              <lucide-icon [img]=\"ChevronRight\" class=\"size-3.5\" />\n              <span class=\"text-foreground font-mono font-medium\">#TICK-8492</span>\n            </div>\n\n            <div class=\"flex items-center gap-2\">\n              <button\n                ui-button\n                variant=\"outline\"\n                size=\"sm\"\n                class=\"gap-1.5 text-xs font-medium\"\n                (click)=\"handleEscalate()\"\n              >\n                <lucide-icon [img]=\"AlertTriangle\" class=\"text-warning size-3.5\" />\n                Escalate\n              </button>\n              <button ui-button size=\"sm\" class=\"gap-1.5 text-xs font-medium\" (click)=\"handleResolve()\">\n                <lucide-icon [img]=\"CheckCircle2\" class=\"size-3.5\" />\n                Resolve Ticket\n              </button>\n            </div>\n          </div>\n\n          <!-- Ticket Title & Metadata -->\n          <div class=\"space-y-1.5\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <span class=\"text-muted-foreground font-mono text-xs font-bold tracking-tight\">#TICK-8492</span>\n              <h1 class=\"text-foreground text-lg font-bold tracking-tight sm:text-xl\">\n                Cannot configure custom domain SSL certificate\n              </h1>\n            </div>\n            <p class=\"text-muted-foreground text-xs\">\n              Opened 2 hours ago by <span class=\"text-foreground font-medium\">Sarah Davis</span> (Acme Corp) via Web\n              Portal\n            </p>\n          </div>\n\n          <ui-separator />\n\n          <!-- Status Badges & SLA Strip -->\n          <div class=\"flex flex-wrap items-center gap-3\">\n            <span ui-badge variant=\"destructive\" class=\"gap-1 font-medium\">\n              <lucide-icon [img]=\"AlertCircle\" class=\"size-3\" />\n              High Priority\n            </span>\n\n            <div class=\"w-44\">\n              <ui-select [value]=\"ticketStatus()\" (valueChange)=\"ticketStatus.set($event)\">\n                <button ui-select-trigger size=\"sm\" class=\"h-7 text-xs font-medium\">\n                  <ui-select-value placeholder=\"Status\" />\n                </button>\n                <ui-select-content>\n                  <ui-select-item value=\"in_progress\">\n                    <span class=\"flex items-center gap-1.5\"\n                      ><span class=\"bg-warning size-2 rounded-full\"></span>In Progress</span\n                    >\n                  </ui-select-item>\n                  <ui-select-item value=\"waiting_on_customer\">\n                    <span class=\"flex items-center gap-1.5\"\n                      ><span class=\"bg-info size-2 rounded-full\"></span>Waiting on Customer</span\n                    >\n                  </ui-select-item>\n                  <ui-select-item value=\"escalated\">\n                    <span class=\"flex items-center gap-1.5\"\n                      ><span class=\"bg-destructive size-2 rounded-full\"></span>Escalated</span\n                    >\n                  </ui-select-item>\n                  <ui-select-item value=\"resolved\">\n                    <span class=\"flex items-center gap-1.5\"\n                      ><span class=\"bg-success size-2 rounded-full\"></span>Resolved</span\n                    >\n                  </ui-select-item>\n                </ui-select-content>\n              </ui-select>\n            </div>\n\n            <div\n              class=\"border-warning/30 bg-warning/10 text-warning inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium\"\n            >\n              <lucide-icon [img]=\"Clock\" class=\"size-3.5\" />\n              <span>SLA: 42m remaining</span>\n            </div>\n\n            <span ui-badge variant=\"outline\" class=\"text-muted-foreground text-xs\">SSL &amp; Custom Domains</span>\n          </div>\n        </div>\n      </header>\n\n      <!-- 2-Column Workspace -->\n      <div class=\"grid grid-cols-1 gap-6 lg:grid-cols-12\">\n        <!-- Left Column: Conversation Thread & Composer (2/3) -->\n        <main class=\"space-y-6 lg:col-span-8\">\n          <!-- Message 1: Customer Initial Request -->\n          <article class=\"bg-card rounded-xl border p-5 shadow-xs sm:p-6\">\n            <div class=\"flex items-start justify-between gap-3\">\n              <div class=\"flex items-center gap-3\">\n                <span ui-avatar class=\"size-10\">\n                  <span ui-avatar-fallback class=\"bg-primary/10 text-primary text-xs font-semibold\">SD</span>\n                </span>\n                <div>\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"text-foreground text-sm font-semibold\">Sarah Davis</span>\n                    <span ui-badge variant=\"secondary\" class=\"text-xs font-normal\">Customer</span>\n                  </div>\n                  <p class=\"text-muted-foreground text-xs\">VP of Engineering · Acme Corp (sarah&#64;acme-corp.io)</p>\n                </div>\n              </div>\n              <time class=\"text-muted-foreground text-xs whitespace-nowrap\">Today, 09:14 AM (2h ago)</time>\n            </div>\n\n            <div class=\"text-foreground/90 mt-4 space-y-3.5 text-sm leading-relaxed\">\n              <p>\n                We are attempting to configure our custom apex domain\n                <code class=\"bg-muted text-foreground rounded px-1.5 py-0.5 font-mono text-xs\">api.acme-corp.io</code>\n                on our production cluster, but automated SSL certificate provisioning fails repeatedly during the ACME\n                DNS-01 verification challenge.\n              </p>\n              <p>\n                We verified that the CNAME and DNS TXT records exist in our Cloudflare dashboard, but the challenge\n                validation daemon continues to time out after 10 minutes with the stderr log below:\n              </p>\n\n              <!-- Error Code Block Placeholder -->\n              <div class=\"overflow-hidden rounded-lg border bg-zinc-950 text-zinc-100\">\n                <div\n                  class=\"text-muted-foreground flex items-center justify-between border-b border-zinc-800 bg-zinc-900/90 px-3.5 py-2 text-xs\"\n                >\n                  <div class=\"flex items-center gap-2\">\n                    <lucide-icon [img]=\"FileCode2\" class=\"text-destructive size-3.5\" />\n                    <span class=\"font-mono text-xs\">acme-provisioner-stderr.log</span>\n                  </div>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    class=\"h-6 gap-1 px-2 text-xs text-zinc-300 hover:bg-zinc-800 hover:text-zinc-100\"\n                    (click)=\"copyErrorLog()\"\n                  >\n                    <lucide-icon [img]=\"Copy\" class=\"size-3\" />\n                    {{ copiedLog() ? 'Copied' : 'Copy log' }}\n                  </button>\n                </div>\n                <pre\n                  class=\"overflow-x-auto p-3.5 font-mono text-xs leading-relaxed text-zinc-300\"\n                ><code>{{ errorLog }}</code></pre>\n              </div>\n\n              <!-- Screenshot / Attachment Placeholder -->\n              <div class=\"bg-muted/40 rounded-lg border p-3\">\n                <div class=\"flex items-center justify-between gap-3\">\n                  <div class=\"flex min-w-0 items-center gap-2.5\">\n                    <div class=\"bg-background flex size-9 shrink-0 items-center justify-center rounded-md border\">\n                      <lucide-icon [img]=\"ImageIcon\" class=\"text-muted-foreground size-4\" />\n                    </div>\n                    <div class=\"min-w-0\">\n                      <p class=\"text-foreground truncate text-xs font-medium\">cloudflare-dns-settings.png</p>\n                      <p class=\"text-muted-foreground text-xs\">1.4 MB · PNG Image Screenshot</p>\n                    </div>\n                  </div>\n                  <div class=\"flex items-center gap-1\">\n                    <button ui-button variant=\"ghost\" size=\"sm\" class=\"h-7 text-xs\">\n                      <lucide-icon [img]=\"Eye\" class=\"size-3.5\" />\n                      Preview\n                    </button>\n                    <button ui-button aria-label=\"Download attachment\" variant=\"ghost\" size=\"sm\" class=\"h-7 text-xs\">\n                      <lucide-icon [img]=\"Download\" class=\"size-3.5\" />\n                      Download\n                    </button>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </article>\n\n          <!-- Message 2: Internal Staff Note (distinct yellow/amber tint background) -->\n          <article class=\"border-warning/30 bg-warning/10 dark:bg-warning/10 rounded-xl border p-5 shadow-xs sm:p-6\">\n            <div class=\"flex items-start justify-between gap-3\">\n              <div class=\"flex items-center gap-3\">\n                <span ui-avatar class=\"size-10\">\n                  <span ui-avatar-fallback class=\"bg-warning/20 text-warning text-xs font-semibold\">AK</span>\n                </span>\n                <div>\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"text-foreground text-sm font-semibold\">Alex Kim</span>\n                    <span\n                      ui-badge\n                      variant=\"outline\"\n                      class=\"border-warning/40 bg-warning/20 text-warning gap-1 text-xs font-medium\"\n                    >\n                      <lucide-icon [img]=\"Lock\" class=\"size-3\" />\n                      Internal Note - Only visible to team\n                    </span>\n                  </div>\n                  <p class=\"text-muted-foreground text-xs\">Senior DevOps Engineer · Tier 2 Infrastructure</p>\n                </div>\n              </div>\n              <time class=\"text-muted-foreground text-xs whitespace-nowrap\">Today, 09:35 AM (1h ago)</time>\n            </div>\n\n            <div class=\"text-foreground/90 mt-4 space-y-2 text-sm leading-relaxed\">\n              <p>\n                Checked Cloudflare DNS propagation on their nameservers via\n                <code class=\"text-foreground bg-warning/20 rounded px-1 py-0.5 font-mono text-xs\"\n                  >dig CAA acme-corp.io</code\n                >. Looks like their root zone has a CAA record restricting certificate issuance strictly to DigiCert\n                (<code class=\"text-foreground bg-warning/20 rounded px-1 py-0.5 font-mono text-xs\"\n                  >0 issue &quot;digicert.com&quot;</code\n                >), while our automated ACME pipeline requests Let&apos;s Encrypt certificates.\n              </p>\n              <p>\n                If they add\n                <code class=\"text-foreground bg-warning/20 rounded px-1 py-0.5 font-mono text-xs\"\n                  >0 issue &quot;letsencrypt.org&quot;</code\n                >\n                to their DNS CAA records, the validation handshake will complete within 2 minutes.\n              </p>\n            </div>\n          </article>\n\n          <!-- Message 3: Agent response -->\n          <article class=\"bg-card rounded-xl border p-5 shadow-xs sm:p-6\">\n            <div class=\"flex items-start justify-between gap-3\">\n              <div class=\"flex items-center gap-3\">\n                <span ui-avatar class=\"size-10\">\n                  <span ui-avatar-fallback class=\"bg-primary text-primary-foreground text-xs font-semibold\">EM</span>\n                </span>\n                <div>\n                  <div class=\"flex items-center gap-2\">\n                    <span class=\"text-foreground text-sm font-semibold\">Elena Martinez</span>\n                    <span ui-badge class=\"text-xs font-normal\">Support Engineer</span>\n                  </div>\n                  <p class=\"text-muted-foreground text-xs\">Lead Support Engineer · UIPKGE Staff</p>\n                </div>\n              </div>\n              <time class=\"text-muted-foreground text-xs whitespace-nowrap\">Today, 09:48 AM (45m ago)</time>\n            </div>\n\n            <div class=\"text-foreground/90 mt-4 space-y-3 text-sm leading-relaxed\">\n              <p>\n                Hi Sarah, thank you for providing the detailed error log! We analyzed the ACME challenge failure and\n                identified that your apex domain&apos;s DNS CAA records currently block Let&apos;s Encrypt certificate\n                issuance.\n              </p>\n              <p>Please follow these step-by-step instructions to enable Let&apos;s Encrypt validation:</p>\n              <ol class=\"text-foreground/90 list-inside list-decimal space-y-1.5 pl-1 text-xs sm:text-sm\">\n                <li>\n                  Log in to your Cloudflare DNS dashboard for\n                  <code class=\"bg-muted rounded px-1.5 py-0.5 font-mono text-xs\">acme-corp.io</code>.\n                </li>\n                <li>Navigate to <strong>DNS Settings</strong> &rarr; <strong>Add Record</strong>.</li>\n                <li>\n                  Select <strong>CAA</strong>, set Flag to\n                  <code class=\"bg-muted rounded px-1.5 py-0.5 font-mono text-xs\">0</code>, Tag to\n                  <code class=\"bg-muted rounded px-1.5 py-0.5 font-mono text-xs\">issue</code>, and Value to\n                  <code class=\"bg-muted rounded px-1.5 py-0.5 font-mono text-xs\">&quot;letsencrypt.org&quot;</code>.\n                </li>\n                <li>Allow up to 2 minutes for DNS TTL cache propagation across edge resolvers.</li>\n                <li>Return to your cluster settings dashboard and click <strong>Retry Verification</strong>.</li>\n              </ol>\n              <p>\n                Feel free to reply directly once you&apos;ve saved the record, and we&apos;ll monitor the validation\n                handshake from our side!\n              </p>\n            </div>\n          </article>\n\n          <!-- Dynamic User Messages -->\n          @for (msg of dynamicMessages(); track msg.id) {\n            <article [class]=\"articleClass(msg)\">\n              <div class=\"flex items-start justify-between gap-3\">\n                <div class=\"flex items-center gap-3\">\n                  <span ui-avatar class=\"size-10\">\n                    <span ui-avatar-fallback [class]=\"avatarFallbackClass(msg)\">{{ msg.avatarText }}</span>\n                  </span>\n                  <div>\n                    <div class=\"flex items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-semibold\">{{ msg.authorName }}</span>\n                      <span ui-badge [variant]=\"msg.isInternal ? 'outline' : 'default'\" [class]=\"badgeClass(msg)\">\n                        @if (msg.isInternal) {\n                          <lucide-icon [img]=\"Lock\" class=\"size-3\" />\n                        }\n                        {{ msg.badgeText }}\n                      </span>\n                    </div>\n                    <p class=\"text-muted-foreground text-xs\">{{ msg.authorRole }}</p>\n                  </div>\n                </div>\n                <time class=\"text-muted-foreground text-xs whitespace-nowrap\">{{ msg.timestamp }}</time>\n              </div>\n              <div class=\"text-foreground/90 mt-4 text-sm leading-relaxed whitespace-pre-wrap\">{{ msg.content }}</div>\n            </article>\n          }\n\n          <!-- Reply Box Composer -->\n          <section class=\"bg-card overflow-hidden rounded-xl border shadow-xs\">\n            <div ui-tabs [value]=\"composerTab()\" (valueChange)=\"setComposerTab($event)\" defaultValue=\"public\">\n              <!-- Composer Header with Tabs & Macro Dropdown -->\n              <div class=\"bg-muted/30 flex flex-wrap items-center justify-between gap-3 border-b px-4 py-2.5\">\n                <div ui-tabs-list class=\"h-8\">\n                  <button ui-tabs-trigger value=\"public\" class=\"gap-1.5 text-xs\">\n                    <lucide-icon [img]=\"MessageSquare\" class=\"size-3.5\" />\n                    Public Reply\n                  </button>\n                  <button ui-tabs-trigger value=\"internal\" class=\"gap-1.5 text-xs\">\n                    <lucide-icon [img]=\"Lock\" class=\"size-3.5\" />\n                    Internal Note\n                  </button>\n                </div>\n\n                <!-- Canned Responses / Macros Dropdown -->\n                <div class=\"w-48\">\n                  <ui-select [value]=\"selectedMacro()\" (valueChange)=\"handleSelectMacro($event)\">\n                    <button ui-select-trigger size=\"sm\" class=\"h-8 text-xs\">\n                      <ui-select-value placeholder=\"Canned macro...\" />\n                    </button>\n                    <ui-select-content>\n                      @for (macro of cannedMacros; track macro.id) {\n                        <ui-select-item [value]=\"macro.id\">{{ macro.name }}</ui-select-item>\n                      }\n                    </ui-select-content>\n                  </ui-select>\n                </div>\n              </div>\n\n              <div class=\"space-y-3 p-4\">\n                <!-- Rich Formatting Toolbar -->\n                <div class=\"text-muted-foreground flex items-center gap-1 border-b pb-2\">\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    class=\"size-7\"\n                    aria-label=\"Format bold\"\n                    (click)=\"applyFormat('bold')\"\n                  >\n                    <lucide-icon [img]=\"Bold\" class=\"size-3.5\" />\n                  </button>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    class=\"size-7\"\n                    aria-label=\"Format italic\"\n                    (click)=\"applyFormat('bold')\"\n                  >\n                    <lucide-icon [img]=\"Italic\" class=\"size-3.5\" />\n                  </button>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    class=\"size-7\"\n                    aria-label=\"Format code\"\n                    (click)=\"applyFormat('code')\"\n                  >\n                    <lucide-icon [img]=\"Code\" class=\"size-3.5\" />\n                  </button>\n                  <button\n                    ui-button\n                    variant=\"ghost\"\n                    size=\"icon\"\n                    class=\"size-7\"\n                    aria-label=\"Insert link\"\n                    (click)=\"applyFormat('link')\"\n                  >\n                    <lucide-icon [img]=\"Link2\" class=\"size-3.5\" />\n                  </button>\n                  <ui-separator orientation=\"vertical\" class=\"mx-1 h-4\" />\n                  <button ui-button variant=\"ghost\" size=\"icon\" class=\"size-7\" aria-label=\"Attach file\">\n                    <lucide-icon [img]=\"Paperclip\" class=\"size-3.5\" />\n                  </button>\n                  <span class=\"text-muted-foreground ml-auto text-xs\">Markdown supported</span>\n                </div>\n\n                <!-- Textarea with dynamic background if internal note -->\n                <ui-textarea\n                  [value]=\"replyDraft()\"\n                  (valueChange)=\"replyDraft.set($event)\"\n                  [placeholder]=\"\n                    composerTab() === 'public'\n                      ? 'Write a public reply to Sarah Davis...'\n                      : 'Add an internal note only visible to team members...'\n                  \"\n                  [rows]=\"4\"\n                  [class]=\"textareaClass()\"\n                />\n\n                <!-- Composer Actions -->\n                <div class=\"flex flex-wrap items-center justify-between gap-3 pt-1\">\n                  <div class=\"text-muted-foreground flex items-center gap-2 text-xs\">\n                    <lucide-icon [img]=\"Paperclip\" class=\"size-3.5\" />\n                    <span>Attachments up to 25MB</span>\n                  </div>\n\n                  <div class=\"flex items-center gap-2\">\n                    @if (composerTab() === 'public') {\n                      <button\n                        ui-button\n                        variant=\"outline\"\n                        size=\"sm\"\n                        class=\"gap-1.5 text-xs\"\n                        [disabled]=\"!hasDraft()\"\n                        (click)=\"sendAndSolve()\"\n                      >\n                        <lucide-icon [img]=\"CheckCheck\" class=\"size-3.5\" />\n                        Send &amp; Mark Solved\n                      </button>\n                    }\n                    <button ui-button size=\"sm\" class=\"gap-1.5 text-xs\" [disabled]=\"!hasDraft()\" (click)=\"sendReply()\">\n                      <lucide-icon [img]=\"Send\" class=\"size-3.5\" />\n                      {{ composerTab() === 'public' ? 'Send Reply' : 'Add Internal Note' }}\n                    </button>\n                  </div>\n                </div>\n              </div>\n            </div>\n          </section>\n        </main>\n\n        <!-- Right Column: Customer & Ticket Metadata Sidebar (1/3) -->\n        <aside class=\"space-y-6 lg:col-span-4\">\n          <!-- Customer Info Card -->\n          <ui-card>\n            <ui-card-header class=\"pb-3\">\n              <div class=\"flex items-start justify-between gap-2\">\n                <div class=\"flex items-center gap-3\">\n                  <span ui-avatar class=\"size-11\">\n                    <span ui-avatar-fallback class=\"bg-primary/10 text-primary text-sm font-semibold\">SD</span>\n                  </span>\n                  <div>\n                    <ui-card-title class=\"text-sm font-semibold\">Sarah Davis</ui-card-title>\n                    <ui-card-description class=\"text-xs\">VP of Engineering</ui-card-description>\n                  </div>\n                </div>\n                <span ui-badge variant=\"default\" class=\"text-xs font-medium\">Enterprise</span>\n              </div>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-3 pt-0\">\n              <ui-separator />\n              <dl class=\"space-y-2.5 text-xs\">\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">Company</dt>\n                  <dd class=\"text-foreground font-medium\">Acme Corp</dd>\n                </div>\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">Email</dt>\n                  <dd class=\"text-foreground max-w-[180px] truncate font-medium\">sarah&#64;acme-corp.io</dd>\n                </div>\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">Total Tickets</dt>\n                  <dd class=\"text-foreground font-medium tabular-nums\">12 (10 resolved)</dd>\n                </div>\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">Customer Since</dt>\n                  <dd class=\"text-foreground font-medium\">Jan 2024</dd>\n                </div>\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">SLA Plan</dt>\n                  <dd class=\"text-foreground font-medium\">1h Critical Response</dd>\n                </div>\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground\">Timezone</dt>\n                  <dd class=\"text-foreground font-medium\">America/Los_Angeles (UTC-7)</dd>\n                </div>\n              </dl>\n              <ui-separator />\n              <button ui-button variant=\"outline\" size=\"sm\" class=\"w-full justify-center gap-1.5 text-xs\">\n                View Customer in CRM\n                <lucide-icon [img]=\"ArrowUpRight\" class=\"size-3.5\" />\n              </button>\n            </ui-card-content>\n          </ui-card>\n\n          <!-- Ticket Attributes Card -->\n          <ui-card>\n            <ui-card-header class=\"pb-3\">\n              <ui-card-title class=\"text-sm font-semibold\">Ticket Attributes</ui-card-title>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-4 pt-0\">\n              <!-- Assigned Agent -->\n              <div class=\"space-y-1.5\">\n                <label class=\"text-muted-foreground text-xs font-medium\">Assigned Agent</label>\n                <ui-select [value]=\"assignedAgent()\" (valueChange)=\"assignedAgent.set($event)\">\n                  <button ui-select-trigger size=\"sm\" class=\"w-full text-xs\">\n                    <ui-select-value placeholder=\"Select Agent\" />\n                  </button>\n                  <ui-select-content>\n                    <ui-select-item value=\"elena_martinez\">Elena Martinez (DevOps)</ui-select-item>\n                    <ui-select-item value=\"alex_kim\">Alex Kim (Tier 2)</ui-select-item>\n                    <ui-select-item value=\"marcus_vance\">Marcus Vance (Platform)</ui-select-item>\n                    <ui-select-item value=\"unassigned\">Unassigned</ui-select-item>\n                  </ui-select-content>\n                </ui-select>\n              </div>\n\n              <!-- Department -->\n              <div class=\"space-y-1.5\">\n                <label class=\"text-muted-foreground text-xs font-medium\">Department</label>\n                <ui-select [value]=\"department()\" (valueChange)=\"department.set($event)\">\n                  <button ui-select-trigger size=\"sm\" class=\"w-full text-xs\">\n                    <ui-select-value placeholder=\"Department\" />\n                  </button>\n                  <ui-select-content>\n                    <ui-select-item value=\"platform\">Platform Infrastructure</ui-select-item>\n                    <ui-select-item value=\"billing\">Billing &amp; Subscriptions</ui-select-item>\n                    <ui-select-item value=\"core_api\">Core API Support</ui-select-item>\n                    <ui-select-item value=\"security\">Security &amp; Compliance</ui-select-item>\n                  </ui-select-content>\n                </ui-select>\n              </div>\n\n              <!-- Priority -->\n              <div class=\"space-y-1.5\">\n                <label class=\"text-muted-foreground text-xs font-medium\">Priority</label>\n                <ui-select [value]=\"ticketPriority()\" (valueChange)=\"ticketPriority.set($event)\">\n                  <button ui-select-trigger size=\"sm\" class=\"w-full text-xs\">\n                    <ui-select-value placeholder=\"Priority\" />\n                  </button>\n                  <ui-select-content>\n                    <ui-select-item value=\"urgent\">Urgent (P0)</ui-select-item>\n                    <ui-select-item value=\"high\">High (P1)</ui-select-item>\n                    <ui-select-item value=\"medium\">Medium (P2)</ui-select-item>\n                    <ui-select-item value=\"low\">Low (P3)</ui-select-item>\n                  </ui-select-content>\n                </ui-select>\n              </div>\n\n              <ui-separator />\n\n              <!-- Tags Section -->\n              <div class=\"space-y-2\">\n                <div class=\"flex items-center justify-between\">\n                  <span class=\"text-muted-foreground text-xs font-medium\">Tags</span>\n                  @if (!isAddingTag()) {\n                    <button\n                      type=\"button\"\n                      class=\"text-primary min-h-6 text-xs font-medium hover:underline\"\n                      (click)=\"isAddingTag.set(true)\"\n                    >\n                      + Add Tag\n                    </button>\n                  }\n                </div>\n\n                <div class=\"flex flex-wrap gap-1.5\">\n                  @for (tag of tags(); track tag) {\n                    <span\n                      class=\"bg-muted text-foreground inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs\"\n                    >\n                      <lucide-icon [img]=\"Tag\" class=\"text-muted-foreground size-3\" />\n                      {{ tag }}\n                      <button\n                        type=\"button\"\n                        class=\"text-muted-foreground hover:text-foreground ml-0.5\"\n                        aria-label=\"Remove tag\"\n                        (click)=\"removeTag(tag)\"\n                      >\n                        <lucide-icon [img]=\"X\" class=\"size-3\" />\n                      </button>\n                    </span>\n                  }\n                </div>\n\n                @if (isAddingTag()) {\n                  <div class=\"flex items-center gap-1.5 pt-1\">\n                    <ui-input\n                      [value]=\"newTagInput()\"\n                      (valueChange)=\"newTagInput.set($event)\"\n                      placeholder=\"New tag...\"\n                      class=\"h-7 text-xs\"\n                      (keydown)=\"onTagKeydown($event)\"\n                    />\n                    <button ui-button size=\"sm\" class=\"h-7 px-2 text-xs\" (click)=\"addTag()\">Add</button>\n                    <button\n                      ui-button\n                      variant=\"ghost\"\n                      size=\"sm\"\n                      class=\"h-7 px-2 text-xs\"\n                      (click)=\"isAddingTag.set(false)\"\n                    >\n                      Cancel\n                    </button>\n                  </div>\n                }\n              </div>\n            </ui-card-content>\n          </ui-card>\n\n          <!-- SLA & Metrics Card -->\n          <ui-card>\n            <ui-card-header class=\"pb-3\">\n              <div class=\"flex items-center justify-between\">\n                <ui-card-title class=\"text-sm font-semibold\">SLA &amp; Metrics</ui-card-title>\n                <span ui-badge variant=\"outline\" class=\"text-xs font-medium\">Active SLA</span>\n              </div>\n            </ui-card-header>\n            <ui-card-content class=\"space-y-3 pt-0\">\n              <dl class=\"space-y-3 text-xs\">\n                <div class=\"flex items-center justify-between gap-2\">\n                  <div>\n                    <dt class=\"text-foreground font-medium\">First Response Time</dt>\n                    <dd class=\"text-muted-foreground text-xs\">Target: &lt; 15m</dd>\n                  </div>\n                  <div class=\"text-right\">\n                    <span class=\"text-success font-semibold\">8m</span>\n                    <span class=\"text-muted-foreground block text-xs\">7m ahead</span>\n                  </div>\n                </div>\n\n                <ui-separator />\n\n                <div class=\"flex items-center justify-between gap-2\">\n                  <div>\n                    <dt class=\"text-foreground font-medium\">Target Resolution</dt>\n                    <dd class=\"text-muted-foreground text-xs\">Target: 4h (Elapsed: 3h 18m)</dd>\n                  </div>\n                  <div class=\"text-right\">\n                    <span class=\"text-warning font-semibold\">42m</span>\n                    <span class=\"text-muted-foreground block text-xs\">remaining</span>\n                  </div>\n                </div>\n\n                <ui-separator />\n\n                <div class=\"flex items-center justify-between gap-2\">\n                  <div>\n                    <dt class=\"text-foreground font-medium\">CSAT Prediction</dt>\n                    <dd class=\"text-muted-foreground text-xs\">Based on sentiment analysis</dd>\n                  </div>\n                  <span ui-badge variant=\"secondary\" class=\"font-medium\">98% High</span>\n                </div>\n\n                <ui-separator />\n\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"Globe\" class=\"size-3.5\" />\n                    Source Channel\n                  </dt>\n                  <dd class=\"text-foreground font-medium\">Web Portal</dd>\n                </div>\n\n                <div class=\"flex items-center justify-between gap-2\">\n                  <dt class=\"text-muted-foreground flex items-center gap-1.5\">\n                    <lucide-icon [img]=\"ShieldAlert\" class=\"size-3.5\" />\n                    Escalation Tier\n                  </dt>\n                  <dd class=\"text-foreground font-medium\">Tier 2 Support</dd>\n                </div>\n              </dl>\n            </ui-card-content>\n          </ui-card>\n        </aside>\n      </div>\n    </div>\n  `,\n})\nexport class UiSupportTicketDetailComponent {\n  private copyTimer?: ReturnType<typeof setTimeout>\n\n  readonly ticketStatus = signal('in_progress')\n  readonly ticketPriority = signal('high')\n  readonly assignedAgent = signal('elena_martinez')\n  readonly department = signal('platform')\n  readonly composerTab = signal<SupportTicketComposerTab>('public')\n  readonly replyDraft = signal('')\n  readonly selectedMacro = signal('')\n  readonly copiedLog = signal(false)\n  readonly tags = signal(['ssl-certificate', 'custom-domain', 'acme-challenge', 'enterprise-tier'])\n  readonly newTagInput = signal('')\n  readonly isAddingTag = signal(false)\n  readonly dynamicMessages = signal<SupportTicketThreadMessage[]>([])\n\n  protected readonly cannedMacros = cannedMacros\n  protected readonly errorLog = errorLog\n\n  protected readonly AlertCircle = AlertCircle\n  protected readonly AlertTriangle = AlertTriangle\n  protected readonly ArrowUpRight = ArrowUpRight\n  protected readonly Bold = Bold\n  protected readonly CheckCheck = CheckCheck\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly ChevronRight = ChevronRight\n  protected readonly Clock = Clock\n  protected readonly Code = Code\n  protected readonly Copy = Copy\n  protected readonly Download = Download\n  protected readonly Eye = Eye\n  protected readonly FileCode2 = FileCode2\n  protected readonly Globe = Globe\n  protected readonly ImageIcon = ImageIcon\n  protected readonly Italic = Italic\n  protected readonly Link2 = Link2\n  protected readonly Lock = Lock\n  protected readonly MessageSquare = MessageSquare\n  protected readonly Paperclip = Paperclip\n  protected readonly Send = Send\n  protected readonly ShieldAlert = ShieldAlert\n  protected readonly Tag = Tag\n  protected readonly X = X\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => clearTimeout(this.copyTimer))\n  }\n\n  hasDraft(): boolean {\n    return this.replyDraft().trim() !== ''\n  }\n\n  setComposerTab(value: string): void {\n    this.composerTab.set(value === 'internal' ? 'internal' : 'public')\n  }\n\n  handleEscalate(): void {\n    this.ticketStatus.set('escalated')\n  }\n\n  handleResolve(): void {\n    this.ticketStatus.set('resolved')\n  }\n\n  handleSelectMacro(value: string): void {\n    this.selectedMacro.set(value)\n    const macro = cannedMacros.find((m) => m.id === value)\n    if (macro) this.replyDraft.set(macro.text)\n  }\n\n  applyFormat(type: 'bold' | 'code' | 'link'): void {\n    const prev = this.replyDraft()\n    if (type === 'bold') {\n      this.replyDraft.set(prev ? `${prev} **bold text**` : '**bold text**')\n    } else if (type === 'code') {\n      this.replyDraft.set(prev ? `${prev}\\n\\`\\`\\`\\ncode snippet\\n\\`\\`\\`` : '```\\ncode snippet\\n```')\n    } else if (type === 'link') {\n      this.replyDraft.set(prev ? `${prev} [link title](https://example.com)` : '[link title](https://example.com)')\n    }\n  }\n\n  copyErrorLog(): void {\n    this.copiedLog.set(true)\n    clearTimeout(this.copyTimer)\n    this.copyTimer = setTimeout(() => this.copiedLog.set(false), 2000)\n  }\n\n  sendReply(): void {\n    const body = this.replyDraft().trim()\n    if (!body) return\n    const isInternal = this.composerTab() === 'internal'\n    this.dynamicMessages.update((prev) => [\n      ...prev,\n      {\n        id: `msg-${Date.now()}`,\n        authorName: isInternal ? 'Alex Kim' : 'Elena Martinez',\n        authorRole: isInternal ? 'Senior DevOps' : 'Lead Support Engineer',\n        avatarText: isInternal ? 'AK' : 'EM',\n        badgeText: isInternal ? 'Internal Note - Only visible to team' : 'Support Engineer',\n        badgeVariant: isInternal ? 'warning' : 'default',\n        timestamp: 'Just now',\n        isInternal,\n        content: body,\n      },\n    ])\n    this.replyDraft.set('')\n    this.selectedMacro.set('')\n  }\n\n  sendAndSolve(): void {\n    this.sendReply()\n    this.ticketStatus.set('resolved')\n  }\n\n  addTag(): void {\n    const t = this.newTagInput().trim().toLowerCase()\n    if (t && !this.tags().includes(t)) this.tags.update((prev) => [...prev, t])\n    this.newTagInput.set('')\n    this.isAddingTag.set(false)\n  }\n\n  removeTag(tagToRemove: string): void {\n    this.tags.update((prev) => prev.filter((t) => t !== tagToRemove))\n  }\n\n  onTagKeydown(event: Event): void {\n    if ((event as KeyboardEvent).key === 'Enter') {\n      event.preventDefault()\n      this.addTag()\n    }\n  }\n\n  textareaClass(): string {\n    return cn(\n      'resize-y text-sm',\n      this.composerTab() === 'internal' && 'border-warning/30 bg-warning/5 focus-visible:ring-warning/20',\n    )\n  }\n\n  articleClass(msg: SupportTicketThreadMessage): string {\n    return cn('rounded-xl border p-5 shadow-xs sm:p-6', msg.isInternal ? 'border-warning/30 bg-warning/10' : 'bg-card')\n  }\n\n  avatarFallbackClass(msg: SupportTicketThreadMessage): string {\n    return cn(\n      'text-xs font-semibold',\n      msg.isInternal ? 'bg-warning/20 text-warning' : 'bg-primary text-primary-foreground',\n    )\n  }\n\n  badgeClass(msg: SupportTicketThreadMessage): string {\n    return msg.isInternal\n      ? 'border-warning/40 bg-warning/20 text-warning gap-1 text-xs font-medium'\n      : 'text-xs font-normal'\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/support-ticket-detail/support-ticket-detail.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/support-ticket-detail/index.ts",
      "content": "export {\n  UiSupportTicketDetailComponent,\n  type SupportTicketComposerTab,\n  type SupportTicketMacro,\n  type SupportTicketThreadMessage,\n} from './support-ticket-detail.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/support-ticket-detail/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "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",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/tabs.json",
    "https://uipkge.dev/r/angular/textarea.json"
  ],
  "description": "Customer support ticket conversation thread, SLA monitor & staff workbench with internal notes, canned response macros, and customer metadata sidebar.",
  "categories": [
    "communication",
    "app",
    "support"
  ]
}