166 lines
5.8 KiB
JavaScript
166 lines
5.8 KiB
JavaScript
import jwt from "jsonwebtoken";
|
|
import { VAModel, ProjectModel, LoggedModel, ClientModel } from "../../../models/vaModel.js";
|
|
|
|
// Helper function to parse date string in DD-MM-YYYY format
|
|
function parseDate(dateString) {
|
|
if (!dateString) return null;
|
|
|
|
const parts = dateString.split('-');
|
|
if (parts.length !== 3) return null;
|
|
|
|
const day = parseInt(parts[0], 10);
|
|
const month = parseInt(parts[1], 10) - 1; // Months are 0-indexed in JavaScript
|
|
const year = parseInt(parts[2], 10);
|
|
|
|
// Validate date components
|
|
if (isNaN(day) || isNaN(month) || isNaN(year)) return null;
|
|
if (day < 1 || day > 31 || month < 0 || month > 11 || year < 1900) return null;
|
|
|
|
return new Date(year, month, day);
|
|
}
|
|
|
|
// Getting logged hours in a specific project of VA
|
|
async function client_dashboard_va_log(req, res) {
|
|
console.log(req)
|
|
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;
|
|
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;
|
|
const clientProjectIds = clientUser.projectIds || [];
|
|
|
|
const payload = req.body || {};
|
|
const { vaId } = payload;
|
|
|
|
// const va = await VAModel.findOne({
|
|
// _id: vaId,
|
|
// clientIds: clientID
|
|
// });
|
|
|
|
// if (!va) {
|
|
// return res.status(403).json({
|
|
// success: false,
|
|
// message: "VA not found or not assigned to this client"
|
|
// });
|
|
// }
|
|
|
|
let logs = await LoggedModel.find({
|
|
vaId,
|
|
projectId: { $in: clientProjectIds },
|
|
})
|
|
.sort({ time: 1 })
|
|
.populate("projectId", "name") // attach project name
|
|
.lean();
|
|
|
|
if (!logs.length) {
|
|
return res.status(200).json({
|
|
success: true,
|
|
message: "No logs found in given range",
|
|
data: [],
|
|
totalWorkTime: "00:00:00",
|
|
});
|
|
}
|
|
|
|
let totalWorkSeconds = 0;
|
|
const projectWork = {}; // key: projectId -> seconds
|
|
|
|
// Group logs by date (YYYY-MM-DD format)
|
|
const logsByDate = {};
|
|
logs.forEach(log => {
|
|
const date = new Date(log.time).toISOString().split('T')[0]; // YYYY-MM-DD
|
|
if (!logsByDate[date]) {
|
|
logsByDate[date] = [];
|
|
}
|
|
logsByDate[date].push(log);
|
|
});
|
|
|
|
// Process each day separately
|
|
for (const [date, dayLogs] of Object.entries(logsByDate)) {
|
|
// Check if this day has any adjustment logs
|
|
const hasAdjustmentLogs = dayLogs.some(log => log.isAdjustment === true);
|
|
|
|
let dayWorkSeconds = 0;
|
|
let lastLogin = null;
|
|
let lastProjectId = null;
|
|
|
|
// If day has adjustment logs, only process adjustment logs
|
|
// If no adjustment logs, process all logs for that day
|
|
const logsToProcess = hasAdjustmentLogs
|
|
? dayLogs.filter(log => log.isAdjustment === true)
|
|
: dayLogs;
|
|
|
|
// Sort logs by time for proper pairing (already sorted by query, but ensure)
|
|
logsToProcess.sort((a, b) => new Date(a.time) - new Date(b.time));
|
|
|
|
// Calculate work duration for this day
|
|
for (const log of logsToProcess) {
|
|
const time = new Date(log.time);
|
|
|
|
if (log.loggedType === "LOGIN") {
|
|
lastLogin = time;
|
|
lastProjectId = log.projectId?._id?.toString() || null;
|
|
} else if (log.loggedType === "LOGOUT" && lastLogin) {
|
|
const diffSec = (time - lastLogin) / 1000;
|
|
dayWorkSeconds += diffSec;
|
|
|
|
if (lastProjectId) {
|
|
projectWork[lastProjectId] = (projectWork[lastProjectId] || 0) + diffSec;
|
|
}
|
|
|
|
// reset after each pair
|
|
lastLogin = null;
|
|
lastProjectId = null;
|
|
}
|
|
}
|
|
|
|
totalWorkSeconds += dayWorkSeconds;
|
|
}
|
|
|
|
// 🕓 Convert total seconds to hh:mm:ss
|
|
const formatTime = (seconds) => {
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = Math.floor(seconds % 60);
|
|
return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
|
};
|
|
|
|
// 🧠 Build project-wise array
|
|
const projectDetails = await Promise.all(
|
|
Object.entries(projectWork).map(async ([projectId, sec]) => {
|
|
const project = await ProjectModel.findById(projectId).lean();
|
|
return {
|
|
projectId,
|
|
projectName: project?.name || "Unknown Project",
|
|
totalTime: formatTime(sec),
|
|
};
|
|
})
|
|
);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
totalWorkTime: formatTime(totalWorkSeconds),
|
|
projects: projectDetails,
|
|
});
|
|
|
|
} catch (error) {
|
|
console.log(error)
|
|
return res.status(500).send("Internal Server Error");
|
|
}
|
|
}
|
|
|
|
export { client_dashboard_va_log }; |