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
27 changes: 26 additions & 1 deletion Learnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,29 @@

-->Make sure to include type:module

-->correct file name extensions i.e .js .ts etc
-->correct file name extensions i.e .js .ts etc


-->🔥 Real backend example
❌ Without Promise.all (slow)
const user = await User.findById(id);
const orders = await Order.find({ userId: id });
const payments = await Payment.find({ userId: id });

👉 Runs one after another (sequential)

✅ With Promise.all (fast)
const [user, orders, payments] = await Promise.all([
User.findById(id),
Order.find({ userId: id }),
Payment.find({ userId: id })
]);

👉 Runs all queries together (parallel)

⚡ Performance gain
Sequential → takes sum of all times
Parallel → takes max of all times


-->
21 changes: 0 additions & 21 deletions package-lock.json

This file was deleted.

5 changes: 0 additions & 5 deletions package.json

This file was deleted.

68 changes: 68 additions & 0 deletions server/controllers/dashboardController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { DEPARTMENTS } from "../constants/departments.js";
import Attendance from "../models/Attendance.js"
import Employee from "../models/Employee.js"
import LeaveApplication from "../models/LeaveApplication.js";
Comment on lines +1 to +4
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

Import Payslip before using it on Line 53.

The non-admin branch calls Payslip.findOne(...), but this model is never imported in this file. That path will crash with a ReferenceError.

Suggested fix
 import { DEPARTMENTS } from "../constants/departments.js";
 import Attendance from "../models/Attendance.js"
 import Employee from "../models/Employee.js"
 import LeaveApplication from "../models/LeaveApplication.js";
+import Payslip from "../models/Payslip.js";
📝 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
import { DEPARTMENTS } from "../constants/departments.js";
import Attendance from "../models/Attendance.js"
import Employee from "../models/Employee.js"
import LeaveApplication from "../models/LeaveApplication.js";
import { DEPARTMENTS } from "../constants/departments.js";
import Attendance from "../models/Attendance.js"
import Employee from "../models/Employee.js"
import LeaveApplication from "../models/LeaveApplication.js";
import Payslip from "../models/Payslip.js";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/dashboardController.js` around lines 1 - 4, Add an import
for the Payslip model at the top of dashboardController.js so the non-admin
branch that calls Payslip.findOne(...) (the Payslip symbol used around the
non-admin dashboard logic) doesn't throw a ReferenceError; mirror the existing
model imports (e.g., Attendance, Employee, LeaveApplication) and import Payslip
before it's referenced in the file.



//Get dashboard for employee and admin
//GET /api/dashboard
export const getDashboard = async (req, res) => {
try {

const session = req.session;

if (session.role === "ADMIN") {
const [totalEmployees, todayAttendance, pendingLeaves] = await
Promise.all([
Employee.countDocuments({ isDeleted: { $ne: true } }),
Attendance.countDocuments({
date: {
$gte: new Date(new Date().setHours(0, 0, 0, 0)),
$lt: new Date(new Date().setHours(0, 0, 0, 0)),
}
}),
Comment on lines +18 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

todayAttendance will always be 0 with this range.

Line 20 and Line 21 both resolve to the same timestamp, so the query becomes date >= startOfDay && date < startOfDay, which no document can satisfy.

Suggested fix
+            const startOfToday = new Date();
+            startOfToday.setHours(0, 0, 0, 0);
+            const startOfTomorrow = new Date(startOfToday);
+            startOfTomorrow.setDate(startOfTomorrow.getDate() + 1);
+
             const [totalEmployees, todayAttendance, pendingLeaves] = await
                 Promise.all([
                     Employee.countDocuments({ isDeleted: { $ne: true } }),
                     Attendance.countDocuments({
                         date: {
-                            $gte: new Date(new Date().setHours(0, 0, 0, 0)),
-                            $lt: new Date(new Date().setHours(0, 0, 0, 0)),
+                            $gte: startOfToday,
+                            $lt: startOfTomorrow,
                         }
                     }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/dashboardController.js` around lines 18 - 23, The
todayAttendance query uses Attendance.countDocuments with a date range where
both bounds are the same (startOfDay), causing zero results; update the upper
bound to the start of the next day (e.g., compute startOfDay with new
Date().setHours(0,0,0,0) and set the $lt bound to new
Date(startOfDay).setDate(startOfDay.getDate() + 1) or use setHours(24,0,0,0)) so
the filter becomes date >= startOfDay && date < startOfNextDay; adjust the code
around Attendance.countDocuments and the todayAttendance variable accordingly.

LeaveApplication.countDocuments({ status: "PENDING" })
]);

return res.json({
role: "ADMIN",
totalEmployees,
totalDepartments: DEPARTMENTS.length,
todayAttendance,
pendingLeaves,
});
} else {
const employee = await Employee.findOne({ userId: session.userId }).lean();

if (!employee)
return res.status(404).json({ error: "Employee not found" });
Comment on lines +35 to +38
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

Soft-deleted employees can still fetch dashboard data.

This lookup only checks userId. Because the JWT stays valid after login, a soft-deleted employee can still resolve here until the token expires unless isDeleted is filtered too.

Suggested fix
-            const employee = await Employee.findOne({ userId: session.userId }).lean();
+            const employee = await Employee.findOne({
+                userId: session.userId,
+                isDeleted: { $ne: true },
+            }).lean();
📝 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 employee = await Employee.findOne({ userId: session.userId }).lean();
if (!employee)
return res.status(404).json({ error: "Employee not found" });
const employee = await Employee.findOne({
userId: session.userId,
isDeleted: { $ne: true },
}).lean();
if (!employee)
return res.status(404).json({ error: "Employee not found" });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/dashboardController.js` around lines 35 - 38, The Employee
lookup in dashboardController.js uses Employee.findOne({ userId: session.userId
}) and therefore returns soft-deleted records; update the query to also filter
out deleted employees (e.g., include isDeleted: false) or explicitly check
employee.isDeleted after retrieval and return 404 if true so soft-deleted users
cannot fetch dashboard data; ensure you still call .lean() and return the same
404 response when the filtered query yields no result.


const [currentMonthAttendance, pendingLeaves, latestPayslip] = await Promise.all([
Attendance.countDocuments({
employeeId: employee._id,
date: {
$gte: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 1),
$lt: new Date(today.getFullYear(), today.getMonth() + 1, 1),
}
}),
Comment on lines +40 to +47
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

currentMonthAttendance can never work as written.

Line 44 and Line 45 reference today, which is never defined, so the employee dashboard will throw before this query runs. Even after defining it, the lower bound starts on the current day at 01:00, not at the first day of the month.

Suggested fix
+            const today = new Date();
+            const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
+            const startOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
+
             const [currentMonthAttendance, pendingLeaves, latestPayslip] = await Promise.all([
                 Attendance.countDocuments({
                     employeeId: employee._id,
                     date: {
-                        $gte: new Date(today.getFullYear(), today.getMonth(), today.getDate(), 1),
-                        $lt: new Date(today.getFullYear(), today.getMonth() + 1, 1),
+                        $gte: startOfMonth,
+                        $lt: startOfNextMonth,
                     }
                 }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/controllers/dashboardController.js` around lines 40 - 47,
currentMonthAttendance query uses an undefined today and incorrectly sets the
lower bound to the current day at 01:00; declare const today = new Date() before
the Attendance.countDocuments call and change the date range to the start of the
month and start of next month (e.g., new Date(today.getFullYear(),
today.getMonth(), 1, 0, 0, 0) as the $gte and new Date(today.getFullYear(),
today.getMonth() + 1, 1, 0, 0, 0) as the $lt) when building the
Attendance.countDocuments query that references employee._id.


LeaveApplication.countDocuments({
employeeId: employee._id,
status: "PENDING",
}),
Payslip.findOne({ employeeId: employee._id }).sort({ createdAt: -1 }).lean()
])

return res.json({
role: "EMPLOYEE",
employee: { ...employee, id: employee._id.toString() },
currentMonthAttendance,
pendingLeaves,
latestPayslip: latestPayslip ? { ...latestPayslip, id: latestPayslip._id.toString() } : null,
});
}
} catch (error) {
console.error("Dashbaord error:", error);
return res.status(500).json({ error: "Failed" });
}
}
10 changes: 10 additions & 0 deletions server/package-lock.json

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

3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"description": "",
"dependencies": {
"bcrypt": "^6.0.0",
"bcryptjs": "^3.0.3",
"cors": "^2.8.6",
"dotenv": "^17.4.2",
"express": "^5.2.1",
Expand All @@ -23,4 +24,4 @@
"devDependencies": {
"nodemon": "^3.1.14"
}
}
}
13 changes: 13 additions & 0 deletions server/routes/dashboardRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Router } from "express";
import { protect } from "../middlewares/protect.js";
import { getDashboard } from "../controllers/dashboardController.js";
Comment on lines +2 to +3
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e

echo "Candidate middleware files:"
fd -t f 'protect\.js|auth\.js' server

echo
echo "Where protect is exported:"
rg -n -C2 --glob 'server/**/*.js' 'export\s+(const|function)\s+protect|export\s*\{\s*protect\s*\}' server

echo
echo "Where the dashboard route imports protect from:"
rg -n -C2 --glob 'server/routes/**/*.js' 'import\s*\{\s*protect\s*\}\s*from' server/routes

Repository: harshGupta090722/EMS

Length of output: 1771


Fix incorrect import path for the protect middleware.

The protect middleware is exported from server/middleware/auth.js, but this file imports from a non-existent path. Update line 2:

import { protect } from "../middleware/auth.js";

All other route files use this correct path. The current import will fail during module resolution.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/routes/dashboardRoutes.js` around lines 2 - 3, The import for the
protect middleware is pointing to the wrong module; update the import statement
that brings in protect (the one paired with getDashboard) so it imports protect
from the auth module in the middleware directory (i.e., import protect from the
auth.js middleware export) to match other routes and fix module resolution
errors.




const dashboardRouter = Router();


dashboardRouter.get("/", protect, getDashboard);


export default dashboardRouter;
3 changes: 2 additions & 1 deletion server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import profileRouter from "./routes/profileRoutes.js";
import attendanceRouter from "./routes/AttendanceRoutes.js";
import leaveRouter from "./routes/leaveRoutes.js";
import paySlipRouter from "./routes/paySlipsRoutes.js";

import dashboardRouter from "./routes/dashboardRoutes.js";

const app = express()

Expand All @@ -34,6 +34,7 @@ app.use('/api/profile', profileRouter);
app.use('/api/attendance', attendanceRouter);
app.use('/api/leave', leaveRouter);
app.use('/api/payslip', paySlipRouter);
app.use('/api/dashboard', dashboardRouter);


await connectDb();
Expand Down