From 96cc1bb68474e2efdfcff22fc9834deeffa31a06 Mon Sep 17 00:00:00 2001 From: sumona-banerjeee Date: Wed, 29 Apr 2026 19:14:58 +0530 Subject: [PATCH] Added working-day-only calculation, Fixed leave logic so balance is deducted only on approval and pending requests are considered to prevent over-application. Ensured consistent day calculation using stored days field and removed duplicate daysBetween logic. Implemented working-day-only calculation, excluding weekends (Saturday & Sunday), and blocked invalid date selections. Corrected handling of Unpaid leave so it does not affect leave balance. Added routing and integrated Sonner notifications for login, manager actions, and employee leave submission. --- app/layout.tsx | 4 +- app/manager/page.tsx | 9 +- app/page.tsx | 21 +--- components/LeaveSheet.tsx | 246 +++++++++++++++++--------------------- components/ui/sonner.tsx | 49 ++++++++ data/leaves.json | 2 + lib/leavesStore.ts | 93 +++++++++----- package-lock.json | 22 ++++ package.json | 2 + 9 files changed, 265 insertions(+), 183 deletions(-) create mode 100644 components/ui/sonner.tsx diff --git a/app/layout.tsx b/app/layout.tsx index a709b2f..018f9a0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -3,11 +3,12 @@ import "./globals.css"; import { AuthProvider } from "@/context/AuthContext"; import { Geist } from "next/font/google"; import { cn } from "@/lib/utils"; +import { Toaster } from "@/components/ui/sonner"; const geist = Geist({subsets:['latin'],variable:'--font-sans'}); export const metadata: Metadata = { - title: "LeaveFlow – Leave Management", + title: "LeaveFlow - Leave Management", description: "Streamlined leave management for teams", }; @@ -24,6 +25,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) {children} + ); diff --git a/app/manager/page.tsx b/app/manager/page.tsx index fb9f445..e092176 100644 --- a/app/manager/page.tsx +++ b/app/manager/page.tsx @@ -15,6 +15,7 @@ import { LayoutDashboard, } from "lucide-react"; import { ROUTES } from "@/lib/routes"; +import { toast } from "sonner"; type FilterStatus = "All" | "Pending" | "Approved" | "Rejected"; @@ -33,25 +34,27 @@ export default function ManagerPage() { }, []); 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) => { + const leave = leaves.find((l) => l.id === id); setLoadingId(id); await updateLeaveStatus(id, "Approved"); await loadData(); setLoadingId(null); + toast.success(`Leave approved for ${leave?.userName ?? "employee"}`); }; const handleReject = async (id: number) => { + const leave = leaves.find((l) => l.id === id); setLoadingId(id); await updateLeaveStatus(id, "Rejected"); await loadData(); setLoadingId(null); + toast.error(`Leave rejected for ${leave?.userName ?? "employee"}`); }; if (isLoading || !user || !dataLoaded) { @@ -147,4 +150,4 @@ export default function ManagerPage() { ); -} +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 6f76749..e3b4d5f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -7,21 +7,19 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Alert, AlertDescription } from "@/components/ui/alert"; -import { CalendarDays, AlertCircle, Loader2 } from "lucide-react"; +import { CalendarDays, Loader2 } from "lucide-react"; import { ROUTES } from "@/lib/routes"; +import { toast } from "sonner"; export default function LoginPage() { const { login, user, isLoading } = useAuth(); const router = useRouter(); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); - const [error, setError] = useState(""); const [loading, setLoading] = useState(false); useEffect(() => { if (!isLoading && user) { - // router.replace(user.role === "manager" ? "/manager" : "/employee"); router.replace(user.role === "manager" ? ROUTES.manager : ROUTES.employee); } }, [user, isLoading, router]); @@ -29,17 +27,14 @@ export default function LoginPage() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!username || !password) { - setError("Please enter username and password"); + toast.error("Please enter username and password"); return; } setLoading(true); - setError(""); const result = await login(username, password); setLoading(false); if (!result.ok) { - setError(result.error ?? "Login failed"); - } else if (result.ok) { - // redirect handled by useEffect + toast.error(result.error ?? "Login failed"); } }; @@ -77,7 +72,6 @@ export default function LoginPage() {

LeaveFlow

- - {error && ( - - - {error} - - )} -