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
};