From bdc55f1032eaa498f69368e5feef91dea46c65b1 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Mon, 10 Nov 2025 19:30:32 +0000 Subject: [PATCH] Create Staff Expense Reports page for reimbursements and budgets cgen-767808b46fb242d1bec5993097d6ac9e --- client/pages/staff/StaffExpenseReports.tsx | 360 +++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 client/pages/staff/StaffExpenseReports.tsx diff --git a/client/pages/staff/StaffExpenseReports.tsx b/client/pages/staff/StaffExpenseReports.tsx new file mode 100644 index 00000000..05abbccf --- /dev/null +++ b/client/pages/staff/StaffExpenseReports.tsx @@ -0,0 +1,360 @@ +import { useState } from "react"; +import Layout from "@/components/Layout"; +import SEO from "@/components/SEO"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Progress } from "@/components/ui/progress"; +import { + DollarSign, + CreditCard, + FileText, + Calendar, + CheckCircle, + AlertCircle, + Plus, +} from "lucide-react"; + +interface Expense { + id: string; + description: string; + amount: number; + category: string; + date: string; + status: "Pending" | "Approved" | "Reimbursed" | "Rejected"; + receipt: boolean; +} + +interface Budget { + category: string; + allocated: number; + spent: number; + percentage: number; +} + +const expenses: Expense[] = [ + { + id: "1", + description: "Conference Registration - GDC 2025", + amount: 1200, + category: "Training", + date: "March 10, 2025", + status: "Approved", + receipt: true, + }, + { + id: "2", + description: "Laptop Stand and Keyboard", + amount: 180, + category: "Equipment", + date: "March 5, 2025", + status: "Reimbursed", + receipt: true, + }, + { + id: "3", + description: "Client Dinner Meeting", + amount: 85.50, + category: "Entertainment", + date: "February 28, 2025", + status: "Reimbursed", + receipt: true, + }, + { + id: "4", + description: "Cloud Services - AWS", + amount: 450, + category: "Software", + date: "February 20, 2025", + status: "Pending", + receipt: true, + }, + { + id: "5", + description: "Travel to NYC Office", + amount: 320, + category: "Travel", + date: "February 15, 2025", + status: "Rejected", + receipt: true, + }, +]; + +const budgets: Budget[] = [ + { + category: "Training & Development", + allocated: 5000, + spent: 2100, + percentage: 42, + }, + { + category: "Equipment & Hardware", + allocated: 2500, + spent: 1850, + percentage: 74, + }, + { + category: "Travel", + allocated: 3000, + spent: 2200, + percentage: 73, + }, + { + category: "Software & Tools", + allocated: 1500, + spent: 1200, + percentage: 80, + }, + { + category: "Entertainment & Client Meals", + allocated: 1000, + spent: 320, + percentage: 32, + }, +]; + +const getStatusColor = (status: string) => { + switch (status) { + case "Reimbursed": + return "bg-green-500/20 text-green-300 border-green-500/30"; + case "Approved": + return "bg-blue-500/20 text-blue-300 border-blue-500/30"; + case "Pending": + return "bg-amber-500/20 text-amber-300 border-amber-500/30"; + case "Rejected": + return "bg-red-500/20 text-red-300 border-red-500/30"; + default: + return "bg-slate-500/20 text-slate-300"; + } +}; + +const getProgressColor = (percentage: number) => { + if (percentage >= 80) return "bg-red-500"; + if (percentage >= 60) return "bg-amber-500"; + return "bg-green-500"; +}; + +export default function StaffExpenseReports() { + const [filterStatus, setFilterStatus] = useState(null); + + const filtered = filterStatus + ? expenses.filter((e) => e.status === filterStatus) + : expenses; + + const totalSpent = expenses.reduce((sum, e) => sum + e.amount, 0); + const totalApproved = expenses + .filter((e) => e.status === "Approved" || e.status === "Reimbursed") + .reduce((sum, e) => sum + e.amount, 0); + + return ( + + + +
+ {/* Background effects */} +
+
+
+
+ +
+ {/* Header */} +
+
+
+ +
+
+

+ Expense Reports +

+

+ Reimbursement requests and budget tracking +

+
+
+ + {/* Summary Cards */} +
+ + +
+
+

Total Submitted

+

+ ${totalSpent.toFixed(2)} +

+
+ +
+
+
+ + +
+
+

Approved

+

+ ${totalApproved.toFixed(2)} +

+
+ +
+
+
+ + +
+
+

Pending

+

+ $ + {expenses + .filter((e) => e.status === "Pending") + .reduce((sum, e) => sum + e.amount, 0) + .toFixed(2)} +

+
+ +
+
+
+
+ + {/* Budget Overview */} +
+

+ Budget Overview +

+
+ {budgets.map((budget) => ( + + +
+
+

+ {budget.category} +

+

+ ${budget.spent.toFixed(2)} of ${budget.allocated} +

+
+

+ {budget.percentage}% +

+
+ +
+
+ ))} +
+
+ + {/* Expense List */} +
+
+

+ Expense Reports +

+ +
+ + {/* Status Filter */} +
+ + {["Pending", "Approved", "Reimbursed", "Rejected"].map( + (status) => ( + + ), + )} +
+ + {/* Expenses */} +
+ {filtered.map((expense) => ( + + +
+
+

+ {expense.description} +

+
+ + + {expense.date} + + + {expense.category} + + {expense.receipt && ( + ✓ Receipt + )} +
+
+
+

+ ${expense.amount.toFixed(2)} +

+ + {expense.status} + +
+
+
+
+ ))} +
+
+
+
+
+ + ); +}