Finance
Savings Goal Card - Personal Finance
Monitor savings goals and progress tracking. Track savings targets and achievement metrics.
Finance Savings Goal Card
The Finance Savings Goal Card helps users track progress toward savings goals, visualizing current savings against targets.
Preview
Installation
npx shadcn@latest add https://vectormotion.vercel.app/registry/finance-savings-goal-card.jsonFinance Savings Goal Card
'use client'import React from 'react';import { Target, Plus } from 'lucide-react';import { motion } from 'motion/react';import { clsx, type ClassValue } from "clsx"import { twMerge } from "tailwind-merge"import { ResponsiveContainer, RadialBarChart, RadialBar, PolarAngleAxis } from 'recharts';function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs))}interface SavingsData { name: string; value: number; fill: string; [key: string]: any;}interface SavingsGoalCardProps { isInteractive?: boolean; className?: string; title?: string; currentAmount?: string; targetAmount?: string; progressLabel?: string; progressPercentage?: string; data?: SavingsData[];}const DEFAULT_TITLE = "Savings Goal";const DEFAULT_CURRENT_AMOUNT = "$12.4k";const DEFAULT_TARGET_AMOUNT = "of $25k";const DEFAULT_PROGRESS_LABEL = "Progress";const DEFAULT_PROGRESS_PERCENTAGE = "49.6%";const DEFAULT_DATA: SavingsData[] = [{ name: 'Saved', value: 12400, fill: '#10b981' }]; // Emeraldconst DEFAULT_GOAL_AMOUNT = 25000;export const SavingsGoalCard: React.FC<SavingsGoalCardProps> = ({ isInteractive = true, className = "", title = DEFAULT_TITLE, currentAmount = DEFAULT_CURRENT_AMOUNT, targetAmount = DEFAULT_TARGET_AMOUNT, progressLabel = DEFAULT_PROGRESS_LABEL, progressPercentage = DEFAULT_PROGRESS_PERCENTAGE, data = DEFAULT_DATA,}) => { const index = 44; 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-5 shadow-sm transition-all flex flex-col h-full group", isInteractive ? "cursor-pointer hover:border-primary/50 hover:shadow-md" : "", className )} > <div className="mb-2 flex items-start justify-between relative z-10"> <div> <h3 className="font-semibold text-lg text-foreground"> {title} </h3> <div className="flex items-center gap-2 mt-1"> <span className="text-2xl font-bold text-foreground">{currentAmount}</span> <span className="text-xs text-muted-foreground">{targetAmount}</span> </div> </div> <div className="rounded-lg bg-emerald-500/10 p-2 text-emerald-500"> <Target className="h-5 w-5" /> </div> </div> <div className="relative z-10 flex-1 min-h-[140px] flex items-center justify-center"> <div className="h-[160px] w-full relative"> <ResponsiveContainer width="100%" height="100%"> <RadialBarChart cx="50%" cy="60%" innerRadius="60%" outerRadius="100%" barSize={16} data={data} startAngle={180} endAngle={0} > <PolarAngleAxis type="number" domain={[0, DEFAULT_GOAL_AMOUNT]} angleAxisId={0} tick={false} /> <RadialBar background={{ fill: '#f4f4f5' }} // zinc-100 dataKey="value" cornerRadius={10} /> </RadialBarChart> </ResponsiveContainer> <div className="absolute inset-0 top-10 flex flex-col items-center justify-center pointer-events-none pb-4"> <span className="text-xs text-muted-foreground uppercase tracking-widest">{progressLabel}</span> <span className="font-bold text-foreground text-lg">{progressPercentage}</span> </div> </div> </div> <div className="z-10 mt-1 flex items-center justify-center pt-2 border-t border-border"> <button className="flex items-center gap-1 text-xs font-medium text-emerald-500 hover:underline"> <Plus className="w-3 h-3" /> Add Funds </button> </div> </motion.div> );};Props
Prop
Type
Usage
This component is a demo card displaying savings goal progress with animated visualizations and dark mode support.