|
|
|
import { useState } from "react";
|
|
import {
|
|
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
|
|
PieChart, Pie, Cell
|
|
} from "recharts";
|
|
import { ChevronDown } from "lucide-react";
|
|
import AppLayout from "@/components/layout/AppLayout";
|
|
import Header from "@/components/shared/Header";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
|
const monthlyData = [
|
|
{ name: "Jan", expenses: 1200, income: 3500 },
|
|
{ name: "Feb", expenses: 1800, income: 3500 },
|
|
{ name: "Mar", expenses: 1400, income: 3500 },
|
|
{ name: "Apr", expenses: 1300, income: 3800 },
|
|
{ name: "May", expenses: 1900, income: 3800 },
|
|
{ name: "Jun", expenses: 1700, income: 3800 },
|
|
{ name: "Jul", expenses: 1500, income: 4000 },
|
|
{ name: "Aug", expenses: 1600, income: 4000 },
|
|
{ name: "Sep", expenses: 1400, income: 4000 },
|
|
{ name: "Oct", expenses: 1800, income: 4200 },
|
|
{ name: "Nov", expenses: 2000, income: 4200 },
|
|
{ name: "Dec", expenses: 2500, income: 4200 },
|
|
];
|
|
|
|
const categoryData = [
|
|
{ name: "Food", value: 1200, color: "#FF6384" },
|
|
{ name: "Housing", value: 1800, color: "#36A2EB" },
|
|
{ name: "Transport", value: 800, color: "#FFCE56" },
|
|
{ name: "Utilities", value: 500, color: "#4BC0C0" },
|
|
{ name: "Shopping", value: 700, color: "#9966FF" },
|
|
{ name: "Healthcare", value: 300, color: "#FF9F40" },
|
|
];
|
|
|
|
const Reports = () => {
|
|
const [reportType, setReportType] = useState<"income-expense" | "categories">("income-expense");
|
|
const [timeframe, setTimeframe] = useState<"monthly" | "yearly">("monthly");
|
|
|
|
const formatCurrency = (value: number) => {
|
|
return new Intl.NumberFormat('en-US', {
|
|
style: 'currency',
|
|
currency: 'USD',
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: 0,
|
|
}).format(value);
|
|
};
|
|
|
|
return (
|
|
<AppLayout>
|
|
<div className="max-w-md mx-auto">
|
|
<Header
|
|
title="Reports"
|
|
subtitle="Analyze your financial data"
|
|
/>
|
|
|
|
<div className="px-4 pb-6 space-y-6 animate-fade-in">
|
|
<div className="flex space-x-2">
|
|
<button
|
|
onClick={() => setReportType("income-expense")}
|
|
className={cn(
|
|
"flex-1 py-2.5 text-center text-sm font-medium rounded-lg transition-colors",
|
|
reportType === "income-expense"
|
|
? "bg-primary text-white"
|
|
: "bg-secondary text-foreground hover:bg-secondary/80"
|
|
)}
|
|
>
|
|
Income & Expenses
|
|
</button>
|
|
<button
|
|
onClick={() => setReportType("categories")}
|
|
className={cn(
|
|
"flex-1 py-2.5 text-center text-sm font-medium rounded-lg transition-colors",
|
|
reportType === "categories"
|
|
? "bg-primary text-white"
|
|
: "bg-secondary text-foreground hover:bg-secondary/80"
|
|
)}
|
|
>
|
|
Categories
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="font-medium">
|
|
{reportType === "income-expense" ? "Income & Expenses" : "Spending by Category"}
|
|
</h3>
|
|
|
|
{reportType === "income-expense" && (
|
|
<div className="relative inline-block text-left">
|
|
<button
|
|
className="flex items-center space-x-1 text-sm bg-secondary px-3 py-1.5 rounded-lg"
|
|
onClick={() => setTimeframe(timeframe === "monthly" ? "yearly" : "monthly")}
|
|
>
|
|
<span>{timeframe === "monthly" ? "Monthly" : "Yearly"}</span>
|
|
<ChevronDown size={14} />
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="h-72 w-full bg-card rounded-xl border border-border p-4">
|
|
{reportType === "income-expense" ? (
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<BarChart
|
|
data={monthlyData}
|
|
margin={{ top: 10, right: 10, left: 0, bottom: 20 }}
|
|
barGap={0}
|
|
barSize={timeframe === "monthly" ? 12 : 24}
|
|
>
|
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#eee" />
|
|
<XAxis
|
|
dataKey="name"
|
|
axisLine={false}
|
|
tickLine={false}
|
|
fontSize={12}
|
|
tickMargin={8}
|
|
/>
|
|
<YAxis
|
|
axisLine={false}
|
|
tickLine={false}
|
|
fontSize={12}
|
|
tickFormatter={formatCurrency}
|
|
width={60}
|
|
/>
|
|
<Tooltip
|
|
formatter={(value) => formatCurrency(value as number)}
|
|
contentStyle={{
|
|
borderRadius: '8px',
|
|
border: '1px solid #eee',
|
|
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.08)'
|
|
}}
|
|
/>
|
|
<Bar dataKey="income" fill="#4ade80" radius={[4, 4, 0, 0]} />
|
|
<Bar dataKey="expenses" fill="#f87171" radius={[4, 4, 0, 0]} />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
) : (
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<PieChart>
|
|
<Pie
|
|
data={categoryData}
|
|
cx="50%"
|
|
cy="50%"
|
|
innerRadius={60}
|
|
outerRadius={90}
|
|
paddingAngle={2}
|
|
dataKey="value"
|
|
label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
|
|
labelLine={false}
|
|
>
|
|
{categoryData.map((entry, index) => (
|
|
<Cell key={`cell-${index}`} fill={entry.color} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip
|
|
formatter={(value) => formatCurrency(value as number)}
|
|
contentStyle={{
|
|
borderRadius: '8px',
|
|
border: '1px solid #eee',
|
|
boxShadow: '0px 4px 12px rgba(0, 0, 0, 0.08)'
|
|
}}
|
|
/>
|
|
</PieChart>
|
|
</ResponsiveContainer>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<h3 className="font-medium">Summary</h3>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="bg-card rounded-xl border border-border p-4">
|
|
<p className="text-sm text-muted-foreground">Total Income</p>
|
|
<p className="text-2xl font-semibold text-green-500">$46,000</p>
|
|
</div>
|
|
|
|
<div className="bg-card rounded-xl border border-border p-4">
|
|
<p className="text-sm text-muted-foreground">Total Expenses</p>
|
|
<p className="text-2xl font-semibold text-red-500">$19,100</p>
|
|
</div>
|
|
|
|
<div className="bg-card rounded-xl border border-border p-4 col-span-2">
|
|
<p className="text-sm text-muted-foreground">Net Savings</p>
|
|
<p className="text-2xl font-semibold text-primary">$26,900</p>
|
|
<p className="text-xs text-muted-foreground mt-1">58% of income saved</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
};
|
|
|
|
export default Reports;
|
|
|