Fixed API issues
This commit is contained in:
parent
7d73299822
commit
9f086d55ba
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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" });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue