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..cda71f0 100644 --- a/src/controllers/va/vaLog/vaLogTrackWork.controller.js +++ b/src/controllers/va/vaLog/vaLogTrackWork.controller.js @@ -26,140 +26,147 @@ 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" }); + if (logs.length === 0) { + 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 @@ -173,104 +180,94 @@ async function va_log_track_work_callback(req, res) { }); } - if (!projectId || !logId || !title || !appName) { - return res.status(400).json({ success: false, message: "Missing Props" }); - } - // Handle POST → create entry - if (req.method === "POST") { - const dailyCheck = await checkDailyHoursAndLogout(vaId, vaEmail, projectId, project); - - 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 createdTrack = await WorkTrackModel.create({ - vaId, - projectId, - logId, - title, - appName, - url, - from: Date.now(), - }); + // 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, + }); + + return createdTrack; + }) + ); 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: createdTracks }); - } + // } - // 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/routes/v1/va.routes.js b/src/routes/v1/va.routes.js index bdc991e..bde238d 100644 --- a/src/routes/v1/va.routes.js +++ b/src/routes/v1/va.routes.js @@ -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

+
+
+
+
+ + +
+ +
+