import { ClientModel, ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js"; import jwt from "jsonwebtoken"; function formatHoursToReadable(hours) { const wholeHours = Math.floor(hours); const decimalMinutes = (hours - wholeHours) * 60; const wholeMinutes = Math.round(decimalMinutes); if (wholeHours > 0 && wholeMinutes > 0) { return `${wholeHours}h ${wholeMinutes}m`; } else if (wholeHours > 0) { return `${wholeHours}h`; } else { return `${wholeMinutes}m`; } } // Getting client dashboard data - projects with assigned VA details async function client_dashboard_projects(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 clientEmail = decoded.email; // STEP 1: Get client ID from token const clientUser = await ClientModel.findOne({ "userDetails.email": clientEmail }); console.log("Line 38" , clientUser) if (!clientUser) { return res.status(404).json({ success: false, message: "Client not found" }); } const clientId = clientUser._id; // STEP 2: Find all projects of this client const clientProjects = await ProjectModel.find({ clientId: clientId }) .select('name hourlyRate vaAssigned dedicatedHours status') .lean() .exec(); // Get all VA IDs from all projects for activity check const allProjectVaIds = []; clientProjects.forEach(project => { if (project.vaAssigned && project.vaAssigned.length > 0) { allProjectVaIds.push(...project.vaAssigned); } }); // STEP 3: Get all VAs assigned to these projects const allAssignedVAs = await VAModel.find({ _id: { $in: allProjectVaIds.vaId } }) .select('userDetails.firstName userDetails.lastName userDetails.email status') .lean() .exec(); // Create a map for quick VA lookup const vaMap = allAssignedVAs.reduce((acc, va) => { acc[va._id.toString()] = va; return acc; }, {}); // STEP 4: Check VA activity per project const currentTime = new Date(); // Format projects with assigned VA details const projectsFormatted = await Promise.all(clientProjects.map(async (project) => { const assignedVAIds = project.vaAssigned || []; // Get VA details and check activity for each assigned VA const assignedVADetails = await Promise.all(assignedVAIds.map(async (vaId) => { const va = vaMap[vaId.toString()]; if (!va) return null; // Check if this VA is currently active on THIS specific project const latestLog = await LoggedModel.findOne({ vaId: vaId, projectId: project._id, loggedType: "LOGIN" }).sort({ time: -1 }).lean(); let isActive = false; if (latestLog) { const loginTime = new Date(latestLog.time); const hoursSinceLastActivity = (currentTime - loginTime) / (1000 * 60 * 60); if (hoursSinceLastActivity <= 24) { isActive = true; } } return { id: va._id, firstName: va.userDetails?.firstName || '', lastName: va.userDetails?.lastName || '', email: va.userDetails?.email || '', status: va.status || '', isActive: isActive }; })); // Filter out null entries and count active VAs const validVADetails = assignedVADetails.filter(Boolean); const activeVAsCount = validVADetails.filter(va => va.isActive).length; return { id: project._id, name: project.name, hourlyRate: project.hourlyRate, vaAssigned: validVADetails.length, activeVAs: activeVAsCount, dedicatedHours: formatHoursToReadable(project.dedicatedHours) || "0h", status: project.status || '', assignedVADetails: validVADetails }; })); // Calculate summary counts const totalProjects = projectsFormatted.length; // Count total unique VAs across all projects const allVAsAcrossProjects = new Set(); projectsFormatted.forEach(project => { project.assignedVADetails.forEach(va => { allVAsAcrossProjects.add(va.id); }); }); // Count total active VAs (across all projects) const totalActiveVAs = new Set(); projectsFormatted.forEach(project => { project.assignedVADetails.forEach(va => { if (va.isActive) { totalActiveVAs.add(va.id); } }); }); const projectsWithActiveVAs = projectsFormatted.filter(project => project.activeVAs > 0).length; return res.status(200).json({ success: true, data: { projects: projectsFormatted }, counts: { totalProjects: totalProjects, totalVAs: allVAsAcrossProjects.size, activeVAs: totalActiveVAs.size, projectsWithActiveVAs: projectsWithActiveVAs } }); } catch (error) { console.error("Client dashboard projects error:", error); return res.status(500).json({ success: false, message: "Internal Server Error", error: error.message }); } } export { client_dashboard_projects };