Medical
Patient Satisfaction Card - Quality of Care
Monitor patient satisfaction scores and feedback. Track patient experience and care quality metrics.
Patient Satisfaction Card
The Patient Satisfaction Card Track patient satisfaction scores and feedback metrics.
Preview
Installation
ash npx shadcn@latest add https://vectormotion.vercel.app/registry/patient-satisfaction-card.json
Patient Satisfaction Card
'use client'import React from 'react';import { Smile, TrendingUp, Star } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx";import { twMerge } from "tailwind-merge";import { BarChart, Bar, Cell, ResponsiveContainer } from 'recharts';function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs));}interface ReviewData { score: number; count: number; [key: string]: any;}interface PatientSatisfactionCardProps { className?: string; title?: string; subtitle?: string; npsScore?: number; npsLabel?: string; npsTrend?: string; data?: ReviewData[];}const DEFAULT_DATA: ReviewData[] = [ { score: 5, count: 64 }, { score: 4, count: 24 }, { score: 3, count: 8 }, { score: 2, count: 2 }, { score: 1, count: 1 },];const DEFAULT_TITLE = "Satisfaction";const DEFAULT_SUBTITLE = "NPS Score";const DEFAULT_NPS_SCORE = 72;const DEFAULT_NPS_LABEL = "Excellent";const DEFAULT_NPS_TREND = "Top 10% Regionally";export const PatientSatisfactionCard: React.FC<PatientSatisfactionCardProps> = ({ className = "", title = DEFAULT_TITLE, subtitle = DEFAULT_SUBTITLE, npsScore = DEFAULT_NPS_SCORE, npsLabel = DEFAULT_NPS_LABEL, npsTrend = DEFAULT_NPS_TREND, data = DEFAULT_DATA,}) => { const isInteractive = true; const index = 23; return ( <motion.div layoutId={isInteractive ? `card-${index}-${title}` : undefined} transition={{ duration: 0.4, ease: "easeOut" }} className={cn( "relative overflow-hidden rounded-2xl border border-border bg-card text-card-foreground shadow-sm transition-all flex flex-col h-full", isInteractive ? "hover:border-yellow-300 dark:hover:border-yellow-700 hover:shadow-md" : "", className )} > <div className="p-5 flex flex-col h-full relative z-10"> <div className="mb-4 flex items-start justify-between"> <div> <h3 className="font-bold text-lg text-foreground"> {title} </h3> <div className="flex items-center gap-1.5 mt-1"> <span className="relative flex h-2 w-2"> <span className="relative inline-flex rounded-full h-2 w-2 bg-yellow-500"></span> </span> <p className="text-sm text-muted-foreground font-medium"> {subtitle} </p> </div> </div> <div className="rounded-xl bg-yellow-50 dark:bg-yellow-900/20 p-2.5 text-yellow-600 dark:text-yellow-400 flex items-center justify-center ring-1 ring-yellow-100 dark:ring-yellow-800"> <Smile className="h-5 w-5" /> </div> </div> <div className="flex-1 flex flex-col"> <div className="flex items-center gap-4 mb-4"> <div className="text-5xl font-bold text-foreground tracking-tighter">{npsScore}</div> <div> <div className="flex gap-0.5 mb-1"> {[1, 2, 3, 4, 5].map(i => ( <Star key={i} className="h-3 w-3 fill-yellow-400 text-yellow-400" /> ))} </div> <p className="text-xs text-muted-foreground font-medium">{npsLabel}</p> </div> </div> <div className="flex-1 relative"> <p className="text-[10px] text-zinc-400 font-bold uppercase mb-2">Rating Distribution</p> <div className="h-20 w-full"> <ResponsiveContainer width="100%" height="100%"> <BarChart data={data}> <Bar dataKey="count" radius={[4, 4, 4, 4]}> {data.map((entry, index) => ( <Cell key={`cell-${index}`} fill={index === 0 ? '#fbbf24' : '#e4e4e7'} /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> </div> <div className="mt-2 flex items-center gap-2 text-xs font-semibold text-emerald-500 bg-emerald-50 dark:bg-emerald-900/10 p-2 rounded-lg border border-emerald-100 dark:border-emerald-900/20"> <TrendingUp className="h-3.5 w-3.5" /> {npsTrend} </div> </div> </div> </motion.div> );};Usage
This component is a demo card displaying medical metrics with animated visualizations and dark mode support.
Prop
Type