139 lines
6.9 KiB
TypeScript
139 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { useAuth } from "@/context/AuthContext";
|
|
import {
|
|
getLeavesForUser,
|
|
getUserBalance,
|
|
LeaveRequest,
|
|
} from "@/lib/leavesStore";
|
|
import { LeaveSheet } from "@/components/LeaveSheet";
|
|
import { LeaveTable } from "@/components/LeaveTable";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import {
|
|
CalendarDays,
|
|
PlusCircle,
|
|
Clock,
|
|
CheckCircle2,
|
|
XCircle,
|
|
Wallet,
|
|
} from "lucide-react";
|
|
import { ROUTES } from "@/lib/routes";
|
|
|
|
export default function EmployeePage() {
|
|
const { user, logout, isLoading } = useAuth();
|
|
const router = useRouter();
|
|
const [leaves, setLeaves] = useState<LeaveRequest[]>([]);
|
|
const [balance, setBalance] = useState(0);
|
|
const [sheetOpen, setSheetOpen] = useState(false);
|
|
const [dataLoaded, setDataLoaded] = useState(false);
|
|
|
|
const loadData = useCallback(async () => {
|
|
if (!user) return;
|
|
const [data, bal] = await Promise.all([
|
|
getLeavesForUser(user.id),
|
|
Promise.resolve(getUserBalance(user.id)),
|
|
]);
|
|
setLeaves(data.sort((a, b) => b.appliedAt.localeCompare(a.appliedAt)));
|
|
setBalance(bal);
|
|
setDataLoaded(true);
|
|
}, [user]);
|
|
|
|
useEffect(() => {
|
|
// if (!isLoading && !user) router.replace("/");
|
|
// else if (!isLoading && user?.role === "manager") router.replace("/manager");
|
|
if (!isLoading && !user) router.replace(ROUTES.home);
|
|
else if (!isLoading && user?.role === "manager") router.replace(ROUTES.manager);
|
|
else if (!isLoading && user) loadData();
|
|
}, [user, isLoading, router, loadData]);
|
|
|
|
if (isLoading || !user || !dataLoaded) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center" style={{ background: "var(--surface-base)" }}>
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="w-10 h-10 rounded-xl flex items-center justify-center" style={{ background: "linear-gradient(135deg, var(--brand-600), var(--brand-400))" }}>
|
|
<CalendarDays className="w-5 h-5" style={{ color: "var(--text-on-brand)" }} />
|
|
</div>
|
|
<p className="text-sm font-medium" style={{ color: "var(--text-muted)" }}>Loading…</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const pending = leaves.filter((l) => l.status === "Pending").length;
|
|
const approved = leaves.filter((l) => l.status === "Approved").length;
|
|
const rejected = leaves.filter((l) => l.status === "Rejected").length;
|
|
|
|
const stats = [
|
|
{ label: "Leave Balance", value: balance, unit: "days", icon: Wallet, color: "var(--brand-600)", bg: "var(--brand-50)", border: "var(--brand-200)" },
|
|
{ label: "Pending", value: pending, icon: Clock, color: "var(--color-pending)", bg: "var(--color-pending-bg)", border: "var(--color-pending-border)" },
|
|
{ label: "Approved", value: approved, icon: CheckCircle2, color: "var(--color-success)", bg: "var(--color-success-bg)", border: "var(--color-success-border)" },
|
|
{ label: "Rejected", value: rejected, icon: XCircle, color: "var(--color-danger)", bg: "var(--color-danger-bg)", border: "var(--color-danger-border)" },
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen" style={{ background: "var(--surface-base)" }}>
|
|
<header style={{ background: "var(--surface-card)", borderBottom: "1px solid var(--surface-border)", position: "sticky", top: 0, zIndex: 40 }}>
|
|
<div className="max-w-5xl mx-auto px-4 py-3.5 flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-9 h-9 rounded-xl flex items-center justify-center" style={{ background: "linear-gradient(135deg, var(--brand-600), var(--brand-400))", boxShadow: "0 4px 10px -2px rgb(99 102 241 / 0.3)" }}>
|
|
<CalendarDays className="w-4.5 h-4.5" style={{ color: "var(--text-on-brand)" }} />
|
|
</div>
|
|
<div><span className="font-bold text-base" style={{ color: "var(--text-primary)" }}>LeaveFlow</span></div>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<div className="hidden sm:flex items-center gap-2">
|
|
<span className="text-sm font-medium" style={{ color: "var(--text-secondary)" }}>{user.name}</span>
|
|
</div>
|
|
<Button size="sm" onClick={logout} className="text-xs font-semibold px-4" style={{ background: "linear-gradient(135deg, var(--brand-600), var(--brand-500))", color: "var(--text-on-brand)", border: "none", borderRadius: "var(--radius-sm)" }}>Logout</Button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="max-w-5xl mx-auto px-4 py-8 space-y-8">
|
|
<div className="flex items-start justify-between flex-wrap gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-bold" style={{ color: "var(--text-primary)" }}>Hello, {user.name.split(" ")[0]}</h1>
|
|
<p className="text-sm mt-1" style={{ color: "var(--text-muted)" }}>Apply for leave from here</p>
|
|
</div>
|
|
<Button onClick={() => setSheetOpen(true)} className="gap-2 font-semibold" style={{ background: "linear-gradient(135deg, var(--brand-600), var(--brand-500))", border: "none", borderRadius: "var(--radius-md)", boxShadow: "0 4px 12px -2px rgb(99 102 241 / 0.35)" }}>
|
|
<PlusCircle className="w-4 h-4" />Apply Leave
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
|
{stats.map((s) => {
|
|
const Icon = s.icon;
|
|
return (
|
|
<Card key={s.label} style={{ border: `1px solid ${s.border}`, background: s.bg, borderRadius: "var(--radius-md)", boxShadow: "none" }}>
|
|
<CardContent className="p-4">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<Icon className="w-4 h-4" style={{ color: s.color }} />
|
|
<span className="text-xs font-medium" style={{ color: s.color }}>{s.label}</span>
|
|
</div>
|
|
<p className="text-2xl font-bold" style={{ color: s.color }}>
|
|
{s.value}{s.unit && <span className="text-sm font-normal ml-1">{s.unit}</span>}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<Card style={{ border: "1px solid var(--surface-border)", borderRadius: "var(--radius-lg)", background: "var(--surface-card)", boxShadow: "var(--shadow-card)" }}>
|
|
<CardHeader className="pb-0" style={{ borderBottom: "1px solid var(--surface-border)", paddingBottom: "1rem", marginBottom: 0 }}>
|
|
<CardTitle className="text-base font-semibold" style={{ color: "var(--text-primary)" }}>Leave History</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
<LeaveTable leaves={leaves} />
|
|
</CardContent>
|
|
</Card>
|
|
</main>
|
|
|
|
<LeaveSheet open={sheetOpen} onOpenChange={setSheetOpen} userId={user.id} leaveBalance={balance} onSuccess={loadData} />
|
|
</div>
|
|
);
|
|
}
|