Menu

Change language

GitHub repo

Use Cases

blockmarketing

Use-case set spanning a filterable industry grid, persona-switched tabs, self-select role cards, and a numbered end-to-end scenario walkthrough.

Installation

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

Variants

Loading interactive previews…

Schema

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

IndustryUseCase
interface IndustryUseCase {
  icon: unknown
  segment: string
  title: string
  body: string
  metric: string
}
Persona
interface Persona {
  value: string
  role: string
  pain: string
  outcomes: string[]
  metric: { value: string; label: string }
  cta: string
}

Files installed (5)

  • src/app/components/blocks/use-cases/use-cases-industry-grid.component.ts6.3 kB
    // Filterable industry use-case grid: segment chips narrow a six-card grid in place,
    // each card carrying an icon, industry label, outcome headline, proof metric, and a
    // quiet read-more link. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import {
      ArrowUpRight,
      Banknote,
      Factory,
      HeartPulse,
      LucideAngularModule,
      Ship,
      ShoppingCart,
      Truck,
    } 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 { UiToggleGroupComponent, UiToggleGroupItemComponent } from '@/ui/toggle-group/toggle-group.component'
    
    interface IndustryUseCase {
      icon: unknown
      segment: string
      title: string
      body: string
      metric: string
    }
    
    const useCases: IndustryUseCase[] = [
      {
        icon: Banknote,
        segment: 'Financial services',
        title: 'Close the books without a spreadsheet relay',
        body: 'Ledger-reconciled metrics that lock on period close, with an auditable trail for every restatement.',
        metric: '5 days off close',
      },
      {
        icon: HeartPulse,
        segment: 'Healthcare',
        title: 'Report on care quality without exposing PHI',
        body: 'Row-level scoping evaluated per query, so a shared dashboard never leaks a chart the viewer cannot open.',
        metric: 'HIPAA-scoped by default',
      },
      {
        icon: ShoppingCart,
        segment: 'Retail',
        title: 'One margin definition across every channel',
        body: 'Store, marketplace, and wholesale roll up to the same certified metric instead of three competing exports.',
        metric: '3 channels, 1 number',
      },
      {
        icon: Truck,
        segment: 'Logistics',
        title: 'Track service levels against the contract',
        body: 'On-time thresholds encoded per customer contract, so breach reporting stops being a manual comparison.',
        metric: '98.2% SLA visibility',
      },
      {
        icon: Factory,
        segment: 'Manufacturing',
        title: 'Yield reporting that survives a line change',
        body: 'Definitions version with the production line, so last quarter’s numbers stay comparable after a retool.',
        metric: '12 lines reconciled',
      },
      {
        icon: Ship,
        segment: 'Logistics',
        title: 'Landed cost per shipment, not per guess',
        body: 'Freight, duty, and demurrage join at query time against the booking rather than a monthly allocation.',
        metric: 'Per-shipment costing',
      },
    ]
    
    const ALL = 'All'
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-use-cases-industry-grid, [ui-use-cases-industry-grid]',
      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,
        UiToggleGroupComponent,
        UiToggleGroupItemComponent,
      ],
      template: `
        <section data-slot="use-cases-industry-grid" [class]="rootClass">
          <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28">
            <div class="flex flex-wrap items-end justify-between gap-6">
              <div class="max-w-2xl">
                <span ui-badge variant="secondary">By industry</span>
                <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Where teams start</h2>
                <p class="text-muted-foreground mt-3 text-lg">
                  Six rollouts we run often enough to have opinions about. Filter to yours.
                </p>
              </div>
    
              <div
                ui-toggle-group
                type="single"
                variant="outline"
                size="sm"
                class="flex-nowrap overflow-x-auto"
                aria-label="Filter use cases by industry"
                [value]="active()"
                (valueChange)="onSegmentChange($event)"
              >
                @for (segment of segments(); track segment) {
                  <button ui-toggle-group-item [value]="segment">{{ segment }}</button>
                }
              </div>
            </div>
    
            <div class="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
              @for (useCase of visible(); track useCase.title) {
                <div ui-card class="group">
                  <div ui-card-content class="flex h-full flex-col p-6">
                    <lucide-icon [img]="useCase.icon" class="text-muted-foreground size-5" aria-hidden="true" />
                    <p class="text-muted-foreground mt-4 font-mono text-xs tracking-[0.14em] uppercase">
                      {{ useCase.segment }}
                    </p>
                    <h3 class="mt-2 text-base leading-snug font-semibold">{{ useCase.title }}</h3>
                    <p class="text-muted-foreground mt-2 text-sm leading-relaxed">{{ useCase.body }}</p>
    
                    <div class="border-border mt-auto flex items-center justify-between gap-3 border-t pt-4">
                      <span class="text-sm font-medium">{{ useCase.metric }}</span>
                      <button ui-button variant="ghost" size="sm" class="h-auto px-2 py-1">
                        Read
                        <lucide-icon
                          [img]="ArrowUpRight"
                          class="ml-1 size-3.5 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5"
                          aria-hidden="true"
                        />
                      </button>
                    </div>
                  </div>
                </div>
              }
            </div>
          </div>
        </section>
      `,
    })
    export class UiUseCasesIndustryGridComponent {
      protected readonly ArrowUpRight = ArrowUpRight
    
      @Input('class') className?: string
      @Input() set initialSegment(v: string | undefined) {
        if (v) this.active.set(v)
      }
    
      readonly active = signal(ALL)
      readonly segments = computed(() => [ALL, ...new Set(useCases.map((u) => u.segment))])
      readonly visible = computed(() =>
        this.active() === ALL ? useCases : useCases.filter((u) => u.segment === this.active()),
      )
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    
      // ToggleGroup single-select emits '' when the active item is pressed again;
      // fall back to ALL so the grid can never end up empty.
      onSegmentChange(value: unknown): void {
        this.active.set(typeof value === 'string' && value ? value : ALL)
      }
    }
    
  • src/app/components/blocks/use-cases/use-cases-persona-tabs.component.ts6.4 kB
  • src/app/components/blocks/use-cases/use-cases-role-cards.component.ts4.8 kB
  • src/app/components/blocks/use-cases/use-cases-scenario-walkthrough.component.ts4.7 kB
  • src/app/components/blocks/use-cases/index.ts0.3 kB

Raw manifest:https://uipkge.dev/r/angular/use-cases.json