Menu

Change language

GitHub repo

Tax Summary Report

blockfinance

QuickBooks and Stripe Tax style quarterly estimated tax calculator and liability report: four KPI summary cards, jurisdictional liability breakdown (federal, state, FICA), IRS Schedule C deductible expenses table with tax savings, and a quarterly filing timeline.

Installation

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

Variants

Loading interactive previews…

Files installed (2)

  • src/app/components/blocks/tax-summary-report/tax-summary-report.component.ts19.8 kB
    // QuickBooks and Stripe Tax style quarterly estimated tax calculator and liability report:
    // four KPI summary cards, jurisdictional liability breakdown (federal, state, FICA), IRS
    // Schedule C deductible expenses table with tax savings, and a quarterly filing timeline.
    // Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'
    import {
      Calendar,
      CheckCircle2,
      Clock,
      CreditCard,
      Download,
      FileCheck,
      LucideAngularModule,
      Receipt,
      Scale,
      ShieldCheck,
      TrendingUp,
    } 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 {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableFooterComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    
    type BadgeVariant = 'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' | 'info'
    
    const metrics: {
      title: string
      value: string
      subtitle: string
      badge: string
      badgeVariant: BadgeVariant
      icon: unknown
    }[] = [
      {
        title: 'Estimated Q3 Tax Liability',
        value: '$14,280.00',
        subtitle: 'Due Sep 15, 2026 · Payment pending',
        badge: 'Due in 24 days',
        badgeVariant: 'warning',
        icon: Receipt,
      },
      {
        title: 'Gross Taxable Income',
        value: '$128,450.00',
        subtitle: 'YTD revenue across 4 client contracts',
        badge: '+18.4% YoY',
        badgeVariant: 'success',
        icon: TrendingUp,
      },
      {
        title: 'Total Deductible Expenses',
        value: '$34,820.00',
        subtitle: 'YTD deductions across 5 categories',
        badge: '27.1% ratio',
        badgeVariant: 'info',
        icon: FileCheck,
      },
      {
        title: 'Effective Tax Rate',
        value: '21.4%',
        subtitle: 'Federal (15.2%) + CA State (6.2%)',
        badge: 'Blended rate',
        badgeVariant: 'secondary',
        icon: Scale,
      },
    ]
    
    const jurisdictions = [
      {
        id: 'federal',
        name: 'Federal Income Tax',
        authority: 'Internal Revenue Service (IRS)',
        amount: '$8,420.00',
        percentage: 59,
        rateDescription: 'Estimated at 24% marginal individual / corporate bracket',
        accentColor: 'bg-primary',
      },
      {
        id: 'state',
        name: 'State & Local Tax',
        authority: 'California Franchise Tax Board (FTB)',
        amount: '$3,860.00',
        percentage: 27,
        rateDescription: 'California Schedule CA (540) estimated at 9.3% bracket',
        accentColor: 'bg-warning bg-warning',
      },
      {
        id: 'fica',
        name: 'Self-Employment / FICA',
        authority: 'Social Security & Medicare (Schedule SE)',
        amount: '$2,000.00',
        percentage: 14,
        rateDescription: '15.3% self-employment contribution on 92.35% net earnings',
        accentColor: 'bg-success bg-success',
      },
    ]
    
    const deductionCategories: {
      id: string
      name: string
      description: string
      spent: string
      rate: string
      rateBadgeVariant: BadgeVariant
      savings: string
    }[] = [
      {
        id: 'software',
        name: 'Software & Subscriptions',
        description: 'Cloud hosting, SaaS tooling, API subscriptions, and dev licenses',
        spent: '$12,450.00',
        rate: '100%',
        rateBadgeVariant: 'default',
        savings: '$2,664.30',
      },
      {
        id: 'contractors',
        name: 'Contractor Payments',
        description: '1099-NEC engineering contractors, QA specialists, and design partners',
        spent: '$11,200.00',
        rate: '100%',
        rateBadgeVariant: 'default',
        savings: '$2,396.80',
      },
      {
        id: 'office',
        name: 'Office & Equipment',
        description: 'Workstations, displays, ergonomic chairs, and home office deduction',
        spent: '$5,670.00',
        rate: '100%',
        rateBadgeVariant: 'default',
        savings: '$1,213.38',
      },
      {
        id: 'travel',
        name: 'Travel & Meals',
        description: 'Client on-sites, tech conferences, and 50% eligible business dining',
        spent: '$3,100.00',
        rate: '50% for meals',
        rateBadgeVariant: 'secondary',
        savings: '$331.70',
      },
      {
        id: 'professional',
        name: 'Professional Services',
        description: 'Corporate legal counsel, CPA tax advisory, and payroll processing',
        spent: '$2,400.00',
        rate: '100%',
        rateBadgeVariant: 'default',
        savings: '$513.60',
      },
    ]
    
    const quarterlySchedule = [
      {
        quarter: 'Q1 2026',
        period: 'Jan 1 – Mar 31',
        dueDate: 'April 15, 2026',
        status: 'Paid',
        amount: '$11,850.00',
        note: 'EFTPS Conf #98214 · Paid Apr 12',
        isCurrent: false,
      },
      {
        quarter: 'Q2 2026',
        period: 'Apr 1 – May 31',
        dueDate: 'June 15, 2026',
        status: 'Paid',
        amount: '$12,400.00',
        note: 'EFTPS Conf #45091 · Paid Jun 14',
        isCurrent: false,
      },
      {
        quarter: 'Q3 2026',
        period: 'Jun 1 – Aug 31',
        dueDate: 'September 15, 2026',
        status: 'Upcoming',
        amount: '$14,280.00',
        note: 'Auto-debit scheduled for Sep 15',
        isCurrent: true,
      },
      {
        quarter: 'Q4 2026',
        period: 'Sep 1 – Dec 31',
        dueDate: 'January 15, 2027',
        status: 'Future',
        amount: '$13,500.00',
        note: 'Projected year-end estimate',
        isCurrent: false,
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-tax-summary-report, [ui-tax-summary-report]',
      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,
        UiTableComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableFooterComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
      ],
      template: `
        <div data-slot="tax-summary-report" [class]="rootClass">
          <!-- Header Section -->
          <div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
            <div>
              <div class="flex flex-wrap items-center gap-3">
                <h2 class="text-foreground text-2xl font-bold tracking-tight">Tax Summary & Estimated Liability</h2>
                <span ui-badge variant="warning" class="gap-1.5 px-2.5 py-1 text-xs font-medium">
                  <lucide-icon [img]="Clock" class="size-3.5" />
                  Q3 Estimated Taxes Due Sep 15
                </span>
              </div>
              <p class="text-muted-foreground mt-1 text-sm">
                Quarterly estimated tax obligations, IRS Schedule C deductible expenses, and jurisdictional liabilities.
              </p>
            </div>
    
            <div class="flex flex-wrap items-center gap-2.5">
              <ui-select [value]="selectedYear()" (valueChange)="selectedYear.set($event)">
                <button ui-select-trigger size="sm" class="w-36 text-xs font-medium" aria-label="Select Tax Year">
                  <ui-select-value placeholder="Tax Year" />
                </button>
                <ui-select-content>
                  <ui-select-item value="2026">2026 Tax Year</ui-select-item>
                  <ui-select-item value="2025">2025 Tax Year</ui-select-item>
                  <ui-select-item value="2024">2024 Tax Year</ui-select-item>
                </ui-select-content>
              </ui-select>
    
              <button ui-button aria-label="Download attachment" variant="outline" size="sm" class="gap-1.5 text-xs">
                <lucide-icon [img]="Download" class="size-4" />
                Export Tax Package
              </button>
    
              <button ui-button size="sm" class="gap-1.5 text-xs">
                <lucide-icon [img]="CreditCard" class="size-4" />
                Pay Estimated Tax
              </button>
            </div>
          </div>
    
          <!-- 4 Key Tax Metrics Cards -->
          <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
            @for (metric of metrics; track metric.title) {
              <div ui-card class="shadow-xs">
                <div ui-card-content class="p-5">
                  <div class="flex items-center justify-between gap-2">
                    <p class="text-muted-foreground text-xs font-medium">{{ metric.title }}</p>
                    <div
                      class="bg-muted text-muted-foreground border-border/60 flex size-8 shrink-0 items-center justify-center rounded-md border shadow-2xs"
                      aria-hidden="true"
                    >
                      <lucide-icon [img]="metricIcon(metric.icon)" class="size-4" />
                    </div>
                  </div>
                  <div class="mt-3">
                    <p class="text-foreground text-2xl font-bold tracking-tight tabular-nums">{{ metric.value }}</p>
                    <div class="mt-2 flex items-center gap-2">
                      <span ui-badge [variant]="metric.badgeVariant" class="px-1.5 py-0 text-xs font-medium">
                        {{ metric.badge }}
                      </span>
                      <span class="text-muted-foreground truncate text-xs">{{ metric.subtitle }}</span>
                    </div>
                  </div>
                </div>
              </div>
            }
          </div>
    
          <!-- Tax Liability Breakdown by Jurisdiction -->
          <div ui-card class="shadow-xs">
            <div ui-card-header>
              <div class="flex flex-wrap items-center justify-between gap-2">
                <div>
                  <h3 ui-card-title class="text-base font-semibold">Tax Liability Breakdown by Jurisdiction</h3>
                  <p ui-card-description class="text-xs sm:text-sm">
                    Estimated Q3 distribution across federal, state, and payroll authorities ($14,280.00 total).
                  </p>
                </div>
                <span ui-badge variant="outline" class="text-xs tabular-nums">3 Jurisdictions Active</span>
              </div>
            </div>
            <div ui-card-content class="space-y-6">
              <!-- Visual Multi-segment bar -->
              <div class="space-y-1.5">
                <div class="bg-muted/60 ring-border/50 flex h-3 w-full overflow-hidden rounded-full p-0.5 ring-1">
                  <div
                    class="bg-primary h-full rounded-l-full transition-[width] duration-300"
                    style="width: 59%"
                    title="Federal Income Tax: 59%"
                  ></div>
                  <div
                    class="bg-warning bg-warning h-full transition-[width] duration-300"
                    style="width: 27%"
                    title="State & Local Tax: 27%"
                  ></div>
                  <div
                    class="bg-success bg-success h-full rounded-r-full transition-[width] duration-300"
                    style="width: 14%"
                    title="Self-Employment / FICA: 14%"
                  ></div>
                </div>
                <div class="text-muted-foreground flex items-center justify-between text-xs">
                  <span>Federal (59%)</span>
                  <span>State (27%)</span>
                  <span>Self-Employment (14%)</span>
                </div>
              </div>
    
              <!-- Jurisdiction Cards Grid -->
              <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
                @for (j of jurisdictions; track j.id) {
                  <div class="bg-muted/30 border-border/80 flex flex-col justify-between rounded-lg border p-4 shadow-2xs">
                    <div>
                      <div class="flex flex-wrap items-start justify-between gap-2">
                        <div>
                          <div class="flex items-center gap-2">
                            <span [class]="jurisdictionDotClass(j.accentColor)" aria-hidden="true"></span>
                            <h4 class="text-foreground min-w-0 text-sm font-semibold">{{ j.name }}</h4>
                          </div>
                          <p class="text-muted-foreground mt-0.5 text-xs">{{ j.authority }}</p>
                        </div>
                        <span ui-badge variant="secondary" class="text-xs font-semibold tabular-nums"
                          >{{ j.percentage }}%</span
                        >
                      </div>
    
                      <div class="my-4">
                        <p class="text-foreground text-2xl font-bold tracking-tight tabular-nums">{{ j.amount }}</p>
                        <p class="text-muted-foreground mt-1 text-xs">{{ j.rateDescription }}</p>
                      </div>
                    </div>
    
                    <ui-progress [value]="j.percentage" class="h-1.5" />
                  </div>
                }
              </div>
            </div>
          </div>
    
          <!-- Deductible Expense Categories Table -->
          <div ui-card class="shadow-xs">
            <div ui-card-header>
              <div class="flex flex-wrap items-center justify-between gap-2">
                <div>
                  <h3 ui-card-title class="text-base font-semibold">Deductible Expense Categories</h3>
                  <p ui-card-description class="text-xs sm:text-sm">
                    IRS Schedule C business deductions applied YTD to reduce gross taxable revenue ($34,820.00 total spent).
                  </p>
                </div>
                <span ui-badge variant="outline" class="text-xs font-medium tabular-nums">YTD Tax Savings: $7,119.78</span>
              </div>
            </div>
            <div ui-card-content class="p-0">
              <table ui-table>
                <thead ui-table-header>
                  <tr ui-table-row>
                    <th ui-table-head class="w-[45%]">Category & Description</th>
                    <th ui-table-head class="text-right">Total Spent YTD</th>
                    <th ui-table-head class="text-center">Deductible Rate</th>
                    <th ui-table-head class="text-right">Total Tax Savings</th>
                  </tr>
                </thead>
                <tbody ui-table-body>
                  @for (category of deductionCategories; track category.id) {
                    <tr ui-table-row>
                      <td ui-table-cell>
                        <div class="text-foreground text-sm font-medium">{{ category.name }}</div>
                        <div class="text-muted-foreground text-xs">{{ category.description }}</div>
                      </td>
                      <td ui-table-cell class="text-foreground text-right font-medium tabular-nums">
                        {{ category.spent }}
                      </td>
                      <td ui-table-cell class="text-center">
                        <span ui-badge [variant]="category.rateBadgeVariant" class="text-xs font-medium">
                          {{ category.rate }}
                        </span>
                      </td>
                      <td ui-table-cell class="text-success text-success text-right font-medium tabular-nums">
                        {{ category.savings }}
                      </td>
                    </tr>
                  }
                </tbody>
                <tfoot ui-table-footer>
                  <tr ui-table-row class="font-medium">
                    <td ui-table-cell>Total Business Deductions (5 categories)</td>
                    <td ui-table-cell class="text-foreground text-right font-bold tabular-nums">$34,820.00</td>
                    <td ui-table-cell class="text-center">
                      <span ui-badge variant="outline" class="text-xs font-medium">95.5% blended</span>
                    </td>
                    <td ui-table-cell class="text-success text-success text-right font-bold tabular-nums">$7,119.78</td>
                  </tr>
                </tfoot>
              </table>
            </div>
          </div>
    
          <!-- Quarterly Tax Filing Schedule Timeline -->
          <div ui-card class="shadow-xs">
            <div ui-card-header>
              <div class="flex flex-wrap items-center justify-between gap-2">
                <div>
                  <h3 ui-card-title class="text-base font-semibold">Quarterly Estimated Tax Schedule</h3>
                  <p ui-card-description class="text-xs sm:text-sm">
                    2026 fiscal tax year remittance timeline, due dates, and IRS EFTPS confirmations.
                  </p>
                </div>
                <div class="text-muted-foreground flex items-center gap-1.5 text-xs">
                  <lucide-icon [img]="ShieldCheck" class="text-success size-4" />
                  <span>IRS EFTPS Integrated</span>
                </div>
              </div>
            </div>
            <div ui-card-content>
              <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
                @for (q of quarterlySchedule; track q.quarter) {
                  <div [class]="quarterCardClass(q.isCurrent)">
                    <div>
                      <div class="flex items-center justify-between gap-2">
                        <span class="text-foreground text-sm font-semibold">{{ q.quarter }}</span>
                        <span
                          ui-badge
                          [variant]="quarterStatusVariant(q.status)"
                          class="text-xs font-medium whitespace-normal"
                        >
                          {{ q.status }}
                        </span>
                      </div>
    
                      <div class="mt-2 space-y-1">
                        <div class="text-muted-foreground flex items-center gap-1.5 text-xs">
                          <lucide-icon [img]="Calendar" class="size-3.5" />
                          <span>Due: {{ q.dueDate }}</span>
                        </div>
                        <p class="text-muted-foreground text-xs">Period: {{ q.period }}</p>
                      </div>
                    </div>
    
                    <div class="border-border/60 mt-4 border-t pt-3">
                      <div class="flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5">
                        <span class="text-muted-foreground text-xs">Amount</span>
                        <span [class]="quarterAmountClass(q.isCurrent)">{{ q.amount }}</span>
                      </div>
                      <div [class]="quarterNoteClass(q.status)">
                        <lucide-icon [img]="quarterNoteIcon(q.status)" class="size-3.5 shrink-0" />
                        <span class="truncate">{{ q.note }}</span>
                      </div>
                    </div>
                  </div>
                }
              </div>
            </div>
          </div>
        </div>
      `,
    })
    export class UiTaxSummaryReportComponent {
      protected readonly Calendar = Calendar
      protected readonly CheckCircle2 = CheckCircle2
      protected readonly Clock = Clock
      protected readonly CreditCard = CreditCard
      protected readonly Download = Download
      protected readonly ShieldCheck = ShieldCheck
    
      protected readonly metrics = metrics
      protected readonly jurisdictions = jurisdictions
      protected readonly deductionCategories = deductionCategories
      protected readonly quarterlySchedule = quarterlySchedule
    
      @Input('class') className?: string
    
      readonly selectedYear = signal('2026')
    
      get rootClass(): string {
        return cn('w-full space-y-6', this.className)
      }
    
      jurisdictionDotClass(accentColor: string): string {
        return cn('size-2.5 shrink-0 self-start rounded-full', accentColor)
      }
    
      quarterCardClass(isCurrent: boolean): string {
        return cn(
          'relative flex flex-col justify-between rounded-lg border p-4 transition-colors',
          isCurrent ? 'border-primary/60 bg-primary/[0.03] shadow-xs' : 'border-border bg-card shadow-2xs',
        )
      }
    
      quarterAmountClass(isCurrent: boolean): string {
        return cn('text-lg font-bold tabular-nums', isCurrent ? 'text-primary' : 'text-foreground')
      }
    
      quarterStatusVariant(status: string): BadgeVariant {
        if (status === 'Paid') return 'success'
        if (status === 'Upcoming') return 'warning'
        return 'secondary'
      }
    
      quarterNoteClass(status: string): string {
        return cn(
          'mt-1 flex items-center gap-1.5 text-xs',
          status === 'Paid' ? 'text-success' : status === 'Upcoming' ? 'text-warning' : 'text-muted-foreground',
        )
      }
    
      quarterNoteIcon(status: string): unknown {
        if (status === 'Paid') return CheckCircle2
        if (status === 'Upcoming') return Clock
        return Calendar
      }
    
      metricIcon(icon: unknown): unknown {
        return icon
      }
    }
    
  • src/app/components/blocks/tax-summary-report/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/tax-summary-report.json