import jwt from "jsonwebtoken"; 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 sf_token = req.headers["x-auth-token"]; let vaId; let vaUser; let vaEmail; if(sf_token) { vaId = req.body.vaId; vaUser = await VAModel.findById(vaId); if (!vaUser) { return res.status(404).json({ success: false, message: "VA not found" }); } vaEmail = vaUser.userDetails.email } else { const accessToken = req.headers.token; if (!accessToken) { return res.status(401).json({ success: false, message: "Missing token" }); } 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" }); } vaEmail = decoded.email; vaUser = await VAModel.findOne({ "userDetails.email": vaEmail }); if (!vaUser) { return res.status(404).json({ success: false, message: "VA not found" }); } vaId = vaUser._id; } 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, vaId, 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: [] }); } // Get all work tracks for this VA and project in the date range const workTracks = await WorkTrackModel.find({ projectId, vaId, $or: [ { from: { $gte: fromDate, $lte: toDate } }, { to: { $gte: fromDate, $lte: toDate } } ] }) .sort({ from: 1 }) .lean(); // 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 currentVaId of vaIds) { const vaLogList = logs.filter(l => l.vaId.toString() === currentVaId); let sessions = []; let currentLogin = null; const vaWorkTracks = workTracks.filter(w => w.vaId.toString() === currentVaId); // 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, logId: currentLogin._id }); currentLogin = null; } } // If still logged in, close at end of day if (currentLogin) { sessions.push({ from: currentLogin.time, to: toDate, logId: currentLogin._id }); } const vaDetails = vaUsers.find(v => v._id.toString() === currentVaId); 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, // }); // } for (const session of sessions) { // Find work tracks for this specific session (matching logId) const sessionWorkTracks = vaWorkTracks.filter( track => track.logId && track.logId.toString() === session.logId.toString() ); let sessionTabs = []; let sessionTotalMs = 0; // Extract all logs from work tracks for this session for (const workTrack of sessionWorkTracks) { if (workTrack.logs && Array.isArray(workTrack.logs)) { // Process each log entry in the logs array for (const logEntry of workTrack.logs) { // Calculate duration for this log entry const logFrom = new Date(logEntry.from); const logTo = new Date(logEntry.to || workTrack.to || session.to); const logDuration = logEntry.totalTime || (logTo - logFrom); sessionTotalMs += logDuration; sessionTabs.push({ title: logEntry.title || "", appName: logEntry.appName || "", url: logEntry.url || null, from: logFrom, to: logTo, durationMs: logDuration, durationFormatted: formatHMS(logDuration), }); } } // Also add the main work track if it has separate data if (workTrack.totalTime && workTrack.totalTime > 0) { sessionTotalMs += workTrack.totalTime; // Only add if we don't have logs array data if (!workTrack.logs || workTrack.logs.length === 0) { sessionTabs.push({ title: workTrack.title || "", appName: workTrack.appName || "", url: workTrack.url || null, from: workTrack.from, to: workTrack.to, durationMs: workTrack.totalTime, durationFormatted: formatHMS(workTrack.totalTime), }); } } } totalWorkMs += sessionTotalMs; sessionDetails.push({ from: session.from, to: session.to, totalSessionTime: formatHMS(sessionTotalMs), totalSessionMs: sessionTotalMs, workTracksCount: sessionWorkTracks.length, tabs: sessionTabs.sort((a, b) => a.from - b.from), // Sort tabs by time }); } 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 };