main
tsx 303 lines 14.3 KB
Raw
1 import { Button } from "@/components/ui/button";
2 import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
3 import { ArrowLeft, TrendingUp, DollarSign, Users, CreditCard, AlertCircle, ArrowUpRight } from "lucide-react";
4 import { Link } from "react-router-dom";
5 import { ThemeToggle } from "@/components/ThemeToggle";
6 import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from "recharts";
7
8 const monthlySpendData = [
9 { month: "Jan", spend: 125000, budget: 150000 },
10 { month: "Feb", spend: 142000, budget: 150000 },
11 { month: "Mar", spend: 138000, budget: 150000 },
12 { month: "Apr", spend: 156000, budget: 160000 },
13 { month: "May", spend: 149000, budget: 160000 },
14 { month: "Jun", spend: 167000, budget: 170000 },
15 ];
16
17 const departmentSpend = [
18 { name: "Engineering", spend: 85000, color: "#9fe870" },
19 { name: "Marketing", spend: 45000, color: "#163300" },
20 { name: "Sales", spend: 38000, color: "#37d505" },
21 { name: "Operations", spend: 29000, color: "#1a1f2c" },
22 { name: "HR", spend: 18000, color: "#6bd147" },
23 ];
24
25 const recentTransactions = [
26 { id: 1, vendor: "AWS Cloud Services", amount: 12450, category: "Infrastructure", date: "Nov 26, 2025", status: "completed" },
27 { id: 2, vendor: "Figma Enterprise", amount: 2400, category: "Software", date: "Nov 25, 2025", status: "completed" },
28 { id: 3, vendor: "Delta Airlines", amount: 3200, category: "Travel", date: "Nov 24, 2025", status: "pending" },
29 { id: 4, vendor: "WeWork Office Space", amount: 8500, category: "Facilities", date: "Nov 23, 2025", status: "completed" },
30 { id: 5, vendor: "HubSpot CRM", amount: 1800, category: "Software", date: "Nov 22, 2025", status: "completed" },
31 ];
32
33 const CFOOverview = () => {
34 const totalSpend = 167000;
35 const budgetTotal = 200000;
36 const budgetUsedPercent = (totalSpend / budgetTotal) * 100;
37
38 return (
39 <div className="min-h-screen bg-muted/30">
40 {/* Header */}
41 <header className="bg-card border-b border-border sticky top-0 z-50">
42 <div className="container flex items-center justify-between h-16">
43 <div className="flex items-center gap-4">
44 <Link to="/">
45 <Button variant="ghost" size="sm" className="gap-2">
46 <ArrowLeft className="w-4 h-4" />
47 Back
48 </Button>
49 </Link>
50 <div className="h-6 w-px bg-border" />
51 <h1 className="text-xl font-semibold text-foreground">CFO Dashboard</h1>
52 </div>
53 <div className="flex items-center gap-3">
54 <ThemeToggle />
55 <Button variant="outline" size="sm">
56 Export Report
57 </Button>
58 <Button size="sm" className="bg-primary text-primary-foreground hover:bg-lime-dark">
59 Add Expense
60 </Button>
61 </div>
62 </div>
63 </header>
64
65 <main className="container py-8">
66 {/* Key Metrics */}
67 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
68 <Card className="animate-fade-up">
69 <CardHeader className="flex flex-row items-center justify-between pb-2">
70 <CardTitle className="text-sm font-medium text-muted-foreground">Total Spend (Nov)</CardTitle>
71 <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
72 <DollarSign className="w-4 h-4 text-lime-dark" />
73 </div>
74 </CardHeader>
75 <CardContent>
76 <div className="text-3xl font-bold text-foreground">${totalSpend.toLocaleString()}</div>
77 <div className="flex items-center gap-1 mt-2">
78 <div className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-destructive/10">
79 <TrendingUp className="w-3 h-3 text-destructive" />
80 <span className="text-xs text-destructive">+12.5%</span>
81 </div>
82 <span className="text-xs text-muted-foreground">from last month</span>
83 </div>
84 </CardContent>
85 </Card>
86
87 <Card className="animate-fade-up animation-delay-100">
88 <CardHeader className="flex flex-row items-center justify-between pb-2">
89 <CardTitle className="text-sm font-medium text-muted-foreground">Budget Remaining</CardTitle>
90 <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
91 <CreditCard className="w-4 h-4 text-lime-dark" />
92 </div>
93 </CardHeader>
94 <CardContent>
95 <div className="text-3xl font-bold text-foreground">${(budgetTotal - totalSpend).toLocaleString()}</div>
96 <div className="w-full bg-muted rounded-full h-2 mt-3">
97 <div
98 className="bg-primary h-2 rounded-full transition-all"
99 style={{ width: `${budgetUsedPercent}%` }}
100 />
101 </div>
102 <span className="text-xs text-muted-foreground mt-1 block">{budgetUsedPercent.toFixed(0)}% of budget used</span>
103 </CardContent>
104 </Card>
105
106 <Card className="animate-fade-up animation-delay-200">
107 <CardHeader className="flex flex-row items-center justify-between pb-2">
108 <CardTitle className="text-sm font-medium text-muted-foreground">Pending Approvals</CardTitle>
109 <div className="w-8 h-8 rounded-full bg-amber-500/20 flex items-center justify-center">
110 <AlertCircle className="w-4 h-4 text-amber-600" />
111 </div>
112 </CardHeader>
113 <CardContent>
114 <div className="text-3xl font-bold text-foreground">8</div>
115 <div className="flex items-center gap-1 mt-2">
116 <span className="text-xs text-muted-foreground">$24,500 awaiting review</span>
117 </div>
118 </CardContent>
119 </Card>
120
121 <Card className="animate-fade-up animation-delay-300">
122 <CardHeader className="flex flex-row items-center justify-between pb-2">
123 <CardTitle className="text-sm font-medium text-muted-foreground">Active Cardholders</CardTitle>
124 <div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
125 <Users className="w-4 h-4 text-lime-dark" />
126 </div>
127 </CardHeader>
128 <CardContent>
129 <div className="text-3xl font-bold text-foreground">47</div>
130 <div className="flex items-center gap-1 mt-2">
131 <div className="flex items-center gap-1 px-2 py-0.5 rounded-full bg-primary/10">
132 <TrendingUp className="w-3 h-3 text-lime-dark" />
133 <span className="text-xs text-lime-dark">+3</span>
134 </div>
135 <span className="text-xs text-muted-foreground">this month</span>
136 </div>
137 </CardContent>
138 </Card>
139 </div>
140
141 {/* Charts Row */}
142 <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
143 {/* Monthly Spend Trend */}
144 <Card className="lg:col-span-2 animate-fade-up animation-delay-200">
145 <CardHeader>
146 <CardTitle className="text-lg">Monthly Spend vs Budget</CardTitle>
147 <CardDescription>Track your spending against monthly budgets</CardDescription>
148 </CardHeader>
149 <CardContent>
150 <div className="h-[300px]">
151 <ResponsiveContainer width="100%" height="100%">
152 <AreaChart data={monthlySpendData}>
153 <defs>
154 <linearGradient id="spendGradient" x1="0" y1="0" x2="0" y2="1">
155 <stop offset="5%" stopColor="#9fe870" stopOpacity={0.4} />
156 <stop offset="95%" stopColor="#9fe870" stopOpacity={0.05} />
157 </linearGradient>
158 </defs>
159 <CartesianGrid strokeDasharray="3 3" stroke="hsl(220, 13%, 91%)" />
160 <XAxis dataKey="month" stroke="hsl(220, 13%, 46%)" fontSize={12} />
161 <YAxis stroke="hsl(220, 13%, 46%)" fontSize={12} tickFormatter={(value) => `$${value / 1000}k`} />
162 <Tooltip
163 contentStyle={{
164 backgroundColor: "hsl(0, 0%, 100%)",
165 border: "1px solid hsl(220, 13%, 91%)",
166 borderRadius: "8px",
167 color: "hsl(220, 26%, 14%)"
168 }}
169 formatter={(value: number) => [`$${value.toLocaleString()}`, '']}
170 />
171 <Area
172 type="monotone"
173 dataKey="spend"
174 stroke="#37d505"
175 strokeWidth={3}
176 fill="url(#spendGradient)"
177 name="Spend"
178 />
179 <Area
180 type="monotone"
181 dataKey="budget"
182 stroke="#1a1f2c"
183 strokeWidth={2}
184 strokeDasharray="5 5"
185 fill="transparent"
186 name="Budget"
187 />
188 </AreaChart>
189 </ResponsiveContainer>
190 </div>
191 </CardContent>
192 </Card>
193
194 {/* Department Breakdown */}
195 <Card className="animate-fade-up animation-delay-300">
196 <CardHeader>
197 <CardTitle className="text-lg">Spend by Department</CardTitle>
198 <CardDescription>November breakdown</CardDescription>
199 </CardHeader>
200 <CardContent>
201 <div className="h-[180px] mb-4">
202 <ResponsiveContainer width="100%" height="100%">
203 <PieChart>
204 <Pie
205 data={departmentSpend}
206 cx="50%"
207 cy="50%"
208 innerRadius={50}
209 outerRadius={80}
210 paddingAngle={2}
211 dataKey="spend"
212 >
213 {departmentSpend.map((entry, index) => (
214 <Cell key={`cell-${index}`} fill={entry.color} stroke="transparent" />
215 ))}
216 </Pie>
217 <Tooltip
218 formatter={(value: number) => `$${value.toLocaleString()}`}
219 contentStyle={{
220 backgroundColor: "hsl(0, 0%, 100%)",
221 border: "1px solid hsl(220, 13%, 91%)",
222 borderRadius: "8px"
223 }}
224 />
225 </PieChart>
226 </ResponsiveContainer>
227 </div>
228 <div className="space-y-3">
229 {departmentSpend.map((dept) => (
230 <div key={dept.name} className="flex items-center justify-between text-sm">
231 <div className="flex items-center gap-2">
232 <div className="w-3 h-3 rounded-full" style={{ backgroundColor: dept.color }} />
233 <span className="text-muted-foreground">{dept.name}</span>
234 </div>
235 <span className="font-medium text-foreground">${dept.spend.toLocaleString()}</span>
236 </div>
237 ))}
238 </div>
239 </CardContent>
240 </Card>
241 </div>
242
243 {/* Recent Transactions */}
244 <Card className="animate-fade-up animation-delay-400">
245 <CardHeader className="flex flex-row items-center justify-between">
246 <div>
247 <CardTitle className="text-lg">Recent Transactions</CardTitle>
248 <CardDescription>Latest company expenses</CardDescription>
249 </div>
250 <Button variant="ghost" size="sm" className="text-lime-dark hover:bg-primary/10">
251 View All
252 <ArrowUpRight className="w-4 h-4 ml-1" />
253 </Button>
254 </CardHeader>
255 <CardContent>
256 <div className="overflow-x-auto">
257 <table className="w-full">
258 <thead>
259 <tr className="border-b border-border">
260 <th className="text-left py-3 px-4 text-sm font-medium text-muted-foreground">Vendor</th>
261 <th className="text-left py-3 px-4 text-sm font-medium text-muted-foreground">Category</th>
262 <th className="text-left py-3 px-4 text-sm font-medium text-muted-foreground">Date</th>
263 <th className="text-left py-3 px-4 text-sm font-medium text-muted-foreground">Status</th>
264 <th className="text-right py-3 px-4 text-sm font-medium text-muted-foreground">Amount</th>
265 </tr>
266 </thead>
267 <tbody>
268 {recentTransactions.map((tx) => (
269 <tr key={tx.id} className="border-b border-border last:border-0 hover:bg-muted/50 transition-colors">
270 <td className="py-4 px-4">
271 <span className="font-medium text-foreground">{tx.vendor}</span>
272 </td>
273 <td className="py-4 px-4">
274 <span className="text-sm text-muted-foreground">{tx.category}</span>
275 </td>
276 <td className="py-4 px-4">
277 <span className="text-sm text-muted-foreground">{tx.date}</span>
278 </td>
279 <td className="py-4 px-4">
280 <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
281 tx.status === "completed"
282 ? "bg-primary/20 text-lime-dark"
283 : "bg-amber-500/20 text-amber-600"
284 }`}>
285 {tx.status}
286 </span>
287 </td>
288 <td className="py-4 px-4 text-right">
289 <span className="font-semibold text-foreground">${tx.amount.toLocaleString()}</span>
290 </td>
291 </tr>
292 ))}
293 </tbody>
294 </table>
295 </div>
296 </CardContent>
297 </Card>
298 </main>
299 </div>
300 );
301 };
302
303 export default CFOOverview;