Fixed API issues

This commit is contained in:
Aakash Mittal 2025-11-10 12:35:00 +05:30
parent 7d73299822
commit 9f086d55ba
4 changed files with 149 additions and 113 deletions

View File

@ -4,7 +4,7 @@ import { VAModel } from "../../../models/vaModel.js";
// Register a new VA // Register a new VA
async function va_register(req, res) { async function va_register(req, res) {
try { try {
const payload = req.body ? JSON.parse(req.body) : {}; const payload = req.body || {};
const { userDetails } = payload; const { userDetails } = payload;
if (!userDetails || !userDetails.email || !userDetails.firstName || !userDetails.password) { if (!userDetails || !userDetails.email || !userDetails.firstName || !userDetails.password) {

View File

@ -1,3 +1,4 @@
import jwt from "jsonwebtoken";
import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js"; import { ProjectModel, VAModel, LoggedModel } from "../../../models/vaModel.js";
function formatHMS(ms) { function formatHMS(ms) {
@ -11,50 +12,63 @@ function formatHMS(ms) {
async function va_projects_log_list_function(req, res) { async function va_projects_log_list_function(req, res) {
try { try {
const { projectId, from, to } = req.params; const { projectId, from, to } = req.params;
const accessToken = req.headers.token;
if (!projectId) { if (!accessToken) {
return res.status(400).json({ success: false, message: "Missing projectId" }); return res.status(401).json({ success: false, message: "Missing token" });
} }
// 📅 Date filtering 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;
// Date filtering
const now = new Date(); const now = new Date();
const fromDate = from && from !== "undefined" ? new Date(from) : new Date(now.setHours(0, 0, 0, 0)); 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(); const toDate = to && to !== "undefined" ? new Date(to) : new Date();
toDate.setHours(23, 59, 59, 999); toDate.setHours(23, 59, 59, 999);
// 🧩 Fetch Project let projectList = [];
const project = await ProjectModel.findById(projectId).populate("vaAssigned", { if (projectId) {
"userDetails.firstName": 1, const project = await ProjectModel.findById(projectId);
"userDetails.lastName": 1, if (!project) {
"userDetails.email": 1, return res.status(404).json({ success: false, message: "Project not found" });
status: 1 }
});
if (!project) { // Check if VA is assigned to this project
return res.status(404).json({ success: false, message: "Project not found" }); const isAssigned = project.vaAssigned.some(id => id.toString() === vaId.toString());
if (!isAssigned) {
return res.status(403).json({ success: false, message: "VA not assigned to this project" });
}
projectList = [project];
} else {
projectList = await ProjectModel.find({ vaAssigned: vaId });
} }
const vaList = project.vaAssigned || []; if (!projectList.length) {
if (!vaList.length) { return res.status(200).json({ success: true, message: "No assigned projects found", data: [] });
return res.status(200).json({ success: true, message: "No VAs assigned to this project", data: [] });
} }
let totalProjectWorkMs = 0; const finalReport = [];
let activeCount = 0;
const vaReport = []; for (const project of projectList) {
// 🔁 Loop through each VA assigned to the project
for (const va of vaList) {
const vaId = va._id;
const vaEmail = va.userDetails?.email;
// 🔎 Get all login/logout logs for this VA in the date range
const logs = await LoggedModel.find({ const logs = await LoggedModel.find({
vaId, vaId,
projectId, projectId: project._id,
time: { $gte: fromDate, $lte: toDate } time: { $gte: fromDate, $lte: toDate }
}).sort({ time: 1 }).lean(); })
.sort({ time: 1 })
.lean();
let totalWorkMs = 0; let totalWorkMs = 0;
let currentLogin = null; let currentLogin = null;
@ -68,46 +82,40 @@ async function va_projects_log_list_function(req, res) {
} }
} }
// if still logged in (no logout yet) — count up to now // still logged in → count till now
if (currentLogin) { if (currentLogin) {
totalWorkMs += new Date() - new Date(currentLogin.time); totalWorkMs += new Date() - new Date(currentLogin.time);
} }
totalProjectWorkMs += totalWorkMs;
// 🟢 Determine Active / Away status
const lastLog = logs[logs.length - 1]; const lastLog = logs[logs.length - 1];
let activityStatus = "AWAY"; const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY";
if (lastLog?.loggedType === "LOGIN") {
activityStatus = "ACTIVE";
activeCount++;
}
vaReport.push({ finalReport.push({
vaId, projectId: project._id,
name: `${va.userDetails?.firstName || ""} ${va.userDetails?.lastName || ""}`.trim(), projectName: project.name,
email: vaEmail,
status: va.status,
activityStatus, activityStatus,
totalWorkTime: formatHMS(totalWorkMs), totalWorkTime: formatHMS(totalWorkMs),
totalWorkMs, totalWorkMs,
totalLogs: logs.length
}); });
} }
// 📊 Prepare Summary // Summary for current VA
const totalVAs = vaReport.length; const totalProjects = finalReport.length;
const awayCount = totalVAs - activeCount; const activeProjects = finalReport.filter(r => r.activityStatus === "ACTIVE").length;
const awayProjects = totalProjects - activeProjects;
const totalMs = finalReport.reduce((acc, r) => acc + r.totalWorkMs, 0);
res.status(200).json({ res.status(200).json({
success: true, success: true,
projectId, vaId,
projectName: project.name, vaName: `${vaUser.userDetails.firstName} ${vaUser.userDetails.lastName}`,
totalVAs, totalProjects,
activeVAs: activeCount, activeProjects,
awayVAs: awayCount, awayProjects,
totalProjectWorkTime: formatHMS(totalProjectWorkMs), totalWorkTime: formatHMS(totalMs),
totalProjectWorkMs, totalWorkMs: totalMs,
data: vaReport data: finalReport
}); });
} catch (err) { } catch (err) {

View File

@ -1,89 +1,109 @@
import { VAModel, LoggedModel } from "../../models/vaModel.js"; import { VAModel, ProjectModel, LoggedModel } from "../../models/vaModel.js";
// Getting VA details by username // Getting VA details by username
async function va_username_callback(req, res) { async function va_username_callback(req, res) {
try { try {
const { userName } = req.params; const { userName } = req.params;
if (!userName) { if (!userName) {
return res.status(400).json({ success: false, message: "Missing userName param" }); return res.status(400).json({ success: false, message: "Missing username" });
} }
// Find VA by common identifier fields (id, email, username, first/last name) // Find VA by firstName or lastName or email (depending on how you store username)
const va = await VAModel.findOne({ const vaUser = await VAModel.findOne({
$or: [ $or: [
{ _id: userName },
{ "userDetails.email": userName }, { "userDetails.email": userName },
{ "userDetails.userName": userName },
{ "userDetails.firstName": userName }, { "userDetails.firstName": userName },
{ "userDetails.lastName": userName }, { "userDetails.lastName": userName },
{ "userDetails.fullName": userName } ],
] })
}).lean().exec(); .populate("projectIds", "name totalLoggedHours")
.lean();
if (!va) { if (!vaUser) {
return res.status(404).json({ success: false, message: "VA not found" }); return res.status(404).json({ success: false, message: "VA not found" });
} }
// Latest log entry to determine active state const vaId = vaUser._id;
const lastLog = await LoggedModel.findOne({ vaId: va._id }).sort({ time: -1 }).lean().exec();
const active = lastLog ? String(lastLog.loggedType).toUpperCase() === "LOGIN" : false;
// Fetch all logs for this VA (ordered ascending) to compute logged hours // Get all logs for this VA
const logs = await LoggedModel.find({ vaId: va._id }).sort({ time: 1 }).lean().exec(); const logs = await LoggedModel.find({ vaId }).sort({ time: 1 }).populate("projectId", "name").lean();
let totalSeconds = 0; if (!logs.length) {
return res.status(200).json({
success: true,
message: "No logs found for this VA",
data: {
va: vaUser,
totalWorkTime: "00h 00m 00s",
totalWorkMs: 0,
projects: [],
},
});
}
if (logs.length) { // Compute time spent on each project
// If logs have an explicit duration field, sum it let totalWorkMs = 0;
if (logs[0].duration !== undefined) { const projectWork = {};
totalSeconds = logs.reduce((sum, l) => sum + (Number(l.duration) || 0), 0);
} else {
// Otherwise compute from LOGIN/LOGOUT pairs
let lastLogin = null;
for (const l of logs) {
const t = l.time ? new Date(l.time) : null;
const type = String(l.loggedType || "").toUpperCase();
if (type === "LOGIN" && t) { let currentLogin = null;
lastLogin = t; let currentProject = null;
} else if (type === "LOGOUT" && t && lastLogin) {
const diff = (t.getTime() - lastLogin.getTime()) / 1000; for (const log of logs) {
if (diff > 0) totalSeconds += diff; const time = new Date(log.time);
lastLogin = null; if (log.loggedType === "LOGIN") {
} currentLogin = time;
} currentProject = log.projectId?._id?.toString() || null;
// If currently logged in, add time since last login } else if (log.loggedType === "LOGOUT" && currentLogin) {
if (lastLogin) { const diff = time - currentLogin;
totalSeconds += (Date.now() - lastLogin.getTime()) / 1000; totalWorkMs += diff;
if (currentProject) {
projectWork[currentProject] = (projectWork[currentProject] || 0) + diff;
} }
currentLogin = null;
currentProject = null;
} }
} }
const hours = Math.floor(totalSeconds / 3600); // Format helper
const minutes = Math.floor((totalSeconds % 3600) / 60); const formatHMS = (ms) => {
const seconds = Math.floor(totalSeconds % 60); const totalSeconds = Math.floor(ms / 1000);
const totalHours = `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; const h = Math.floor(totalSeconds / 3600);
const m = Math.floor((totalSeconds % 3600) / 60);
const s = totalSeconds % 60;
return `${h}h ${m}m ${s}s`;
};
// Project / client info (fall back to empty array) // Build per-project breakdown
const projects = va.clientIds || va.projects || []; const projects = await Promise.all(
Object.entries(projectWork).map(async ([projectId, ms]) => {
const name = `${va.userDetails?.firstName || ""}`.trim() + (va.userDetails?.lastName ? ` ${va.userDetails.lastName}` : ""); const project = await ProjectModel.findById(projectId).lean();
const email = va.userDetails?.email || null; return {
projectId,
projectName: project?.name || "Unknown Project",
totalWorkMs: ms,
totalWorkTime: formatHMS(ms),
};
})
);
// Final Response
return res.status(200).json({ return res.status(200).json({
success: true, success: true,
data: { va: {
id: va._id, id: vaUser._id,
name: name || null, name: `${vaUser.userDetails.firstName || ""} ${vaUser.userDetails.lastName || ""}`.trim(),
email, email: vaUser.userDetails.email,
active, status: vaUser.status,
totalHours, schedule: vaUser.schedule,
lastLog: lastLog || null, tracking: vaUser.tracking,
projects totalAssignedProjects: vaUser.projectIds?.length || 0,
} },
totalWorkMs,
totalWorkTime: formatHMS(totalWorkMs),
projects,
}); });
} catch (error) { } catch (error) {
console.error("va_username_callback error:", error); console.error("Error in va_username_callback:", error);
return res.status(500).json({ success: false, message: "Internal Server Error" }); return res.status(500).json({ success: false, message: "Internal Server Error" });
} }
} }

View File

@ -32,6 +32,10 @@ vaRoutes.get("/profile", authValidator, va_profile_callback); // Get VA Profile
vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update VA Profile vaRoutes.patch("/profile", authValidator, va_profile_update_callback); // Update VA Profile
vaRoutes.get("/:userName", va_username_callback); // VA Username Callback vaRoutes.get("/:userName", va_username_callback); // VA Username Callback
// Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31 // Example: GET /va/search?name=john&email=@example.com&from=2025-01-01&to=2025-01-31
vaRoutes.get("/search", va_filter_search); vaRoutes.get("/search", va_filter_search);
@ -42,12 +46,16 @@ vaRoutes.get("/clients/:clientId/projects/:projectId", va_clients_projects_callb
// Getting Project List assigned to VA should be ACTIVE and add total logged hours added by VA
vaRoutes.get("/list/project/:projectId/:from?/:to?", va_projects_log_list_function);
// GET Windows Tab Logged Data for VA IN Between Login and LOGOUT
vaRoutes.get("/report/project/:projectId/:from?/:to?", va_projects_report_function);
// GET Windows Tab Logged Data for VA IN Between Login and LOGOUT
vaRoutes.get("/report/project/:projectId/:from?/:to?", authValidator, va_projects_report_function);
// Getting Project List assigned to VA should be ACTIVE and add total logged hours added by VA
vaRoutes.get("/list/projects/:projectId?/:from?/:to?", authValidator, va_projects_log_list_function);
vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project vaRoutes.post("/log/project", authValidator, va_project_log_callback); // VA Logged IN / Logged Out in project
vaRoutes.post("/log/project/track", authValidator, va_log_track_work_callback); // Track Window Tabs Logs Data vaRoutes.post("/log/project/track", authValidator, va_log_track_work_callback); // Track Window Tabs Logs Data