Menu

Change language

GitHub repo

Use Cases — Persona Tabs

blockmarketing

Persona-switched use-case section: a tab list of roles swaps the pain-point summary, a three-item outcome list, a supporting metric, and a role-specific CTA without leaving the page.

Installation

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

Variants

Loading interactive previews…

Schema

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

Persona
interface Persona {
  value: string
  role: string
  pain: string
  outcomes: string[]
  metric: { value: string; label: string }
  cta: string
}

npm dependencies

Files installed (2)

  • src/app/components/blocks/use-cases-persona-tabs/use-cases-persona-tabs.component.ts6.4 kB
    // Persona-switched use cases: selecting a role swaps the pain-point summary, outcome
    // list, supporting metric, and CTA in place — no navigation, no layout shift between
    // tabs. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, signal } from '@angular/core'
    import { ArrowRight, Check, LucideAngularModule } 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 {
      UiTabsComponent,
      UiTabsContentComponent,
      UiTabsListComponent,
      UiTabsTriggerComponent,
    } from '@/ui/tabs/tabs.component'
    
    interface Persona {
      value: string
      role: string
      pain: string
      outcomes: string[]
      metric: { value: string; label: string }
      cta: string
    }
    
    const personas: Persona[] = [
      {
        value: 'analytics',
        role: 'Data & analytics',
        pain: 'Every request becomes a bespoke SQL ticket, and nobody trusts the number in the deck.',
        outcomes: [
          'Publish one certified metric definition per number',
          'Retire the ad-hoc query queue without losing coverage',
          'Trace any figure back to the column it came from',
        ],
        metric: { value: '71%', label: 'fewer ad-hoc data requests after one quarter' },
        cta: 'See the analyst workflow',
      },
      {
        value: 'finance',
        role: 'Finance',
        pain: 'Close depends on four spreadsheets and one person who knows how they link.',
        outcomes: [
          'Reconcile against the ledger before anything publishes',
          'Lock a period so restated figures cannot drift',
          'Hand auditors a change log instead of a folder',
        ],
        metric: { value: '5 days', label: 'cut from the average monthly close' },
        cta: 'See the close workflow',
      },
      {
        value: 'revops',
        role: 'RevOps',
        pain: 'Pipeline reporting disagrees with the CRM, so forecast reviews start with an argument.',
        outcomes: [
          'One pipeline definition shared by sales and finance',
          'Stage changes tracked with the rep and the timestamp',
          'Forecast snapshots you can diff week over week',
        ],
        metric: { value: '2.4pp', label: 'tighter forecast accuracy band' },
        cta: 'See the forecast workflow',
      },
      {
        value: 'engineering',
        role: 'Engineering',
        pain: 'The reporting layer is an undocumented service nobody wants to own.',
        outcomes: [
          'Definitions live in the repo, reviewed like any other change',
          'No bespoke ETL to babysit at 3am',
          'Warehouse cost ceilings enforced per team',
        ],
        metric: { value: '0', label: 'bespoke pipelines to maintain' },
        cta: 'See the engineering setup',
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-use-cases-persona-tabs, [ui-use-cases-persona-tabs]',
      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,
        UiTabsComponent,
        UiTabsContentComponent,
        UiTabsListComponent,
        UiTabsTriggerComponent,
      ],
      template: `
        <section data-slot="use-cases-persona-tabs" [class]="rootClass">
          <div class="mx-auto max-w-5xl px-6 py-20 lg:py-28">
            <div class="max-w-2xl">
              <span ui-badge variant="secondary">Use cases</span>
              <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">Pick the seat you sit in</h2>
              <p class="text-muted-foreground mt-3 text-lg">
                Same platform, four very different first weeks. Start with the one that matches your job.
              </p>
            </div>
    
            <ui-tabs [value]="activeTab()" (valueChange)="activeTab.set($event)" class="mt-10">
              <!-- flex-nowrap + horizontal scroll: the trigger row keeps its height
                   whether there are three roles or thirty. -->
              <ui-tabs-list variant="segmented" class="flex-nowrap overflow-x-auto">
                @for (persona of personas; track persona.value) {
                  <button ui-tabs-trigger [value]="persona.value" variant="segmented">
                    {{ persona.role }}
                  </button>
                }
              </ui-tabs-list>
    
              @for (persona of personas; track persona.value) {
                <ui-tabs-content [value]="persona.value" class="mt-8">
                  <!-- min-h keeps the panel from collapsing between tabs with shorter copy. -->
                  <div class="grid min-h-[19rem] gap-8 lg:grid-cols-[1.3fr_1fr]">
                    <div>
                      <p class="text-muted-foreground text-sm">Today</p>
                      <p class="mt-1.5 text-lg leading-snug font-medium text-balance">{{ persona.pain }}</p>
    
                      <p class="text-muted-foreground mt-7 text-sm">With uipkge</p>
                      <ul class="mt-3 space-y-3">
                        @for (outcome of persona.outcomes; track outcome) {
                          <li class="flex items-start gap-2.5 text-sm">
                            <lucide-icon [img]="Check" class="text-primary mt-0.5 size-4 shrink-0" aria-hidden="true" />
                            <span>{{ outcome }}</span>
                          </li>
                        }
                      </ul>
    
                      <button ui-button class="mt-7">
                        {{ persona.cta }}
                        <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" />
                      </button>
                    </div>
    
                    <div ui-card class="self-start">
                      <div ui-card-content class="p-6">
                        <p class="font-display text-4xl font-bold tracking-tight">{{ persona.metric.value }}</p>
                        <p class="text-muted-foreground mt-2 text-sm leading-relaxed">{{ persona.metric.label }}</p>
                      </div>
                    </div>
                  </div>
                </ui-tabs-content>
              }
            </ui-tabs>
          </div>
        </section>
      `,
    })
    export class UiUseCasesPersonaTabsComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly Check = Check
    
      protected readonly personas = personas
    
      @Input('class') className?: string
      @Input() set initialPersona(v: string | undefined) {
        if (v) this.activeTab.set(v)
      }
    
      readonly activeTab = signal('analytics')
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    }
    
  • src/app/components/blocks/use-cases-persona-tabs/index.ts0.1 kB

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