diff --git a/public/css/va-register.css b/public/css/va-register.css index 6818b17..2ffa9d5 100644 --- a/public/css/va-register.css +++ b/public/css/va-register.css @@ -670,6 +670,13 @@ body { background-color: rgba(220, 38, 38, 0.1) !important; } +.additional-info-heading { + margin-top: 24px; + margin-bottom: 16px; + border-top: 1px solid #e5e7eb; + padding-top: 10px; +} + /* Animations */ @keyframes slideDown { from { diff --git a/public/js/va-register.js b/public/js/va-register.js index 7ce3f38..a6ecd35 100644 --- a/public/js/va-register.js +++ b/public/js/va-register.js @@ -617,11 +617,11 @@ } else { const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1]; if (paymentMethod === 'payoneer') { - // const payoneerAccount = document.getElementById('payoneerAccount').value; - // if (!payoneerAccount) { - // showError('Please enter your Payoneer account number', 'payoneerAccount', step); - // isValid = false; - // } + const payoneerAccount = document.getElementById('payoneerAccount').value; + if (!payoneerAccount) { + showError('Please enter your Payoneer account number', 'payoneerAccount', step); + isValid = false; + } } else if (paymentMethod === 'veem') { const veemAccount = document.getElementById('veemAccount').value; if (!veemAccount) { @@ -703,9 +703,9 @@ const paymentMethod = selectedPayment.getAttribute('onclick').match(/'([^']+)'/)[1]; formData.userDetails.paymentMethod = paymentMethod; - // if (document.getElementById('payoneerAccount').value) { - // formData.userDetails.payoneerAccount = document.getElementById('payoneerAccount').value; - // } + if (document.getElementById('payoneerAccount').value) { + formData.userDetails.payoneerAccount = document.getElementById('payoneerAccount').value; + } if (document.getElementById('veemAccount').value) { formData.userDetails.veemAccount = document.getElementById('veemAccount').value; } @@ -734,7 +734,7 @@ // Show appropriate payment details if (method === 'payoneer') { - // document.getElementById('payoneer-details').style.display = 'block'; + document.getElementById('payoneer-details').style.display = 'block'; } else if (method === 'veem') { document.getElementById('veem-details').style.display = 'block'; } else if (method === 'wise') { diff --git a/src/controllers/va/vaLog/vaLogTrackWork.controller.js b/src/controllers/va/vaLog/vaLogTrackWork.controller.js index 7fb27df..4a78a0c 100644 --- a/src/controllers/va/vaLog/vaLogTrackWork.controller.js +++ b/src/controllers/va/vaLog/vaLogTrackWork.controller.js @@ -26,251 +26,274 @@ function formatHoursToReadable(hours) { } } -// Helper function to check daily hours and create logout log if needed -async function checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, currentSessionId = null, additionalTimeMs = 0) { - try { - const today = new Date(); - const { start, end } = getDayRange(today); +// // Helper function to check daily hours and create logout log if needed +// async function checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, currentSessionId = null, additionalTimeMs = 0) { +// try { +// const today = new Date(); +// const { start, end } = getDayRange(today); - // Get today's work entries for this VA and project - // Exclude the current session if it's already in the database - const query = { - vaId, - projectId, - from: { $gte: start, $lte: end } - }; +// // Get today's work entries for this VA and project +// // Exclude the current session if it's already in the database +// const query = { +// vaId, +// projectId, +// from: { $gte: start, $lte: end } +// }; - if (currentSessionId) { - query._id = { $ne: currentSessionId }; - } +// if (currentSessionId) { +// query._id = { $ne: currentSessionId }; +// } - const todayWorkEntries = await WorkTrackModel.find(query); +// const todayWorkEntries = await WorkTrackModel.find(query); - // Calculate total hours worked today - let totalTodayMs = todayWorkEntries.reduce((total, entry) => { - return total + (entry.totalTime || 0); - }, 0); +// // Calculate total hours worked today +// let totalTodayMs = todayWorkEntries.reduce((total, entry) => { +// return total + (entry.totalTime || 0); +// }, 0); - // Add the additional time from current session if provided - totalTodayMs += additionalTimeMs; +// // Add the additional time from current session if provided +// totalTodayMs += additionalTimeMs; - const totalTodayHours = totalTodayMs / (1000 * 60 * 60); +// const totalTodayHours = totalTodayMs / (1000 * 60 * 60); - // Check if daily dedicated hours are completed - if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) { - // Create automatic logout log - const logoutLog = await LoggedModel.create({ - loggedType: "LOGOUT", - vaId, - vaEmail, - projectId - }); +// // Check if daily dedicated hours are completed +// if (project.dedicatedHours > 0 && totalTodayHours >= project.dedicatedHours) { +// // Create automatic logout log +// const logoutLog = await LoggedModel.create({ +// loggedType: "LOGOUT", +// vaId, +// vaEmail, +// projectId +// }); - try { - const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; - const sfDataSync = { - "_id": logoutLog._id, - "loggedType": "LOGOUT", - "vaId": vaId, - "vaEmail": vaEmail, - "projectId": projectId, - "time": logoutLog.time, - } +// try { +// const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; +// const sfDataSync = { +// "_id": logoutLog._id, +// "loggedType": "LOGOUT", +// "vaId": vaId, +// "vaEmail": vaEmail, +// "projectId": projectId, +// "time": logoutLog.time, +// } - const response = await fetch(salesforceUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - // Add any required headers for Salesforce authentication - // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN - }, - body: JSON.stringify(sfDataSync) - }); +// const response = await fetch(salesforceUrl, { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// // Add any required headers for Salesforce authentication +// // 'Authorization': 'Bearer ' + process.env.SF_ACCESS_TOKEN +// }, +// body: JSON.stringify(sfDataSync) +// }); - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); - } +// if (!response.ok) { +// const errorText = await response.text(); +// throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); +// } - const result = await response.json(); - // console.log("✅ Successfully synced VA data to Salesforce:", result); +// const result = await response.json(); +// // console.log("✅ Successfully synced VA data to Salesforce:", result); - return { - shouldLogout: true, - totalTodayHours: totalTodayHours, - dedicatedHours: project.dedicatedHours, - logoutLogId: logoutLog._id, - SyncSalesforce: result - }; - } catch (error) { - console.error("Failed to sync with Salesforce:", error.message); - console.log(error) +// return { +// shouldLogout: true, +// totalTodayHours: totalTodayHours, +// dedicatedHours: project.dedicatedHours, +// logoutLogId: logoutLog._id, +// SyncSalesforce: result +// }; +// } catch (error) { +// console.error("Failed to sync with Salesforce:", error.message); +// console.log(error) - // Create a custom error to distinguish Salesforce errors - const salesforceError = new Error(`Salesforce sync failed: ${error.message}`); - salesforceError.isSalesforceError = true; - throw salesforceError; - } - } +// // Create a custom error to distinguish Salesforce errors +// const salesforceError = new Error(`Salesforce sync failed: ${error.message}`); +// salesforceError.isSalesforceError = true; +// throw salesforceError; +// } +// } - return { - shouldLogout: false, - totalTodayHours: totalTodayHours, - dedicatedHours: project.dedicatedHours - }; - } catch (error) { - console.error("Error in checkDailyHoursAndLogout:", error); - return { shouldLogout: false, error: error.message }; - } -} +// return { +// shouldLogout: false, +// totalTodayHours: totalTodayHours, +// dedicatedHours: project.dedicatedHours +// }; +// } catch (error) { +// console.error("Error in checkDailyHoursAndLogout:", error); +// return { shouldLogout: false, error: error.message }; +// } +// } // Getting logged hours in a specific project of VA async function va_log_track_work_callback(req, res) { try { - const accessToken = req.headers.token; - if (!accessToken) { - return res.status(401).json({ success: false, message: "Missing token" }); - } + // 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); + // 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" }); - } + // 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 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; + // const vaId = vaUser._id; const payload = req.body || {}; - let { projectId, logId, title, appName, url = null } = payload; - - if (title === "") { - title = "" + let { vaId, projectId, logId, logs } = payload; + + if (!projectId || !logId || !vaId || !Array.isArray(logs)) { + return res.status(400).json({ success: false, message: "Missing Props" }); } + const vaUser = await VAModel.findById(vaId); + if (!vaUser) return res.status(400).json({ success: false, message: "VA not found" }); + const project = await ProjectModel.findById(projectId); if (!project) return res.status(400).json({ success: false, message: "Project not found" }); const logEntry = await LoggedModel.findById(logId); if (!logEntry) return res.status(400).json({ success: false, message: "Log entry not found" }); - // Check last log entry for this VA and project - const lastLog = await LoggedModel.findOne({ vaId, projectId }) - .sort({ time: -1 }) // get the most recent log - .lean(); - - // If the last log is a LOGOUT, prevent new work tracking - if (lastLog.loggedType === "LOGOUT") { - return res.status(400).json({ - success: false, - message: "User has already logged out from this project", - }); + if (logs.length === 0) { + return res.status(400).json({ success: false, message: "No logs to track" }); } - if (!projectId || !logId || !title || !appName) { - return res.status(400).json({ success: false, message: "Missing Props" }); - } + logs[0].from = logEntry.time; // Ensure the first log's 'from' matches the main log time + + const firstFromLogTime = new Date(logs[0].from); + const firstToLogTime = new Date(logs[0].to); + const firstTotalLogTime = Math.abs(firstToLogTime - firstFromLogTime); + logs[0].totalTime = firstTotalLogTime; + + + // // Check last log entry for this VA and project + // const lastLog = await LoggedModel.findOne({ vaId, projectId }) + // .sort({ time: -1 }) // get the most recent log + // .lean(); + + // // If the last log is a LOGOUT, prevent new work tracking + // if (lastLog.loggedType === "LOGOUT") { + // return res.status(400).json({ + // success: false, + // message: "User has already logged out from this project", + // }); + // } // Handle POST → create entry - if (req.method === "POST") { - const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project); + // if (req.method === "POST") { + // const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project); + // const createdTracks = await Promise.all( + // logs.map(async (log) => { + // // Calculate total time from timestamps + // const fromTime = new Date(log.from); + // const toTime = new Date(log.to); + // const totalTime = Math.abs(toTime - fromTime); + // const createdTrack = await WorkTrackModel.create({ + // vaId, + // projectId, + // logId, + // title: log.title, + // appName: log.appName, + // url: log.url, + // from: log.from, + // to: log.to, + // totalTime: totalTime, + // }); + + // return createdTrack; + // }) + // ); - if (dailyCheck.shouldLogout) { - return res.status(400).json({ - success: false, - message: `Cannot start work - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`, - autoLogout: true - }); - } + const fromLogTime = new Date(logs[0].from); + const toLogTime = new Date(logs[logs.length -1].to); + const totalLogTime = Math.abs(toLogTime - fromLogTime); const createdTrack = await WorkTrackModel.create({ vaId, projectId, logId, - title, - appName, - url, - from: Date.now(), + from: logs[0].from, + to: logs[logs.length -1].to, + totalTime: totalLogTime, + logs: logs }); return res.status(201).json({ success: true, - message: "Work tracking started", - data: createdTrack, - dailyHours: { - todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours), - dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours), - remaining: formatHoursToReadable(dailyCheck.dedicatedHours - dailyCheck.totalTodayHours) - } + message: "Work tracking created", + data: createdTrack }); - } + // } - // Handle PATCH → update entry (add 'to' and calculate total time) - if (req.method === "PATCH") { - const { id } = payload; // expecting the workTrack record ID to update - if (!id) { - return res.status(400).json({ success: false, message: "Missing workTrack ID" }); - } + // // Handle PATCH → update entry (add 'to' and calculate total time) + // if (req.method === "PATCH") { + // const { id } = payload; // expecting the workTrack record ID to update + // if (!id) { + // return res.status(400).json({ success: false, message: "Missing workTrack ID" }); + // } - const workEntry = await WorkTrackModel.findById(id); - if (!workEntry) { - return res.status(404).json({ success: false, message: "Work entry not found" }); - } + // const workEntry = await WorkTrackModel.findById(id); + // if (!workEntry) { + // return res.status(404).json({ success: false, message: "Work entry not found" }); + // } - const toTime = new Date(); - const fromTime = new Date(workEntry.from); - const totalTimeMs = toTime - fromTime; + // const toTime = new Date(); + // const fromTime = new Date(workEntry.from); + // const totalTimeMs = toTime - fromTime; - // Optional: store totalTime in minutes or hours instead of raw ms - const hours = Math.floor(totalTimeMs / (1000 * 60 * 60)); - const minutes = Math.floor((totalTimeMs % (1000 * 60 * 60)) / (1000 * 60)); - const seconds = Math.floor((totalTimeMs % (1000 * 60)) / 1000); + // // Optional: store totalTime in minutes or hours instead of raw ms + // const hours = Math.floor(totalTimeMs / (1000 * 60 * 60)); + // const minutes = Math.floor((totalTimeMs % (1000 * 60 * 60)) / (1000 * 60)); + // const seconds = Math.floor((totalTimeMs % (1000 * 60)) / 1000); - const formattedTotalTime = `${hours}h ${minutes}m ${seconds}s`; + // const formattedTotalTime = `${hours}h ${minutes}m ${seconds}s`; - // Check if daily dedicated hours are completed after this work session - const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, id, totalTimeMs); + // // Check if daily dedicated hours are completed after this work session + // const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project, id, totalTimeMs); - workEntry.to = toTime; - workEntry.totalTime = totalTimeMs; // store as number of minutes - await workEntry.save(); + // workEntry.to = toTime; + // workEntry.totalTime = totalTimeMs; // store as number of minutes + // await workEntry.save(); - const response = { - success: true, - message: "Work tracking stopped", - data: { - id: workEntry._id, - from: workEntry.from, - to: workEntry.to, - formattedTotalTime, - totalTimeMs, - sessionHours: totalTimeMs / (1000 * 60 * 60) - }, - dailyHours: { - todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours), - dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours), - remaining: formatHoursToReadable(Math.max(0, dailyCheck.dedicatedHours - dailyCheck.totalTodayHours)) - } - }; + // const response = { + // success: true, + // message: "Work tracking stopped", + // data: { + // id: workEntry._id, + // from: workEntry.from, + // to: workEntry.to, + // formattedTotalTime, + // totalTimeMs, + // sessionHours: totalTimeMs / (1000 * 60 * 60) + // }, + // dailyHours: { + // todayTotal: formatHoursToReadable(dailyCheck.totalTodayHours), + // dailyLimit: formatHoursToReadable(dailyCheck.dedicatedHours), + // remaining: formatHoursToReadable(Math.max(0, dailyCheck.dedicatedHours - dailyCheck.totalTodayHours)) + // } + // }; - // If auto-logout was triggered, add that info to response - if (dailyCheck.shouldLogout) { - response.autoLogout = true; - response.message = `Work tracking stopped. Auto-logout created - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`; - response.logoutLogId = dailyCheck.logoutLogId; - } + // // If auto-logout was triggered, add that info to response + // if (dailyCheck.shouldLogout) { + // response.autoLogout = true; + // response.message = `Work tracking stopped. Auto-logout created - daily hours completed (${formatHoursToReadable(dailyCheck.totalTodayHours)} / ${formatHoursToReadable(dailyCheck.dedicatedHours)})`; + // response.logoutLogId = dailyCheck.logoutLogId; + // } - return res.status(200).json(response); - } + // return res.status(200).json(response); + // } // Fallback for unsupported methods - return res.status(405).json({ success: false, message: "Method not allowed" }); + // return res.status(405).json({ success: false, message: "Method not allowed" }); } catch (error) { return res.status(500).send("Internal Server Error"); } diff --git a/src/controllers/va/vaProjects/vaProjectLog.controller.js b/src/controllers/va/vaProjects/vaProjectLog.controller.js index 45ed2c2..c456407 100644 --- a/src/controllers/va/vaProjects/vaProjectLog.controller.js +++ b/src/controllers/va/vaProjects/vaProjectLog.controller.js @@ -289,6 +289,129 @@ async function va_project_log_callback(req, res) { // }); // } + // If Logout then Update work track entries + if (type === "LOGOUT") { + const { logId } = payload; + + const workTrackData = await WorkTrackModel.find({ + logId: logId + }); + + // console.log(`Found work track data for logout:`, workTrackData); + + if (workTrackData[0]) { + const fromLogTime = new Date(workTrackData[0].from); + const toLogTime = new Date(logTime); + const totalTimeMs = toLogTime - fromLogTime; + + const updatedLogs = workTrackData[0].logs ? [...workTrackData[0].logs] : []; + + if (updatedLogs.length > 0) { + const lastLogIndex = updatedLogs.length - 1; + const lastLogEntry = updatedLogs[lastLogIndex]; + + // Update the last log's 'to' time and recalculate its totalTime + const lastLogFromTime = new Date(lastLogEntry.from); + const lastLogTotalTime = Math.abs(toLogTime - lastLogFromTime); + + updatedLogs[lastLogIndex] = { + ...lastLogEntry, + to: toLogTime, + totalTime: lastLogTotalTime + }; + + // console.log(`Updated last log in array - from: ${lastLogFromTime.toISOString()}, to: ${toLogTime.toISOString()}, totalTime: ${lastLogTotalTime}ms`); + } + + const updateResult = await WorkTrackModel.updateOne( + { _id: workTrackData[0]._id }, + { + $set: { + to: toLogTime, + totalTime: totalTimeMs, + logs: updatedLogs + } + } + ); + + console.log(`Updated work track ${workTrackData[0]._id}:`, { + from: fromLogTime.toISOString(), + to: toLogTime.toISOString(), + totalTime: totalTimeMs, + logsUpdated: updatedLogs.length, + mongodbUpdateResult: updateResult + }); + + // const updatedWorkTrack = await WorkTrackModel.findById(workTrackData[0]._id); + // console.log("Verified updated work track:", { + // to: updatedWorkTrack?.to, + // totalTime: updatedWorkTrack?.totalTime, + // logsCount: updatedWorkTrack?.logs?.length, + // lastLog: updatedWorkTrack?.logs?.[updatedWorkTrack.logs.length - 1] + // }); + } else { + console.log(`No work track found for vaId: ${vaId}, projectId: ${projectId}, logId: ${logId}`); + } + + // Create the logout log entry + const createdLog = await LoggedModel.create({ + loggedType: type, + vaId, + vaEmail, + projectId, + time: logTime + }); + + try { + const salesforceUrl = `${process.env.SF_DOCTRACKER_API_URL}services/apexrest/docva/api/va-project-log`; + const sfData = { + "_id": createdLog._id, + "loggedType": type, + "vaId": vaId, + "vaEmail": vaEmail, + "projectId": projectId, + "time": createdLog.time, + } + + const response = await fetch(salesforceUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(sfData) + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Salesforce API responded with status ${response.status}: ${errorText}`); + } + + const result = await response.json(); + + return res.status(201).json({ + success: true, + message: `${type} Successfully`, + logId: createdLog._id, + SyncSalesforce: result, + timeUsed: receivedTimeString ? 'received' : 'current', + actualTime: createdLog.time, + }); + } catch (error) { + console.error("Failed to sync with Salesforce:", error.message); + console.log(error) + + // Still return success since the logout was recorded locally + return res.status(201).json({ + success: true, + message: `${type} Successfully (Salesforce sync failed)`, + logId: createdLog._id, + timeUsed: receivedTimeString ? 'received' : 'current', + actualTime: createdLog.time, + warning: "Salesforce sync failed" + }); + } + } + // For LOGIN requests, check daily hours (using UTC day range) if (type === "LOGIN") { const { start, end } = getDayRange(); // UTC day range diff --git a/src/controllers/va/vaProjects/vaProjectReport.controller.js b/src/controllers/va/vaProjects/vaProjectReport.controller.js index d266aad..985be53 100644 --- a/src/controllers/va/vaProjects/vaProjectReport.controller.js +++ b/src/controllers/va/vaProjects/vaProjectReport.controller.js @@ -63,6 +63,18 @@ async function va_projects_report_function(req, res) { if (!logs.length) { return res.json({ success: true, message: "No logs found for this project", data: [] }); } + + // Get all work tracks for this VA and project in the date range + const workTracks = await WorkTrackModel.find({ + projectId, + vaId, + $or: [ + { from: { $gte: fromDate, $lte: toDate } }, + { to: { $gte: fromDate, $lte: toDate } } + ] + }) + .sort({ from: 1 }) + .lean(); // Unique VA IDs const vaIds = [...new Set(logs.map(log => log.vaId?.toString()))].filter(Boolean); @@ -73,55 +85,120 @@ async function va_projects_report_function(req, res) { const projectReport = []; - for (const vaId of vaIds) { - const vaLogList = logs.filter(l => l.vaId.toString() === vaId); + for (const currentVaId of vaIds) { + const vaLogList = logs.filter(l => l.vaId.toString() === currentVaId); let sessions = []; let currentLogin = null; + const vaWorkTracks = workTracks.filter(w => w.vaId.toString() === currentVaId); // Pair login → logout for (const entry of vaLogList) { if (entry.loggedType === "LOGIN") { currentLogin = entry; } else if (entry.loggedType === "LOGOUT" && currentLogin) { - sessions.push({ from: currentLogin.time, to: entry.time }); + sessions.push({ from: currentLogin.time, to: entry.time, logId: currentLogin._id }); currentLogin = null; } } // If still logged in, close at end of day if (currentLogin) { - sessions.push({ from: currentLogin.time, to: toDate }); + sessions.push({ from: currentLogin.time, to: toDate, logId: currentLogin._id }); } - const vaDetails = vaUsers.find(v => v._id.toString() === vaId); + const vaDetails = vaUsers.find(v => v._id.toString() === currentVaId); let totalWorkMs = 0; const sessionDetails = []; - for (const s of sessions) { - const workData = await WorkTrackModel.find({ - vaId, - projectId, - from: { $gte: s.from }, - to: { $lte: s.to } - }).sort({ from: 1 }).lean(); + // for (const s of sessions) { + // const workData = await WorkTrackModel.find({ + // vaId, + // projectId, + // from: { $gte: s.from }, + // to: { $lte: s.to } + // }).sort({ from: 1 }).lean(); - const tabs = workData.map(t => ({ - title: t.title, - appName: t.appName, - from: t.from, - to: t.to, - durationMs: t.totalTime, - durationFormatted: formatHMS(t.totalTime), - })); + // const tabs = workData.map(t => ({ + // title: t.title, + // appName: t.appName, + // from: t.from, + // to: t.to, + // durationMs: t.totalTime, + // durationFormatted: formatHMS(t.totalTime), + // })); - const sessionWorkMs = workData.reduce((sum, w) => sum + (w.totalTime || 0), 0); - totalWorkMs += sessionWorkMs; + // const sessionWorkMs = workData.reduce((sum, w) => sum + (w.totalTime || 0), 0); + // totalWorkMs += sessionWorkMs; + + // sessionDetails.push({ + // from: s.from, + // to: s.to, + // totalSessionTime: formatHMS(sessionWorkMs), + // tabs, + // }); + // } + + for (const session of sessions) { + // Find work tracks for this specific session (matching logId) + const sessionWorkTracks = vaWorkTracks.filter( + track => track.logId && track.logId.toString() === session.logId.toString() + ); + + let sessionTabs = []; + let sessionTotalMs = 0; + + // Extract all logs from work tracks for this session + for (const workTrack of sessionWorkTracks) { + if (workTrack.logs && Array.isArray(workTrack.logs)) { + // Process each log entry in the logs array + for (const logEntry of workTrack.logs) { + // Calculate duration for this log entry + const logFrom = new Date(logEntry.from); + const logTo = new Date(logEntry.to || workTrack.to || session.to); + const logDuration = logEntry.totalTime || (logTo - logFrom); + + sessionTotalMs += logDuration; + + sessionTabs.push({ + title: logEntry.title || "", + appName: logEntry.appName || "", + url: logEntry.url || null, + from: logFrom, + to: logTo, + durationMs: logDuration, + durationFormatted: formatHMS(logDuration), + }); + } + } + + // Also add the main work track if it has separate data + if (workTrack.totalTime && workTrack.totalTime > 0) { + sessionTotalMs += workTrack.totalTime; + + // Only add if we don't have logs array data + if (!workTrack.logs || workTrack.logs.length === 0) { + sessionTabs.push({ + title: workTrack.title || "", + appName: workTrack.appName || "", + url: workTrack.url || null, + from: workTrack.from, + to: workTrack.to, + durationMs: workTrack.totalTime, + durationFormatted: formatHMS(workTrack.totalTime), + }); + } + } + } + + totalWorkMs += sessionTotalMs; sessionDetails.push({ - from: s.from, - to: s.to, - totalSessionTime: formatHMS(sessionWorkMs), - tabs, + from: session.from, + to: session.to, + totalSessionTime: formatHMS(sessionTotalMs), + totalSessionMs: sessionTotalMs, + workTracksCount: sessionWorkTracks.length, + tabs: sessionTabs.sort((a, b) => a.from - b.from), // Sort tabs by time }); } diff --git a/src/models/vaModel.js b/src/models/vaModel.js index 3b797f3..30138d6 100644 --- a/src/models/vaModel.js +++ b/src/models/vaModel.js @@ -344,15 +344,6 @@ const vaWorkTrackSchema = new Schema({ type: Types.ObjectId, ref: "TrackLog", }, - title: { - type: String, - }, - appName: { - type: String, - }, - url: { - type: String, - }, from: { type: Date, default: Date.now, @@ -365,6 +356,10 @@ const vaWorkTrackSchema = new Schema({ type: Number, default: 0, }, + logs: { + type: Array, + default: [], + } }, { timestamps: true } ) diff --git a/src/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index 8da9bf8..bde238d 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -30,7 +30,7 @@ import { va_remove_function } from "../../controllers/va/vaProfile/vaDelete.cont import { va_payroll_schedule_function } from "../../controllers/va/vaPayroll/vaPayrollSchedule.controller.js"; import { va_payslip_data_function } from "../../controllers/va/vaPayroll/vaPaySlip.controller.js"; -vaRoutes.get("/", va_callback); // VA Callback for getting active VA +vaRoutes.get("/", va_callback); // VA Callback for getting active VA list vaRoutes.post("/login", va_login_callback); // VA Callback for login VA vaRoutes.get("/logout", authValidator, va_logout_callback); // VA Callback for login VA @@ -76,7 +76,7 @@ vaRoutes.get("/list/projects/:projectId?/:from?/:to?", authValidator, va_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.patch("/log/project/track", authValidator, va_log_track_work_callback); // Update Track Window Tab Closing Time +// vaRoutes.patch("/log/project/track", authValidator, va_log_track_work_callback); // Update Track Window Tab Closing Time vaRoutes.get("/log/workReport/", authValidator, va_log_work_report_callback); // VA Audit Report // All Projects vaRoutes.get("/log/project/:projectId/:from?/:to?", authValidator, va_log_callback); // VA / Project Audit Report // VA filter search: supports query params `name`, `email`, `from`, `to` diff --git a/views/va_register.ejs b/views/va_register.ejs index a3c1435..cbd8866 100644 --- a/views/va_register.ejs +++ b/views/va_register.ejs @@ -261,23 +261,7 @@
-
- - +

Veem

Receive payments via Veem

@@ -309,6 +293,23 @@
+ +

Additional Information

+
+

Payoneer

+

Receive payments via Payoneer

+
+
+
+
+ + +
+ +
+