first commit for the leave management system. built a Leave Management System with role-based access:
Employees can apply for leave and track balance, status, and history. Managers can view all requests and approve/reject them. Login with separate dashboards for employees and managers with the dummy json. Tables with filters (All, Pending, Approved, Rejected) for easy management.
This commit is contained in:
1 parent
3fdca895cc
commit
c5946ce95e
33 files changed
+7173
-212
No files matched your search
@@ -0,0 +1,149 @@
|
||||
export interface LeaveRequest {
|
||||
id: number;
|
||||
userId: number;
|
||||
userName?: string;
|
||||
type: "Sick" | "Casual" | "Annual" | "Unpaid";
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
reason: string;
|
||||
status: "Pending" | "Approved" | "Rejected";
|
||||
appliedAt: string;
|
||||
}
|
||||
|
||||
export interface UserRecord {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
role: "employee" | "manager";
|
||||
leaveBalance?: number;
|
||||
}
|
||||
|
||||
const LEAVES_KEY = "lms_leaves";
|
||||
const USERS_KEY = "lms_users";
|
||||
|
||||
// Seed from JSON on first load
|
||||
async function seedIfNeeded() {
|
||||
if (typeof window === "undefined") return;
|
||||
if (!localStorage.getItem(LEAVES_KEY)) {
|
||||
const mod = await import("@/data/leaves.json");
|
||||
const raw = mod.default ?? mod;
|
||||
const leaves = Array.isArray(raw) ? raw : ((raw as any).leaves ?? []);
|
||||
localStorage.setItem(LEAVES_KEY, JSON.stringify(leaves));
|
||||
}
|
||||
if (!localStorage.getItem(USERS_KEY)) {
|
||||
const mod = await import("@/data/users.json");
|
||||
const raw = mod.default ?? mod;
|
||||
const users = Array.isArray(raw) ? raw : ((raw as any).users ?? []);
|
||||
localStorage.setItem(USERS_KEY, JSON.stringify(users));
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLeaves(): Promise<LeaveRequest[]> {
|
||||
await seedIfNeeded();
|
||||
const raw = localStorage.getItem(LEAVES_KEY) ?? "[]";
|
||||
const leaves: LeaveRequest[] = JSON.parse(raw);
|
||||
// Attach userName from users
|
||||
const users = getUsers();
|
||||
return leaves.map((l) => {
|
||||
const u = users.find((u) => u.id === l.userId);
|
||||
return { ...l, userName: u?.name ?? "Unknown" };
|
||||
});
|
||||
}
|
||||
|
||||
export function getUsers(): UserRecord[] {
|
||||
const raw = localStorage.getItem(USERS_KEY) ?? "[]";
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
export async function getLeavesForUser(userId: number): Promise<LeaveRequest[]> {
|
||||
const all = await getLeaves();
|
||||
return all.filter((l) => l.userId === userId);
|
||||
}
|
||||
|
||||
export async function applyLeave(
|
||||
userId: number,
|
||||
data: { type: LeaveRequest["type"]; fromDate: string; toDate: string; reason: string }
|
||||
): Promise<{ ok: boolean; error?: string }> {
|
||||
await seedIfNeeded();
|
||||
|
||||
// Calculate days
|
||||
const from = new Date(data.fromDate);
|
||||
const to = new Date(data.toDate);
|
||||
const days = Math.ceil((to.getTime() - from.getTime()) / 86400000) + 1;
|
||||
|
||||
// Check balance
|
||||
const users = getUsers();
|
||||
const user = users.find((u) => u.id === userId);
|
||||
if (!user) return { ok: false, error: "User not found" };
|
||||
if ((user.leaveBalance ?? 0) < days)
|
||||
return { ok: false, error: `Insufficient balance. You have ${user.leaveBalance} days left.` };
|
||||
|
||||
const leaves: LeaveRequest[] = JSON.parse(localStorage.getItem(LEAVES_KEY) ?? "[]");
|
||||
const newLeave: LeaveRequest = {
|
||||
id: Date.now(),
|
||||
userId,
|
||||
type: data.type,
|
||||
fromDate: data.fromDate,
|
||||
toDate: data.toDate,
|
||||
reason: data.reason,
|
||||
status: "Pending",
|
||||
appliedAt: new Date().toISOString().split("T")[0],
|
||||
};
|
||||
leaves.push(newLeave);
|
||||
localStorage.setItem(LEAVES_KEY, JSON.stringify(leaves));
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function updateLeaveStatus(
|
||||
leaveId: number,
|
||||
status: "Approved" | "Rejected"
|
||||
): Promise<void> {
|
||||
await seedIfNeeded();
|
||||
const leaves: LeaveRequest[] = JSON.parse(localStorage.getItem(LEAVES_KEY) ?? "[]");
|
||||
const idx = leaves.findIndex((l) => l.id === leaveId);
|
||||
if (idx === -1) return;
|
||||
|
||||
const leave = leaves[idx];
|
||||
const wasApproved = leave.status === "Approved";
|
||||
leaves[idx] = { ...leave, status };
|
||||
localStorage.setItem(LEAVES_KEY, JSON.stringify(leaves));
|
||||
|
||||
// Adjust balance
|
||||
const users = getUsers();
|
||||
const user = users.find((u) => u.id === leave.userId);
|
||||
if (!user || user.role !== "employee") return;
|
||||
|
||||
const from = new Date(leave.fromDate);
|
||||
const to = new Date(leave.toDate);
|
||||
const days = Math.ceil((to.getTime() - from.getTime()) / 86400000) + 1;
|
||||
|
||||
if (status === "Approved" && !wasApproved) {
|
||||
user.leaveBalance = (user.leaveBalance ?? 0) - days;
|
||||
} else if (status === "Rejected" && wasApproved) {
|
||||
user.leaveBalance = (user.leaveBalance ?? 0) + days;
|
||||
}
|
||||
|
||||
localStorage.setItem(USERS_KEY, JSON.stringify(users));
|
||||
|
||||
// Also update session user if it's the same person
|
||||
const sessionUser = localStorage.getItem("lms_user");
|
||||
if (sessionUser) {
|
||||
const su = JSON.parse(sessionUser);
|
||||
if (su.id === user.id) {
|
||||
su.leaveBalance = user.leaveBalance;
|
||||
localStorage.setItem("lms_user", JSON.stringify(su));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getUserBalance(userId: number): number {
|
||||
const users = getUsers();
|
||||
const u = users.find((u) => u.id === userId);
|
||||
return u?.leaveBalance ?? 0;
|
||||
}
|
||||
|
||||
export function daysBetween(from: string, to: string): number {
|
||||
const f = new Date(from);
|
||||
const t = new Date(to);
|
||||
return Math.ceil((t.getTime() - f.getTime()) / 86400000) + 1;
|
||||
}
|
||||
Reference in new issue
Block a user