va-tracker-middleware/src/controllers/client/dashboard/clientDashboardProjects.con...

176 lines
6.3 KiB
JavaScript

import { ClientModel, ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
import jwt from "jsonwebtoken";
import getDayNumber from "../../../utils/getDayNumber.js";
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 });
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;
const dayNumber = getDayNumber();
return {
id: project._id,
name: project.name,
hourlyRate: project.hourlyRate,
vaAssigned: validVADetails.length,
activeVAs: activeVAsCount,
dedicatedHours: formatHoursToReadable(project.dedicatedHours[dayNumber]) || "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 };