Changes

gpt-engineer-app[bot] committed Nov 27, 2025 at 14:21 UTC df43957c55311e22314182ad0fa68f33b2a0d709
6 files changed +344 -2
src/components/employee/EmployeeHeader.tsx new
+37
@@ -0,0 +1,37 @@
1 +import { Button } from "@/components/ui/button";
2 +import { ArrowLeft, Bell, User } from "lucide-react";
3 +import { Link } from "react-router-dom";
4 +import { ThemeToggle } from "@/components/ThemeToggle";
5 +
6 +const EmployeeHeader = () => {
7 + return (
8 + <header className="fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-md border-b border-border">
9 + <div className="container flex items-center justify-between h-16 md:h-20">
10 + <div className="flex items-center gap-4">
11 + <Link to="/" className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors">
12 + <ArrowLeft className="w-5 h-5" />
13 + <span className="hidden sm:inline text-sm font-medium">Back to Home</span>
14 + </Link>
15 + </div>
16 +
17 + <div className="flex items-center gap-2">
18 + <span className="text-xl font-bold text-foreground">Snac</span>
19 + <span className="text-sm text-muted-foreground">| Employee Portal</span>
20 + </div>
21 +
22 + <div className="flex items-center gap-2">
23 + <ThemeToggle />
24 + <Button variant="ghost" size="icon" className="relative">
25 + <Bell className="w-5 h-5 text-muted-foreground" />
26 + <span className="absolute top-1 right-1 w-2 h-2 bg-primary rounded-full" />
27 + </Button>
28 + <Button variant="ghost" size="icon">
29 + <User className="w-5 h-5 text-muted-foreground" />
30 + </Button>
31 + </div>
32 + </div>
33 + </header>
34 + );
35 +};
36 +
37 +export default EmployeeHeader;
src/components/employee/EmployeeHero.tsx new
+80
@@ -0,0 +1,80 @@
1 +import { TrendingUp, Wallet } from "lucide-react";
2 +
3 +const EmployeeHero = () => {
4 + const budgetUsed = 2450;
5 + const budgetTotal = 5000;
6 + const percentUsed = (budgetUsed / budgetTotal) * 100;
7 +
8 + return (
9 + <section className="pt-28 pb-12 md:pt-36 md:pb-16 bg-background">
10 + <div className="container">
11 + <div className="max-w-4xl mx-auto">
12 + {/* Welcome Message */}
13 + <div className="text-center mb-10 animate-fade-up">
14 + <p className="text-sm font-medium text-primary mb-2">Welcome back, Sarah</p>
15 + <h1 className="text-3xl md:text-5xl font-bold text-foreground leading-tight">
16 + You're doing great with your budget
17 + </h1>
18 + <p className="mt-4 text-lg text-muted-foreground">
19 + Engineering Department • Q4 2024
20 + </p>
21 + </div>
22 +
23 + {/* Main Budget Card */}
24 + <div className="bg-card rounded-2xl border border-border p-6 md:p-10 animate-fade-up animation-delay-100">
25 + <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
26 + <div className="flex-1">
27 + <div className="flex items-center gap-3 mb-4">
28 + <div className="w-12 h-12 bg-primary/20 rounded-xl flex items-center justify-center">
29 + <Wallet className="w-6 h-6 text-primary" />
30 + </div>
31 + <div>
32 + <p className="text-sm text-muted-foreground">Department Budget</p>
33 + <p className="text-2xl md:text-3xl font-bold text-foreground">
34 + ${budgetTotal.toLocaleString()}
35 + </p>
36 + </div>
37 + </div>
38 +
39 + {/* Progress Bar */}
40 + <div className="space-y-2">
41 + <div className="h-3 bg-muted rounded-full overflow-hidden">
42 + <div
43 + className="h-full bg-primary rounded-full transition-all duration-500"
44 + style={{ width: `${percentUsed}%` }}
45 + />
46 + </div>
47 + <div className="flex justify-between text-sm">
48 + <span className="text-muted-foreground">
49 + ${budgetUsed.toLocaleString()} spent
50 + </span>
51 + <span className="text-foreground font-medium">
52 + ${(budgetTotal - budgetUsed).toLocaleString()} remaining
53 + </span>
54 + </div>
55 + </div>
56 + </div>
57 +
58 + {/* Stats */}
59 + <div className="flex gap-6 md:gap-8">
60 + <div className="text-center">
61 + <div className="flex items-center justify-center gap-1 text-primary mb-1">
62 + <TrendingUp className="w-4 h-4" />
63 + <span className="text-sm font-medium">12%</span>
64 + </div>
65 + <p className="text-xs text-muted-foreground">Under budget</p>
66 + </div>
67 + <div className="text-center">
68 + <p className="text-2xl font-bold text-foreground">18</p>
69 + <p className="text-xs text-muted-foreground">Days left</p>
70 + </div>
71 + </div>
72 + </div>
73 + </div>
74 + </div>
75 + </div>
76 + </section>
77 + );
78 +};
79 +
80 +export default EmployeeHero;
src/components/employee/QuickActions.tsx new
+46
@@ -0,0 +1,46 @@
1 +import { Plus, Receipt, FileText, HelpCircle } from "lucide-react";
2 +import { Button } from "@/components/ui/button";
3 +
4 +const actions = [
5 + { label: "Submit Expense", icon: Plus, primary: true },
6 + { label: "Upload Receipt", icon: Receipt, primary: false },
7 + { label: "View Reports", icon: FileText, primary: false },
8 + { label: "Get Help", icon: HelpCircle, primary: false },
9 +];
10 +
11 +const QuickActions = () => {
12 + return (
13 + <section className="py-12 bg-background">
14 + <div className="container">
15 + <div className="max-w-4xl mx-auto">
16 + <h2 className="text-xl md:text-2xl font-bold text-foreground mb-6 animate-fade-up">
17 + Quick actions
18 + </h2>
19 +
20 + <div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-fade-up animation-delay-100">
21 + {actions.map((action) => {
22 + const Icon = action.icon;
23 +
24 + return (
25 + <Button
26 + key={action.label}
27 + variant={action.primary ? "default" : "outline"}
28 + className={`h-auto py-6 flex flex-col gap-3 ${
29 + action.primary
30 + ? 'bg-primary text-primary-foreground hover:bg-primary/90'
31 + : 'hover:border-primary/50 hover:bg-primary/5'
32 + }`}
33 + >
34 + <Icon className="w-6 h-6" />
35 + <span className="text-sm font-medium">{action.label}</span>
36 + </Button>
37 + );
38 + })}
39 + </div>
40 + </div>
41 + </div>
42 + </section>
43 + );
44 +};
45 +
46 +export default QuickActions;
src/components/employee/RecentTransactions.tsx new
+97
@@ -0,0 +1,97 @@
1 +import { ArrowUpRight, Coffee, Monitor, Plane } from "lucide-react";
2 +import { Button } from "@/components/ui/button";
3 +
4 +const transactions = [
5 + {
6 + id: 1,
7 + description: "Figma Pro Subscription",
8 + category: "Software",
9 + amount: -15.00,
10 + date: "Today",
11 + icon: Monitor,
12 + status: "approved"
13 + },
14 + {
15 + id: 2,
16 + description: "Team Lunch - Pizza Hut",
17 + category: "Food & Meals",
18 + amount: -89.50,
19 + date: "Yesterday",
20 + icon: Coffee,
21 + status: "approved"
22 + },
23 + {
24 + id: 3,
25 + description: "Flight to NYC - Conference",
26 + category: "Travel",
27 + amount: -324.00,
28 + date: "Nov 22",
29 + icon: Plane,
30 + status: "pending"
31 + },
32 + {
33 + id: 4,
34 + description: "AWS Credits",
35 + category: "Software",
36 + amount: -120.00,
37 + date: "Nov 20",
38 + icon: Monitor,
39 + status: "approved"
40 + },
41 +];
42 +
43 +const RecentTransactions = () => {
44 + return (
45 + <section className="py-12 bg-muted/30">
46 + <div className="container">
47 + <div className="max-w-4xl mx-auto">
48 + <div className="flex items-center justify-between mb-6 animate-fade-up">
49 + <h2 className="text-xl md:text-2xl font-bold text-foreground">
50 + Recent activity
51 + </h2>
52 + <Button variant="ghost" className="text-primary hover:text-primary/80">
53 + View all <ArrowUpRight className="w-4 h-4 ml-1" />
54 + </Button>
55 + </div>
56 +
57 + <div className="bg-card rounded-2xl border border-border overflow-hidden animate-fade-up animation-delay-100">
58 + {transactions.map((transaction, index) => {
59 + const Icon = transaction.icon;
60 +
61 + return (
62 + <div
63 + key={transaction.id}
64 + className={`flex items-center gap-4 p-4 hover:bg-muted/50 transition-colors cursor-pointer ${
65 + index !== transactions.length - 1 ? 'border-b border-border' : ''
66 + }`}
67 + >
68 + <div className="w-10 h-10 bg-muted rounded-lg flex items-center justify-center">
69 + <Icon className="w-5 h-5 text-muted-foreground" />
70 + </div>
71 + <div className="flex-1 min-w-0">
72 + <p className="font-medium text-foreground truncate">{transaction.description}</p>
73 + <p className="text-sm text-muted-foreground">{transaction.category} • {transaction.date}</p>
74 + </div>
75 + <div className="text-right">
76 + <p className="font-semibold text-foreground">
77 + ${Math.abs(transaction.amount).toFixed(2)}
78 + </p>
79 + <span className={`text-xs font-medium px-2 py-0.5 rounded-full ${
80 + transaction.status === 'approved'
81 + ? 'bg-primary/20 text-primary'
82 + : 'bg-orange-100 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400'
83 + }`}>
84 + {transaction.status}
85 + </span>
86 + </div>
87 + </div>
88 + );
89 + })}
90 + </div>
91 + </div>
92 + </div>
93 + </section>
94 + );
95 +};
96 +
97 +export default RecentTransactions;
src/components/employee/SpendingBreakdown.tsx new
+60
@@ -0,0 +1,60 @@
1 +import { Coffee, Monitor, Plane, BookOpen, MoreHorizontal } from "lucide-react";
2 +
3 +const categories = [
4 + { name: "Software & Tools", amount: 890, icon: Monitor, color: "bg-blue-500" },
5 + { name: "Travel", amount: 650, icon: Plane, color: "bg-purple-500" },
6 + { name: "Food & Meals", amount: 420, icon: Coffee, color: "bg-orange-500" },
7 + { name: "Learning & Dev", amount: 320, icon: BookOpen, color: "bg-primary" },
8 + { name: "Other", amount: 170, icon: MoreHorizontal, color: "bg-muted-foreground" },
9 +];
10 +
11 +const totalSpent = categories.reduce((sum, cat) => sum + cat.amount, 0);
12 +
13 +const SpendingBreakdown = () => {
14 + return (
15 + <section className="py-12 bg-background">
16 + <div className="container">
17 + <div className="max-w-4xl mx-auto">
18 + <h2 className="text-xl md:text-2xl font-bold text-foreground mb-6 animate-fade-up">
19 + Where your money goes
20 + </h2>
21 +
22 + <div className="grid gap-3 animate-fade-up animation-delay-100">
23 + {categories.map((category, index) => {
24 + const percentage = (category.amount / totalSpent) * 100;
25 + const Icon = category.icon;
26 +
27 + return (
28 + <div
29 + key={category.name}
30 + className="bg-card rounded-xl border border-border p-4 hover:border-primary/30 transition-colors cursor-pointer"
31 + style={{ animationDelay: `${index * 50}ms` }}
32 + >
33 + <div className="flex items-center gap-4">
34 + <div className={`w-10 h-10 ${category.color} rounded-lg flex items-center justify-center`}>
35 + <Icon className="w-5 h-5 text-white" />
36 + </div>
37 + <div className="flex-1 min-w-0">
38 + <div className="flex items-center justify-between mb-2">
39 + <span className="font-medium text-foreground">{category.name}</span>
40 + <span className="font-semibold text-foreground">${category.amount}</span>
41 + </div>
42 + <div className="h-2 bg-muted rounded-full overflow-hidden">
43 + <div
44 + className={`h-full ${category.color} rounded-full transition-all duration-500`}
45 + style={{ width: `${percentage}%` }}
46 + />
47 + </div>
48 + </div>
49 + </div>
50 + </div>
51 + );
52 + })}
53 + </div>
54 + </div>
55 + </div>
56 + </section>
57 + );
58 +};
59 +
60 +export default SpendingBreakdown;
src/pages/EmployeePortal.tsx
+24 -2
@@ -1,7 +1,29 @@
1 +import EmployeeHeader from "@/components/employee/EmployeeHeader";
2 +import EmployeeHero from "@/components/employee/EmployeeHero";
3 +import SpendingBreakdown from "@/components/employee/SpendingBreakdown";
4 +import RecentTransactions from "@/components/employee/RecentTransactions";
5 +import QuickActions from "@/components/employee/QuickActions";
6 +
7 const EmployeePortal = () => {
8 return (
3 - <div className="min-h-screen bg-background flex items-center justify-center">
4 - <h1 className="text-2xl font-bold text-foreground">Employee Spending Portal</h1>
9 + <div className="min-h-screen bg-background">
10 + <EmployeeHeader />
11 + <EmployeeHero />
12 + <QuickActions />
13 + <SpendingBreakdown />
14 + <RecentTransactions />
15 +
16 + {/* Footer */}
17 + <footer className="py-8 bg-background border-t border-border">
18 + <div className="container">
19 + <div className="max-w-4xl mx-auto text-center">
20 + <p className="text-sm text-muted-foreground">
21 + Need help? Contact your finance team or visit our{" "}
22 + <a href="#" className="text-primary hover:underline">help center</a>
23 + </p>
24 + </div>
25 + </div>
26 + </footer>
27 </div>
28 );
29 };