Exit Interview Survey
blockhrStructured employee offboarding survey with department sentiment analysis, departure drivers, 5-star experience ratings, qualitative feedback textareas, and a knowledge handover & asset return checklist.
Installation
$pnpm dlx uipkge-ng@latest add exit-interview-survey$npx uipkge-ng@latest add exit-interview-survey$yarn dlx uipkge-ng@latest add exit-interview-survey$bunx uipkge-ng@latest add exit-interview-surveyManifest:
https://uipkge.dev/r/angular/exit-interview-survey.jsonInstalls to:src/app/components/blocks/exit-interview-survey/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
DepartureReasoninterface DepartureReason {
value: string
label: string
description: string
icon: LucideIconData
}RatingDimensioninterface RatingDimension {
id: RatingDimensionId
title: string
description: string
category: string
}ReturnOptioninterface ReturnOption {
value: string
label: string
description: string
badge: string
badgeVariant: BadgeVariant
}Files installed (2)
src/app/components/blocks/exit-interview-survey/exit-interview-survey.component.ts31.5 kB
// Structured employee offboarding survey with department sentiment analysis, departure // drivers, 5-star experience ratings, qualitative feedback textareas, and a knowledge // handover & asset return checklist. Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core' import { Award, Building2, Calendar, CheckCircle2, ClipboardCheck, Compass, FileCheck, FileCode2, HeartHandshake, KeyRound, Laptop, Lock, LucideAngularModule, MessageSquareQuote, Rocket, RotateCcw, Send, ShieldCheck, Star, TrendingUp, UserCheck, type LucideIconData, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent, type BadgeVariant } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component' import { UiLabelComponent } from '@/ui/label/label.component' import { UiProgressComponent } from '@/ui/progress/progress.component' import { UiRadioGroupComponent, UiRadioGroupItemComponent } from '@/ui/radio-group/radio-group.component' import { UiRatingComponent } from '@/ui/rating/rating.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiTextareaComponent } from '@/ui/textarea/textarea.component' interface DepartureReason { value: string label: string description: string icon: LucideIconData } const departureReasons: DepartureReason[] = [ { value: 'career-growth', label: 'Career Growth & Promotion', description: 'Seeking broader technical leadership scope, faster progression, or new architectural challenges.', icon: TrendingUp, }, { value: 'compensation', label: 'Compensation / Total Rewards', description: 'Competitive market offers, higher base salary band, equity incentives, or bonus structures.', icon: Award, }, { value: 'relocation', label: 'Relocation & Remote Freedom', description: 'Geographic move, personal relocation, or seeking broader international and async flexibility.', icon: Building2, }, { value: 'work-life', label: 'Work-Life Balance', description: 'Need for sustainable working cadence, reduced on-call intensity, and dedicated personal time.', icon: HeartHandshake, }, { value: 'leadership', label: 'Leadership & Direction', description: 'Strategic alignment with executive roadmap, product vision, or communication cadence.', icon: Compass, }, { value: 'venture', label: 'Starting Own Venture', description: 'Launching an independent technical startup, consulting practice, or entrepreneurial venture.', icon: Rocket, }, ] export type RatingDimensionId = 'management' | 'collaboration' | 'compensation' | 'culture' | 'flexibility' interface RatingDimension { id: RatingDimensionId title: string description: string category: string } const ratingDimensions: RatingDimension[] = [ { id: 'management', title: 'Management & Leadership', description: 'Direct manager support, transparent 1:1 mentorship, goal clarity, and career advocacy.', category: 'Leadership', }, { id: 'collaboration', title: 'Team Collaboration', description: 'Cross-functional synergy, peer code review quality, mutual trust, and psychological safety.', category: 'Team Dynamics', }, { id: 'compensation', title: 'Compensation & Benefits', description: 'Base salary competitiveness, equity appreciation, health coverage, and wellness allowances.', category: 'Total Rewards', }, { id: 'culture', title: 'Engineering Culture', description: 'Architectural standards, modern tooling, automated testing, CI/CD speed, and technical autonomy.', category: 'Craft & Standards', }, { id: 'flexibility', title: 'Work-Life Flexibility', description: 'Async-first communication, sustainable sprint planning, and reasonable on-call expectations.', category: 'Well-being', }, ] interface ReturnOption { value: string label: string description: string badge: string badgeVariant: BadgeVariant } const returnOptions: ReturnOption[] = [ { value: 'yes', label: 'Yes, definitely open', description: 'Strongly open to returning for future leadership or staff-level architectural initiatives.', badge: 'Alumni Priority', badgeVariant: 'success', }, { value: 'maybe', label: 'Maybe, under right conditions', description: 'Would consider returning under different organizational structure or roadmap scope.', badge: 'Conditionally Open', badgeVariant: 'secondary', }, { value: 'no', label: 'No, pursuing new pathways', description: 'Focused on long-term career growth in independent ventures or other sectors.', badge: 'New Trajectory', badgeVariant: 'outline', }, ] export type HandoverKey = 'codebase' | 'architecture' | 'credentials' | 'equipment' @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-exit-interview-survey, [ui-exit-interview-survey]', standalone: true, // React renders the root <div> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardFooterComponent, UiCardHeaderComponent, UiCardTitleComponent, UiCheckboxComponent, UiLabelComponent, UiProgressComponent, UiRadioGroupComponent, UiRadioGroupItemComponent, UiRatingComponent, UiSeparatorComponent, UiTextareaComponent, ], template: ` <div data-slot="exit-interview-survey" [class]="rootClass"> <!-- Header & Employee Context Card --> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-content class="p-6"> <div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between"> <div class="flex flex-col gap-4 sm:flex-row sm:items-center"> <ui-avatar class="border-border size-16 shrink-0 border sm:size-20"> <ui-avatar-image src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200&auto=format&fit=crop&q=80" alt="Elena Rostova" /> <ui-avatar-fallback>ER</ui-avatar-fallback> </ui-avatar> <div class="space-y-1.5"> <div class="flex flex-wrap items-center gap-2"> <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl"> Employee Exit Survey & Offboarding Review </h1> </div> <p class="text-muted-foreground text-sm font-medium"> Elena Rostova · Senior Staff Engineer · 3.5 Years Tenure </p> <div class="flex flex-wrap items-center gap-2 pt-1 text-xs"> <span ui-badge variant="secondary" class="font-medium"> Engineering · Core Platform </span> <span ui-badge variant="outline" class="gap-1 text-xs font-normal"> <lucide-icon [img]="Calendar" class="size-3" /> Final Day: Sep 30, 2026 </span> <span class="text-muted-foreground"> Manager: <strong class="text-foreground font-medium">Marcus Vance</strong> </span> </div> </div> </div> <div class="flex flex-wrap items-center gap-3 sm:self-start lg:self-center"> @if (!isSubmitted()) { <button ui-button size="sm" class="bg-primary text-primary-foreground gap-2 shadow-xs" (click)="handleSubmit()" > <lucide-icon [img]="Send" class="size-4" /> Submit Survey </button> } @else { <button ui-button variant="outline" size="sm" class="gap-2 shadow-xs" (click)="handleReset()"> <lucide-icon [img]="RotateCcw" class="size-4" /> Edit Survey Answers </button> } </div> </div> <div class="border-border/80 bg-muted/40 mt-6 flex items-start gap-3 rounded-lg border p-3.5 text-xs"> <lucide-icon [img]="ShieldCheck" class="text-primary mt-0.5 size-4 shrink-0" /> <div class="space-y-0.5"> <p class="text-foreground font-semibold">Strictly Confidential People Ops Review</p> <p class="text-muted-foreground leading-relaxed"> Your candid responses are confidential and used exclusively by People Ops to improve company culture, leadership effectiveness, and engineering workflows. Summary insights are aggregated and anonymized. </p> </div> </div> </div> </div> @if (isSubmitted()) { <div class="border-success/30 bg-success/10 text-foreground dark:text-foreground flex items-start gap-3 rounded-xl border p-4 shadow-xs" > <lucide-icon [img]="CheckCircle2" class="text-success mt-0.5 size-5 shrink-0" /> <div class="flex-1 space-y-1"> <div class="flex flex-wrap items-center justify-between gap-2"> <p class="text-sm font-semibold">Exit Survey Successfully Submitted</p> <span class="font-mono text-xs font-medium">Receipt #{{ submissionId }}</span> </div> <p class="text-success text-xs leading-relaxed"> Thank you for your dedicated 3.5 years of service and leadership. Your constructive feedback has been securely logged for the People Ops quarterly retention review and alumni network registration. </p> </div> </div> } <!-- Department Sentiment & Handover Scorecard Strip --> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 ui-card-title class="text-muted-foreground text-xs font-medium">Overall Experience Score</h3> <lucide-icon [img]="Star" class="text-warning size-4" /> </div> <div ui-card-content class="space-y-2"> <div class="flex items-baseline gap-2"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl"> {{ averageRating() }} </span> <span class="text-muted-foreground text-xs font-medium">/ 5.0</span> </div> <div class="space-y-1"> <div ui-progress [value]="sentimentPercentage()" class="h-1.5 w-full"></div> <div class="flex items-center justify-between text-xs"> <span class="text-success font-medium">+0.4 vs Dept Avg</span> <span class="text-muted-foreground font-mono tabular-nums">{{ sentimentPercentage() }}%</span> </div> </div> </div> </div> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 ui-card-title class="text-muted-foreground text-xs font-medium">Primary Departure Driver</h3> <lucide-icon [img]="Compass" class="text-primary size-4" /> </div> <div ui-card-content class="space-y-1.5"> <div class="text-foreground text-base font-bold tracking-tight sm:text-lg">Career Growth</div> <p class="text-muted-foreground text-xs leading-relaxed"> Targeting Principal/Architect roles in cloud infrastructure. </p> </div> </div> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 ui-card-title class="text-muted-foreground text-xs font-medium">Knowledge Handover</h3> <lucide-icon [img]="ClipboardCheck" class="text-primary size-4" /> </div> <div ui-card-content class="space-y-2"> <div class="flex items-baseline gap-2"> <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl"> {{ handoverPercentage() }}% </span> <span class="text-muted-foreground text-xs font-medium">({{ handoverCompletedCount() }} of 4 tasks)</span> </div> <div class="space-y-1"> <div ui-progress [value]="handoverPercentage()" class="h-1.5 w-full"></div> <p class="text-muted-foreground text-xs">IT asset return pending</p> </div> </div> </div> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="flex flex-row items-center justify-between space-y-0 pb-2"> <h3 ui-card-title class="text-muted-foreground text-xs font-medium">Alumni Status</h3> <lucide-icon [img]="UserCheck" class="text-success size-4" /> </div> <div ui-card-content class="space-y-1.5"> <div class="flex items-center gap-1.5"> <span ui-badge variant="success" class="text-xs font-semibold">Eligible for Re-Hire</span> </div> <p class="text-muted-foreground text-xs leading-relaxed"> Registered for Alumni Leadership Network & fast-track referrals. </p> </div> </div> </div> <!-- SECTION 1: Primary Reason for Departure --> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-4"> <div class="flex items-center gap-2"> <div class="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md"> <lucide-icon [img]="Compass" class="size-4" /> </div> <div> <h3 ui-card-title class="text-base font-semibold">1. Primary Reason for Departure</h3> <p ui-card-description class="text-xs"> Select the primary factor that most heavily influenced your decision to pursue new opportunities. </p> </div> </div> </div> <div ui-card-content class="space-y-4"> <ui-radio-group [value]="primaryReason()" (valueChange)="primaryReason.set($event)" class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3" > @for (item of reasons; track item.value) { <label [attr.for]="'reason-' + item.value" [class]="optionCardClass(primaryReason() === item.value)" > <ui-radio-group-item [id]="'reason-' + item.value" [value]="item.value" class="mt-0.5" /> <div class="space-y-1"> <div class="flex items-center gap-1.5"> <span class="text-foreground text-xs leading-none font-semibold">{{ item.label }}</span> </div> <p class="text-muted-foreground text-xs leading-relaxed">{{ item.description }}</p> </div> </label> } </ui-radio-group> <div class="space-y-2 pt-2"> <label ui-label for="departure-notes" class="text-xs font-medium"> Additional Context on Your Departure Decision (Optional) </label> <ui-textarea id="departure-notes" [value]="departureNotes()" (valueChange)="departureNotes.set($event)" [rows]="2" placeholder="Share specific context regarding your career progression, next steps, or decision drivers..." class="text-xs" /> </div> </div> </div> <!-- SECTION 2: Experience & Culture Ratings --> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-4"> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div class="flex items-center gap-2"> <div class="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md"> <lucide-icon [img]="Star" class="size-4" /> </div> <div> <h3 ui-card-title class="text-base font-semibold">2. Experience & Culture Ratings</h3> <p ui-card-description class="text-xs"> Rate each dimension of your experience on a scale of 1 (Needs Serious Improvement) to 5 (Outstanding). </p> </div> </div> <span ui-badge variant="outline" class="w-fit text-xs font-medium tabular-nums"> Average: {{ averageRating() }} / 5.0 </span> </div> </div> <div ui-card-content class="space-y-4"> <div class="divide-border divide-y rounded-lg border"> @for (dim of dimensions; track dim.id) { <div class="hover:bg-muted/20 flex flex-col gap-3 p-4 transition-colors sm:flex-row sm:items-center sm:justify-between" > <div class="space-y-1"> <div class="flex items-center gap-2"> <span class="text-foreground text-sm font-semibold">{{ dim.title }}</span> <span ui-badge variant="secondary" class="text-xs font-normal">{{ dim.category }}</span> </div> <p class="text-muted-foreground max-w-xl text-xs leading-relaxed">{{ dim.description }}</p> </div> <div class="flex shrink-0 items-center gap-3 sm:self-center"> <ui-rating [value]="ratingValue(dim.id)" (valueChange)="setRating(dim.id, $event)" [max]="5" density="comfortable" size="small" /> <span class="text-foreground min-w-8 text-right font-mono text-xs font-semibold tabular-nums"> {{ ratingValue(dim.id) }} / 5 </span> </div> </div> } </div> </div> </div> <!-- SECTION 3: Qualitative In-Depth Feedback --> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-4"> <div class="flex items-center gap-2"> <div class="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md"> <lucide-icon [img]="MessageSquareQuote" class="size-4" /> </div> <div> <h3 ui-card-title class="text-base font-semibold">3. Qualitative In-Depth Feedback</h3> <p ui-card-description class="text-xs"> Detailed reflections to guide organizational enhancements and executive leadership planning. </p> </div> </div> </div> <div ui-card-content class="space-y-5"> <div class="space-y-2"> <label ui-label for="enjoyed-most" class="text-foreground text-xs font-semibold tracking-wide"> What did you enjoy most about working at the company? </label> <ui-textarea id="enjoyed-most" [value]="enjoyedMost()" (valueChange)="enjoyedMost.set($event)" [rows]="3" placeholder="Highlight positive aspects of your team, projects, technical challenges, or company traditions..." class="text-xs leading-relaxed" /> </div> <ui-separator /> <div class="space-y-2"> <label ui-label for="to-improve" class="text-foreground text-xs font-semibold tracking-wide"> What is one thing the leadership team should immediately improve? </label> <ui-textarea id="to-improve" [value]="toImprove()" (valueChange)="toImprove.set($event)" [rows]="3" placeholder="Suggest actionable improvements for strategy, tooling, engineering velocity, or team communication..." class="text-xs leading-relaxed" /> </div> <ui-separator /> <div class="space-y-3"> <label ui-label class="text-foreground text-xs font-semibold tracking-wide"> Would you consider returning to the company in the future? </label> <ui-radio-group [value]="returnWillingness()" (valueChange)="returnWillingness.set($event)" class="grid grid-cols-1 gap-3 sm:grid-cols-3" > @for (opt of returnOpts; track opt.value) { <label [attr.for]="'return-' + opt.value" [class]="optionCardClass(returnWillingness() === opt.value)" > <ui-radio-group-item [id]="'return-' + opt.value" [value]="opt.value" class="mt-0.5" /> <div class="space-y-1.5"> <div class="flex items-center gap-1.5"> <span class="text-foreground text-xs font-semibold">{{ opt.label }}</span> </div> <p class="text-muted-foreground text-xs leading-relaxed">{{ opt.description }}</p> <span ui-badge [variant]="opt.badgeVariant" class="text-xs font-normal">{{ opt.badge }}</span> </div> </label> } </ui-radio-group> </div> </div> </div> <!-- SECTION 4: Knowledge Handover & Asset Return Checklist --> <div ui-card class="border-border bg-card shadow-xs"> <div ui-card-header class="pb-4"> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div class="flex items-center gap-2"> <div class="bg-primary/10 text-primary flex size-7 items-center justify-center rounded-md"> <lucide-icon [img]="ClipboardCheck" class="size-4" /> </div> <div> <h3 ui-card-title class="text-base font-semibold">4. Knowledge Handover & Asset Return Checklist</h3> <p ui-card-description class="text-xs"> Administrative and technical transition milestones prior to official offboarding date. </p> </div> </div> <span ui-badge [variant]="handoverCompletedCount() === 4 ? 'success' : 'secondary'" class="w-fit text-xs font-medium tabular-nums" > {{ handoverCompletedCount() }} of 4 Completed ({{ handoverPercentage() }}%) </span> </div> </div> <div ui-card-content class="space-y-4"> <div class="divide-border divide-y rounded-lg border"> <div class="hover:bg-muted/20 flex items-start gap-3.5 p-4 transition-colors"> <ui-checkbox id="ho-codebase" [checked]="handover().codebase" (checkedChange)="toggleHandover('codebase', $event)" class="mt-0.5" /> <div class="space-y-1"> <div class="flex items-center gap-2"> <lucide-icon [img]="FileCode2" class="text-primary size-4" /> <label ui-label for="ho-codebase" class="text-foreground cursor-pointer text-xs font-semibold"> Codebase repository ownership transferred </label> </div> <p class="text-muted-foreground text-xs leading-relaxed"> Transferred GitHub admin rights, codeowners assignments, and CI/CD secret manager access to incoming lead (Liam Vance). </p> </div> </div> <div class="hover:bg-muted/20 flex items-start gap-3.5 p-4 transition-colors"> <ui-checkbox id="ho-architecture" [checked]="handover().architecture" (checkedChange)="toggleHandover('architecture', $event)" class="mt-0.5" /> <div class="space-y-1"> <div class="flex items-center gap-2"> <lucide-icon [img]="FileCheck" class="text-primary size-4" /> <label ui-label for="ho-architecture" class="text-foreground cursor-pointer text-xs font-semibold"> Architecture documentation updated </label> </div> <p class="text-muted-foreground text-xs leading-relaxed"> Core system architecture diagrams, service topology maps, and disaster recovery runbooks completed in Notion & DevPortal. </p> </div> </div> <div class="hover:bg-muted/20 flex items-start gap-3.5 p-4 transition-colors"> <ui-checkbox id="ho-credentials" [checked]="handover().credentials" (checkedChange)="toggleHandover('credentials', $event)" class="mt-0.5" /> <div class="space-y-1"> <div class="flex items-center gap-2"> <lucide-icon [img]="KeyRound" class="text-primary size-4" /> <label ui-label for="ho-credentials" class="text-foreground cursor-pointer text-xs font-semibold"> Passwords & credentials rotated </label> </div> <p class="text-muted-foreground text-xs leading-relaxed"> Revoked personal staging SSH keys, rotated AWS IAM secret access tokens, and decommissioned VPN and Vault credentials. </p> </div> </div> <div class="hover:bg-muted/20 flex items-start gap-3.5 p-4 transition-colors"> <ui-checkbox id="ho-equipment" [checked]="handover().equipment" (checkedChange)="toggleHandover('equipment', $event)" class="mt-0.5" /> <div class="space-y-1"> <div class="flex items-center gap-2"> <lucide-icon [img]="Laptop" class="text-primary size-4" /> <label ui-label for="ho-equipment" class="text-foreground cursor-pointer text-xs font-semibold"> Company laptop & security hardware returned </label> </div> <p class="text-muted-foreground text-xs leading-relaxed"> MacBook Pro M3 Max, hardware security YubiKey, and company physical access badge received by IT Asset Ops. </p> </div> </div> </div> </div> <div ui-card-footer class="border-border/60 bg-muted/20 flex flex-wrap items-center justify-between gap-3 border-t p-4" > <div class="text-muted-foreground flex items-center gap-2 text-xs"> <lucide-icon [img]="Lock" class="size-3.5" /> <span>Requires final sign-off from IT Security & People Operations</span> </div> @if (!isSubmitted()) { <button ui-button size="sm" class="bg-primary text-primary-foreground gap-2 shadow-xs" (click)="handleSubmit()" > <lucide-icon [img]="Send" class="size-4" /> Submit Final Exit Review </button> } </div> </div> </div> `, }) export class UiExitInterviewSurveyComponent { protected readonly Calendar = Calendar protected readonly CheckCircle2 = CheckCircle2 protected readonly ClipboardCheck = ClipboardCheck protected readonly Compass = Compass protected readonly FileCheck = FileCheck protected readonly FileCode2 = FileCode2 protected readonly KeyRound = KeyRound protected readonly Laptop = Laptop protected readonly Lock = Lock protected readonly MessageSquareQuote = MessageSquareQuote protected readonly RotateCcw = RotateCcw protected readonly Send = Send protected readonly ShieldCheck = ShieldCheck protected readonly Star = Star protected readonly UserCheck = UserCheck protected readonly reasons = departureReasons protected readonly dimensions = ratingDimensions protected readonly returnOpts = returnOptions protected readonly submissionId = 'EXIT-2026-9042' @Input('class') className?: string @Input() set initialSubmitted(v: boolean) { this.isSubmitted.set(!!v) } readonly primaryReason = signal('career-growth') readonly departureNotes = signal( 'Accepted a Principal Systems Architect role at a Series B infrastructure startup focusing on WebAssembly runtime tooling.', ) readonly ratings = signal<Record<RatingDimensionId, number>>({ management: 4, collaboration: 5, compensation: 4, culture: 5, flexibility: 4, }) readonly enjoyedMost = signal( 'The engineering team’s exceptional craft and collaborative spirit. Building high-scale distributed platform services alongside humble, brilliant peers was deeply rewarding. Leadership always supported architectural innovation and design system investments.', ) readonly toImprove = signal( 'Cross-functional roadmap alignment between product management and core platform squads. Late-quarter requirement shifts occasionally created sprint friction and compressed delivery timelines.', ) readonly returnWillingness = signal('yes') readonly handover = signal<Record<HandoverKey, boolean>>({ codebase: true, architecture: true, credentials: true, equipment: false, }) readonly isSubmitted = signal(false) readonly averageRating = computed(() => { const values = Object.values(this.ratings()) return (values.reduce((acc, val) => acc + val, 0) / values.length).toFixed(1) }) readonly sentimentPercentage = computed(() => Math.round((Number(this.averageRating()) / 5) * 100)) readonly handoverCompletedCount = computed(() => Object.values(this.handover()).filter(Boolean).length) readonly handoverPercentage = computed(() => Math.round((this.handoverCompletedCount() / 4) * 100)) get rootClass(): string { return cn('w-full space-y-6', this.className) } optionCardClass(selected: boolean): string { return cn( 'hover:bg-muted/30 flex cursor-pointer items-start gap-3 rounded-lg border p-3.5 transition-colors', selected ? 'border-primary/80 bg-primary/5 ring-primary/20 ring-1' : 'border-border bg-card', ) } ratingValue(id: RatingDimensionId): number { return this.ratings()[id] } setRating(id: RatingDimensionId, val: number): void { this.ratings.update((prev) => ({ ...prev, [id]: val })) } toggleHandover(key: HandoverKey, checked: unknown): void { this.handover.update((prev) => ({ ...prev, [key]: checked === true })) } handleSubmit(): void { this.isSubmitted.set(true) } handleReset(): void { this.isSubmitted.set(false) } }src/app/components/blocks/exit-interview-survey/index.ts0.1 kB
export { UiExitInterviewSurveyComponent, type HandoverKey, type RatingDimensionId, } from './exit-interview-survey.component'
Raw manifest:https://uipkge.dev/r/angular/exit-interview-survey.json