Menu

Change language

GitHub repo

Payouts Management Table

blockfinance

Merchant payout management page: automated payout schedule controls, instant payout trigger, summary balance cards for available, in-transit, and rolling reserve balances, verified settlement bank account destination card, and an itemized batch transfer history table with gross/fee/net amounts, status badges, and action menus.

Installation

$npx uipkge-ng@latest add payouts-management-table
Manifest:https://uipkge.dev/r/angular/payouts-management-table.jsonInstalls to:src/app/components/blocks/payouts-management-table/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

PayoutBatch
interface PayoutBatch {
  id: string
  initiatedDate: string
  arrivalDate: string
  destination: string
  grossAmount: string
  feeAmount: string
  netAmount: string
  status: PayoutStatus
}
SummaryItem
interface SummaryItem {
  title: string
  amount: string
  description: string
  icon: unknown
  iconClass: string
}

Files installed (2)

  • src/app/components/blocks/payouts-management-table/payouts-management-table.component.ts13 kB
    import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
    import {
      Building2,
      CalendarClock,
      CheckCircle2,
      Clock,
      Download,
      ExternalLink,
      FileText,
      Landmark,
      LucideAngularModule,
      MoreHorizontal,
      ShieldCheck,
      Wallet,
      Zap,
    } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'
    import {
      UiDropdownMenuComponent,
      UiDropdownMenuContentComponent,
      UiDropdownMenuItemComponent,
      UiDropdownMenuSeparatorComponent,
      UiDropdownMenuTriggerComponent,
    } from '@/ui/dropdown-menu/dropdown-menu.component'
    import {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    
    export type PayoutStatus = 'paid' | 'in_transit' | 'pending' | 'failed'
    
    export interface PayoutBatch {
      id: string
      initiatedDate: string
      arrivalDate: string
      destination: string
      grossAmount: string
      feeAmount: string
      netAmount: string
      status: PayoutStatus
    }
    
    const statusConfig: Record<PayoutStatus, { label: string; className: string }> = {
      paid: { label: 'Paid', className: 'bg-success/10 text-success border-success/20' },
      in_transit: { label: 'In Transit', className: 'bg-info/10 text-info border-info/20' },
      pending: { label: 'Pending', className: 'bg-warning/10 text-warning border-warning/20' },
      failed: { label: 'Failed', className: 'bg-destructive/10 text-destructive border-destructive/20' },
    }
    
    interface SummaryItem {
      title: string
      amount: string
      description: string
      icon: unknown
      iconClass: string
    }
    
    const summary: SummaryItem[] = [
      {
        title: 'Available for Payout',
        amount: '$18,420.50',
        description: 'Auto-transfers daily at 00:00 UTC',
        icon: Wallet,
        iconClass: 'bg-success/10 text-success',
      },
      {
        title: 'In Transit / Scheduled',
        amount: '$4,250.00',
        description: 'Estimated arrival: Aug 23, 2026',
        icon: Clock,
        iconClass: 'bg-info/10 text-info',
      },
      {
        title: 'Rolling Reserve / Held',
        amount: '$1,500.00',
        description: 'Released in 14 days',
        icon: ShieldCheck,
        iconClass: 'bg-warning/10 text-warning',
      },
    ]
    
    const payouts: PayoutBatch[] = [
      {
        id: 'PO-84920',
        initiatedDate: 'Aug 21, 2026',
        arrivalDate: 'Est. Aug 23, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$12,780.00',
        feeAmount: '-$330.00',
        netAmount: '$12,450.00',
        status: 'in_transit',
      },
      {
        id: 'PO-84919',
        initiatedDate: 'Aug 20, 2026',
        arrivalDate: 'Est. Aug 22, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$8,950.00',
        feeAmount: '-$245.50',
        netAmount: '$8,704.50',
        status: 'pending',
      },
      {
        id: 'PO-84918',
        initiatedDate: 'Aug 19, 2026',
        arrivalDate: 'Aug 21, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$15,400.00',
        feeAmount: '-$398.00',
        netAmount: '$15,002.00',
        status: 'paid',
      },
      {
        id: 'PO-84917',
        initiatedDate: 'Aug 18, 2026',
        arrivalDate: 'Aug 20, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$9,220.00',
        feeAmount: '-$238.40',
        netAmount: '$8,981.60',
        status: 'paid',
      },
      {
        id: 'PO-84916',
        initiatedDate: 'Aug 17, 2026',
        arrivalDate: 'Aug 19, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$6,100.00',
        feeAmount: '-$162.00',
        netAmount: '$5,938.00',
        status: 'failed',
      },
      {
        id: 'PO-84915',
        initiatedDate: 'Aug 16, 2026',
        arrivalDate: 'Aug 18, 2026',
        destination: 'Chase •••• 4892',
        grossAmount: '$18,340.00',
        feeAmount: '-$460.00',
        netAmount: '$17,880.00',
        status: 'paid',
      },
    ]
    
    /**
     * PayoutsManagementTable -- the React block, 1:1: header with payout schedule + instant
     * payout actions, three KPI cards (available, in-transit, rolling reserve), a verified
     * settlement bank destination card, and a batch transfer history table with gross/fee/net
     * amounts, status badges and per-row action menus, closed by a pagination hint.
     */
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-payouts-management-table, [ui-payouts-management-table]',
      standalone: true,
      // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiBadgeComponent,
        UiButtonComponent,
        UiCardComponent,
        UiCardContentComponent,
        UiDropdownMenuComponent,
        UiDropdownMenuContentComponent,
        UiDropdownMenuItemComponent,
        UiDropdownMenuSeparatorComponent,
        UiDropdownMenuTriggerComponent,
        UiTableComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
      ],
      template: `
        <div data-slot="payouts-management-table" [class]="rootClass">
          <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
            <div>
              <h2 class="text-2xl font-bold tracking-tight">Payouts & Transfers</h2>
              <p class="text-muted-foreground mt-1 text-sm">
                Track automated scheduled settlements, rolling reserves, and bank transfers.
              </p>
            </div>
            <div class="flex flex-wrap items-center gap-2.5">
              <button ui-button variant="outline">
                <lucide-icon [img]="CalendarClock" />
                Configure Payout Schedule
              </button>
              <button ui-button>
                <lucide-icon [img]="Zap" />
                Instant Payout
              </button>
            </div>
          </div>
    
          <div class="grid grid-cols-1 gap-4 sm:grid-cols-3">
            @for (item of summary; track item.title) {
              <div ui-card class="shadow-xs">
                <div ui-card-content class="space-y-2 p-5">
                  <div class="flex items-center justify-between">
                    <p class="text-muted-foreground text-sm font-medium">{{ item.title }}</p>
                    <div [class]="summaryIconClass(item)">
                      <lucide-icon [img]="asIcon(item.icon)" class="size-4" />
                    </div>
                  </div>
                  <div>
                    <p class="text-2xl font-bold tracking-tight tabular-nums">{{ item.amount }}</p>
                    <p class="text-muted-foreground mt-1 text-xs">{{ item.description }}</p>
                  </div>
                </div>
              </div>
            }
          </div>
    
          <div ui-card class="shadow-xs">
            <div ui-card-content class="flex flex-col gap-4 p-5 sm:flex-row sm:items-center sm:justify-between">
              <div class="flex items-start gap-3.5">
                <div class="bg-primary/10 text-primary flex size-10 shrink-0 items-center justify-center rounded-lg">
                  <lucide-icon [img]="Landmark" class="size-5" />
                </div>
                <div class="space-y-1">
                  <div class="flex flex-wrap items-center gap-2">
                    <span class="text-sm font-semibold">Chase Commercial •••• 4892</span>
                    <span
                      ui-badge
                      variant="outline"
                      class="border-success/20 bg-success/10 text-success text-xs font-medium"
                    >
                      <lucide-icon [img]="CheckCircle2" class="size-3" />
                      Verified
                    </span>
                    <span ui-badge variant="secondary" class="text-xs">Primary Settlement</span>
                  </div>
                  <p class="text-muted-foreground text-xs">
                    Routing <span class="font-mono tabular-nums">021000021</span> · Checking account · Daily automated
                    transfer
                  </p>
                </div>
              </div>
              <button ui-button variant="outline" size="sm" class="self-start sm:self-auto">
                <lucide-icon [img]="Building2" />
                Update Bank Account
              </button>
            </div>
          </div>
    
          <div class="bg-card overflow-x-auto rounded-lg border shadow-xs">
            <table ui-table>
              <thead ui-table-header>
                <tr ui-table-row>
                  <th ui-table-head>Payout ID</th>
                  <th ui-table-head>Initiated / Expected</th>
                  <th ui-table-head>Destination</th>
                  <th ui-table-head class="text-right">Gross</th>
                  <th ui-table-head class="text-right">Fees</th>
                  <th ui-table-head class="text-right">Net Amount</th>
                  <th ui-table-head>Status</th>
                  <th ui-table-head class="w-12">
                    <span class="sr-only">Actions</span>
                  </th>
                </tr>
              </thead>
              <tbody ui-table-body>
                @for (payout of payouts; track payout.id) {
                  <tr ui-table-row class="hover:bg-muted/50">
                    <td ui-table-cell class="font-mono text-xs font-semibold">{{ payout.id }}</td>
                    <td ui-table-cell>
                      <div class="text-sm font-medium">{{ payout.initiatedDate }}</div>
                      <div class="text-muted-foreground text-xs">{{ payout.arrivalDate }}</div>
                    </td>
                    <td ui-table-cell>
                      <div class="flex items-center gap-1.5 text-sm font-medium">
                        <lucide-icon [img]="Landmark" class="text-muted-foreground size-3.5 shrink-0" />
                        <span>{{ payout.destination }}</span>
                      </div>
                    </td>
                    <td ui-table-cell class="text-muted-foreground text-right text-sm tabular-nums">
                      {{ payout.grossAmount }}
                    </td>
                    <td ui-table-cell class="text-muted-foreground text-right text-xs tabular-nums">
                      {{ payout.feeAmount }}
                    </td>
                    <td ui-table-cell class="text-right text-sm font-semibold tabular-nums">{{ payout.netAmount }}</td>
                    <td ui-table-cell>
                      <span ui-badge variant="outline" [class]="statusClass(payout.status)">
                        {{ statusLabel(payout.status) }}
                      </span>
                    </td>
                    <td ui-table-cell>
                      <ui-dropdown-menu>
                        <button
                          ui-button
                          ui-dropdown-menu-trigger
                          variant="ghost"
                          size="icon-sm"
                          class="text-muted-foreground"
                        >
                          <lucide-icon [img]="MoreHorizontal" />
                          <span class="sr-only">Open actions for {{ payout.id }}</span>
                        </button>
                        <ui-dropdown-menu-content align="end" class="w-52">
                          <ui-dropdown-menu-item>
                            <lucide-icon [img]="FileText" class="mr-2 size-3.5" />
                            <span>View itemized breakdown</span>
                          </ui-dropdown-menu-item>
                          <ui-dropdown-menu-item>
                            <lucide-icon [img]="Download" class="mr-2 size-3.5" />
                            <span>Download receipt</span>
                          </ui-dropdown-menu-item>
                          <ui-dropdown-menu-separator />
                          <ui-dropdown-menu-item>
                            <lucide-icon [img]="ExternalLink" class="mr-2 size-3.5" />
                            <span>Trace transfer</span>
                          </ui-dropdown-menu-item>
                        </ui-dropdown-menu-content>
                      </ui-dropdown-menu>
                    </td>
                  </tr>
                }
              </tbody>
            </table>
          </div>
    
          <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
            <p class="text-muted-foreground text-sm">
              Showing <span class="text-foreground font-medium">6</span> of
              <span class="text-foreground font-medium">128</span> payout batches
            </p>
            <div class="flex items-center gap-2">
              <button ui-button variant="outline" size="sm" disabled>Previous</button>
              <button ui-button variant="outline" size="sm">Next</button>
            </div>
          </div>
        </div>
      `,
    })
    export class UiPayoutsManagementTableComponent {
      protected readonly Building2 = Building2
      protected readonly CalendarClock = CalendarClock
      protected readonly CheckCircle2 = CheckCircle2
      protected readonly Download = Download
      protected readonly ExternalLink = ExternalLink
      protected readonly FileText = FileText
      protected readonly Landmark = Landmark
      protected readonly MoreHorizontal = MoreHorizontal
      protected readonly Zap = Zap
    
      @Input('class') className?: string
    
      protected readonly summary = summary
      protected readonly payouts = payouts
    
      get rootClass(): string {
        return cn('w-full space-y-6', this.className)
      }
    
      summaryIconClass(item: SummaryItem): string {
        return cn('flex size-8 items-center justify-center rounded-md', item.iconClass)
      }
    
      asIcon(icon: unknown): never {
        return icon as never
      }
    
      statusClass(status: PayoutStatus): string {
        return cn('font-medium', statusConfig[status].className)
      }
    
      statusLabel(status: PayoutStatus): string {
        return statusConfig[status].label
      }
    }
    
  • src/app/components/blocks/payouts-management-table/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/payouts-management-table.json