Menu

Change language

GitHub repo

Case Study — Metric Comparison

blockmarketing

Single customer result shown as a before-and-after table of four metrics, each row carrying the prior value, the current value, and the delta, under a short context line.

Installation

$npx uipkge-ng@latest add case-study-metric-comparison
Manifest:https://uipkge.dev/r/angular/case-study-metric-comparison.jsonInstalls to:src/app/components/blocks/case-study-metric-comparison/

Variants

Loading interactive previews…

Schema

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

CaseStudyComparisonMetric
interface CaseStudyComparisonMetric {
  name: string
  before: string
  after: string
  delta: string
  /** Which direction is good, so the delta colour never has to be hard-coded per row. */
  better: 'down' | 'up'
}

Files installed (2)

  • src/app/components/blocks/case-study-metric-comparison/case-study-metric-comparison.component.ts5.2 kB
    // Single customer result as a before-and-after table: prior value, current value, and
    // delta per metric under a short context line. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
    import { ArrowRight, LucideAngularModule, TrendingDown, 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 } from '@/ui/card/card.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    import {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    
    export interface CaseStudyComparisonMetric {
      name: string
      before: string
      after: string
      delta: string
      /** Which direction is good, so the delta colour never has to be hard-coded per row. */
      better: 'down' | 'up'
    }
    
    const defaultMetrics: CaseStudyComparisonMetric[] = [
      { name: 'Days to close', before: '9.4', after: '4.2', delta: '−55%', better: 'down' },
      { name: 'Ad-hoc data requests / month', before: '212', after: '61', delta: '−71%', better: 'down' },
      { name: 'Certified metrics published', before: '0', after: '340', delta: '+340', better: 'up' },
      { name: 'Forecast accuracy band', before: '±6.1pp', after: '±3.7pp', delta: '−2.4pp', better: 'down' },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-case-study-metric-comparison, [ui-case-study-metric-comparison]',
      standalone: true,
      // React renders the root <section> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiBadgeComponent,
        UiButtonComponent,
        UiCardComponent,
        UiCardContentComponent,
        UiSeparatorComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
      ],
      template: `
        <section data-slot="case-study-metric-comparison" [class]="rootClass">
          <div class="mx-auto max-w-4xl px-6 py-20 lg:py-28">
            <span ui-badge variant="secondary">Northwind Logistics</span>
            <h2 class="mt-4 text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
              Two quarters either side of the rollout
            </h2>
            <p class="text-muted-foreground mt-3 text-lg">
              Their numbers, their definitions, measured the same way before and after. Nothing normalised.
            </p>
    
            <div ui-card class="mt-8">
              <div ui-card-content class="p-0">
                <table ui-table>
                  <thead ui-table-header>
                    <tr ui-table-row>
                      <th ui-table-head>Metric</th>
                      <th ui-table-head class="text-right">Q4 2025</th>
                      <th ui-table-head class="text-right">Q2 2026</th>
                      <th ui-table-head class="text-right">Change</th>
                    </tr>
                  </thead>
                  <tbody ui-table-body>
                    @for (metric of metrics; track metric.name) {
                      <tr ui-table-row>
                        <td ui-table-cell class="font-medium">{{ metric.name }}</td>
                        <td ui-table-cell class="text-muted-foreground text-right font-mono text-sm">
                          {{ metric.before }}
                        </td>
                        <td ui-table-cell class="text-right font-mono text-sm font-semibold">{{ metric.after }}</td>
                        <td ui-table-cell class="text-right">
                          <span class="text-success inline-flex items-center gap-1.5 font-mono text-sm">
                            @if (metric.better === 'down') {
                              <lucide-icon [img]="TrendingDown" class="size-3.5" aria-hidden="true" />
                            } @else {
                              <lucide-icon [img]="TrendingUp" class="size-3.5" aria-hidden="true" />
                            }
                            {{ metric.delta }}
                          </span>
                        </td>
                      </tr>
                    }
                  </tbody>
                </table>
    
                <ui-separator />
    
                <p class="text-muted-foreground px-6 py-4 text-xs leading-relaxed">
                  Measured from their own close calendar and ticket queue. The rollout completed between the two quarters;
                  no headcount was added or removed in finance during the period.
                </p>
              </div>
            </div>
    
            <button ui-button class="mt-8">
              Read how they did it
              <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" />
            </button>
          </div>
        </section>
      `,
    })
    export class UiCaseStudyMetricComparisonComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly TrendingDown = TrendingDown
      protected readonly TrendingUp = TrendingUp
    
      @Input() metrics: CaseStudyComparisonMetric[] = defaultMetrics.map((m) => ({ ...m }))
      @Input('class') className?: string
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    }
    
  • src/app/components/blocks/case-study-metric-comparison/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/case-study-metric-comparison.json