Personal Finance
Personal Finance Dashboard with budget tracking, expense analytics, savings goals, and transaction management micro UI components.
Folder Structure
Installation and Setup
To set up the Personal Finance Dashboard, follow these steps
Install the necessary components for the dashboard
Run the following command to install all finance dashboard components
npx mvpblocks add personal-finance-dashboard-1Install required dependencies
npm install recharts lucide-react next-themeor
yarn add recharts lucide-react next-themeCopy the following components and paste them into your project
Add index.tsx => Copy the code from the above on clicking the "Code" tab which is present corresponding to "Preview tab" after the component title.
Add dashboard-layout.tsx
'use client';import { ReactNode, useState } from 'react';import PersonalSidebar from './personal-sidebar';import PersonalHeader from './personal-header';import PersonalFooter from './personal-footer';interface DashboardLayoutProps { children: ReactNode;}export default function DashboardLayout({ children }: DashboardLayoutProps) { const [collapsed, setCollapsed] = useState<boolean>(false); return ( <div className="min-h-screen w-full overflow-hidden"> <div className={`mt-16 grid min-h-screen grid-cols-1 transition-all duration-500 ${ collapsed ? 'ml-20 lg:grid-cols-[1fr]' : 'lg:grid-cols-[16rem_1fr]' }`} > <div> <PersonalSidebar collapsed={collapsed} setCollapsed={setCollapsed} /> </div> <div className="flex w-full flex-col transition-all duration-500"> <PersonalHeader /> <main className="flex-1 overflow-y-auto p-3 md:p-6">{children}</main> <PersonalFooter /> </div> </div> </div> );}Add personal-sidebar.tsx
'use client';import { Dispatch, SetStateAction, useState } from 'react';import { LayoutDashboard, Wallet, Target, BarChart3, Settings, PiggyBank, ChevronRight, Bell, User, Menu, DollarSign,} from 'lucide-react';import { cn } from '@/lib/utils';import Link from 'next/link';import { Button } from '@/components/ui/button';interface SidebarProps { collapsed: boolean; setCollapsed: Dispatch<SetStateAction<boolean>>;}const navigation = [ { name: 'Dashboard', href: '#', icon: LayoutDashboard, current: true }, { name: 'Transactions', href: '#', icon: Wallet, current: false }, { name: 'Budget', href: '#', icon: BarChart3, current: false }, { name: 'Goals', href: '#', icon: Target, current: false }, { name: 'Investments', href: '#', icon: PiggyBank, current: false }, { name: 'Settings', href: '#', icon: Settings, current: false },];export default function Sidebar({ collapsed, setCollapsed }: SidebarProps) { const [mobileOpen, setMobileOpen] = useState(false); return ( <> <div className="fixed top-[0.8rem] left-3 z-60 md:hidden"> <Button size="icon" onClick={() => setMobileOpen(!mobileOpen)} className="rounded-sm !bg-primary text-white hover:!bg-primary" aria-label="Toggle sidebar" > <Menu className="size-5" /> </Button> </div> {/* Sidebar */} <div className={cn( 'bg-background fixed inset-y-0 top-14 left-0 z-50 transform overflow-hidden border-r transition-all duration-500 ease-in-out', // On mobile, slide sidebar in/out based on mobileOpen 'md:translate-x-0', mobileOpen ? 'w-64 translate-x-0' : 'w-64 -translate-x-full', collapsed && 'w-16 md:w-20', )} > <div className="flex h-full flex-col"> {/* Header */} <div className="hidden items-center justify-between border-b md:flex"> <button onClick={() => setCollapsed(!collapsed)} className="hover:bg-muted flex w-full cursor-pointer items-center justify-between p-3.5 transition-all duration-500" > <div className={cn( 'flex w-full items-center gap-3', collapsed ? 'm-auto justify-center' : 'justify-start', )} > <DollarSign className="size-8 text-primary" /> {!collapsed && ( <h1 className="text-2xl font-medium">FinDash Pro</h1> )} </div> <ChevronRight className={cn( 'size-5 transition-all duration-500', collapsed ? 'hidden' : 'block', )} /> </button> </div> {/* Navigation */} <nav className="flex-1 space-y-2 p-2 pt-4 md:p-4"> {navigation.map((item) => ( <Link key={item.name} href={item.href} className={cn( 'group flex items-center rounded-sm border border-transparent p-3 text-sm font-medium transition-all duration-300 hover:scale-105', item.current ? 'border-primary/30 bg-gradient-to-br from-primary/10 text-primary' : 'text-muted-foreground from-primary/10 hover:border-primary/30 hover:bg-gradient-to-br hover:text-primary', )} onClick={() => setMobileOpen(false)} > <item.icon className={cn( item.current ? 'text-primary' : 'text-muted-foreground group-hover:text-primary', collapsed ? 'mx-auto size-5' : 'mr-3 size-5', )} /> {!collapsed && item.name} </Link> ))} </nav> {/* Mobile user & notification buttons */} <nav className="flex flex-col space-y-2 p-2 md:hidden md:p-4"> <button className={cn( 'group flex items-center rounded-sm border border-transparent px-3 py-3 text-sm font-medium transition-all duration-300 hover:scale-105', 'border-primary/30 bg-gradient-to-br from-primary/10 text-primary', )} aria-label="Notifications" onClick={() => setMobileOpen(false)} > <Bell className={cn( 'text-primary', collapsed ? 'mx-auto size-5' : 'mr-3 size-5', )} /> {!collapsed && 'Notifications'} </button> <button className={cn( 'group flex items-center rounded-sm border border-transparent px-3 py-3 text-sm font-medium transition-all duration-300 hover:scale-105', 'border-primary/30 bg-gradient-to-br from-primary/10 text-primary', )} aria-label="Profile" onClick={() => setMobileOpen(false)} > <User className={cn( 'text-primary', collapsed ? 'mx-auto size-5' : 'mr-3 size-5', )} /> {!collapsed && 'Profile'} </button> </nav> {/* Footer */} {!collapsed && ( <div className="h-20 w-full border-t p-5"> <div className="w-full rounded-sm border border-primary/30 bg-gradient-to-b from-primary/20 p-3 text-center text-xs text-primary"> š° Save more this month ! </div> </div> )} </div> </div> </> );}Add personal-header.tsx
'use client';import { useEffect, useState } from 'react';import { Bell, User, Calendar, Moon, Sun, DollarSign } from 'lucide-react';import { useTheme } from 'next-themes';import { Button } from '@/components/ui/button';import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,} from '@/components/ui/dropdown-menu';import { cn } from '@/lib/utils';export default function Header() { const [currentDate, setCurrentDate] = useState<string>(''); const { theme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); useEffect(() => { const formattedDate = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', }); setCurrentDate(formattedDate); }, []); return ( <header className="bg-background/80 fixed top-0 left-0 z-50 w-full border-b backdrop-blur-md"> <div className="flex w-full items-center justify-between px-3 py-3 md:px-6"> {/* Desktop and tablet layout */} <div className="m-auto flex w-full items-center justify-center text-center"> {currentDate && ( <div className="text-muted-foreground hidden w-full items-center gap-2 text-xs md:flex md:text-sm"> <Calendar className="size-4" /> <span>{currentDate}</span> </div> )} <div className={cn( 'm-auto flex w-full items-center justify-center gap-1.5 text-center md:hidden', )} > <DollarSign className="text-primary" /> <h1 className="text-xl font-medium">FinDash Pro</h1> </div> <div className="block w-auto md:hidden"> <Button size="icon" className="!bg-primary rounded-sm text-white hover:!bg-rose-600" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} aria-label="Toggle theme" > {mounted && (theme === 'dark' ? ( <Sun className="size-5" /> ) : ( <Moon className="size-5" /> ))} </Button> </div> </div> <div className="ml-auto hidden w-auto items-center justify-end gap-2 md:flex md:w-full md:max-w-xs"> <Button variant="outline" size="icon" className="relative rounded-sm"> <Bell className="size-5" /> <span className="bg-primary absolute -top-1 -right-1 size-3 rounded-full border border-white" /> </Button> <DropdownMenu> <DropdownMenuTrigger asChild> <Button variant="outline" size="icon" className="relative rounded-sm" > <User className="size-5" /> </Button> </DropdownMenuTrigger> <DropdownMenuContent align="end" className="rounded-sm"> <DropdownMenuItem className="hover:!bg-primary rounded hover:!text-white"> Profile </DropdownMenuItem> <DropdownMenuItem className="hover:!bg-primary rounded hover:!text-white"> Settings </DropdownMenuItem> <DropdownMenuItem className="hover:!bg-primary rounded hover:!text-white"> Logout </DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> <Button variant="outline" size="icon" className="rounded-sm" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')} aria-label="Toggle theme" > {mounted && (theme === 'dark' ? ( <Sun className="size-5" /> ) : ( <Moon className="size-5" /> ))} </Button> </div> </div> </header> );}Add personal-footer.tsx
import React from 'react';import Link from 'next/link';import { Button } from '@/components/ui/button';import { Twitter, Facebook, Linkedin } from 'lucide-react';export default function Footer() { const currentYear = new Date().getFullYear(); return ( <footer className="mt-8 flex h-20 w-full flex-col items-center justify-between gap-1 border-t p-5 text-center md:flex-row"> <span className="text-muted-foreground text-xs"> Ā© {currentYear} FinDash Pro. All rights reserved. </span> <div className="flex justify-center gap-1"> <Button variant="ghost" size="icon" asChild className="text-muted-foreground hover:!bg-primary rounded transition-all duration-500 hover:scale-105 hover:text-white" > <Link href="https://twitter.com" target="_blank" rel="noopener noreferrer" aria-label="Twitter" > <Twitter className="size-4" /> </Link> </Button> <Button variant="ghost" size="icon" asChild className="text-muted-foreground hover:!bg-primary rounded transition-all duration-500 hover:scale-105 hover:text-white" > <Link href="https://facebook.com" target="_blank" rel="noopener noreferrer" aria-label="Facebook" > <Facebook className="size-4" /> </Link> </Button> <Button variant="ghost" size="icon" asChild className="text-muted-foreground hover:!bg-primary rounded transition-all duration-500 hover:scale-105 hover:text-white" > <Link href="https://linkedin.com" target="_blank" rel="noopener noreferrer" aria-label="LinkedIn" > <Linkedin className="size-4" /> </Link> </Button> </div> </footer> );}Add stats-cards.tsx
'use client';import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';import { ArrowUpIcon, ArrowDownIcon, Wallet, Target, TrendingUp,} from 'lucide-react';import { Badge } from '@/components/ui/badge';const stats = [ { title: 'Total Balance', value: '$12,456', change: '+12.4%', trend: 'up', icon: Wallet, description: 'From last month', gradient: 'from-green-500 to-emerald-500', }, { title: 'Monthly Income', value: '$4,200', change: '+8.2%', trend: 'up', icon: ArrowUpIcon, description: 'This month', gradient: 'from-blue-500 to-cyan-500', }, { title: 'Monthly Expenses', value: '$2,843', change: '-3.1%', trend: 'down', icon: ArrowDownIcon, description: 'This month', gradient: 'from-orange-500 to-red-500', }, { title: 'Savings Rate', value: '32.4%', change: '+5.7%', trend: 'up', icon: Target, description: 'Of total income', gradient: 'from-purple-500 to-pink-500', },];export default function StatsCards() { return ( <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 md:gap-6 xl:grid-cols-4"> {stats.map((stat) => ( <Card key={stat.title} className="bg-gradient-to-br from-secondary/30 gap-2 overflow-hidden rounded-lg shadow-none transition-all duration-500 hover:scale-105" > <CardHeader className="flex w-full flex-row items-center justify-between"> <CardTitle className="text-lg font-medium">{stat.title}</CardTitle> <div className={`rounded-sm bg-gradient-to-br p-2 ${stat.gradient}`} > <stat.icon className="size-5 text-white" /> </div> </CardHeader> <CardContent> <div className="mb-3 text-2xl font-bold">{stat.value}</div> <div className="flex items-center justify-between"> <Badge variant="secondary" className="rounded px-3 py-1.5 text-xs" > <TrendingUp className="mr-1 size-3" /> {stat.change} </Badge> <span className="text-muted-foreground text-xs"> {stat.description} </span> </div> </CardContent> </Card> ))} </div> );}Add budget-progress.tsx
"use client";import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";import { Progress } from "@/components/ui/progress";import { AlertTriangle, CheckCircle2, FileText } from "lucide-react";const categories = [ { name: "Food & Dining", spent: 320, budget: 500, color: "bg-blue-500", icon: "š", }, { name: "Transportation", spent: 180, budget: 300, color: "bg-green-500", icon: "š", }, { name: "Entertainment", spent: 120, budget: 200, color: "bg-purple-500", icon: "š¬", }, { name: "Shopping", spent: 450, budget: 400, color: "bg-red-500", icon: "šļø", }, { name: "Utilities", spent: 220, budget: 250, color: "bg-yellow-500", icon: "š”", }, { name: "Healthcare", spent: 150, budget: 200, color: "bg-pink-500", icon: "š„", },];export default function BudgetProgress() { const totalSpent = categories.reduce((sum, cat) => sum + cat.spent, 0); const totalBudget = categories.reduce((sum, cat) => sum + cat.budget, 0); return ( <Card className="bg-background rounded-lg p-0 gap-0 overflow-hidden shadow-none max-h-[30rem]"> <CardHeader className="p-3 !pb-2 flex items-center justify-center text-center m-auto bg-background border-b h-16 w-full"> <CardTitle className="flex items-center justify-between text-center m-auto w-full h-full"> <div className="flex items-center gap-2"> <FileText className="size-6 text-primary" /> <span>Budget Tracking</span> </div> <span className="text-base md:text-lg font-medium"> ${totalSpent} / ${totalBudget} </span> </CardTitle> </CardHeader> <CardContent className="space-y-3 p-3 overflow-auto bg-background h-full"> {categories.map((category) => { const percentage = (category.spent / category.budget) * 100; const isOverBudget = category.spent > category.budget; const isCloseToBudget = percentage > 80 && !isOverBudget; return ( <div key={category.name} className="space-y-3 p-5 rounded-md border bg-gradient-to-tl from-secondary/30" > <div className="flex items-center justify-between"> <div className="flex items-center gap-2 font-medium"> <span>{category.icon}</span> <span>{category.name}</span> </div> <div className="flex items-center space-x-2"> {isOverBudget && ( <AlertTriangle className="size-5 text-red-500" /> )} {!isOverBudget && percentage >= 100 && ( <CheckCircle2 className="size-5 text-green-500" /> )} <span className={`text-sm font-semibold ${ isOverBudget ? "text-red-500" : "" }`} > ${category.spent} / ${category.budget} </span> </div> </div> <Progress value={percentage > 100 ? 100 : percentage} className={`h-2 ${ isOverBudget ? "bg-red-500/30 [&>div]:bg-red-500" : isCloseToBudget ? "bg-yellow-400/30 [&>div]:bg-yellow-400" : "bg-green-500/30 [&>div]:bg-green-500" }`} /> <div className="flex justify-between text-xs"> <span>{percentage.toFixed(1)}% of budget</span> {isOverBudget && ( <span className="text-red-500 font-semibold"> Over by ${category.spent - category.budget} </span> )} {isCloseToBudget && !isOverBudget && ( <span className="text-yellow-400 font-semibold"> Close to limit </span> )} </div> </div> ); })} </CardContent> </Card> );}Add income-expense-chart.tsx
'use client';import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';import { BarChart, Bar, CartesianGrid, XAxis, YAxis } from 'recharts';import { ChartContainer, ChartTooltip, ChartTooltipContent,} from '@/components/ui/chart';const data = [ { month: 'Jan', income: 4200, expenses: 2843, net: 1357 }, { month: 'Feb', income: 3800, expenses: 2950, net: 850 }, { month: 'Mar', income: 5200, expenses: 3100, net: 2100 }, { month: 'Apr', income: 4800, expenses: 2650, net: 2150 }, { month: 'May', income: 5100, expenses: 2900, net: 2200 }, { month: 'Jun', income: 4700, expenses: 2750, net: 1950 }, { month: 'Jul', income: 5300, expenses: 3000, net: 2300 },];export default function IncomeExpenseChart() { return ( <Card className="bg-gradient-to-t from-secondary/30 rounded-lg shadow-none"> <CardHeader className="flex flex-col items-center justify-between gap-3 md:flex-row"> <CardTitle>Income vs Expenses</CardTitle> <div className="flex items-center justify-center gap-2"> <div className="flex items-center gap-2"> <div className="size-3 rounded-full bg-green-500"></div> <span>Income</span> </div> <div className="flex items-center gap-2"> <div className="size-3 rounded-full bg-red-500"></div> <span>Expenses</span> </div> <div className="flex items-center gap-2"> <div className="size-3 rounded-full bg-blue-500"></div> <span>Net</span> </div> </div> </CardHeader> <CardContent className="px-0"> <ChartContainer config={{ income: { label: 'Income', color: '#10b981' }, expenses: { label: 'Expenses', color: '#ef4444' }, net: { label: 'Net', color: '#3b82f6' }, }} className="h-[15rem] md:h-[20rem] w-full" > <BarChart data={data}> <CartesianGrid vertical={true} /> <XAxis dataKey="month" /> <YAxis /> <ChartTooltip content={<ChartTooltipContent />} /> <Bar dataKey="income" fill="var(--color-income)" radius={[4, 4, 0, 0]} /> <Bar dataKey="expenses" fill="var(--color-expenses)" radius={[4, 4, 0, 0]} /> <Bar dataKey="net" fill="var(--color-net)" radius={[4, 4, 0, 0]} /> </BarChart> </ChartContainer> </CardContent> </Card> );}Add monthly-spending-chart.tsx
"use client";import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, Legend,} from "recharts";const data = [ { name: "Food & Dining", value: 320, color: "#3b82f6" }, { name: "Transportation", value: 180, color: "#10b981" }, { name: "Entertainment", value: 120, color: "#8b5cf6" }, { name: "Shopping", value: 450, color: "#ef4444" }, { name: "Utilities", value: 220, color: "#f59e0b" }, { name: "Healthcare", value: 150, color: "#ec4899" },];interface CustomTooltipProps { active?: boolean; payload?: Array<{ name: string; value: number; color?: string; }>; label?: string;}const CustomTooltip = ({ active, payload }: CustomTooltipProps) => { if (active && payload && payload.length) { const data = payload[0]; // directly access payload[0] instead of payload[0].payload return ( <div className="bg-background p-3 border border-green-500/70 rounded-md shadow-xl"> <p className="font-medium">{data.name}</p> <p className="text-sm" style={{ color: data.color }}> Amount : <span className="font-bold">${data.value}</span> </p> <p className="text-sm"> Percentage : {((data.value / 1440) * 100).toFixed(1)}% </p> </div> ); } return null;};export default function MonthlySpendingChart() { return ( <Card className="bg-gradient-to-t from-secondary/30 rounded-lg shadow-none"> <CardHeader> <CardTitle>Monthly Spending by Category</CardTitle> </CardHeader> <CardContent> <div className="h-40 md:h-60"> <ResponsiveContainer width="100%" height="100%"> <PieChart> <Pie data={data} cx="50%" cy="50%" innerRadius="50%" // relative to chart size outerRadius="100%" paddingAngle={2} dataKey="value" > {data.map((entry, index) => ( <Cell key={`cell-${index}`} fill={entry.color} className="ml-10" /> ))} </Pie> <Tooltip content={<CustomTooltip />} /> <Legend layout="vertical" verticalAlign="middle" align="right" /> </PieChart> </ResponsiveContainer> </div> <div className="grid grid-cols-2 gap-4 mt-4 pt-4 border-t"> <div className="text-center"> <p className="text-2xl font-bold">$1,440</p> <p className="text-sm text-muted-foreground">Total Spent</p> </div> <div className="text-center"> <p className="text-2xl font-bold text-green-500">$760</p> <p className="text-sm text-muted-foreground">Remaining Budget</p> </div> </div> </CardContent> </Card> );}Add recent-transactions.tsx
'use client';import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';import { Badge } from '@/components/ui/badge';import { Button } from '@/components/ui/button';import { ArrowUpIcon, ArrowDownIcon, Plus, MoreVertical, History,} from 'lucide-react';const transactions = [ { id: 1, description: 'Grocery Store', amount: -85.2, category: 'Food & Dining', date: '2024-01-15', type: 'expense', merchant: 'Whole Foods', }, { id: 2, description: 'Salary Deposit', amount: 3200.0, category: 'Income', date: '2024-01-14', type: 'income', merchant: 'Company Inc', }, { id: 3, description: 'Netflix Subscription', amount: -15.99, category: 'Entertainment', date: '2024-01-13', type: 'expense', merchant: 'Netflix', }, { id: 4, description: 'Gas Station', amount: -45.0, category: 'Transportation', date: '2024-01-12', type: 'expense', merchant: 'Shell', }, { id: 5, description: 'Freelance Work', amount: 850.0, category: 'Income', date: '2024-01-11', type: 'income', merchant: 'Client Co', }, { id: 6, description: 'Coffee Shop', amount: -12.5, category: 'Food & Dining', date: '2024-01-10', type: 'expense', merchant: 'Starbucks', }, { id: 7, description: 'Stock Dividend', amount: 120.0, category: 'Income', date: '2024-01-09', type: 'income', merchant: 'Investments LLC', }, { id: 8, description: 'Gym Membership', amount: -35.0, category: 'Health & Fitness', date: '2024-01-08', type: 'expense', merchant: 'Gym Co', }, { id: 9, description: 'Dining Out', amount: -60.0, category: 'Food & Dining', date: '2024-01-07', type: 'expense', merchant: 'Olive Garden', }, { id: 10, description: 'Project Bonus', amount: 500.0, category: 'Income', date: '2024-01-06', type: 'income', merchant: 'Client Inc', },];export default function RecentTransactions() { return ( <> <Card className="bg-background gap-0 overflow-hidden rounded-lg p-0 shadow-none"> <CardHeader className="bg-background flex flex-col items-center justify-between gap-2 border-b !p-3 md:flex-row"> <div className="flex items-center gap-2"> <History className="size-10" /> <div className="flex flex-col items-start gap-1"> <CardTitle> <span>Recent 10 Transactions</span> </CardTitle> <span className="text-muted-foreground text-xs italic"> <span className="text-green-500">Green</span> means gain,{' '} <span className="text-red-500">Red</span> means spend - track your flow easily </span> </div> </div> <Button className="bg-primary hover:bg-primary flex w-full items-center gap-2 rounded-sm text-white md:w-auto"> <Plus className="size-4" /> <span>Add New</span> </Button> </CardHeader> <CardContent className="p-0"> <div className="max-h-[25rem] space-y-3 overflow-auto p-3"> {transactions.map((transaction) => ( <div key={transaction.id} className="group flex items-center justify-between rounded-md border bg-gradient-to-r from-secondary/30 hover:border-primary/50 p-2" > <div className="flex items-center gap-3"> <div className={`rounded-sm p-3 text-white ${ transaction.type === 'income' ? 'bg-green-500' : 'bg-red-500' }`} > {transaction.type === 'income' ? ( <ArrowUpIcon className="size-5" /> ) : ( <ArrowDownIcon className="size-5" /> )} </div> <div> <p className="text-sm font-medium md:text-base"> {transaction.description} </p> <div className="flex flex-col gap-0.5 opacity-60 md:flex-row md:items-center md:gap-2"> <p className="text-[0.65rem] md:text-xs"> {transaction.merchant} </p> <span className="hidden md:block">ā¢</span> <p className="text-[0.65rem] md:text-xs"> {transaction.date} </p> </div> </div> </div> <div className="ml-auto flex items-center justify-end gap-1 text-end"> <div className="flex flex-col items-center md:flex-row md:gap-4"> <span className={`w-full text-sm font-bold md:text-lg ${ transaction.type === 'income' ? 'text-green-500' : 'text-primary' }`} > {transaction.type === 'income' ? '+' : '-'}$ {Math.abs(transaction.amount).toFixed(2)} </span> <Badge variant="outline" className="mt-1 ml-auto rounded px-1.5 py-0 text-[0.6rem] md:mt-0 md:px-3 md:py-1.5 md:text-xs" > {transaction.category} </Badge> </div> <button> <MoreVertical className="size-4" /> </button> </div> </div> ))} </div> </CardContent> </Card> </> );}Add savings-goals.tsx
'use client';import { useEffect, useState } from 'react';import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';import { Progress } from '@/components/ui/progress';import { Button } from '@/components/ui/button';import { Target, Calendar, Plus } from 'lucide-react';const goals = [ { name: 'Emergency Fund', target: 10000, current: 7500, deadline: '2026-10-31', color: 'from-blue-500 to-cyan-500', icon: 'š”ļø', }, { name: 'New Car', target: 25000, current: 12000, deadline: '2025-01-30', color: 'from-green-500 to-emerald-500', icon: 'š', }, { name: 'Vacation', target: 5000, current: 3200, deadline: '2025-12-15', color: 'from-purple-500 to-pink-500', icon: 'šļø', },];export default function SavingsGoals() { const [formattedDates, setFormattedDates] = useState<{ [key: string]: string; }>({}); useEffect(() => { const newFormattedDates: { [key: string]: string } = {}; goals.forEach((goal) => { newFormattedDates[goal.name] = new Date(goal.deadline).toLocaleDateString( 'en-GB', { day: '2-digit', month: 'short', year: 'numeric', }, ); }); setFormattedDates(newFormattedDates); }, []); return ( <Card className="bg-background max-h-[30rem] gap-0 overflow-hidden rounded-lg p-0 shadow-none"> <CardHeader className="bg-background m-auto flex h-16 w-full flex-row items-center justify-between border-b p-3 !pb-3"> <CardTitle className="m-auto flex h-full w-full items-center gap-2 text-center"> <Target className="text-primary size-6" /> <span>Savings Goals</span> </CardTitle> <Button className="flex items-center gap-2 rounded-sm"> <Plus className="size-4" /> <span>Add Goal</span> </Button> </CardHeader> <CardContent className="bg-background h-full space-y-3 overflow-auto p-3"> {goals.map((goal) => { const percentage = (goal.current / goal.target) * 100; const monthsLeft = Math.ceil( (new Date(goal.deadline).getTime() - new Date().getTime()) / (30 * 24 * 60 * 60 * 1000), ); return ( <div key={goal.name} className="space-y-3 rounded-md border bg-gradient-to-tl from-secondary/30 p-5" > <div className="flex items-center justify-between"> <div className="flex items-center gap-3"> <span className="text-2xl">{goal.icon}</span> <div> <h4 className="font-semibold">{goal.name}</h4> <p className="text-sm"> Target : ${goal.target.toLocaleString()} </p> </div> </div> <div className="text-right"> <p className="text-lg font-bold"> ${goal.current.toLocaleString()} </p> <p className="text-sm">{percentage.toFixed(1)}%</p> </div> </div> <div className="space-y-2"> <Progress value={percentage} className="h-2 bg-green-500/20 [&>div]:bg-green-500" /> <div className="flex justify-between text-xs"> <span>Saved : ${goal.current.toLocaleString()}</span> <span> Left : ${(goal.target - goal.current).toLocaleString()} </span> </div> </div> <div className="flex items-center justify-between text-xs"> <div className="flex items-center gap-1 text-sm"> <Calendar className="size-4" /> <span>Due : {formattedDates[goal.name] || ''}</span> </div> <span className={`rounded-full border px-3 py-1.5 text-xs font-medium ${ monthsLeft < 3 ? 'border-red-500/70 bg-red-500/5 text-red-500' : monthsLeft < 6 ? 'border-yellow-400/70 bg-yellow-400/5 text-yellow-400' : 'border-green-500/70 bg-green-500/5 text-green-500' }`} > {monthsLeft} months left </span> </div> </div> ); })} </CardContent> </Card> );}Import and use the dashboard in your application
import PersonalFinanceDashboard from '@/components/dashboards/personal-finance-dashboard-1/index';
export default function FinancePage() {
return <PersonalFinanceDashboard />;
}Customization
Adding New Categories
Update the categories array in BudgetProgress.tsx:
const categories = [
{
name: 'Food & Dining',
spent: 320,
budget: 500,
color: 'bg-blue-500',
icon: 'š',
},
// ...add more like this
];Adding New Monthly Spending Data
Update the data array in MonthlySpendingChart.tsx:
const data = [
{ name: 'Food & Dining', value: 320, color: '#3b82f6' },
{ name: 'Transportation', value: 180, color: '#10b981' },
{ name: 'Entertainment', value: 120, color: '#8b5cf6' },
// ...add more like this
];Like this do changes in the data or add new data in particular files.
Admin DashboardNew
A comprehensive admin dashboard with real-time analytics, user management, and system monitoring. Features custom components with minimal shadcn usage.
User Dashboard
User dashboards provide insights into user activity, preferences, and interactions. They help users manage their profiles, settings, and other personal information.
