diff --git a/src/controllers/va/vaProjects/vaProjectLogList.controller.js b/src/controllers/va/vaProjects/vaProjectLogList.controller.js new file mode 100644 index 0000000..13e9f72 --- /dev/null +++ b/src/controllers/va/vaProjects/vaProjectLogList.controller.js @@ -0,0 +1,118 @@ +import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js"; + +function formatHMS(ms) { + const totalSeconds = Math.floor(ms / 1000); + const h = Math.floor(totalSeconds / 3600); + const m = Math.floor((totalSeconds % 3600) / 60); + const s = totalSeconds % 60; + return `${h}h ${m}m ${s}s`; +} + +async function va_projects_log_list_function(req, res) { + try { + const { projectId, from, to } = req.params; + + if (!projectId) { + return res.status(400).json({ success: false, message: "Missing projectId" }); + } + + // 📅 Date filtering + const now = new Date(); + const fromDate = from && from !== "undefined" ? new Date(from) : new Date(now.setHours(0, 0, 0, 0)); + const toDate = to && to !== "undefined" ? new Date(to) : new Date(); + toDate.setHours(23, 59, 59, 999); + + // 🧩 Fetch Project + const project = await ProjectModel.findById(projectId).populate("vaAssigned", { + "userDetails.firstName": 1, + "userDetails.lastName": 1, + "userDetails.email": 1, + status: 1 + }); + + if (!project) { + return res.status(404).json({ success: false, message: "Project not found" }); + } + + const vaList = project.vaAssigned || []; + if (!vaList.length) { + return res.status(200).json({ success: true, message: "No VAs assigned to this project", data: [] }); + } + + let totalProjectWorkMs = 0; + let activeCount = 0; + + const vaReport = []; + + // 🔁 Loop through each VA assigned to the project + for (const va of vaList) { + const vaId = va._id; + const vaEmail = va.userDetails?.email; + + // 🔎 Get all login/logout logs for this VA in the date range + const logs = await LoggedModel.find({ + vaId, + projectId, + time: { $gte: fromDate, $lte: toDate } + }).sort({ time: 1 }).lean(); + + let totalWorkMs = 0; + let currentLogin = null; + + for (const entry of logs) { + if (entry.loggedType === "LOGIN") { + currentLogin = entry; + } else if (entry.loggedType === "LOGOUT" && currentLogin) { + totalWorkMs += new Date(entry.time) - new Date(currentLogin.time); + currentLogin = null; + } + } + + // if still logged in (no logout yet) — count up to now + if (currentLogin) { + totalWorkMs += new Date() - new Date(currentLogin.time); + } + + totalProjectWorkMs += totalWorkMs; + + // 🟢 Determine Active / Away status + const lastLog = logs[logs.length - 1]; + let activityStatus = "AWAY"; + if (lastLog?.loggedType === "LOGIN") { + activityStatus = "ACTIVE"; + activeCount++; + } + + vaReport.push({ + vaId, + name: `${va.userDetails?.firstName || ""} ${va.userDetails?.lastName || ""}`.trim(), + email: vaEmail, + status: va.status, + activityStatus, + totalWorkTime: formatHMS(totalWorkMs), + totalWorkMs, + }); + } + + // 📊 Prepare Summary + const totalVAs = vaReport.length; + const awayCount = totalVAs - activeCount; + + res.status(200).json({ + success: true, + projectId, + projectName: project.name, + totalVAs, + activeVAs: activeCount, + awayVAs: awayCount, + totalProjectWorkTime: formatHMS(totalProjectWorkMs), + totalProjectWorkMs, + data: vaReport + }); + + } catch (err) { + res.status(500).json({ success: false, message: err.message }); + } +} + +export { va_projects_log_list_function }; diff --git a/src/controllers/va/vaProjects/vaProjectReport.controller.js b/src/controllers/va/vaProjects/vaProjectReport.controller.js new file mode 100644 index 0000000..c140fa1 --- /dev/null +++ b/src/controllers/va/vaProjects/vaProjectReport.controller.js @@ -0,0 +1,120 @@ +import { LoggedModel, WorkTrackModel, VAModel } from "../../../models/vaModel.js"; + +function formatHMS(ms) { + const totalSeconds = Math.floor(ms / 1000); + const h = Math.floor(totalSeconds / 3600); + const m = Math.floor((totalSeconds % 3600) / 60); + const s = totalSeconds % 60; + return `${h}h ${m}m ${s}s`; +} + +async function va_projects_report_function(req, res) { + try { + const { projectId, from, to } = req.params; + + if (!projectId) { + return res.status(400).json({ success: false, message: "Missing projectId" }); + } + + // Default: current day range + const now = new Date(); + const fromDate = from && from !== "undefined" ? new Date(from) : new Date(now.setHours(0, 0, 0, 0)); + const toDate = to && to !== "undefined" ? new Date(to) : new Date(); + toDate.setHours(23, 59, 59, 999); + + // Get all VA login/logout logs for this project in range + const logs = await LoggedModel.find({ + projectId, + time: { $gte: fromDate, $lte: toDate } + }).sort({ time: 1 }).lean(); + + if (!logs.length) { + return res.json({ success: true, message: "No logs found for this project", data: [] }); + } + + // Unique VA IDs + const vaIds = [...new Set(logs.map(log => log.vaId?.toString()))].filter(Boolean); + const vaUsers = await VAModel.find( + { _id: { $in: vaIds } }, + { "userDetails.firstName": 1, "userDetails.lastName": 1, "userDetails.email": 1 } + ).lean(); + + const projectReport = []; + + for (const vaId of vaIds) { + const vaLogList = logs.filter(l => l.vaId.toString() === vaId); + let sessions = []; + let currentLogin = null; + + // Pair login → logout + for (const entry of vaLogList) { + if (entry.loggedType === "LOGIN") { + currentLogin = entry; + } else if (entry.loggedType === "LOGOUT" && currentLogin) { + sessions.push({ from: currentLogin.time, to: entry.time }); + currentLogin = null; + } + } + + // If still logged in, close at end of day + if (currentLogin) { + sessions.push({ from: currentLogin.time, to: toDate }); + } + + const vaDetails = vaUsers.find(v => v._id.toString() === vaId); + let totalWorkMs = 0; + const sessionDetails = []; + + for (const s of sessions) { + const workData = await WorkTrackModel.find({ + vaId, + projectId, + from: { $gte: s.from }, + to: { $lte: s.to } + }).sort({ from: 1 }).lean(); + + const tabs = workData.map(t => ({ + title: t.title, + appName: t.appName, + from: t.from, + to: t.to, + durationMs: t.totalTime, + durationFormatted: formatHMS(t.totalTime), + })); + + const sessionWorkMs = workData.reduce((sum, w) => sum + (w.totalTime || 0), 0); + totalWorkMs += sessionWorkMs; + + sessionDetails.push({ + from: s.from, + to: s.to, + totalSessionTime: formatHMS(sessionWorkMs), + tabs, + }); + } + + projectReport.push({ + vaId, + vaName: `${vaDetails?.userDetails?.firstName || ""} ${vaDetails?.userDetails?.lastName || ""}`.trim(), + vaEmail: vaDetails?.userDetails?.email, + totalWorkTime: formatHMS(totalWorkMs), + totalWorkMs, + sessions: sessionDetails + }); + } + + res.json({ + success: true, + projectId, + from: fromDate, + to: toDate, + totalVAs: projectReport.length, + data: projectReport, + }); + + } catch (err) { + res.status(500).json({ success: false, message: err.message }); + } +} + +export { va_projects_report_function }; diff --git a/src/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index 5f5ef48..7e1398f 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -18,6 +18,8 @@ import { va_projects_callback } from "../../controllers/va/vaProjects/vaProjects import { authValidator } from "../../middlewares/auth.middleware.js"; import { va_log_track_work_callback } from "../../controllers/va/vaLog/vaLogTrackWork.controller.js"; import { va_log_work_report_callback } from "../../controllers/va/vaLog/vaLogWorkReport.js"; +import { va_projects_report_function } from "../../controllers/va/vaProjects/vaProjectReport.controller.js"; +import { va_projects_log_list_function } from "../../controllers/va/vaProjects/vaProjectLogList.controller.js"; vaRoutes.get("/", va_callback); // VA Callback for getting active VA @@ -41,9 +43,9 @@ vaRoutes.get("/clients/:clientId/projects/:projectId", va_clients_projects_callb // Getting Project List assigned to VA should be ACTIVE and add total logged hours added by VA -vaRoutes.get("/list/project/:projectId", va_projects_callback); +vaRoutes.get("/list/project/:projectId/:from?/:to?", va_projects_log_list_function); // GET Windows Tab Logged Data for VA IN Between Login and LOGOUT -vaRoutes.get("/report/project/:projectId", va_projects_callback); +vaRoutes.get("/report/project/:projectId/:from?/:to?", va_projects_report_function); vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project