From 9f086d55ba89dbfd169ea179d692c2f385ba7fc3 Mon Sep 17 00:00:00 2001 From: Aakash Date: Mon, 10 Nov 2025 12:35:00 +0530 Subject: [PATCH] Fixed API issues --- .../va/vaProfile/vaRegister.controller.js | 2 +- .../vaProjects/vaProjectLogList.controller.js | 112 ++++++++------- src/controllers/va/vaUsername.controller.js | 132 ++++++++++-------- src/routes/v1/va.routes.js | 16 ++- 4 files changed, 149 insertions(+), 113 deletions(-) diff --git a/src/controllers/va/vaProfile/vaRegister.controller.js b/src/controllers/va/vaProfile/vaRegister.controller.js index 9354858..360aaed 100644 --- a/src/controllers/va/vaProfile/vaRegister.controller.js +++ b/src/controllers/va/vaProfile/vaRegister.controller.js @@ -4,7 +4,7 @@ import { VAModel } from "../../../models/vaModel.js"; // Register a new VA async function va_register(req, res) { try { - const payload = req.body ? JSON.parse(req.body) : {}; + const payload = req.body || {}; const { userDetails } = payload; if (!userDetails || !userDetails.email || !userDetails.firstName || !userDetails.password) { diff --git a/src/controllers/va/vaProjects/vaProjectLogList.controller.js b/src/controllers/va/vaProjects/vaProjectLogList.controller.js index 13e9f72..be7b7fb 100644 --- a/src/controllers/va/vaProjects/vaProjectLogList.controller.js +++ b/src/controllers/va/vaProjects/vaProjectLogList.controller.js @@ -1,3 +1,4 @@ +import jwt from "jsonwebtoken"; import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js"; function formatHMS(ms) { @@ -11,50 +12,63 @@ function formatHMS(ms) { async function va_projects_log_list_function(req, res) { try { const { projectId, from, to } = req.params; + const accessToken = req.headers.token; - if (!projectId) { - return res.status(400).json({ success: false, message: "Missing projectId" }); + if (!accessToken) { + return res.status(401).json({ success: false, message: "Missing token" }); } - // 📅 Date filtering + const decoded = jwt.verify(accessToken, process.env.JWT_SECRET_ACCESS_TOKEN); + if (!decoded || !decoded.email) { + return res.status(401).json({ success: false, message: "Invalid token" }); + } + + const vaEmail = decoded.email; + const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail }); + if (!vaUser) { + return res.status(404).json({ success: false, message: "VA not found" }); + } + + const vaId = vaUser._id; + + // 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 - }); + let projectList = []; + if (projectId) { + const project = await ProjectModel.findById(projectId); + if (!project) { + return res.status(404).json({ success: false, message: "Project not found" }); + } - if (!project) { - return res.status(404).json({ success: false, message: "Project not found" }); + // Check if VA is assigned to this project + const isAssigned = project.vaAssigned.some(id => id.toString() === vaId.toString()); + if (!isAssigned) { + return res.status(403).json({ success: false, message: "VA not assigned to this project" }); + } + + projectList = [project]; + } else { + projectList = await ProjectModel.find({ vaAssigned: vaId }); } - const vaList = project.vaAssigned || []; - if (!vaList.length) { - return res.status(200).json({ success: true, message: "No VAs assigned to this project", data: [] }); + if (!projectList.length) { + return res.status(200).json({ success: true, message: "No assigned projects found", data: [] }); } - let totalProjectWorkMs = 0; - let activeCount = 0; + const finalReport = []; - 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 + for (const project of projectList) { const logs = await LoggedModel.find({ vaId, - projectId, + projectId: project._id, time: { $gte: fromDate, $lte: toDate } - }).sort({ time: 1 }).lean(); + }) + .sort({ time: 1 }) + .lean(); let totalWorkMs = 0; let currentLogin = null; @@ -68,46 +82,40 @@ async function va_projects_log_list_function(req, res) { } } - // if still logged in (no logout yet) — count up to now + // still logged in → count till 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++; - } + const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY"; - vaReport.push({ - vaId, - name: `${va.userDetails?.firstName || ""} ${va.userDetails?.lastName || ""}`.trim(), - email: vaEmail, - status: va.status, + finalReport.push({ + projectId: project._id, + projectName: project.name, activityStatus, totalWorkTime: formatHMS(totalWorkMs), totalWorkMs, + totalLogs: logs.length }); } - // 📊 Prepare Summary - const totalVAs = vaReport.length; - const awayCount = totalVAs - activeCount; + // Summary for current VA + const totalProjects = finalReport.length; + const activeProjects = finalReport.filter(r => r.activityStatus === "ACTIVE").length; + const awayProjects = totalProjects - activeProjects; + const totalMs = finalReport.reduce((acc, r) => acc + r.totalWorkMs, 0); res.status(200).json({ success: true, - projectId, - projectName: project.name, - totalVAs, - activeVAs: activeCount, - awayVAs: awayCount, - totalProjectWorkTime: formatHMS(totalProjectWorkMs), - totalProjectWorkMs, - data: vaReport + vaId, + vaName: `${vaUser.userDetails.firstName} ${vaUser.userDetails.lastName}`, + totalProjects, + activeProjects, + awayProjects, + totalWorkTime: formatHMS(totalMs), + totalWorkMs: totalMs, + data: finalReport }); } catch (err) { diff --git a/src/controllers/va/vaUsername.controller.js b/src/controllers/va/vaUsername.controller.js index 72af821..038fe6e 100644 --- a/src/controllers/va/vaUsername.controller.js +++ b/src/controllers/va/vaUsername.controller.js @@ -1,89 +1,109 @@ -import { VAModel, LoggedModel } from "../../models/vaModel.js"; +import { VAModel, ProjectModel, LoggedModel } from "../../models/vaModel.js"; // Getting VA details by username async function va_username_callback(req, res) { try { const { userName } = req.params; if (!userName) { - return res.status(400).json({ success: false, message: "Missing userName param" }); + return res.status(400).json({ success: false, message: "Missing username" }); } - // Find VA by common identifier fields (id, email, username, first/last name) - const va = await VAModel.findOne({ + // Find VA by firstName or lastName or email (depending on how you store username) + const vaUser = await VAModel.findOne({ $or: [ - { _id: userName }, { "userDetails.email": userName }, - { "userDetails.userName": userName }, { "userDetails.firstName": userName }, { "userDetails.lastName": userName }, - { "userDetails.fullName": userName } - ] - }).lean().exec(); + ], + }) + .populate("projectIds", "name totalLoggedHours") + .lean(); - if (!va) { + if (!vaUser) { return res.status(404).json({ success: false, message: "VA not found" }); } - // Latest log entry to determine active state - const lastLog = await LoggedModel.findOne({ vaId: va._id }).sort({ time: -1 }).lean().exec(); - const active = lastLog ? String(lastLog.loggedType).toUpperCase() === "LOGIN" : false; + const vaId = vaUser._id; - // Fetch all logs for this VA (ordered ascending) to compute logged hours - const logs = await LoggedModel.find({ vaId: va._id }).sort({ time: 1 }).lean().exec(); + // Get all logs for this VA + const logs = await LoggedModel.find({ vaId }).sort({ time: 1 }).populate("projectId", "name").lean(); - let totalSeconds = 0; + if (!logs.length) { + return res.status(200).json({ + success: true, + message: "No logs found for this VA", + data: { + va: vaUser, + totalWorkTime: "00h 00m 00s", + totalWorkMs: 0, + projects: [], + }, + }); + } - if (logs.length) { - // If logs have an explicit duration field, sum it - if (logs[0].duration !== undefined) { - totalSeconds = logs.reduce((sum, l) => sum + (Number(l.duration) || 0), 0); - } else { - // Otherwise compute from LOGIN/LOGOUT pairs - let lastLogin = null; - for (const l of logs) { - const t = l.time ? new Date(l.time) : null; - const type = String(l.loggedType || "").toUpperCase(); + // Compute time spent on each project + let totalWorkMs = 0; + const projectWork = {}; - if (type === "LOGIN" && t) { - lastLogin = t; - } else if (type === "LOGOUT" && t && lastLogin) { - const diff = (t.getTime() - lastLogin.getTime()) / 1000; - if (diff > 0) totalSeconds += diff; - lastLogin = null; - } - } - // If currently logged in, add time since last login - if (lastLogin) { - totalSeconds += (Date.now() - lastLogin.getTime()) / 1000; + let currentLogin = null; + let currentProject = null; + + for (const log of logs) { + const time = new Date(log.time); + if (log.loggedType === "LOGIN") { + currentLogin = time; + currentProject = log.projectId?._id?.toString() || null; + } else if (log.loggedType === "LOGOUT" && currentLogin) { + const diff = time - currentLogin; + totalWorkMs += diff; + if (currentProject) { + projectWork[currentProject] = (projectWork[currentProject] || 0) + diff; } + currentLogin = null; + currentProject = null; } } - const hours = Math.floor(totalSeconds / 3600); - const minutes = Math.floor((totalSeconds % 3600) / 60); - const seconds = Math.floor(totalSeconds % 60); - const totalHours = `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; + // Format helper + const 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`; + }; - // Project / client info (fall back to empty array) - const projects = va.clientIds || va.projects || []; - - const name = `${va.userDetails?.firstName || ""}`.trim() + (va.userDetails?.lastName ? ` ${va.userDetails.lastName}` : ""); - const email = va.userDetails?.email || null; + // Build per-project breakdown + const projects = await Promise.all( + Object.entries(projectWork).map(async ([projectId, ms]) => { + const project = await ProjectModel.findById(projectId).lean(); + return { + projectId, + projectName: project?.name || "Unknown Project", + totalWorkMs: ms, + totalWorkTime: formatHMS(ms), + }; + }) + ); + // Final Response return res.status(200).json({ success: true, - data: { - id: va._id, - name: name || null, - email, - active, - totalHours, - lastLog: lastLog || null, - projects - } + va: { + id: vaUser._id, + name: `${vaUser.userDetails.firstName || ""} ${vaUser.userDetails.lastName || ""}`.trim(), + email: vaUser.userDetails.email, + status: vaUser.status, + schedule: vaUser.schedule, + tracking: vaUser.tracking, + totalAssignedProjects: vaUser.projectIds?.length || 0, + }, + totalWorkMs, + totalWorkTime: formatHMS(totalWorkMs), + projects, }); } catch (error) { - console.error("va_username_callback error:", error); + console.error("Error in va_username_callback:", error); return res.status(500).json({ success: false, message: "Internal Server Error" }); } } diff --git a/src/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index 7e1398f..2baadd2 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -32,6 +32,10 @@ vaRoutes.get("/profile", authValidator, va_profile_callback); // Get VA Profile vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update VA Profile vaRoutes.get("/:userName", va_username_callback); // VA Username Callback + + + + // Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31 vaRoutes.get("/search", va_filter_search); @@ -42,12 +46,16 @@ 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/:from?/:to?", va_projects_log_list_function); -// GET Windows Tab Logged Data for VA IN Between Login and LOGOUT -vaRoutes.get("/report/project/:projectId/:from?/:to?", va_projects_report_function); +// GET Windows Tab Logged Data for VA IN Between Login and LOGOUT +vaRoutes.get("/report/project/:projectId/:from?/:to?", authValidator, va_projects_report_function); + + + +// Getting Project List assigned to VA should be ACTIVE and add total logged hours added by VA +vaRoutes.get("/list/projects/:projectId?/:from?/:to?", authValidator, va_projects_log_list_function); + vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project vaRoutes.post("/log/project/track", authValidator, va_log_track_work_callback); // Track Window Tabs Logs Data