Finance
Budget vs Actual Card - Financial Planning
Compare budget forecasts with actual spending. Monitor budget variance and spending efficiency.
Finance Budget vs Actual Card
The Finance Budget vs Actual Card helps teams track spending against budgets, identifying variances and enabling better financial control.
Preview
Installation
npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-budget-vs-actual-card.jsonFinance Budget vs Actual Card
'use client';'use client';import React from 'react';import { Calculator } from 'lucide-react';import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, Tooltip, Cell } from 'recharts';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs))}interface DataItem { category: string; budget: number; actual: number;}interface BudgetVsActualCardProps { isInteractive?: boolean; className?: string; title?: string; description?: string; dateLabel?: string; data?: DataItem[];}const DEFAULT_TITLE = "Budget vs Actual";const DEFAULT_DESCRIPTION = "Departmental performance";const DEFAULT_DATE_LABEL = "Oct 2023";const DEFAULT_DATA: DataItem[] = [ { category: 'Marketing', budget: 5000, actual: 4200 }, { category: 'R&D', budget: 8000, actual: 8500 }, // Over budget { category: 'Ops', budget: 3000, actual: 2800 }, { category: 'Sales', budget: 6000, actual: 5500 },];export const BudgetVsActualCard: React.FC<BudgetVsActualCardProps> = ({ isInteractive = true, className = "", title = DEFAULT_TITLE, description = DEFAULT_DESCRIPTION, dateLabel = DEFAULT_DATE_LABEL, data = DEFAULT_DATA,}) => { const index = 30; return ( <motion.div layoutId={isInteractive ? `card-${index}-${title}` : undefined} transition={{ duration: 0.4, ease: "easeOut" }} className={cn( "relative overflow-hidden rounded-xl border border-border bg-card text-card-foreground p-6 shadow-sm transition-all flex flex-col group", isInteractive ? "cursor-pointer hover:border-zinc-300 dark:hover:border-zinc-700" : "", className )} > <div className="mb-4 flex items-start justify-between relative z-10"> <div> <h3 className="font-semibold text-lg tracking-tight text-foreground"> {title} </h3> {description && ( <p className="text-sm text-muted-foreground mt-1"> {description} </p> )} </div> <div className="rounded-full bg-zinc-100 dark:bg-zinc-800 p-2 text-foreground flex items-center justify-center"> <Calculator className="h-5 w-5" /> </div> </div> <div className="relative z-10 flex-1"> <div className="h-[200px] w-full -ml-4"> <ResponsiveContainer width="100%" height="100%"> <BarChart data={data} layout="vertical" barGap={2} barCategoryGap={20}> <XAxis type="number" hide /> <YAxis dataKey="category" type="category" axisLine={false} tickLine={false} width={70} tick={{ fill: '#71717a', fontSize: 12 }} /> <Tooltip cursor={{ fill: 'transparent' }} contentStyle={{ backgroundColor: 'var(--tooltip-bg)', borderRadius: '8px', border: 'none', color: 'var(--tooltip-text)', boxShadow: '0 4px 12px rgba(0,0,0,0.1)' }} /> <Bar dataKey="budget" fill="#e4e4e7" radius={[0, 4, 4, 0]} barSize={12} animationDuration={1500} className="fill-zinc-200 dark:fill-zinc-800" /> <Bar dataKey="actual" radius={[0, 4, 4, 0]} barSize={12} animationDuration={1500} animationBegin={300} > {data.map((entry, index) => ( <Cell key={`cell-${index}`} fill={entry.actual > entry.budget ? '#ef4444' : '#10b981'} // Red if over, Green if under /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 1 }} className="mt-2 flex justify-between items-center px-1" > <div className="flex items-center gap-2 text-xs text-muted-foreground"> <div className="w-2 h-2 rounded-full bg-zinc-200 dark:bg-zinc-800"></div> <span>Budget</span> <div className="w-2 h-2 rounded-full bg-emerald-500 ml-2"></div> <span>Actual (Safe)</span> <div className="w-2 h-2 rounded-full bg-red-500 ml-2"></div> <span>Over</span> </div> <span className="text-xs font-medium text-foreground">{dateLabel}</span> </motion.div> </div> </motion.div> );};Props
Prop
Type