diff --git a/src/controllers/va/vaLog/vaLogTrackWork.controller.js b/src/controllers/va/vaLog/vaLogTrackWork.controller.js index cda71f0..4a78a0c 100644 --- a/src/controllers/va/vaLog/vaLogTrackWork.controller.js +++ b/src/controllers/va/vaLog/vaLogTrackWork.controller.js @@ -167,44 +167,70 @@ async function va_log_track_work_callback(req, res) { return res.status(400).json({ success: false, message: "No logs to track" }); } - // 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(); + logs[0].from = logEntry.time; // Ensure the first log's 'from' matches the main log time - // 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", - }); - } + 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); - const createdTracks = await Promise.all( - logs.map(async (log) => { - const createdTrack = await WorkTrackModel.create({ - vaId, - projectId, - logId, - titlew: log.title, - appName: log.appName, - url: log.url, - from: log.from, - to: log.to, // Fixed: Changed from duplicate "from" to "to" - totalTime: log.totalTime, - }); + // 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; - }) - ); + // return createdTrack; + // }) + // ); + + 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, + from: logs[0].from, + to: logs[logs.length -1].to, + totalTime: totalLogTime, + logs: logs + }); return res.status(201).json({ success: true, message: "Work tracking created", - data: createdTracks + data: createdTrack }); // } 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 } )