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
async function va_register(req, res) {
try {
const payload = req.body ? JSON.parse(req.body) : {};
const payload = req.body || {};
const { userDetails } = payload;
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";
function formatHMS(ms) {
@ -11,50 +12,63 @@ function formatHMS(ms) {
async function va_projects_log_list_function(req, res) {
try {
const { projectId, from, to } = req.params;
const accessToken = req.headers.token;
if (!projectId) {
return res.status(400).json({ success: false, message: "Missing projectId" });
if (!accessToken) {
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 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);
// 🧩 Fetch Project
const project = await ProjectModel.findById(projectId).populate("vaAssigned", {
"userDetails.firstName": 1,
"userDetails.lastName": 1,
"userDetails.email": 1,
status: 1
});
let projectList = [];
if (projectId) {
const project = await ProjectModel.findById(projectId);
if (!project) {
return res.status(404).json({ success: false, message: "Project not found" });
}
if (!project) {
return res.status(404).json({ success: false, message: "Project not found" });
// Check if VA is assigned to this project
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 (!vaList.length) {
return res.status(200).json({ success: true, message: "No VAs assigned to this project", data: [] });
if (!projectList.length) {
return res.status(200).json({ success: true, message: "No assigned projects found", data: [] });
}
let totalProjectWorkMs = 0;
let activeCount = 0;
const finalReport = [];
const vaReport = [];
// 🔁 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
for (const project of projectList) {
const logs = await LoggedModel.find({
vaId,
projectId,
projectId: project._id,
time: { $gte: fromDate, $lte: toDate }
}).sort({ time: 1 }).lean();
})
.sort({ time: 1 })
.lean();
let totalWorkMs = 0;
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) {
totalWorkMs += new Date() - new Date(currentLogin.time);
}
totalProjectWorkMs += totalWorkMs;
// 🟢 Determine Active / Away status
const lastLog = logs[logs.length - 1];
let activityStatus = "AWAY";
if (lastLog?.loggedType === "LOGIN") {
activityStatus = "ACTIVE";
activeCount++;
}
const activityStatus = lastLog?.loggedType === "LOGIN" ? "ACTIVE" : "AWAY";
vaReport.push({
vaId,
name: `${va.userDetails?.firstName || ""} ${va.userDetails?.lastName || ""}`.trim(),
email: vaEmail,
status: va.status,
finalReport.push({
projectId: project._id,
projectName: project.name,
activityStatus,
totalWorkTime: formatHMS(totalWorkMs),
totalWorkMs,
totalLogs: logs.length
});
}
// 📊 Prepare Summary
const totalVAs = vaReport.length;
const awayCount = totalVAs - activeCount;
// Summary for current VA
const totalProjects = finalReport.length;
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({
success: true,
projectId,
projectName: project.name,
totalVAs,
activeVAs: activeCount,
awayVAs: awayCount,
totalProjectWorkTime: formatHMS(totalProjectWorkMs),
totalProjectWorkMs,
data: vaReport
vaId,
vaName: `${vaUser.userDetails.firstName} ${vaUser.userDetails.lastName}`,
totalProjects,
activeProjects,
awayProjects,
totalWorkTime: formatHMS(totalMs),
totalWorkMs: totalMs,
data: finalReport
});
} 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
async function va_username_callback(req, res) {
try {
const { userName } = req.params;
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)
const va = await VAModel.findOne({
// Find VA by firstName or lastName or email (depending on how you store username)
const vaUser = await VAModel.findOne({
$or: [
{ _id: userName },
{ "userDetails.email": userName },
{ "userDetails.userName": userName },
{ "userDetails.firstName": 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" });
}
// Latest log entry to determine active state
const lastLog = await LoggedModel.findOne({ vaId: va._id }).sort({ time: -1 }).lean().exec();
const active = lastLog ? String(lastLog.loggedType).toUpperCase() === "LOGIN" : false;
const vaId = vaUser._id;
// Fetch all logs for this VA (ordered ascending) to compute logged hours
const logs = await LoggedModel.find({ vaId: va._id }).sort({ time: 1 }).lean().exec();
// Get all logs for this VA
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) {
// If logs have an explicit duration field, sum it
if (logs[0].duration !== undefined) {
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();
// Compute time spent on each project
let totalWorkMs = 0;
const projectWork = {};
if (type === "LOGIN" && t) {
lastLogin = t;
} else if (type === "LOGOUT" && t && lastLogin) {
const diff = (t.getTime() - lastLogin.getTime()) / 1000;
if (diff > 0) totalSeconds += diff;
lastLogin = null;
}
}
// If currently logged in, add time since last login
if (lastLogin) {
totalSeconds += (Date.now() - lastLogin.getTime()) / 1000;
let currentLogin = null;
let currentProject = null;
for (const log of logs) {
const time = new Date(log.time);
if (log.loggedType === "LOGIN") {
currentLogin = time;
currentProject = log.projectId?._id?.toString() || null;
} else if (log.loggedType === "LOGOUT" && currentLogin) {
const diff = time - currentLogin;
totalWorkMs += diff;
if (currentProject) {
projectWork[currentProject] = (projectWork[currentProject] || 0) + diff;
}
currentLogin = null;
currentProject = null;
}
}
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = Math.floor(totalSeconds % 60);
const totalHours = `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
// Format helper
const 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`;
};
// Project / client info (fall back to empty array)
const projects = va.clientIds || va.projects || [];
const name = `${va.userDetails?.firstName || ""}`.trim() + (va.userDetails?.lastName ? ` ${va.userDetails.lastName}` : "");
const email = va.userDetails?.email || null;
// Build per-project breakdown
const projects = await Promise.all(
Object.entries(projectWork).map(async ([projectId, ms]) => {
const project = await ProjectModel.findById(projectId).lean();
return {
projectId,
projectName: project?.name || "Unknown Project",
totalWorkMs: ms,
totalWorkTime: formatHMS(ms),
};
})
);
// Final Response
return res.status(200).json({
success: true,
data: {
id: va._id,
name: name || null,
email,
active,
totalHours,
lastLog: lastLog || null,
projects
}
va: {
id: vaUser._id,
name: `${vaUser.userDetails.firstName || ""} ${vaUser.userDetails.lastName || ""}`.trim(),
email: vaUser.userDetails.email,
status: vaUser.status,
schedule: vaUser.schedule,
tracking: vaUser.tracking,
totalAssignedProjects: vaUser.projectIds?.length || 0,
},
totalWorkMs,
totalWorkTime: formatHMS(totalWorkMs),
projects,
});
} 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" });
}
}

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.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
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/track", authValidator, va_log_track_work_callback); // Track Window Tabs Logs Data