Menu

Change language

GitHub repo

Billing Account

blockfinance

Stripe-style account billing page: current-plan card with Pro badge, price and renewal date plus Upgrade / Cancel actions, usage meters for seats and projects, a payment-method row with update and delete actions, and a compact billing-history list with paid badges and per-invoice downloads.

Installation

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

Variants

Loading interactive previews…

Schema

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

UsageMetric
interface UsageMetric {
  label: string
  used: number
  total: number
}
Invoice
interface Invoice {
  id: string
  date: string
  amount: string
  status: 'Paid' | 'Open' | 'Past due'
}

Files installed (2)

  • src/app/components/blocks/billing-account/billing-account.component.ts7.2 kB
    // Stripe-style account billing page: current-plan card with Pro badge, price and renewal
    // date plus Upgrade / Cancel actions, usage meters for seats and projects, a payment-method
    // row with update and delete actions, and a compact billing-history list with paid badges
    // and per-invoice downloads. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
    import { CreditCard, Download, LucideAngularModule, Trash2 } 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,
      UiCardDescriptionComponent,
      UiCardHeaderComponent,
      UiCardTitleComponent,
    } from '@/ui/card/card.component'
    import { UiProgressComponent } from '@/ui/progress/progress.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    
    interface UsageMetric {
      label: string
      used: number
      total: number
    }
    
    interface Invoice {
      id: string
      date: string
      amount: string
      status: 'Paid' | 'Open' | 'Past due'
    }
    
    const plan = {
      name: 'Pro',
      price: '$49',
      cadence: 'month',
      renewalDate: 'September 12, 2026',
    }
    
    const usage: UsageMetric[] = [
      { label: 'Seats', used: 18, total: 20 },
      { label: 'Projects', used: 7, total: 10 },
    ]
    
    const paymentMethod = {
      brand: 'Visa',
      last4: '4242',
      expiry: '04/28',
    }
    
    const invoices: Invoice[] = [
      { id: 'inv-2026-08', date: 'Aug 12, 2026', amount: '$49.00', status: 'Paid' },
      { id: 'inv-2026-07', date: 'Jul 12, 2026', amount: '$49.00', status: 'Paid' },
      { id: 'inv-2026-06', date: 'Jun 12, 2026', amount: '$49.00', status: 'Paid' },
      { id: 'inv-2026-05', date: 'May 12, 2026', amount: '$49.00', status: 'Paid' },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-billing-account, [ui-billing-account]',
      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,
        UiCardDescriptionComponent,
        UiCardHeaderComponent,
        UiCardTitleComponent,
        UiProgressComponent,
        UiSeparatorComponent,
      ],
      template: `
        <div data-slot="billing-account" [class]="rootClass">
          <div ui-card>
            <div ui-card-header>
              <div class="flex items-center gap-2">
                <h3 ui-card-title>Current plan</h3>
                <span ui-badge>{{ plan.name }}</span>
              </div>
              <p ui-card-description>Your subscription renews automatically each month.</p>
            </div>
            <div ui-card-content class="space-y-4">
              <div class="flex flex-wrap items-end justify-between gap-4">
                <div>
                  <p class="text-2xl font-bold tracking-tight">
                    {{ plan.price }}
                    <span class="text-muted-foreground text-sm font-normal">/ {{ plan.cadence }}</span>
                  </p>
                  <p class="text-muted-foreground mt-1 text-xs">Renews on {{ plan.renewalDate }}</p>
                </div>
                <div class="flex items-center gap-2">
                  <button ui-button size="sm">Upgrade</button>
                  <button ui-button variant="ghost" size="sm" class="text-destructive hover:text-destructive">
                    Cancel subscription
                  </button>
                </div>
              </div>
            </div>
          </div>
    
          <div ui-card>
            <div ui-card-header>
              <h3 ui-card-title>Usage</h3>
              <p ui-card-description>Consumption against your plan limits this cycle.</p>
            </div>
            <div ui-card-content class="space-y-5">
              @for (metric of usageMetrics; track metric.label) {
                <div class="space-y-2">
                  <div class="flex items-center justify-between text-sm">
                    <span class="font-medium">{{ metric.label }}</span>
                    <span class="text-muted-foreground text-xs tabular-nums">
                      {{ metric.used }} of {{ metric.total }} used
                    </span>
                  </div>
                  <ui-progress [value]="(metric.used / metric.total) * 100" />
                </div>
              }
            </div>
          </div>
    
          <div ui-card>
            <div ui-card-header>
              <h3 ui-card-title>Payment method</h3>
              <p ui-card-description>Charged when your subscription renews.</p>
            </div>
            <div ui-card-content>
              <div class="flex items-center justify-between gap-4">
                <div class="flex items-center gap-3">
                  <div
                    class="bg-muted text-muted-foreground border-border flex size-9 shrink-0 items-center justify-center rounded-md border shadow-xs"
                    aria-hidden="true"
                  >
                    <lucide-icon [img]="CreditCard" class="size-4" />
                  </div>
                  <div>
                    <p class="text-sm font-medium tabular-nums">•••• •••• •••• {{ paymentMethod.last4 }}</p>
                    <p class="text-muted-foreground text-xs">
                      {{ paymentMethod.brand }} · Expires {{ paymentMethod.expiry }}
                    </p>
                  </div>
                </div>
                <div class="flex items-center gap-1">
                  <button ui-button variant="ghost" size="sm">Update</button>
                  <button ui-button variant="ghost" size="icon-sm" aria-label="Delete payment method">
                    <lucide-icon [img]="Trash2" class="text-muted-foreground size-4" />
                  </button>
                </div>
              </div>
            </div>
          </div>
    
          <div ui-card>
            <div ui-card-header>
              <h3 ui-card-title>Billing history</h3>
              <p ui-card-description>Invoices from the last 12 months.</p>
            </div>
            <div ui-card-content>
              <ul class="-my-2 divide-y">
                @for (invoice of invoiceList; track invoice.id) {
                  <li class="flex items-center justify-between gap-4 py-3">
                    <div class="min-w-0">
                      <p class="text-sm font-medium">{{ invoice.date }}</p>
                      <p class="text-muted-foreground text-xs tabular-nums">{{ invoice.amount }}</p>
                    </div>
                    <div class="flex items-center gap-2">
                      <span ui-badge variant="success">{{ invoice.status }}</span>
                      <ui-separator orientation="vertical" class="!h-4" />
                      <button ui-button variant="ghost" size="icon-sm" aria-label="Download invoice">
                        <lucide-icon [img]="Download" class="text-muted-foreground size-4" />
                      </button>
                    </div>
                  </li>
                }
              </ul>
            </div>
          </div>
        </div>
      `,
    })
    export class UiBillingAccountComponent {
      protected readonly CreditCard = CreditCard
      protected readonly Download = Download
      protected readonly Trash2 = Trash2
    
      protected readonly plan = plan
      protected readonly usageMetrics = usage
      protected readonly paymentMethod = paymentMethod
      protected readonly invoiceList = invoices
    
      @Input('class') className?: string
    
      get rootClass(): string {
        return cn('mx-auto w-full max-w-2xl space-y-6', this.className)
      }
    }
    
  • src/app/components/blocks/billing-account/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/billing-account.json