Payroll Run Summary
blockfinanceGusto/Rippling style bi-weekly company payroll execution preview and tax reconciliation: cycle header with approval badge and submission controls, 4 primary payroll metric cards (company cost, net pay, employee withholdings, employer taxes), direct deposit ACH funding timeline, and itemized employee payroll table.
Installation
$pnpm dlx uipkge-ng@latest add payroll-run-summary$npx uipkge-ng@latest add payroll-run-summary$yarn dlx uipkge-ng@latest add payroll-run-summary$bunx uipkge-ng@latest add payroll-run-summaryManifest:
https://uipkge.dev/r/angular/payroll-run-summary.jsonInstalls to:src/app/components/blocks/payroll-run-summary/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.
EmployeeRecordinterface EmployeeRecord {
id: string
name: string
role: string
department: string
payType: 'Salary' | 'Hourly'
hours: string
grossPay: string
deductions: string
deductionsDetail: string
netPay: string
paymentMethod: string
bankDetail: string
}TimelineStepinterface TimelineStep {
step: number
title: string
date: string
time: string
description: string
status: string
statusClass: string
icon: unknown
footer: string
}SummaryCardinterface SummaryCard {
label: string
value: string
badge: string
description: string
}Files installed (2)
src/app/components/blocks/payroll-run-summary/payroll-run-summary.component.ts17.1 kB
import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { Building2, Calendar, CheckCircle2, Clock, CreditCard, DollarSign, Download, Landmark, LucideAngularModule, Receipt, Send, ShieldCheck, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export interface EmployeeRecord { id: string name: string role: string department: string payType: 'Salary' | 'Hourly' hours: string grossPay: string deductions: string deductionsDetail: string netPay: string paymentMethod: string bankDetail: string } interface TimelineStep { step: number title: string date: string time: string description: string status: string statusClass: string icon: unknown footer: string } interface SummaryCard { label: string value: string badge: string description: string } const timelineSteps: TimelineStep[] = [ { step: 1, title: 'ACH Debit Date', date: 'Wednesday, Aug 19, 2026', time: '5:00 PM EST', description: 'Company Operating Account (•••• 9021) debited for payroll & tax liability.', status: 'Completed', statusClass: 'bg-success/10 text-success border-success/20', icon: CheckCircle2, footer: 'Verified', }, { step: 2, title: 'Bank Processing', date: 'Thursday, Aug 20, 2026', time: 'Automated Clearing', description: 'Federal Reserve NACHA batch transmission, ACH clearing, and tax escrow lock.', status: 'In Transit', statusClass: 'bg-info/10 text-info border-info/20', icon: Landmark, footer: 'Processing', }, { step: 3, title: 'Employee Payday', date: 'Friday, Aug 21, 2026', time: '12:01 AM Local', description: 'Net funds settled across 42 employee accounts and electronic paystubs released.', status: 'Target Payday', statusClass: 'bg-success/10 text-success border-success/20', icon: DollarSign, footer: 'Scheduled', }, ] const summaryCards: SummaryCard[] = [ { label: 'Total Company Cost', value: '$184,250.00', badge: 'Cycle #16', description: 'Gross pay ($169,670.00) + Employer taxes ($14,580.00)', }, { label: 'Net Direct Deposit Pay', value: '$138,420.00', badge: '42 Employees', description: 'Disbursed to 42 employees via automated 100% direct deposit', }, { label: 'Employee Withholdings', value: '$31,250.00', badge: 'Federal & State Tax', description: '$31,250.00 · Federal & State Tax, FICA & benefit deductions', }, { label: 'Employer Payroll Taxes', value: '$14,580.00', badge: 'FICA & FUTA', description: '$14,580.00 · FICA & FUTA, SUTA employer contributions', }, ] const employees: EmployeeRecord[] = [ { id: 'emp-1', name: 'Elena Rostova', role: 'Principal Engineer', department: 'Engineering', payType: 'Salary', hours: '80.0 hrs', grossPay: '$8,750.00', deductions: '-$2,140.00', deductionsDetail: 'Pre-tax 401k & Health', netPay: '$6,610.00', paymentMethod: 'Direct Deposit', bankDetail: 'Chase •••• 4892', }, { id: 'emp-2', name: 'Marcus Vance', role: 'Staff Architect', department: 'Engineering', payType: 'Salary', hours: '80.0 hrs', grossPay: '$8,125.00', deductions: '-$1,980.00', deductionsDetail: 'Pre-tax 401k & HSA', netPay: '$6,145.00', paymentMethod: 'Direct Deposit', bankDetail: 'Wells Fargo •••• 3109', }, { id: 'emp-3', name: 'David Chen', role: 'Senior Full-Stack', department: 'Engineering', payType: 'Salary', hours: '80.0 hrs', grossPay: '$6,875.00', deductions: '-$1,650.00', deductionsDetail: 'Pre-tax 401k & Dental', netPay: '$5,225.00', paymentMethod: 'Direct Deposit', bankDetail: 'Bank of America •••• 7741', }, { id: 'emp-4', name: 'Sarah Jenkins', role: 'Frontend Specialist', department: 'Engineering', payType: 'Hourly', hours: '80.0 hrs', grossPay: '$5,492.50', deductions: '-$1,280.00', deductionsDetail: 'Medical & Vision', netPay: '$4,212.50', paymentMethod: 'Direct Deposit', bankDetail: 'Citibank •••• 9018', }, { id: 'emp-5', name: 'Alex Rivera', role: 'Product Designer', department: 'Design', payType: 'Salary', hours: '80.0 hrs', grossPay: '$6,250.00', deductions: '-$1,490.00', deductionsDetail: 'Pre-tax 401k & Health', netPay: '$4,760.00', paymentMethod: 'Direct Deposit', bankDetail: 'SVB •••• 6523', }, ] function initials(name: string): string { return name .split(' ') .map((part) => part[0]) .slice(0, 2) .join('') .toUpperCase() } /** * PayrollRunSummary -- the React block, 1:1: cycle header with approval badge and submit * controls, four payroll metric cards, a direct-deposit ACH funding timeline, and an * itemized employee compensation table with avatar, pay-type badge, deductions and net pay. */ @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-payroll-run-summary, [ui-payroll-run-summary]', 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, UiAvatarComponent, UiAvatarFallbackComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardHeaderComponent, UiCardTitleComponent, UiTableComponent, UiTableBodyComponent, UiTableCellComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <div data-slot="payroll-run-summary" [class]="rootClass"> <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1.5"> <div class="flex flex-wrap items-center gap-3"> <h2 class="text-foreground text-2xl font-bold tracking-tight">Payroll Run Summary</h2> <span ui-badge wrap variant="outline" class="border-success/20 bg-success/10 text-success gap-1.5 font-medium" > <span class="bg-success size-1.5 rounded-full" aria-hidden="true"></span> Approved & Ready to Submit </span> </div> <p class="text-muted-foreground flex flex-wrap items-center gap-2 text-sm"> <span class="inline-flex items-center gap-1.5"> <lucide-icon [img]="Calendar" class="size-4" /> Pay Period: <strong class="text-foreground font-medium">Aug 01, 2026 – Aug 15, 2026</strong> </span> <span> Pay Date: <strong class="text-foreground font-medium">Aug 21</strong> </span> </p> </div> <div class="flex flex-wrap items-center gap-2.5"> <button ui-button aria-label="Download attachment" variant="outline"> <lucide-icon [img]="Download" class="size-4" /> Export Payroll CSV </button> <button ui-button> <lucide-icon [img]="Send" class="size-4" /> Submit Payroll ($184,250.00) </button> </div> </div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> @for (card of summaryCards; track card.label) { <div ui-card class="relative overflow-hidden shadow-xs"> <div ui-card-header class="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 ui-card-title class="text-muted-foreground text-sm font-medium">{{ card.label }}</h3> <span ui-badge wrap variant="secondary" class="text-xs font-normal">{{ card.badge }}</span> </div> <div ui-card-content class="space-y-1"> <div class="text-foreground text-3xl font-bold tracking-tight tabular-nums">{{ card.value }}</div> <p class="text-muted-foreground text-xs leading-relaxed">{{ card.description }}</p> </div> </div> } </div> <div ui-card class="border-border shadow-xs"> <div ui-card-header class="border-b pb-3"> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div class="flex items-center gap-2"> <lucide-icon [img]="Clock" class="text-muted-foreground size-4" /> <h3 ui-card-title class="text-sm font-semibold tracking-tight">Direct Deposit Funding Timeline</h3> </div> <span ui-badge wrap variant="outline" class="text-muted-foreground w-fit text-xs font-normal"> ACH 2-Day Settlement Window </span> </div> </div> <div ui-card-content class="pt-4"> <div class="grid grid-cols-1 gap-4 md:grid-cols-3"> @for (item of timelineSteps; track item.step; let idx = $index) { <div class="bg-card relative flex flex-col justify-between rounded-lg border p-4 shadow-xs"> <div class="space-y-2"> <div class="flex flex-wrap items-center justify-between gap-2"> <div class="flex min-w-0 items-center gap-2"> <div class="bg-muted text-foreground flex size-7 items-center justify-center rounded-full text-xs font-semibold" > {{ item.step }} </div> <span class="text-foreground text-xs font-semibold tracking-tight">{{ item.title }}</span> </div> <span ui-badge [class]="timelineStatusClass(item)">{{ item.status }}</span> </div> <div class="pt-1"> <p class="text-foreground text-xs font-medium">{{ item.date }}</p> <p class="text-muted-foreground text-xs">{{ item.time }}</p> </div> <p class="text-muted-foreground text-xs leading-relaxed">{{ item.description }}</p> </div> <div class="text-muted-foreground mt-3 flex items-center justify-between border-t pt-2 text-xs"> <span class="inline-flex items-center gap-1 font-medium"> <lucide-icon [img]="asIcon(item.icon)" class="text-foreground/70 size-3.5" /> {{ item.footer }} </span> @if (idx < timelineSteps.length - 1) { <span class="text-muted-foreground/50 hidden md:inline-flex">Next: Step {{ idx + 2 }}</span> } </div> </div> } </div> </div> </div> <div class="bg-card overflow-hidden rounded-lg border shadow-xs"> <div class="flex flex-col gap-2 border-b p-4 sm:flex-row sm:items-center sm:justify-between"> <div> <h3 class="text-foreground text-base font-semibold tracking-tight">Employee Compensation Breakdown</h3> <p class="text-muted-foreground mt-0.5 text-xs"> 5 of 42 employees shown in active batch · 80.0 standard cycle base hours </p> </div> <div class="flex items-center gap-2"> <span ui-badge wrap variant="secondary" class="text-xs font-medium">42 Total Employees</span> <span ui-badge wrap variant="outline" class="border-success/20 text-success text-xs font-medium"> Approved & Ready to Submit </span> </div> </div> <div class="overflow-x-auto"> <table ui-table> <thead ui-table-header> <tr ui-table-row class="bg-muted/40 hover:bg-muted/40"> <th ui-table-head class="min-w-[220px]">Employee</th> <th ui-table-head>Pay Type</th> <th ui-table-head class="text-right">Hours</th> <th ui-table-head class="text-right">Gross Pay</th> <th ui-table-head class="min-w-[180px] text-right">Total Deductions</th> <th ui-table-head class="text-right">Net Pay</th> <th ui-table-head class="min-w-[220px]">Payment Method</th> </tr> </thead> <tbody ui-table-body> @for (emp of employees; track emp.id) { <tr ui-table-row class="hover:bg-muted/30"> <td ui-table-cell> <div class="flex items-center gap-3"> <ui-avatar class="size-8 border"> <ui-avatar-fallback class="text-xs font-medium">{{ initials(emp.name) }}</ui-avatar-fallback> </ui-avatar> <div class="flex min-w-0 flex-col"> <div class="flex items-center gap-2"> <span class="text-foreground truncate text-sm font-semibold">{{ emp.name }}</span> <span ui-badge wrap variant="outline" class="px-1.5 py-0 text-xs font-normal"> {{ emp.department }} </span> </div> <span class="text-muted-foreground truncate text-xs">{{ emp.role }}</span> </div> </div> </td> <td ui-table-cell> <span ui-badge wrap [variant]="emp.payType === 'Salary' ? 'outline' : 'secondary'" class="text-xs font-medium" > {{ emp.payType }} </span> </td> <td ui-table-cell class="text-foreground text-right font-mono text-xs tabular-nums"> {{ emp.hours }} </td> <td ui-table-cell class="text-foreground text-right font-medium tabular-nums">{{ emp.grossPay }}</td> <td ui-table-cell class="text-right"> <div class="flex flex-col items-end"> <span class="text-destructive text-sm font-medium tabular-nums">{{ emp.deductions }}</span> <span class="text-muted-foreground text-xs">{{ emp.deductionsDetail }}</span> </div> </td> <td ui-table-cell class="text-right"> <span class="text-success text-sm font-bold tabular-nums">{{ emp.netPay }}</span> </td> <td ui-table-cell> <div class="flex items-center gap-2"> <lucide-icon [img]="Landmark" class="text-muted-foreground size-4 shrink-0" /> <div class="flex min-w-0 flex-col"> <span class="text-foreground truncate text-xs font-medium">{{ emp.paymentMethod }}</span> <span class="text-muted-foreground truncate text-xs">{{ emp.bankDetail }}</span> </div> </div> </td> </tr> } </tbody> </table> </div> <div class="bg-muted/20 flex flex-col items-center justify-between gap-3 border-t p-4 sm:flex-row"> <p class="text-muted-foreground text-xs"> Showing 5 of 42 employees · Reconciliation verified against IRS Form 941 schedules </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> <div class="hidden items-center gap-2"> <lucide-icon [img]="Building2" /> <lucide-icon [img]="CreditCard" /> <lucide-icon [img]="Receipt" /> <lucide-icon [img]="ShieldCheck" /> </div> </div> `, }) export class UiPayrollRunSummaryComponent { protected readonly Calendar = Calendar protected readonly Clock = Clock protected readonly Download = Download protected readonly Landmark = Landmark protected readonly Send = Send protected readonly Building2 = Building2 protected readonly CreditCard = CreditCard protected readonly Receipt = Receipt protected readonly ShieldCheck = ShieldCheck @Input('class') className?: string protected readonly summaryCards = summaryCards protected readonly timelineSteps = timelineSteps protected readonly employees = employees get rootClass(): string { return cn('w-full space-y-6', this.className) } initials(name: string): string { return initials(name) } asIcon(icon: unknown): never { return icon as never } timelineStatusClass(item: TimelineStep): string { return cn('text-xs font-medium capitalize', item.statusClass) } }src/app/components/blocks/payroll-run-summary/index.ts0.1 kB
export { UiPayrollRunSummaryComponent, type EmployeeRecord } from './payroll-run-summary.component'
Raw manifest:https://uipkge.dev/r/angular/payroll-run-summary.json