151 lines
7.8 KiB
TypeScript

"use client";
import { useState, useEffect, useCallback } from "react";
import { useRouter } from "next/navigation";
import { useAuth } from "@/context/AuthContext";
import { getLeaves, updateLeaveStatus, LeaveRequest } from "@/lib/leavesStore";
import { LeaveTable } from "@/components/LeaveTable";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
CalendarDays,
Clock,
CheckCircle2,
XCircle,
LayoutDashboard,
} from "lucide-react";
import { ROUTES } from "@/lib/routes";
type FilterStatus = "All" | "Pending" | "Approved" | "Rejected";
export default function ManagerPage() {
const { user, logout, isLoading } = useAuth();
const router = useRouter();
const [leaves, setLeaves] = useState<LeaveRequest[]>([]);
const [filter, setFilter] = useState<FilterStatus>("All");
const [loadingId, setLoadingId] = useState<number | null>(null);
const [dataLoaded, setDataLoaded] = useState(false);
const loadData = useCallback(async () => {
const data = await getLeaves();
setLeaves(data.sort((a, b) => b.appliedAt.localeCompare(a.appliedAt)));
setDataLoaded(true);
}, []);
useEffect(() => {
// if (!isLoading && !user) router.replace("/");
// else if (!isLoading && user?.role === "employee") router.replace("/employee");
if (!isLoading && !user) router.replace(ROUTES.home);
else if (!isLoading && user?.role === "employee") router.replace(ROUTES.employee);
else if (!isLoading && user) loadData();
}, [user, isLoading, router, loadData]);
const handleApprove = async (id: number) => {
setLoadingId(id);
await updateLeaveStatus(id, "Approved");
await loadData();
setLoadingId(null);
};
const handleReject = async (id: number) => {
setLoadingId(id);
await updateLeaveStatus(id, "Rejected");
await loadData();
setLoadingId(null);
};
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 filtered = filter === "All" ? leaves : leaves.filter((l) => l.status === filter);
const stats = [
{ label: "Total Requests", value: leaves.length, icon: LayoutDashboard, 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)" },
];
const filters: FilterStatus[] = ["All", "Pending", "Approved", "Rejected"];
const filterCounts: Record<FilterStatus, number> = { All: leaves.length, Pending: pending, Approved: approved, Rejected: rejected };
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-6xl 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-6xl mx-auto px-4 py-8 space-y-8">
<div>
<h1 className="text-2xl font-bold" style={{ color: "var(--text-primary)" }}>Manager Dashboard</h1>
<p className="text-sm mt-1" style={{ color: "var(--text-muted)" }}>Review and manage all employee leave requests</p>
</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}</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 style={{ borderBottom: "1px solid var(--surface-border)", paddingBottom: "1rem" }}>
<div className="flex items-center justify-between flex-wrap gap-3">
<CardTitle className="text-base font-semibold" style={{ color: "var(--text-primary)" }}>Leave Requests</CardTitle>
<div className="flex gap-1 p-1 rounded-lg" style={{ background: "var(--surface-base)", border: "1px solid var(--surface-border)" }}>
{filters.map((f) => (
<button key={f} onClick={() => setFilter(f)} className="text-xs font-medium px-3 py-1.5 rounded-md transition-all flex items-center gap-1.5" style={filter === f ? { background: "var(--surface-card)", color: "var(--brand-600)", boxShadow: "var(--shadow-card)" } : { color: "var(--text-muted)" }}>
{f}
{filterCounts[f] > 0 && (
<span className="inline-flex items-center justify-center w-4 h-4 rounded-full text-[10px] font-bold" style={{ background: filter === f ? "var(--brand-100)" : "var(--surface-border)", color: filter === f ? "var(--brand-700)" : "var(--text-muted)" }}>{filterCounts[f]}</span>
)}
</button>
))}
</div>
</div>
</CardHeader>
<CardContent className="p-0">
<LeaveTable leaves={filtered} isManager onApprove={handleApprove} onReject={handleReject} loadingId={loadingId} />
</CardContent>
</Card>
</main>
</div>
);
}