UIPackage
Menu

Framework

Change language

Boilerplate repo

Employee Pulse Survey Results

blockhr

Culture Amp style company pulse and eNPS engagement survey results dashboard featuring executive KPI scorecards, department sentiment heatmaps, and priority engagement drivers with action items.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/employee-pulse-survey-results.json
Named registry:npx shadcn-vue@latest add @uipkge/employee-pulse-survey-resultsInstalls to:app/components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classHTMLAttributes['class']optional

Schema

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

PrimaryMetric
interface PrimaryMetric {
  id: string
  title: string
  value: string
  unit?: string
  badgeText: string
  badgeVariant: 'success' | 'secondary' | 'default' | 'outline' | 'warning'
  detail: string
  subtext: string
  progress: number
  icon: typeof TrendingUp
}
DepartmentRow
interface DepartmentRow {
  name: string
  responseCount: string
  participationRate: string
  cultureValues: number
  managementEffectiveness: number
  workLifeBalance: number
  compensationGrowth: number
  enps: number
  enpsFormatted: string
  enpsRank: string
}
DriverItem
interface DriverItem {
  id: string
  title: string
  score: string
  scoreValue: number
  tag: string
  impact: string
  description: string
  benchmarkDiff: string
}
OpportunityItem
interface OpportunityItem {
  id: string
  title: string
  score: string
  scoreValue: number
  statusBadge: string
  statusVariant: 'warning' | 'default' | 'secondary'
  owner: string
  targetDate: string
  actionSummary: string
}

Files installed (1)

  • app/components/blocks/EmployeePulseSurveyResults.vue22.9 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import {
      AlertCircle,
      Award,
      FileDown,
      HeartHandshake,
      Lightbulb,
      ShieldCheck,
      TrendingUp,
      Users,
      Zap,
    } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
    import { Progress } from '@/components/ui/progress'
    import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
    
    interface Props {
      class?: HTMLAttributes['class']
    }
    
    const props = defineProps<Props>()
    
    interface PrimaryMetric {
      id: string
      title: string
      value: string
      unit?: string
      badgeText: string
      badgeVariant: 'success' | 'secondary' | 'default' | 'outline' | 'warning'
      detail: string
      subtext: string
      progress: number
      icon: typeof TrendingUp
    }
    
    interface DepartmentRow {
      name: string
      responseCount: string
      participationRate: string
      cultureValues: number
      managementEffectiveness: number
      workLifeBalance: number
      compensationGrowth: number
      enps: number
      enpsFormatted: string
      enpsRank: string
    }
    
    interface DriverItem {
      id: string
      title: string
      score: string
      scoreValue: number
      tag: string
      impact: string
      description: string
      benchmarkDiff: string
    }
    
    interface OpportunityItem {
      id: string
      title: string
      score: string
      scoreValue: number
      statusBadge: string
      statusVariant: 'warning' | 'default' | 'secondary'
      owner: string
      targetDate: string
      actionSummary: string
    }
    
    const primaryMetrics: PrimaryMetric[] = [
      {
        id: 'enps-score',
        title: 'Employee Net Promoter Score',
        value: '+54',
        unit: 'eNPS',
        badgeText: '+22 vs tech benchmark',
        badgeVariant: 'success',
        detail: '64% Promoters · 26% Passives · 10% Detractors',
        subtext: 'Benchmark: +32 eNPS across peer tech scaleups',
        progress: 77,
        icon: TrendingUp,
      },
      {
        id: 'engagement-index',
        title: 'Overall Engagement Index',
        value: '86%',
        unit: 'Favorable',
        badgeText: '+4% vs Q2',
        badgeVariant: 'success',
        detail: '86% Favorable · 10% Neutral · 4% Unfavorable',
        subtext: '4.3 / 5.0 mean agreement across all survey items',
        progress: 86,
        icon: HeartHandshake,
      },
      {
        id: 'participation-rate',
        title: 'Survey Participation Rate',
        value: '94%',
        unit: '139 / 148',
        badgeText: 'High Confidence',
        badgeVariant: 'secondary',
        detail: '94% across 5 active departments',
        subtext: 'Statistically significant sample size (p < 0.01)',
        progress: 94,
        icon: Users,
      },
      {
        id: 'retention-intent',
        title: 'Intent to Stay 2+ Years',
        value: '88%',
        unit: 'Retention',
        badgeText: '+6% vs Q2',
        badgeVariant: 'success',
        detail: '88% positive retention sentiment',
        subtext: 'Low voluntary flight risk across engineering & product',
        progress: 88,
        icon: ShieldCheck,
      },
    ]
    
    const departments: DepartmentRow[] = [
      {
        name: 'Engineering',
        responseCount: '46 / 48 responses',
        participationRate: '96%',
        cultureValues: 92,
        managementEffectiveness: 88,
        workLifeBalance: 84,
        compensationGrowth: 74,
        enps: 62,
        enpsFormatted: '+62',
        enpsRank: 'Top Quartile',
      },
      {
        name: 'Product & Design',
        responseCount: '28 / 30 responses',
        participationRate: '93%',
        cultureValues: 94,
        managementEffectiveness: 91,
        workLifeBalance: 82,
        compensationGrowth: 76,
        enps: 58,
        enpsFormatted: '+58',
        enpsRank: 'Top Quartile',
      },
      {
        name: 'Marketing',
        responseCount: '22 / 24 responses',
        participationRate: '92%',
        cultureValues: 89,
        managementEffectiveness: 85,
        workLifeBalance: 86,
        compensationGrowth: 71,
        enps: 48,
        enpsFormatted: '+48',
        enpsRank: 'Above Average',
      },
      {
        name: 'Sales & GTM',
        responseCount: '25 / 26 responses',
        participationRate: '96%',
        cultureValues: 86,
        managementEffectiveness: 84,
        workLifeBalance: 78,
        compensationGrowth: 68,
        enps: 45,
        enpsFormatted: '+45',
        enpsRank: 'Above Average',
      },
      {
        name: 'Operations',
        responseCount: '18 / 20 responses',
        participationRate: '90%',
        cultureValues: 90,
        managementEffectiveness: 87,
        workLifeBalance: 88,
        compensationGrowth: 72,
        enps: 52,
        enpsFormatted: '+52',
        enpsRank: 'Top Quartile',
      },
    ]
    
    const topDrivers: DriverItem[] = [
      {
        id: 'driver-1',
        title: 'Psychological safety & peer collaboration',
        score: '96% favorable',
        scoreValue: 96,
        tag: 'Highest Scored Driver',
        impact: 'Highest correlation with company eNPS (+0.74 r)',
        benchmarkDiff: '+14% vs tech benchmark',
        description:
          'Squads report high mutual trust, blameless incident reviews, and transparent pairing culture without fear of failure.',
      },
      {
        id: 'driver-2',
        title: 'Mission alignment & executive transparency',
        score: '92% favorable',
        scoreValue: 92,
        tag: 'Core Driver',
        impact: 'Strongest predictor of organizational retention',
        benchmarkDiff: '+11% vs tech benchmark',
        description:
          'Bi-weekly all-hands, transparent financials, and crisp quarterly OKRs provide unequivocal clarity on company trajectory.',
      },
      {
        id: 'driver-3',
        title: 'Manager trust & day-to-day autonomy',
        score: '90% favorable',
        scoreValue: 90,
        tag: 'Core Driver',
        impact: 'Key catalyst for execution velocity & morale',
        benchmarkDiff: '+9% vs tech benchmark',
        description:
          'Team members highlight minimal micromanagement, supportive weekly 1:1 coaching, and autonomy over technical decisions.',
      },
    ]
    
    const opportunityAreas: OpportunityItem[] = [
      {
        id: 'opp-1',
        title: 'Clarity on promotion cycles and IC compensation bands',
        score: '68% favorable',
        scoreValue: 68,
        statusBadge: 'Action plan active',
        statusVariant: 'warning',
        owner: 'People Ops & Total Rewards',
        targetDate: 'Q4 2026 Rollout',
        actionSummary:
          'Publish transparent engineering & IC level matrices with defined salary bands, equity guidelines, and semi-annual calibration windows.',
      },
      {
        id: 'opp-2',
        title: 'Cross-functional roadmap visibility & tool sprawl',
        score: '71% favorable',
        scoreValue: 71,
        statusBadge: 'Action plan active',
        statusVariant: 'warning',
        owner: 'Product Ops & Platform Squad',
        targetDate: 'End of Q3',
        actionSummary:
          'Consolidate sprint tracking into unified Linear workspaces and establish bi-weekly cross-discipline demo showcases.',
      },
      {
        id: 'opp-3',
        title: 'Focus time & meeting cadence optimization',
        score: '73% favorable',
        scoreValue: 73,
        statusBadge: 'Action plan active',
        statusVariant: 'warning',
        owner: 'Engineering Leadership Council',
        targetDate: 'Active Trial',
        actionSummary:
          'Implement company-wide "No Meeting Wednesdays" and audit recurring status meetings in favor of asynchronous updates.',
      },
    ]
    
    const getScoreBadgeClass = (score: number) => {
      if (score >= 80) {
        return 'border-success/20 bg-success/10 text-success'
      }
      if (score >= 70) {
        return 'border-warning/20 bg-warning/10 text-warning'
      }
      return 'border-destructive/20 bg-destructive/10 text-destructive'
    }
    </script>
    
    <template>
      <div data-uipkge data-slot="employee-pulse-survey-results" :class="cn('w-full space-y-6', props.class)">
        <!-- Header Section -->
        <Card class="border-border bg-card shadow-xs">
          <CardContent class="p-6">
            <div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
              <div class="space-y-1.5">
                <div class="flex flex-wrap items-center gap-2.5">
                  <h2 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">
                    Company Pulse &amp; eNPS Engagement Survey
                  </h2>
                  <Badge wrap variant="outline" class="font-mono text-xs"> Q3 2026 </Badge>
                </div>
                <p class="text-muted-foreground text-xs leading-relaxed sm:text-sm">
                  Q3 2026 Pulse Survey · 94% Participation Rate · 139 / 148 Responses
                </p>
              </div>
    
              <div class="flex flex-wrap items-center gap-3">
                <div
                  class="border-success/30 bg-success/10 text-success dark:text-foreground flex min-w-0 items-center gap-2 rounded-lg border px-3.5 py-2 shadow-xs"
                >
                  <TrendingUp class="text-success size-4 shrink-0" />
                  <span class="text-sm font-bold tracking-tight tabular-nums">+54 eNPS · Excellent</span>
                </div>
                <Button variant="outline" size="sm" class="gap-2 shadow-xs">
                  <FileDown class="size-4" />
                  <span>Export Executive Summary</span>
                </Button>
              </div>
            </div>
          </CardContent>
        </Card>
    
        <!-- 4 Primary Engagement KPI Cards -->
        <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <Card
            v-for="metric in primaryMetrics"
            :key="metric.id"
            class="border-border bg-card hover:border-border/80 shadow-xs transition-colors"
          >
            <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
              <CardTitle class="text-muted-foreground text-xs font-medium">
                {{ metric.title }}
              </CardTitle>
              <component :is="metric.icon" class="text-muted-foreground size-4" />
            </CardHeader>
            <CardContent class="space-y-3">
              <div class="flex flex-wrap items-baseline justify-between gap-2">
                <div class="flex items-baseline gap-1.5">
                  <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl">
                    {{ metric.value }}
                  </span>
                  <span v-if="metric.unit" class="text-muted-foreground text-xs font-medium">
                    {{ metric.unit }}
                  </span>
                </div>
                <Badge wrap :variant="metric.badgeVariant" class="text-xs font-medium tabular-nums">
                  {{ metric.badgeText }}
                </Badge>
              </div>
    
              <div class="space-y-1.5">
                <Progress :model-value="metric.progress" class="h-1.5 w-full" />
                <div class="space-y-0.5 pt-0.5">
                  <p class="text-foreground text-xs leading-normal font-medium">{{ metric.detail }}</p>
                  <p class="text-muted-foreground text-xs leading-normal">{{ metric.subtext }}</p>
                </div>
              </div>
            </CardContent>
          </Card>
        </div>
    
        <!-- Department Sentiment Heatmap Table -->
        <Card class="border-border bg-card shadow-xs">
          <CardHeader class="pb-4">
            <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
              <div>
                <div class="flex min-w-0 items-center gap-2">
                  <CardTitle class="text-base font-semibold">Department Sentiment Heatmap</CardTitle>
                  <Badge wrap variant="outline" class="text-xs font-normal">5 Departments · 139 Responses</Badge>
                </div>
                <CardDescription class="mt-1 text-xs">
                  Culture Amp style multidimensional sentiment matrix mapped across key operational pillars.
                </CardDescription>
              </div>
    
              <!-- Heatmap Legend -->
              <div class="flex flex-wrap items-center gap-2 pt-1 text-xs sm:pt-0">
                <span class="text-muted-foreground font-medium">Sentiment Scale:</span>
                <div
                  class="border-success/20 bg-success/10 text-success flex items-center gap-1 rounded-md border px-2 py-0.5"
                >
                  <span class="bg-success size-1.5 rounded-full" />
                  <span class="font-medium">Favorable (≥80%)</span>
                </div>
                <div
                  class="border-warning/20 bg-warning/10 text-warning flex items-center gap-1 rounded-md border px-2 py-0.5"
                >
                  <span class="bg-warning size-1.5 rounded-full" />
                  <span class="font-medium">Moderate (70-79%)</span>
                </div>
                <div
                  class="border-destructive/20 bg-destructive/10 text-destructive flex items-center gap-1 rounded-md border px-2 py-0.5"
                >
                  <span class="bg-destructive size-1.5 rounded-full" />
                  <span class="font-medium">Opportunity (&lt;70%)</span>
                </div>
              </div>
            </div>
          </CardHeader>
    
          <CardContent class="p-0">
            <div class="border-border overflow-x-auto border-t">
              <Table>
                <TableHeader class="bg-muted/40">
                  <TableRow class="hover:bg-transparent">
                    <TableHead class="text-foreground w-60 text-xs font-semibold">
                      Department &amp; Participation
                    </TableHead>
                    <TableHead class="text-foreground text-center text-xs font-semibold"> Culture &amp; Values </TableHead>
                    <TableHead class="text-foreground text-center text-xs font-semibold">
                      Management Effectiveness
                    </TableHead>
                    <TableHead class="text-foreground text-center text-xs font-semibold"> Work-Life Balance </TableHead>
                    <TableHead class="text-foreground text-center text-xs font-semibold">
                      Compensation &amp; Growth
                    </TableHead>
                    <TableHead class="text-foreground text-right text-xs font-semibold"> Overall eNPS Score </TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  <TableRow v-for="dept in departments" :key="dept.name" class="hover:bg-muted/30">
                    <!-- Department Name & Sample -->
                    <TableCell class="py-3.5">
                      <div class="space-y-0.5">
                        <p class="text-foreground text-sm font-semibold">{{ dept.name }}</p>
                        <p class="text-muted-foreground text-xs tabular-nums">
                          {{ dept.responseCount }} ·
                          <span class="text-foreground font-medium">{{ dept.participationRate }}</span>
                        </p>
                      </div>
                    </TableCell>
    
                    <!-- Culture & Values -->
                    <TableCell class="py-3.5 text-center">
                      <div class="inline-flex flex-col items-center gap-1">
                        <div
                          :class="
                            cn(
                              'flex min-w-[72px] items-center justify-center rounded-md border px-3 py-1.5 text-xs font-bold tabular-nums shadow-xs',
                              getScoreBadgeClass(dept.cultureValues),
                            )
                          "
                        >
                          {{ dept.cultureValues }}%
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Management Effectiveness -->
                    <TableCell class="py-3.5 text-center">
                      <div class="inline-flex flex-col items-center gap-1">
                        <div
                          :class="
                            cn(
                              'flex min-w-[72px] items-center justify-center rounded-md border px-3 py-1.5 text-xs font-bold tabular-nums shadow-xs',
                              getScoreBadgeClass(dept.managementEffectiveness),
                            )
                          "
                        >
                          {{ dept.managementEffectiveness }}%
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Work-Life Balance -->
                    <TableCell class="py-3.5 text-center">
                      <div class="inline-flex flex-col items-center gap-1">
                        <div
                          :class="
                            cn(
                              'flex min-w-[72px] items-center justify-center rounded-md border px-3 py-1.5 text-xs font-bold tabular-nums shadow-xs',
                              getScoreBadgeClass(dept.workLifeBalance),
                            )
                          "
                        >
                          {{ dept.workLifeBalance }}%
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Compensation & Growth -->
                    <TableCell class="py-3.5 text-center">
                      <div class="inline-flex flex-col items-center gap-1">
                        <div
                          :class="
                            cn(
                              'flex min-w-[72px] items-center justify-center rounded-md border px-3 py-1.5 text-xs font-bold tabular-nums shadow-xs',
                              getScoreBadgeClass(dept.compensationGrowth),
                            )
                          "
                        >
                          {{ dept.compensationGrowth }}%
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Overall eNPS Score -->
                    <TableCell class="py-3.5 text-right">
                      <div class="flex flex-col items-end gap-1">
                        <div
                          class="border-success/30 bg-success/10 text-success text-success inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-bold tabular-nums"
                        >
                          <TrendingUp class="text-success size-3" />
                          <span>{{ dept.enpsFormatted }} eNPS</span>
                        </div>
                        <span class="text-muted-foreground text-xs font-normal">
                          {{ dept.enpsRank }}
                        </span>
                      </div>
                    </TableCell>
                  </TableRow>
                </TableBody>
              </Table>
            </div>
          </CardContent>
        </Card>
    
        <!-- Top Engagement Drivers & Action Items Card -->
        <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
          <!-- Left Column: Top Engagement Drivers (Superpowers) -->
          <Card class="border-border bg-card shadow-xs">
            <CardHeader class="pb-4">
              <div class="flex flex-wrap items-center justify-between gap-2">
                <div class="space-y-1">
                  <div class="flex min-w-0 items-center gap-2">
                    <CardTitle class="text-base font-semibold">Top Engagement Drivers</CardTitle>
                    <Badge wrap variant="success" class="text-xs">Organizational Strengths</Badge>
                  </div>
                  <CardDescription class="text-xs">
                    Highest scored sentiment drivers providing outsized positive impact on overall eNPS.
                  </CardDescription>
                </div>
                <Award class="text-success size-5 shrink-0" />
              </div>
            </CardHeader>
    
            <CardContent class="space-y-4">
              <div
                v-for="driver in topDrivers"
                :key="driver.id"
                class="bg-muted/30 border-border/80 space-y-2.5 rounded-lg border p-4 shadow-xs"
              >
                <div class="flex flex-wrap items-start justify-between gap-2">
                  <div class="space-y-1">
                    <div class="flex flex-wrap items-center gap-2">
                      <h4 class="text-foreground text-sm font-semibold">{{ driver.title }}</h4>
                      <Badge wrap variant="outline" class="text-xs font-normal">
                        {{ driver.tag }}
                      </Badge>
                    </div>
                    <p class="text-muted-foreground text-xs">{{ driver.impact }}</p>
                  </div>
    
                  <div
                    class="border-success/20 bg-success/10 text-success text-success flex shrink-0 items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-bold tabular-nums"
                  >
                    <TrendingUp class="text-success size-3" />
                    <span>{{ driver.score }}</span>
                  </div>
                </div>
    
                <p class="text-muted-foreground text-xs leading-relaxed">
                  {{ driver.description }}
                </p>
    
                <div class="border-border/60 flex items-center justify-between border-t pt-2 text-xs">
                  <span class="text-muted-foreground">Peer Benchmark:</span>
                  <span class="text-success text-success font-semibold tabular-nums">
                    {{ driver.benchmarkDiff }}
                  </span>
                </div>
              </div>
            </CardContent>
          </Card>
    
          <!-- Right Column: Priority Opportunity Areas & Action Items -->
          <Card class="border-border bg-card shadow-xs">
            <CardHeader class="pb-4">
              <div class="flex flex-wrap items-center justify-between gap-2">
                <div class="space-y-1">
                  <div class="flex min-w-0 items-center gap-2">
                    <CardTitle class="text-base font-semibold">Priority Opportunity Areas</CardTitle>
                    <Badge wrap variant="warning" class="text-xs">Active Remediation</Badge>
                  </div>
                  <CardDescription class="text-xs">
                    Key focus areas requiring leadership follow-through and documented action plans.
                  </CardDescription>
                </div>
                <Lightbulb class="text-warning size-5 shrink-0" />
              </div>
            </CardHeader>
    
            <CardContent class="space-y-4">
              <div
                v-for="opp in opportunityAreas"
                :key="opp.id"
                class="bg-muted/30 border-border/80 space-y-2.5 rounded-lg border p-4 shadow-xs"
              >
                <div class="flex flex-wrap items-start justify-between gap-2">
                  <div class="space-y-1">
                    <h4 class="text-foreground text-sm font-semibold">{{ opp.title }}</h4>
                    <div class="flex flex-wrap items-center gap-2">
                      <Badge wrap variant="warning" class="text-xs font-medium">
                        {{ opp.statusBadge }}
                      </Badge>
                      <span class="text-muted-foreground text-xs">
                        Target: <strong class="text-foreground font-medium">{{ opp.targetDate }}</strong>
                      </span>
                    </div>
                  </div>
    
                  <div
                    class="border-warning/20 bg-warning/10 text-warning text-warning flex shrink-0 items-center gap-1 rounded-md border px-2.5 py-1 text-xs font-bold tabular-nums"
                  >
                    <AlertCircle class="text-warning size-3" />
                    <span>{{ opp.score }}</span>
                  </div>
                </div>
    
                <div class="bg-card/70 border-border/60 space-y-1 rounded-md border p-2.5">
                  <div class="text-foreground flex items-center gap-1 text-xs font-medium">
                    <Zap class="text-primary size-3 shrink-0" />
                    <span>Action Plan &amp; Deliverables</span>
                  </div>
                  <p class="text-muted-foreground text-xs leading-relaxed">
                    {{ opp.actionSummary }}
                  </p>
                </div>
    
                <div class="border-border/60 flex items-center justify-between border-t pt-2 text-xs">
                  <span class="text-muted-foreground">Action Owner:</span>
                  <span class="text-foreground font-medium">
                    {{ opp.owner }}
                  </span>
                </div>
              </div>
            </CardContent>
          </Card>
        </div>
      </div>
    </template>
    

Raw manifest:https://uipkge.dev/r/vue/employee-pulse-survey-results.json