Usage Metering Dashboard
blockfinanceReal-time consumption metering and overage monitoring dashboard: billing cycle tracking with days remaining, 4 key resource meter cards (API requests, compute hours, vector storage, bandwidth) with color-shifting threshold progress bars and projected month-end usage, projected invoice cost summary with overage breakdown, auto-scale overage protection toggle, email alert threshold selector, and recent usage events audit log.
Installation
$pnpm dlx uipkge-ng@latest add usage-metering-dashboard$npx uipkge-ng@latest add usage-metering-dashboard$yarn dlx uipkge-ng@latest add usage-metering-dashboard$bunx uipkge-ng@latest add usage-metering-dashboardManifest:
https://uipkge.dev/r/angular/usage-metering-dashboard.jsonInstalls to:src/app/components/blocks/usage-metering-dashboard/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
UsageEventinterface UsageEvent {
id: string
timestamp: string
service: string
description: string
volume: string
status: 'warning' | 'normal'
statusLabel: string
}Files installed (2)
src/app/components/blocks/usage-metering-dashboard/usage-metering-dashboard.component.ts22.5 kB
// Real-time consumption metering and overage monitoring dashboard: billing cycle // tracking with days remaining, 4 resource meter cards with threshold progress bars and // projected month-end usage, projected invoice cost summary with overage breakdown, // auto-scale overage protection toggle, email alert threshold selector, and recent // usage events audit log. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core' import { Activity, ArrowDownUp, ArrowUpRight, Cpu, Database, LucideAngularModule, TriangleAlert, 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, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiProgressComponent } from '@/ui/progress/progress.component' import { UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, } from '@/ui/select/select.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiSwitchComponent } from '@/ui/switch/switch.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export interface UsageEvent { id: string timestamp: string service: string description: string volume: string status: 'warning' | 'normal' statusLabel: string } const events: UsageEvent[] = [ { id: 'evt-1', timestamp: 'Aug 18, 2026 14:22', service: 'API Gateway', description: 'Spike: Batch sync job', volume: '+240,000 reqs', status: 'warning', statusLabel: 'High Spike', }, { id: 'evt-2', timestamp: 'Aug 17, 2026 09:15', service: 'Vector Storage', description: 'Index re-indexing & optimization', volume: '+12.4 GB', status: 'normal', statusLabel: 'Normal', }, { id: 'evt-3', timestamp: 'Aug 16, 2026 22:40', service: 'Compute Cluster', description: 'Model inference burst job', volume: '+85.0 hrs', status: 'normal', statusLabel: 'Normal', }, { id: 'evt-4', timestamp: 'Aug 15, 2026 11:05', service: 'CDN Edge', description: 'Media cache invalidation & egress', volume: '+64.2 GB', status: 'normal', statusLabel: 'Normal', }, { id: 'evt-5', timestamp: 'Aug 14, 2026 18:30', service: 'API Gateway', description: 'Webhook delivery retry storm', volume: '+180,000 reqs', status: 'warning', statusLabel: 'High Spike', }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-usage-metering-dashboard, [ui-usage-metering-dashboard]', 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, UiSelectComponent, UiSelectContentComponent, UiSelectItemComponent, UiSelectTriggerComponent, UiSelectValueComponent, UiSeparatorComponent, UiSwitchComponent, UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div data-slot="usage-metering-dashboard" [class]="rootClass"> <div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between"> <div class="space-y-1"> <h1 class="text-foreground text-2xl font-bold tracking-tight sm:text-3xl">Usage & Metering</h1> <p class="text-muted-foreground text-sm"> Monitor your organization's resource consumption and billing cycle limits. </p> </div> <div class="flex items-center gap-2"> <button ui-button variant="outline" size="sm" class="shrink-0 gap-1.5"> <lucide-icon [img]="Zap" class="fill-warning text-warning size-4" /> <span>Change Plan</span> <lucide-icon [img]="ArrowUpRight" class="text-muted-foreground size-3.5" /> </button> </div> </div> <div ui-card class="bg-muted/30 p-4"> <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <div class="flex items-center gap-2"> <span class="text-foreground text-sm font-semibold">Current Billing Period</span> <span ui-badge variant="outline" class="text-xs font-normal">Scale Plan — $199/mo</span> </div> <p class="text-muted-foreground text-xs tabular-nums"> Day 18 of 30 · Current period ends Sep 1, 2026 (12 days remaining) </p> </div> <div class="w-full space-y-1.5 sm:w-72"> <div class="flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground">Cycle Progress</span> <span class="text-foreground font-medium tabular-nums">60%</span> </div> <ui-progress [value]="60" class="h-1.5" /> </div> </div> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> <div ui-card class="flex flex-col justify-between"> <div ui-card-header class="pb-3"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2.5"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Activity" class="size-4" /> </div> <h3 ui-card-title class="truncate text-sm font-medium">API Requests</h3> </div> <span ui-badge variant="warning" class="shrink-0 gap-1 text-xs"> <lucide-icon [img]="TriangleAlert" class="size-3" /> <span>Approaching Limit</span> </span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="mb-1 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 text-xs"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">1.84M</span> <span class="text-muted-foreground tabular-nums">/ 2.0M limit</span> </div> <ui-progress [value]="92" class="[&_[data-slot=progress-indicator]]:bg-warning h-2" /> <div class="mt-1.5 flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground tabular-nums">92% utilized</span> <span class="text-warning font-medium tabular-nums">160K remaining</span> </div> </div> <div class="border-border/60 border-t pt-2.5 text-xs"> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Projected at month-end:</span> <span class="text-foreground font-medium tabular-nums">2.15M</span> </div> <p class="text-warning mt-0.5 text-xs font-medium tabular-nums">+150K overage (~$15.00)</p> </div> </div> </div> <div ui-card class="flex flex-col justify-between"> <div ui-card-header class="pb-3"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2.5"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Cpu" class="size-4" /> </div> <h3 ui-card-title class="truncate text-sm font-medium">Compute Hours</h3> </div> <span ui-badge variant="success" class="shrink-0 text-xs">Healthy</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="mb-1 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 text-xs"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">420</span> <span class="text-muted-foreground tabular-nums">/ 1,000 hrs limit</span> </div> <ui-progress [value]="42" class="[&_[data-slot=progress-indicator]]:bg-success h-2" /> <div class="mt-1.5 flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground tabular-nums">42% utilized</span> <span class="text-muted-foreground tabular-nums">580 hrs remaining</span> </div> </div> <div class="border-border/60 border-t pt-2.5 text-xs"> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Projected at month-end:</span> <span class="text-foreground font-medium tabular-nums">700 hrs</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Within allowance (300 hrs headroom)</p> </div> </div> </div> <div ui-card class="flex flex-col justify-between"> <div ui-card-header class="pb-3"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2.5"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="Database" class="size-4" /> </div> <h3 ui-card-title class="truncate text-sm font-medium">Vector Storage</h3> </div> <span ui-badge variant="success" class="shrink-0 text-xs">Healthy</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="mb-1 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 text-xs"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">78 GB</span> <span class="text-muted-foreground tabular-nums">/ 100 GB limit</span> </div> <ui-progress [value]="78" class="h-2" /> <div class="mt-1.5 flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground tabular-nums">78% utilized</span> <span class="text-muted-foreground tabular-nums">22 GB remaining</span> </div> </div> <div class="border-border/60 border-t pt-2.5 text-xs"> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Projected at month-end:</span> <span class="text-foreground font-medium tabular-nums">84 GB</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Within allowance (16 GB headroom)</p> </div> </div> </div> <div ui-card class="flex flex-col justify-between"> <div ui-card-header class="pb-3"> <div class="flex items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2.5"> <div class="bg-muted text-muted-foreground border-border flex size-8 shrink-0 items-center justify-center rounded-lg border shadow-xs" aria-hidden="true" > <lucide-icon [img]="ArrowDownUp" class="size-4" /> </div> <h3 ui-card-title class="truncate text-sm font-medium">Bandwidth / Egress</h3> </div> <span ui-badge variant="success" class="shrink-0 text-xs">Healthy</span> </div> </div> <div ui-card-content class="space-y-3"> <div> <div class="mb-1 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 text-xs"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">412 GB</span> <span class="text-muted-foreground tabular-nums">/ 500 GB limit</span> </div> <ui-progress [value]="82.4" class="h-2" /> <div class="mt-1.5 flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground tabular-nums">82.4% utilized</span> <span class="text-muted-foreground tabular-nums">88 GB remaining</span> </div> </div> <div class="border-border/60 border-t pt-2.5 text-xs"> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Projected at month-end:</span> <span class="text-foreground font-medium tabular-nums">485 GB</span> </div> <p class="text-muted-foreground mt-0.5 text-xs">Within allowance (15 GB headroom)</p> </div> </div> </div> </div> <div class="grid grid-cols-1 gap-6 lg:grid-cols-2"> <div ui-card> <div ui-card-header> <div class="flex items-center justify-between gap-x-2"> <h3 ui-card-title>Projected Month-End Cost</h3> <span ui-badge variant="warning" class="gap-1 text-xs"> <lucide-icon [img]="TriangleAlert" class="size-3" /> <span>Overage Projected</span> </span> </div> <p ui-card-description>Estimated cycle total factoring base tier and current consumption trajectory.</p> </div> <div ui-card-content class="space-y-4"> <div class="border-border bg-muted/40 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5 rounded-lg border p-4" > <div> <p class="text-muted-foreground text-xs font-medium tracking-wider uppercase">Projected Invoice</p> <p class="text-foreground text-3xl font-bold tracking-tight tabular-nums">$214.00</p> </div> <div class="text-right"> <span class="text-muted-foreground text-xs"> Base Plan: <strong class="text-foreground font-semibold tabular-nums">$199.00</strong> </span> <p class="text-warning text-xs font-semibold tabular-nums">+$15.00 overage</p> </div> </div> <div class="space-y-2.5 text-sm"> <div class="flex items-center justify-between gap-x-2"> <span class="text-foreground">Scale Plan Base Subscription</span> <span class="text-foreground font-medium tabular-nums">$199.00</span> </div> <div class="flex items-center justify-between gap-x-2"> <div class="flex items-center gap-1.5"> <span class="text-foreground">API Requests Overage</span> <span ui-badge variant="outline" class="text-xs font-normal">150K @ $0.10 / 1K</span> </div> <span class="text-warning font-medium tabular-nums">+$15.00</span> </div> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Compute Hours (1,000 hrs allowance)</span> <span class="text-muted-foreground tabular-nums">$0.00</span> </div> <div class="flex items-center justify-between gap-x-2"> <span class="text-muted-foreground">Storage & Egress Bandwidth</span> <span class="text-muted-foreground tabular-nums">$0.00</span> </div> </div> <div ui-separator></div> <div class="text-muted-foreground flex items-center justify-between gap-x-2 text-xs"> <span>Next scheduled invoice date</span> <span class="text-foreground font-medium tabular-nums">September 1, 2026</span> </div> </div> </div> <div ui-card> <div ui-card-header> <h3 ui-card-title>Overage Protection & Alerts</h3> <p ui-card-description>Configure notifications and automated guardrails before limits are breached.</p> </div> <div ui-card-content class="space-y-4"> <div class="flex items-start justify-between gap-4"> <div class="space-y-1"> <label class="text-foreground text-sm leading-none font-medium">Auto-scale Overage Protection</label> <p class="text-muted-foreground text-xs leading-relaxed"> Automatically throttle or scale resources to prevent unexpected billing spikes beyond set allowances. </p> </div> <ui-switch [checked]="autoScaleProtection()" (checkedChange)="autoScaleProtection.set($event)" aria-label="Toggle auto-scale overage protection" /> </div> <div ui-separator></div> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <label class="text-foreground text-sm leading-none font-medium">Email Alert Threshold</label> <p class="text-muted-foreground text-xs"> Notify workspace admins when any metric crosses this percentage. </p> </div> <ui-select [value]="alertThreshold()" (valueChange)="alertThreshold.set($event)"> <button ui-select-trigger class="w-full text-xs sm:w-44" aria-label="Email alert threshold"> <ui-select-value placeholder="Select threshold" /> </button> <ui-select-content> <ui-select-item value="80">80% of limit</ui-select-item> <ui-select-item value="90">90% of limit</ui-select-item> <ui-select-item value="100">100% of limit</ui-select-item> </ui-select-content> </ui-select> </div> <div ui-separator></div> <div class="bg-muted/40 border-border space-y-1.5 rounded-lg border p-3"> <div class="flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground">Hard Spending Cap</span> <span class="text-foreground font-semibold tabular-nums">$300.00 / mo</span> </div> <div class="flex items-center justify-between gap-x-2 text-xs"> <span class="text-muted-foreground">Recipients</span> <span class="text-muted-foreground truncate">billing-alerts@acme.corp</span> </div> </div> </div> </div> </div> <div ui-card> <div ui-card-header> <div class="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between"> <div> <h3 ui-card-title>Recent Usage Events</h3> <p ui-card-description> High-volume consumption spikes and automated resource scaling events this cycle. </p> </div> <span ui-badge variant="outline" class="w-fit text-xs font-normal tabular-nums">5 events logged</span> </div> </div> <div ui-card-content class="p-0 sm:p-6 sm:pt-0"> <div class="overflow-x-auto"> <table ui-table> <thead ui-table-header> <tr ui-table-row> <th ui-table-head>Timestamp</th> <th ui-table-head>Service</th> <th ui-table-head>Event Description</th> <th ui-table-head class="text-right">Volume</th> <th ui-table-head class="text-right">Status</th> </tr> </thead> <tbody ui-table-body> @for (event of usageEvents; track event.id) { <tr ui-table-row> <td ui-table-cell class="text-muted-foreground text-xs whitespace-nowrap tabular-nums"> {{ event.timestamp }} </td> <td ui-table-cell class="text-foreground text-xs font-medium whitespace-nowrap"> {{ event.service }} </td> <td ui-table-cell class="text-muted-foreground text-xs">{{ event.description }}</td> <td ui-table-cell class="text-foreground text-right text-xs font-medium whitespace-nowrap tabular-nums" > {{ event.volume }} </td> <td ui-table-cell class="text-right"> <span ui-badge [variant]="event.status === 'warning' ? 'warning' : 'success'" class="text-xs whitespace-normal" > {{ event.statusLabel }} </span> </td> </tr> } </tbody> </table> </div> </div> </div> </div> `, }) export class UiUsageMeteringDashboardComponent { protected readonly Activity = Activity protected readonly ArrowDownUp = ArrowDownUp protected readonly ArrowUpRight = ArrowUpRight protected readonly Cpu = Cpu protected readonly Database = Database protected readonly TriangleAlert = TriangleAlert protected readonly Zap = Zap protected readonly usageEvents = events @Input('class') className?: string @Input() set initialAutoScaleProtection(v: boolean | undefined) { if (v !== undefined) this.autoScaleProtection.set(v) } @Input() set initialAlertThreshold(v: string | undefined) { if (v) this.alertThreshold.set(v) } readonly autoScaleProtection = signal(true) readonly alertThreshold = signal('90') get rootClass(): string { return cn('mx-auto w-full max-w-6xl space-y-6', this.className) } }src/app/components/blocks/usage-metering-dashboard/index.ts0.1 kB
export { UiUsageMeteringDashboardComponent, type UsageEvent } from './usage-metering-dashboard.component'
Raw manifest:https://uipkge.dev/r/angular/usage-metering-dashboard.json