Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"bcryptjs": "^3.0.3"
}
}
4 changes: 2 additions & 2 deletions server/controllers/attendanceController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Attendance from "../models/Attendance.js";
import { Employee } from "../models/Employee.js"
import Employee from "../models/Employee.js"


//clock in/out for employee
Expand All @@ -24,7 +24,7 @@ export const clockInOut = async (req, res) => {
const today = new Date();
today.setHours(0, 0, 0, 0);

const existing = await getAttendance.findOne({
const existing = await Attendance.findOne({
employeeId: employee._id,
date: today
})
Expand Down
2 changes: 1 addition & 1 deletion server/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import User from "../models/User";
import User from "../models/User.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

Expand Down
4 changes: 2 additions & 2 deletions server/controllers/employeeController.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const getEmployee = async (req, res) => {
export const createEmployee = async (req, res) => {
try {

const { firstName, lastName, email, phone, position, department, basicSalary, allowances, deductions, joinDate, password, role, bio } = req.bod;
const { firstName, lastName, email, phone, position, department, basicSalary, allowances, deductions, joinDate, password, role, bio } = req.body;


if (!email || !password || !firstName || !lastName)
Expand Down Expand Up @@ -88,7 +88,7 @@ export const createEmployee = async (req, res) => {
export const updateEmployee = async (req, res) => {
try {
const { id } = req.params;
const { firstName, lastName, email, phone, position, department, basicSalary, allowances, deductions, password, role, bio, employmentStatus } = req.bod;
const { firstName, lastName, email, phone, position, department, basicSalary, allowances, deductions, password, role, bio, employmentStatus } = req.body;


const employee = await Employee.findById(id);
Expand Down
128 changes: 128 additions & 0 deletions server/controllers/leaveController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import Employee from "../models/Employee.js";
import LeaveApplication from "../models/LeaveApplication.js";

//Create leave
//POST /api/leave

export const createLeave = async (req, res) => {
try {
const session = req.session;
const employee = await Employee.findOne({ userId: session.userId });

if (!employee)
return res.status(404).json({ message: "Employee not found" });

if (employee.isDeleted)
return res.status(403).json({ message: "Your account id deactivated.You cannot create leave" });

const { type, startDate, endDate, reason } = req.body;

if (!type || !startDate || !endDate || !reason)
return res.status(400).json({ message: "Please provide all the required fields" });

const today = new Date();
today.setHours(0, 0, 0, 0);

if (new Date(startDate) < today || new Date(endDate) < today)
return res.status(400).json({ message: "Please provide valid dates" });

if (new Date(endDate) < today || new Date(startDate) < today)
return res.status(400).json({ message: "end date must be greater than start date" });
Comment on lines +26 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical bug: Date validation logic is incorrect and duplicated.

Lines 29-30 have the same condition as lines 26-27, making the second check unreachable and failing to validate that endDate >= startDate. Users can submit a leave where endDate is before startDate.

🐛 Proposed fix
         if (new Date(startDate) < today || new Date(endDate) < today)
             return res.status(400).json({ message: "Please provide valid dates" });

-        if (new Date(endDate) < today || new Date(startDate) < today)
+        if (new Date(endDate) < new Date(startDate))
             return res.status(400).json({ message: "end date must be greater than start date" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/leaveController.js` around lines 26 - 30, The date
validation contains a duplicated condition and doesn't check that endDate is
after startDate; update the second check in leaveController.js to compare parsed
Dates (new Date(endDate) < new Date(startDate)) and return the "end date must be
greater than start date" response, leaving the first check that ensures neither
startDate nor endDate is in the past (new Date(startDate) < today || new
Date(endDate) < today) intact; use the startDate and endDate symbols to locate
and replace the duplicate condition with the proper comparison.


const leave = await LeaveApplication.create({
employeeId: employee._id,
type,
startDate: new Date(startDate),
endDate: new Date(endDate),
reason,
status: "PENDING"
});

return res.json({
success: true,
data: leave
})

} catch (error) {
console.log(error);
return res.status(500).json({ error: "Failed to create leave" });
}
}

//Get leave
//GET /api/leaves
export const getLeaves = async (req, res) => {
try {
const session = req.session;


const isAdmin = session.role === "ADMIN";


if (isAdmin) {
const status = req.query.status;
const where = status ? { status } : {}

const leaves = await LeaveApplication.find(where).
populate("employeeId").sort({ createdAt: -1 });


const data = leaves.map((l) => {
const obj = l.toObject();

return {
...obj,
id: obj._id.toString(),
employee: obj.employeeId,
employeeId: obj.employeeId?._id?.toString()
}
})

return res.json({
data
})
} else {
const employee = await Employee.findOne({ userId: session.userId }).lean();

if (!employee)
return res.status(404).json({ error: "Not Found" });

const leaves = await LeaveApplication.find({
employeeId: employee._id
}).sort({ createdAt: -1 });

return res.json({
data: leaves,
employee: { ...employee, id: employee._id.toString() }
})
}

} catch (error) {
console.log(error);
return res.status(500).json({ error: "Failed to fetch leaves" });
}
}


//Update leave status
//PATCH /api/leaves/:id
export const updateLeaveStatus = async (req, res) => {
try {
const { status } = req.body;

if (!["AP[ROVED", "REJECTED", "PENDING"].includes(status))
return res.status(400).json({ error: "invalid status" });
Comment on lines +113 to +114

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical typo: "AP[ROVED" should be "APPROVED".

This typo causes all legitimate "APPROVED" status updates to be rejected with "invalid status" error.

🐛 Proposed fix
-        if (!["AP[ROVED", "REJECTED", "PENDING"].includes(status))
+        if (!["APPROVED", "REJECTED", "PENDING"].includes(status))
             return res.status(400).json({ error: "invalid status" });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!["AP[ROVED", "REJECTED", "PENDING"].includes(status))
return res.status(400).json({ error: "invalid status" });
if (!["APPROVED", "REJECTED", "PENDING"].includes(status))
return res.status(400).json({ error: "invalid status" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/leaveController.js` around lines 113 - 114, The validation
array for the status check in leaveController.js contains a typo ("AP[ROVED")
causing valid "APPROVED" statuses to be rejected; update the string list used in
the includes check (the array literal used where status is tested) to
["APPROVED", "REJECTED", "PENDING"] so that the variable status is correctly
validated against the proper values (adjust the includes(...) call in the status
validation block).



const leave = await LeaveApplication.findByIdAndUpdate(req.params.id, { status }, { returnDocument: 'after' });

return res.json({
success: true,
data: leave
})
Comment on lines +117 to +122

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing null check when leave is not found.

If req.params.id doesn't match any document, findByIdAndUpdate returns null and the response will indicate success with data: null. This should return a 404 error instead.

🐛 Proposed fix
         const leave = await LeaveApplication.findByIdAndUpdate(req.params.id, { status }, { returnDocument: 'after' });

+        if (!leave)
+            return res.status(404).json({ error: "Leave application not found" });
+
         return res.json({
             success: true,
             data: leave
         })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const leave = await LeaveApplication.findByIdAndUpdate(req.params.id, { status }, { returnDocument: 'after' });
return res.json({
success: true,
data: leave
})
const leave = await LeaveApplication.findByIdAndUpdate(req.params.id, { status }, { returnDocument: 'after' });
if (!leave)
return res.status(404).json({ error: "Leave application not found" });
return res.json({
success: true,
data: leave
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/leaveController.js` around lines 117 - 122, After calling
LeaveApplication.findByIdAndUpdate(req.params.id, { status }, { returnDocument:
'after' }) in the leaveController, add a null check for the returned leave and
if it's null send a 404 response (e.g., res.status(404).json({ success: false,
error: 'Leave application not found' })) instead of returning success: true with
data: null; keep the existing successful res.json({ success: true, data: leave
}) path for non-null results.



} catch (error) {
return res.status(500).json({ error: "Failed" });
}
}
4 changes: 2 additions & 2 deletions server/middleware/auth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { jwt } from "jsonwebtoken";
import jwt from "jsonwebtoken";

export const protect = (req, res, next) => {
try {
Expand Down Expand Up @@ -27,7 +27,7 @@ export const protect = (req, res, next) => {


export const adminProtect = (req, res, next) => {
if (!req?.session?.role !== "ADMIN") {
if (req?.session?.role !== "ADMIN") {
return res.status(401).json({ error: "Admin access required" })
}

Expand Down
2 changes: 1 addition & 1 deletion server/models/Employee.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DEPARTMENTS } from "../constants/departments.js";

const EmployeeSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.types.ObjectId,
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
unique: true
Expand Down
37 changes: 37 additions & 0 deletions server/models/LeaveApplication.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import mongoose from "mongoose";



const leaveApplicationSchema = new mongoose.Schema({
employeeId: {
type: mongoose.Schhema.Types.ObjectId,
ref: "Employee",
Comment on lines +6 to +8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the typo and expected Schema API usage
rg -n 'Schhema|Schema\.Types\.ObjectId' server/models/LeaveApplication.js

Repository: harshGupta090722/EMS

Length of output: 113


Fix typo in ObjectId type definition—Schhema is invalid and will break schema initialization at runtime.

Line 7 uses mongoose.Schhema.Types.ObjectId where Schhema is misspelled. The correct property is mongoose.Schema.Types.ObjectId. This typo will cause the model to fail when loaded.

Proposed fix
-        type: mongoose.Schhema.Types.ObjectId,
+        type: mongoose.Schema.Types.ObjectId,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
employeeId: {
type: mongoose.Schhema.Types.ObjectId,
ref: "Employee",
employeeId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Employee",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/models/LeaveApplication.js` around lines 6 - 8, The schema field
employeeId in LeaveApplication.js uses the misspelled
mongoose.Schhema.Types.ObjectId which breaks model initialization; change the
reference to mongoose.Schema.Types.ObjectId (update the employeeId definition)
so the schema uses the correct ObjectId type.

required: true,
},
type: {
type: String,
enum: ["SICK", "CASUAL", "ANNUAL"],
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: true
},
Comment on lines +16 to +23

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add date-order validation for leave periods.

endDate can currently be earlier than startDate, which allows invalid leave applications to be persisted.

Proposed fix
     endDate: {
         type: Date,
-        required: true
+        required: true,
+        validate: {
+            validator: function (value) {
+                return !this.startDate || value >= this.startDate;
+            },
+            message: "endDate must be greater than or equal to startDate"
+        }
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: true,
validate: {
validator: function (value) {
return !this.startDate || value >= this.startDate;
},
message: "endDate must be greater than or equal to startDate"
}
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/models/LeaveApplication.js` around lines 16 - 23, The model allows
endDate earlier than startDate; add a validation on the LeaveApplication schema
to reject such records: either add a custom validator for the endDate field that
checks (this.startDate && value >= this.startDate) and returns a validation
error message, or add a schema.pre('validate', ...) hook on the LeaveApplication
schema that throws/invalidates when endDate < startDate; ensure the validation
runs on updates (use runValidators for update operations) so invalid ranges
cannot be persisted.

reason: {
type: String,
required: true
},
status: {
type: String,
enum: ["PENDING", "APPROVED", "REJECTED"],
default: "PENDING"
}
});


const LeaveApplication = mongoose.model("LeaveApplication", leaveApplicationSchema);
export default LeaveApplication;
2 changes: 1 addition & 1 deletion server/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const UserSchema = new mongoose.Schema({
role: {
type: String,
enum: ["ADMIN", "EMPLOYEE"],
dafault: "EMPLOYEE"
default: "EMPLOYEE"
}
},
{ timestamps: true });
Expand Down
2 changes: 1 addition & 1 deletion server/routes/AttendanceRoutes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Router } from "express";
import { clockInOut, getAttendance } from "../controllers/attendanceController.js";
import { protect } from "../middlewares/auth.js";
import { protect } from "../middleware/auth.js";

const attendanceRouter = Router();

Expand Down