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.
This commit is contained in:
1 parent
45444ba8bd
commit
96cc1bb684
9 files changed
+265
-183
No files matched your search
+3
-1
@@ -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 })
|
||||
</head>
|
||||
<body>
|
||||
<AuthProvider>{children}</AuthProvider>
|
||||
<Toaster richColors position="top-right" />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -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() {
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
+4
-17
@@ -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() {
|
||||
<h1 className="text-2xl font-bold tracking-tight" style={{ color: "var(--text-primary)" }}>
|
||||
LeaveFlow
|
||||
</h1>
|
||||
|
||||
</div>
|
||||
|
||||
<Card
|
||||
@@ -123,13 +117,6 @@ export default function LoginPage() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert style={{ borderColor: "var(--color-danger-border)", background: "var(--color-danger-bg)", borderRadius: "var(--radius-sm)" }}>
|
||||
<AlertCircle className="h-4 w-4" style={{ color: "var(--color-danger)" }} />
|
||||
<AlertDescription style={{ color: "var(--color-danger)" }}>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full font-semibold"
|
||||
|
||||
Reference in new issue
Block a user