Employee Pulse Survey Results
blockhrCulture Amp style company pulse and eNPS engagement survey results dashboard featuring executive KPI scorecards, department sentiment heatmaps, and priority engagement drivers with action items.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/employee-pulse-survey-results.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/employee-pulse-survey-results.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/employee-pulse-survey-results.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/employee-pulse-survey-results.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/employee-pulse-survey-resultsInstalls to:app/components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
PrimaryMetricinterface 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
}DepartmentRowinterface DepartmentRow {
name: string
responseCount: string
participationRate: string
cultureValues: number
managementEffectiveness: number
workLifeBalance: number
compensationGrowth: number
enps: number
enpsFormatted: string
enpsRank: string
}DriverIteminterface DriverItem {
id: string
title: string
score: string
scoreValue: number
tag: string
impact: string
description: string
benchmarkDiff: string
}OpportunityIteminterface 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 & 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 (<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 & Participation </TableHead> <TableHead class="text-foreground text-center text-xs font-semibold"> Culture & 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 & 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 & 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