230 lines
8.9 KiB
JavaScript
230 lines
8.9 KiB
JavaScript
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 };
|