{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "transaction-feed",
  "title": "Transaction Feed",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/transaction-feed/transaction-feed.component.ts",
      "content": "// Brex/Ramp-style real-time corporate transaction feed with expense categorization,\n// summary financial stat cards, search and filter toolbar, cardholder metadata, receipt\n// attachment triggers, and row action menus. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  Building2,\n  Car,\n  Clock,\n  Cloud,\n  CreditCard,\n  Download,\n  Eye,\n  FileCheck,\n  GitBranch,\n  LucideAngularModule,\n  Mail,\n  MessageSquare,\n  MoreHorizontal,\n  Palette,\n  Paperclip,\n  Plane,\n  Plus,\n  Receipt,\n  RotateCcw,\n  Search,\n  ShieldAlert,\n  TrendingUp,\n  Upload,\n  Utensils,\n  type LucideIconData,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport {\n  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.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 {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport type TransactionStatus = 'cleared' | 'pending' | 'receipt_needed'\nexport type CategoryKey = 'cloud' | 'travel' | 'saas' | 'transport' | 'office' | 'meals'\n\nexport interface Cardholder {\n  name: string\n  avatar: string\n  initials: string\n  cardLast4: string\n  cardType: 'Virtual' | 'Physical'\n}\n\nexport interface Transaction {\n  id: string\n  merchant: string\n  merchantNote: string\n  merchantIcon: LucideIconData\n  category: string\n  categoryKey: CategoryKey\n  cardholder: Cardholder\n  date: string\n  status: TransactionStatus\n  statusLabel: string\n  amount: string\n  isRefund: boolean\n  hasReceipt: boolean\n  receiptFileName?: string\n}\n\nconst categoryStyles: Record<CategoryKey, string> = {\n  cloud: 'bg-info/10 text-info border-info/20',\n  travel: 'bg-chart-1/10 text-chart-1 border-chart-1/20',\n  saas: 'bg-chart-2/10 text-chart-2 border-chart-2/20',\n  transport: 'bg-warning/10 text-warning border-warning/20',\n  office: 'bg-chart-3/10 text-chart-3 border-chart-3/20',\n  meals: 'bg-success/10 text-success border-success/20',\n}\n\nconst statusStyles: Record<TransactionStatus, string> = {\n  cleared: 'bg-success/10 text-success border-success/20',\n  pending: 'bg-info/10 text-info border-info/20',\n  receipt_needed: 'bg-warning/10 text-warning border-warning/20',\n}\n\nconst statusDots: Record<TransactionStatus, string> = {\n  cleared: 'bg-success',\n  pending: 'bg-info',\n  receipt_needed: 'bg-warning',\n}\n\nconst transactions: Transaction[] = [\n  {\n    id: 'tx-01',\n    merchant: 'AWS Cloud Services',\n    merchantNote: 'Production us-east-1 compute & S3',\n    merchantIcon: Cloud,\n    category: 'Cloud Hosting',\n    categoryKey: 'cloud',\n    cardholder: {\n      name: 'Elena Rostova',\n      avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',\n      initials: 'ER',\n      cardLast4: '4892',\n      cardType: 'Virtual',\n    },\n    date: 'Aug 21, 2026 · 14:28',\n    status: 'cleared',\n    statusLabel: 'Cleared',\n    amount: '-$1,420.00',\n    isRefund: false,\n    hasReceipt: true,\n    receiptFileName: 'aws-invoice-aug-2026.pdf',\n  },\n  {\n    id: 'tx-02',\n    merchant: 'Delta Air Lines',\n    merchantNote: 'SFO → JFK nonstop (Q3 Conf)',\n    merchantIcon: Plane,\n    category: 'Travel',\n    categoryKey: 'travel',\n    cardholder: {\n      name: 'Marcus Chen',\n      avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&auto=format&fit=crop&q=80',\n      initials: 'MC',\n      cardLast4: '3190',\n      cardType: 'Physical',\n    },\n    date: 'Aug 21, 2026 · 11:15',\n    status: 'pending',\n    statusLabel: 'Pending',\n    amount: '-$849.00',\n    isRefund: false,\n    hasReceipt: true,\n    receiptFileName: 'delta-eticket-dl8932.pdf',\n  },\n  {\n    id: 'tx-03',\n    merchant: 'GitHub Enterprise',\n    merchantNote: '42 Enterprise team seats',\n    merchantIcon: GitBranch,\n    category: 'SaaS',\n    categoryKey: 'saas',\n    cardholder: {\n      name: 'Amara Okafor',\n      avatar: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&auto=format&fit=crop&q=80',\n      initials: 'AO',\n      cardLast4: '9455',\n      cardType: 'Virtual',\n    },\n    date: 'Aug 20, 2026 · 09:00',\n    status: 'cleared',\n    statusLabel: 'Cleared',\n    amount: '-$882.00',\n    isRefund: false,\n    hasReceipt: true,\n    receiptFileName: 'github-inv-aug2026.pdf',\n  },\n  {\n    id: 'tx-04',\n    merchant: 'Uber for Business',\n    merchantNote: 'Client dinner return ride',\n    merchantIcon: Car,\n    category: 'Transport',\n    categoryKey: 'transport',\n    cardholder: {\n      name: 'Sarah Jenkins',\n      avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&auto=format&fit=crop&q=80',\n      initials: 'SJ',\n      cardLast4: '6021',\n      cardType: 'Physical',\n    },\n    date: 'Aug 19, 2026 · 22:45',\n    status: 'receipt_needed',\n    statusLabel: 'Receipt Needed',\n    amount: '-$48.50',\n    isRefund: false,\n    hasReceipt: false,\n  },\n  {\n    id: 'tx-05',\n    merchant: 'Figma Professional',\n    merchantNote: 'Design team organization plan',\n    merchantIcon: Palette,\n    category: 'SaaS',\n    categoryKey: 'saas',\n    cardholder: {\n      name: 'Priya Nair',\n      avatar: 'https://images.unsplash.com/photo-1580489944761-15a19d654956?w=100&auto=format&fit=crop&q=80',\n      initials: 'PN',\n      cardLast4: '7712',\n      cardType: 'Virtual',\n    },\n    date: 'Aug 19, 2026 · 16:30',\n    status: 'cleared',\n    statusLabel: 'Cleared',\n    amount: '-$360.00',\n    isRefund: false,\n    hasReceipt: true,\n    receiptFileName: 'figma-subscription-receipt.pdf',\n  },\n  {\n    id: 'tx-06',\n    merchant: 'WeWork Coworking',\n    merchantNote: 'NYC satellite team hot desks',\n    merchantIcon: Building2,\n    category: 'Office',\n    categoryKey: 'office',\n    cardholder: {\n      name: 'Jonas Weber',\n      avatar: 'https://images.unsplash.com/photo-1522075469751-3a6694fb2f61?w=100&auto=format&fit=crop&q=80',\n      initials: 'JW',\n      cardLast4: '1109',\n      cardType: 'Physical',\n    },\n    date: 'Aug 18, 2026 · 10:00',\n    status: 'receipt_needed',\n    statusLabel: 'Receipt Needed',\n    amount: '-$1,250.00',\n    isRefund: false,\n    hasReceipt: false,\n  },\n  {\n    id: 'tx-07',\n    merchant: 'Google Workspace',\n    merchantNote: 'Security & suite - 55 users',\n    merchantIcon: Mail,\n    category: 'SaaS',\n    categoryKey: 'saas',\n    cardholder: {\n      name: 'Elena Rostova',\n      avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',\n      initials: 'ER',\n      cardLast4: '4892',\n      cardType: 'Virtual',\n    },\n    date: 'Aug 17, 2026 · 08:30',\n    status: 'cleared',\n    statusLabel: 'Cleared',\n    amount: '-$660.00',\n    isRefund: false,\n    hasReceipt: true,\n    receiptFileName: 'google-workspace-statement.pdf',\n  },\n  {\n    id: 'tx-08',\n    merchant: 'Slack Pro',\n    merchantNote: 'Vendor annual prepay refund adjustment',\n    merchantIcon: MessageSquare,\n    category: 'SaaS',\n    categoryKey: 'saas',\n    cardholder: {\n      name: 'Amara Okafor',\n      avatar: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=100&auto=format&fit=crop&q=80',\n      initials: 'AO',\n      cardLast4: '9455',\n      cardType: 'Virtual',\n    },\n    date: 'Aug 16, 2026 · 15:40',\n    status: 'cleared',\n    statusLabel: 'Cleared',\n    amount: '+$5,000.00',\n    isRefund: true,\n    hasReceipt: true,\n    receiptFileName: 'slack-credit-adjustment.pdf',\n  },\n  {\n    id: 'tx-09',\n    merchant: 'Sweetgreen Catering',\n    merchantNote: 'Quarterly all-hands team lunch',\n    merchantIcon: Utensils,\n    category: 'Meals',\n    categoryKey: 'meals',\n    cardholder: {\n      name: 'Marcus Chen',\n      avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&auto=format&fit=crop&q=80',\n      initials: 'MC',\n      cardLast4: '3190',\n      cardType: 'Physical',\n    },\n    date: 'Aug 15, 2026 · 12:15',\n    status: 'receipt_needed',\n    statusLabel: 'Receipt Needed',\n    amount: '-$320.00',\n    isRefund: false,\n    hasReceipt: false,\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-transaction-feed, [ui-transaction-feed]',\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    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiAvatarImageComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiTableComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"transaction-feed\" [class]=\"rootClass\">\n      <!-- Header -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <h2 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">Transactions</h2>\n          <p class=\"text-muted-foreground text-sm\">\n            Real-time ledger of card expenses, wire transfers, and subscription charges.\n          </p>\n        </div>\n        <div class=\"flex items-center gap-2.5\">\n          <button ui-button aria-label=\"Download attachment\" variant=\"outline\" size=\"sm\">\n            <lucide-icon [img]=\"Download\" class=\"size-4\" />\n            Export CSV\n          </button>\n          <button ui-button size=\"sm\">\n            <lucide-icon [img]=\"Plus\" class=\"size-4\" />\n            Add Transaction\n          </button>\n        </div>\n      </div>\n\n      <!-- 4 Financial Stat Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Total Spent This Month</p>\n              <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"CreditCard\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">$42,850.00</p>\n            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n              <span class=\"text-success inline-flex items-center font-medium\">\n                <lucide-icon [img]=\"TrendingUp\" class=\"mr-0.5 size-3.5\" />\n                +8.4%\n              </span>\n              <span>vs previous month</span>\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Pending Approvals</p>\n              <div class=\"bg-info/10 text-info flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"Clock\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">6 transactions</p>\n            <p class=\"text-muted-foreground text-xs\">$3,410.00 requiring review</p>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Missing Receipts</p>\n              <span ui-badge wrap variant=\"outline\" class=\"border-warning/30 bg-warning/10 text-warning text-xs\">\n                Action required\n              </span>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">3</p>\n            <p class=\"text-muted-foreground text-xs\">Total $1,618.50 unverified</p>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Average Transaction</p>\n              <div class=\"bg-muted text-muted-foreground flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"Receipt\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">$185.20</p>\n            <p class=\"text-muted-foreground text-xs\">Across 231 card charges</p>\n          </div>\n        </div>\n      </div>\n\n      <!-- Filter Toolbar -->\n      <div\n        class=\"border-border bg-card flex flex-col gap-3 rounded-lg border p-3 shadow-xs md:flex-row md:items-center md:justify-between\"\n      >\n        <div class=\"flex flex-1 flex-col gap-2.5 sm:flex-row sm:items-center\">\n          <div class=\"relative min-w-0 flex-1\">\n            <lucide-icon\n              [img]=\"Search\"\n              class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2\"\n            />\n            <ui-input\n              [value]=\"search()\"\n              (valueChange)=\"search.set($event)\"\n              type=\"search\"\n              placeholder=\"Search transactions...\"\n              class=\"h-9 w-full pl-9 text-xs sm:text-sm\"\n            />\n          </div>\n\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <ui-select [value]=\"categoryFilter()\" (valueChange)=\"categoryFilter.set($event)\">\n              <button ui-select-trigger class=\"h-9 w-full text-xs sm:w-40\">\n                <ui-select-value placeholder=\"All Categories\" />\n              </button>\n              <ui-select-content>\n                <ui-select-item value=\"all\">All Categories</ui-select-item>\n                <ui-select-item value=\"cloud\">Cloud Hosting</ui-select-item>\n                <ui-select-item value=\"saas\">Software & SaaS</ui-select-item>\n                <ui-select-item value=\"travel\">Travel</ui-select-item>\n                <ui-select-item value=\"meals\">Meals & Dining</ui-select-item>\n                <ui-select-item value=\"office\">Office</ui-select-item>\n                <ui-select-item value=\"transport\">Transport</ui-select-item>\n              </ui-select-content>\n            </ui-select>\n\n            <ui-select [value]=\"dateRangeFilter()\" (valueChange)=\"dateRangeFilter.set($event)\">\n              <button ui-select-trigger class=\"h-9 w-full text-xs sm:w-36\">\n                <ui-select-value placeholder=\"Date Range\" />\n              </button>\n              <ui-select-content>\n                <ui-select-item value=\"this_month\">This Month</ui-select-item>\n                <ui-select-item value=\"last_30\">Last 30 Days</ui-select-item>\n                <ui-select-item value=\"last_90\">Last 90 Days</ui-select-item>\n                <ui-select-item value=\"ytd\">Year to Date</ui-select-item>\n                <ui-select-item value=\"all_time\">All Time</ui-select-item>\n              </ui-select-content>\n            </ui-select>\n\n            <ui-select [value]=\"statusFilter()\" (valueChange)=\"statusFilter.set($event)\">\n              <button ui-select-trigger class=\"h-9 w-full text-xs sm:w-36\">\n                <ui-select-value placeholder=\"All Statuses\" />\n              </button>\n              <ui-select-content>\n                <ui-select-item value=\"all\">All Statuses</ui-select-item>\n                <ui-select-item value=\"cleared\">Cleared</ui-select-item>\n                <ui-select-item value=\"pending\">Pending</ui-select-item>\n                <ui-select-item value=\"receipt_needed\">Receipt Needed</ui-select-item>\n              </ui-select-content>\n            </ui-select>\n          </div>\n        </div>\n\n        @if (isFiltered()) {\n          <button\n            ui-button\n            variant=\"ghost\"\n            size=\"sm\"\n            class=\"text-muted-foreground hover:text-foreground h-9 self-start px-2 text-xs md:self-auto\"\n            (click)=\"resetFilters()\"\n          >\n            <lucide-icon [img]=\"RotateCcw\" class=\"mr-1.5 size-3.5\" />\n            Reset filters\n          </button>\n        }\n      </div>\n\n      <!-- Transactions Table -->\n      <div class=\"border-border bg-card overflow-hidden rounded-lg border shadow-xs\">\n        <div class=\"overflow-x-auto\">\n          <table ui-table>\n            <thead ui-table-header class=\"bg-muted/40\">\n              <tr ui-table-row>\n                <th ui-table-head class=\"min-w-[220px]\">Merchant / Note</th>\n                <th ui-table-head class=\"min-w-[130px]\">Category</th>\n                <th ui-table-head class=\"min-w-[180px]\">Cardholder</th>\n                <th ui-table-head class=\"min-w-[150px]\">Date & Time</th>\n                <th ui-table-head class=\"min-w-[130px]\">Status</th>\n                <th ui-table-head class=\"min-w-[120px] text-right\">Amount</th>\n                <th ui-table-head class=\"min-w-[110px] text-center\">Receipt</th>\n                <th ui-table-head class=\"w-12 text-right\">\n                  <span class=\"sr-only\">Actions</span>\n                </th>\n              </tr>\n            </thead>\n            <tbody ui-table-body>\n              @for (tx of filteredTransactions(); track tx.id) {\n                <tr ui-table-row class=\"hover:bg-muted/50 transition-colors\">\n                  <td ui-table-cell>\n                    <div class=\"flex items-center gap-3\">\n                      <div\n                        class=\"border-border/60 bg-muted/60 text-foreground flex size-9 shrink-0 items-center justify-center rounded-lg border shadow-xs\"\n                      >\n                        <lucide-icon [img]=\"tx.merchantIcon\" class=\"size-4\" />\n                      </div>\n                      <div class=\"min-w-0\">\n                        <p class=\"text-foreground truncate text-sm font-medium\">{{ tx.merchant }}</p>\n                        <p class=\"text-muted-foreground truncate text-xs\">{{ tx.merchantNote }}</p>\n                      </div>\n                    </div>\n                  </td>\n\n                  <td ui-table-cell>\n                    <span\n                      ui-badge\n                      variant=\"outline\"\n                      [class]=\"cn('text-xs font-normal', categoryStyles[tx.categoryKey])\"\n                    >\n                      {{ tx.category }}\n                    </span>\n                  </td>\n\n                  <td ui-table-cell>\n                    <div class=\"flex items-center gap-2.5\">\n                      <ui-avatar class=\"size-7\">\n                        <img ui-avatar-image [src]=\"tx.cardholder.avatar\" [alt]=\"tx.cardholder.name\" />\n                        <span ui-avatar-fallback class=\"text-xs\">{{ tx.cardholder.initials }}</span>\n                      </ui-avatar>\n                      <div class=\"min-w-0\">\n                        <p class=\"text-foreground truncate text-xs font-medium\">{{ tx.cardholder.name }}</p>\n                        <p class=\"text-muted-foreground flex items-center gap-1 font-mono text-xs\">\n                          <span>••••</span>\n                          <span>{{ tx.cardholder.cardLast4 }}</span>\n                        </p>\n                      </div>\n                    </div>\n                  </td>\n\n                  <td ui-table-cell class=\"text-muted-foreground text-xs whitespace-nowrap tabular-nums\">\n                    {{ tx.date }}\n                  </td>\n\n                  <td ui-table-cell>\n                    <span\n                      ui-badge\n                      variant=\"outline\"\n                      [class]=\"cn('text-xs font-medium capitalize', statusStyles[tx.status])\"\n                    >\n                      <span [class]=\"cn('mr-1.5 size-1.5 rounded-full', statusDots[tx.status])\"></span>\n                      {{ tx.statusLabel }}\n                    </span>\n                  </td>\n\n                  <td ui-table-cell class=\"text-right text-sm font-semibold whitespace-nowrap tabular-nums\">\n                    <span [class]=\"tx.isRefund ? 'text-success' : 'text-foreground'\">{{ tx.amount }}</span>\n                  </td>\n\n                  <td ui-table-cell class=\"text-center\">\n                    <div class=\"flex items-center justify-center\">\n                      @if (tx.hasReceipt) {\n                        <span\n                          class=\"text-muted-foreground hover:text-foreground bg-muted/60 hover:bg-muted inline-flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors\"\n                          [attr.title]=\"tx.receiptFileName || 'View receipt'\"\n                        >\n                          <lucide-icon [img]=\"Paperclip\" class=\"text-muted-foreground size-3.5\" />\n                          <span>Attached</span>\n                        </span>\n                      } @else {\n                        <button\n                          type=\"button\"\n                          class=\"focus-visible:ring-ring bg-warning/10 text-warning hover:bg-warning/20 inline-flex cursor-pointer items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n                        >\n                          <lucide-icon [img]=\"Upload\" class=\"size-3\" />\n                          <span>Upload</span>\n                        </button>\n                      }\n                    </div>\n                  </td>\n\n                  <td ui-table-cell class=\"text-right\">\n                    <ui-dropdown-menu>\n                      <button\n                        ui-button\n                        ui-dropdown-menu-trigger\n                        variant=\"ghost\"\n                        size=\"icon-sm\"\n                        class=\"text-muted-foreground hover:text-foreground\"\n                      >\n                        <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" />\n                        <span class=\"sr-only\">Open actions for {{ tx.merchant }}</span>\n                      </button>\n                      <ui-dropdown-menu-content align=\"end\" class=\"w-44\">\n                        <ui-dropdown-menu-item>\n                          <lucide-icon [img]=\"Eye\" class=\"mr-2 size-4\" />\n                          View Details\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-item>\n                          <lucide-icon [img]=\"Paperclip\" class=\"mr-2 size-4\" />\n                          {{ tx.hasReceipt ? 'View Receipt' : 'Attach Receipt' }}\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-item>\n                          <lucide-icon [img]=\"FileCheck\" class=\"mr-2 size-4\" />\n                          Match Invoice\n                        </ui-dropdown-menu-item>\n                        <ui-dropdown-menu-separator />\n                        <ui-dropdown-menu-item class=\"text-destructive focus:text-destructive focus:bg-destructive/10\">\n                          <lucide-icon [img]=\"ShieldAlert\" class=\"mr-2 size-4\" />\n                          Dispute\n                        </ui-dropdown-menu-item>\n                      </ui-dropdown-menu-content>\n                    </ui-dropdown-menu>\n                  </td>\n                </tr>\n              }\n\n              @if (filteredTransactions().length === 0) {\n                <tr ui-table-row>\n                  <td ui-table-cell [attr.colspan]=\"8\" class=\"h-36 text-center\">\n                    <div class=\"flex flex-col items-center justify-center gap-2\">\n                      <lucide-icon [img]=\"Search\" class=\"text-muted-foreground/50 size-6\" />\n                      <p class=\"text-foreground text-sm font-medium\">No transactions match your filters</p>\n                      <p class=\"text-muted-foreground text-xs\">\n                        Try clearing your search query or reset category/status\n                      </p>\n                      <button ui-button variant=\"outline\" size=\"sm\" class=\"mt-2 text-xs\" (click)=\"resetFilters()\">\n                        Reset filters\n                      </button>\n                    </div>\n                  </td>\n                </tr>\n              }\n            </tbody>\n          </table>\n        </div>\n\n        <!-- Pagination footer strip -->\n        <div\n          class=\"border-border bg-card text-muted-foreground flex flex-col items-center justify-between gap-3 border-t px-4 py-3 text-xs sm:flex-row\"\n        >\n          <p class=\"tabular-nums\">\n            Showing <span class=\"text-foreground font-medium\">{{ filteredTransactions().length }}</span> of\n            <span class=\"text-foreground font-medium\">{{ allTransactions.length }}</span> transactions\n          </p>\n          <div class=\"flex items-center gap-2\">\n            <button ui-button variant=\"outline\" size=\"sm\" class=\"h-8 text-xs\" disabled>Previous</button>\n            <button ui-button variant=\"outline\" size=\"sm\" class=\"h-8 text-xs\" disabled>Next</button>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiTransactionFeedComponent {\n  protected readonly Clock = Clock\n  protected readonly CreditCard = CreditCard\n  protected readonly Download = Download\n  protected readonly Eye = Eye\n  protected readonly FileCheck = FileCheck\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly Paperclip = Paperclip\n  protected readonly Plus = Plus\n  protected readonly Receipt = Receipt\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Search = Search\n  protected readonly ShieldAlert = ShieldAlert\n  protected readonly TrendingUp = TrendingUp\n  protected readonly Upload = Upload\n\n  protected readonly cn = cn\n  protected readonly categoryStyles = categoryStyles\n  protected readonly statusStyles = statusStyles\n  protected readonly statusDots = statusDots\n  protected readonly allTransactions = transactions\n\n  @Input('class') className?: string\n\n  readonly search = signal('')\n  readonly categoryFilter = signal('all')\n  readonly dateRangeFilter = signal('this_month')\n  readonly statusFilter = signal('all')\n\n  readonly isFiltered = computed(\n    () =>\n      this.search().trim() !== '' ||\n      this.categoryFilter() !== 'all' ||\n      this.dateRangeFilter() !== 'this_month' ||\n      this.statusFilter() !== 'all',\n  )\n\n  readonly filteredTransactions = computed(() => {\n    const query = this.search().trim().toLowerCase()\n    return transactions.filter((tx) => {\n      const matchesCategory = this.categoryFilter() === 'all' || tx.categoryKey === this.categoryFilter()\n      const matchesStatus = this.statusFilter() === 'all' || tx.status === this.statusFilter()\n      const matchesQuery =\n        !query ||\n        tx.merchant.toLowerCase().includes(query) ||\n        tx.merchantNote.toLowerCase().includes(query) ||\n        tx.category.toLowerCase().includes(query) ||\n        tx.cardholder.name.toLowerCase().includes(query) ||\n        tx.cardholder.cardLast4.includes(query)\n      return matchesCategory && matchesStatus && matchesQuery\n    })\n  })\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className)\n  }\n\n  resetFilters(): void {\n    this.search.set('')\n    this.categoryFilter.set('all')\n    this.dateRangeFilter.set('this_month')\n    this.statusFilter.set('all')\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/transaction-feed/transaction-feed.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/transaction-feed/index.ts",
      "content": "export {\n  UiTransactionFeedComponent,\n  type Cardholder,\n  type CategoryKey,\n  type Transaction,\n  type TransactionStatus,\n} from './transaction-feed.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/transaction-feed/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/dropdown-menu.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Brex/Ramp-style real-time corporate transaction feed with expense categorization, summary financial stat cards, search and filter toolbar, cardholder metadata, receipt attachment triggers, and row action menus.",
  "categories": [
    "finance",
    "app",
    "billing"
  ]
}