import jwt from "jsonwebtoken"; import { VAModel, ProjectModel, LoggedModel } from "../../../models/vaModel.js"; // Helper function to parse date string in DD-MM-YYYY format function parseDate(dateString) { if (!dateString) return null; const parts = dateString.split('-'); if (parts.length !== 3) return null; const day = parseInt(parts[0], 10); const month = parseInt(parts[1], 10) - 1; // Months are 0-indexed in JavaScript const year = parseInt(parts[2], 10); // Validate date components if (isNaN(day) || isNaN(month) || isNaN(year)) return null; if (day < 1 || day > 31 || month < 0 || month > 11 || year < 1900) return null; return new Date(year, month, day); } // Getting logged hours in a specific project of VA async function va_log_work_report_callback(req, res) { try { 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" }); } 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; const payload = req.body || {}; const { fromDate, toDate } = payload; let start = fromDate ? parseDate(fromDate) : new Date(); let end = toDate ? parseDate(toDate) : new Date(); if ((fromDate && (!start || isNaN(start.getTime()))) || (toDate && (!end || isNaN(end.getTime())))) { return res.status(400).json({ success: false, message: "Invalid Date format. Use DD-MM-YYYY" }); } if (!fromDate && !toDate) { // If no dates provided, default to today start.setHours(0, 0, 0, 0); end.setHours(23, 59, 59, 999); } else { // If dates are provided, set proper time boundaries if (fromDate) start.setHours(0, 0, 0, 0); if (toDate) end.setHours(23, 59, 59, 999); } let logs = await LoggedModel.find({ vaId, time: { $gte: start, $lte: end }, }) .sort({ time: 1 }) .populate("projectId", "name") // attach project name .lean(); if (!logs.length) { return res.status(200).json({ success: true, message: "No logs found in given range", data: [], totalWorkTime: "00:00:00", }); } let totalWorkSeconds = 0; const projectWork = {}; // key: projectId -> seconds // Group logs by date (YYYY-MM-DD format) const logsByDate = {}; logs.forEach(log => { const date = new Date(log.time).toISOString().split('T')[0]; // YYYY-MM-DD if (!logsByDate[date]) { logsByDate[date] = []; } logsByDate[date].push(log); }); // Process each day separately for (const [date, dayLogs] of Object.entries(logsByDate)) { // Check if this day has any adjustment logs const hasAdjustmentLogs = dayLogs.some(log => log.isAdjustment === true); let dayWorkSeconds = 0; let lastLogin = null; let lastProjectId = null; // If day has adjustment logs, only process adjustment logs // If no adjustment logs, process all logs for that day const logsToProcess = hasAdjustmentLogs ? dayLogs.filter(log => log.isAdjustment === true) : dayLogs; // Sort logs by time for proper pairing (already sorted by query, but ensure) logsToProcess.sort((a, b) => new Date(a.time) - new Date(b.time)); // Calculate work duration for this day for (const log of logsToProcess) { const time = new Date(log.time); if (log.loggedType === "LOGIN") { lastLogin = time; lastProjectId = log.projectId?._id?.toString() || null; } else if (log.loggedType === "LOGOUT" && lastLogin) { const diffSec = (time - lastLogin) / 1000; dayWorkSeconds += diffSec; if (lastProjectId) { projectWork[lastProjectId] = (projectWork[lastProjectId] || 0) + diffSec; } // reset after each pair lastLogin = null; lastProjectId = null; } } totalWorkSeconds += dayWorkSeconds; } // 🕓 Convert total seconds to hh:mm:ss const formatTime = (seconds) => { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`; }; // 🧠 Build project-wise array const projectDetails = await Promise.all( Object.entries(projectWork).map(async ([projectId, sec]) => { const project = await ProjectModel.findById(projectId).lean(); return { projectId, projectName: project?.name || "Unknown Project", totalTime: formatTime(sec), }; }) ); return res.status(200).json({ success: true, from: start, to: end, totalWorkTime: formatTime(totalWorkSeconds), projects: projectDetails, logs, // optional raw logs for debugging }); } catch (error) { console.log(error) return res.status(500).send("Internal Server Error"); } } export { va_log_work_report_callback };