From 6cd02b0f85e1af759b6b3645fd62a73c37933a75 Mon Sep 17 00:00:00 2001 From: Aakash Date: Wed, 17 Dec 2025 18:15:17 +0530 Subject: [PATCH] 6:15 --- .../dashboard/clientDashboard.controller.js | 2 +- .../clientDashboardGetVaLogs.controller.js | 193 ++++++++++ src/routes/v1/client.routes.js | 3 + src/server.js | 9 + views/client/dashboard-va-logs.ejs | 334 ++++++++++++++++++ views/client/dashboard-virtual-assistants.ejs | 2 +- 6 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 src/controllers/client/dashboard/clientDashboardGetVaLogs.controller.js create mode 100644 views/client/dashboard-va-logs.ejs diff --git a/src/controllers/client/dashboard/clientDashboard.controller.js b/src/controllers/client/dashboard/clientDashboard.controller.js index c07734e..a0da500 100644 --- a/src/controllers/client/dashboard/clientDashboard.controller.js +++ b/src/controllers/client/dashboard/clientDashboard.controller.js @@ -315,7 +315,7 @@ async function client_dashboard(req, res) { }; }); - const dayNumber = getDayNumber(logTime); + const dayNumber = getDayNumber(); // Calculate dashboard statistics const totalHoursLogged = clientProjects.reduce((sum, p) => sum + (p.totalLoggedHours || 0), 0); diff --git a/src/controllers/client/dashboard/clientDashboardGetVaLogs.controller.js b/src/controllers/client/dashboard/clientDashboardGetVaLogs.controller.js new file mode 100644 index 0000000..06c1f2d --- /dev/null +++ b/src/controllers/client/dashboard/clientDashboardGetVaLogs.controller.js @@ -0,0 +1,193 @@ +async function clientDashboardGetVaLogsController(req, res) { + try { + const clientId = req.body.clientId; + const clientUser = await ClientModel.findById(clientId); + if (!clientUser) { + return res.status(404).json({ success: false, message: "Client not found" }); + } + + const { vaId } = req.params; + const vaUser = await VAModel.findById(vaId); + if (!vaUser) { + return res.status(404).json({ success: false, message: "Virtual Assistant not found" }); + } + + // Get the last 15 days + const endDate = new Date(); + endDate.setHours(23, 59, 59, 999); // End of day + const startDate = new Date(); + startDate.setDate(startDate.getDate() - 15); + startDate.setHours(0, 0, 0, 0); // Start of day + + // Get shared projects + const sharedProjects = await ProjectModel.find({ + clientId: clientUser._id, + 'vaAssigned.vaId': vaId, + status: "ACTIVE" + }).select('name hourlyRate dedicatedHours vaWorkingScheduleStart vaWorkingScheduleEnd isFlexibleAllowed'); + + if (!sharedProjects || sharedProjects.length === 0) { + return res.status(200).json({ + success: true, + message: "No shared projects found", + data: {} + }); + } + + const projectLogs = {}; + const projectIds = sharedProjects.map(p => p._id); + + // Get all work tracks for the last 15 days + const workTracks = await WorkTrackModel.find({ + vaId: vaId, + projectId: { $in: projectIds }, + from: { $gte: startDate }, + to: { $lte: endDate } + }) + .sort({ from: -1 }) + .lean(); + + // Process each project + for (const project of sharedProjects) { + const projectIdStr = project._id.toString(); + + // Filter tracks for this project + const projectTracks = workTracks.filter(track => + track.projectId.toString() === projectIdStr + ); + + // Group tracks by date + const tracksByDate = {}; + projectTracks.forEach(track => { + const dateKey = new Date(track.from).toISOString().split('T')[0]; + if (!tracksByDate[dateKey]) { + tracksByDate[dateKey] = []; + } + tracksByDate[dateKey].push(track); + }); + + // Create daily logs for the last 15 days + const dailyLogs = {}; + let totalProjectHours = 0; + let totalProjectTracks = 0; + + // Generate dates for the last 15 days + for (let i = 0; i < 15; i++) { + const date = new Date(); + date.setDate(date.getDate() - i); + date.setHours(0, 0, 0, 0); + const dateStr = date.toISOString().split('T')[0]; + + const dayTracks = tracksByDate[dateStr] || []; + const totalTimeDay = dayTracks.reduce((sum, track) => sum + (track.totalTime || 0), 0); + + // Calculate productive time vs idle time if logs are available + let productiveTime = 0; + let idleTime = 0; + + if (dayTracks.length > 0) { + dayTracks.forEach(track => { + if (track.logs && Array.isArray(track.logs)) { + track.logs.forEach(log => { + if (log.status === 'productive' || log.status === 'working') { + productiveTime += log.duration || 0; + } else if (log.status === 'idle' || log.status === 'away') { + idleTime += log.duration || 0; + } + }); + } + }); + } + + dailyLogs[dateStr] = { + logs: dayTracks.map(track => ({ + id: track._id, + from: track.from, + to: track.to, + totalTime: track.totalTime, + logs: track.logs || [], + createdAt: track.createdAt, + updatedAt: track.updatedAt + })), + summary: { + totalTracks: dayTracks.length, + totalTime: totalTimeDay, + productiveTime: productiveTime, + idleTime: idleTime, + efficiency: totalTimeDay > 0 ? (productiveTime / totalTimeDay) * 100 : 0 + } + }; + + totalProjectHours += totalTimeDay; + totalProjectTracks += dayTracks.length; + } + + projectLogs[projectIdStr] = { + projectDetails: { + id: project._id, + name: project.name, + hourlyRate: project.hourlyRate, + schedule: { + start: project.vaWorkingScheduleStart, + end: project.vaWorkingScheduleEnd, + isFlexibleAllowed: project.isFlexibleAllowed + } + }, + dailyLogs: dailyLogs, + summary: { + totalWorkTracks: totalProjectTracks, + totalHours: totalProjectHours, + averageDailyHours: totalProjectHours / 15, + daysWithActivity: Object.values(dailyLogs).filter(day => day.logs.length > 0).length, + lastActivity: projectTracks.length > 0 + ? new Date(projectTracks[0].from) + : null + } + }; + } + + // Calculate overall statistics + const allTracks = workTracks; + const totalHoursAllProjects = allTracks.reduce((sum, track) => sum + (track.totalTime || 0), 0); + + return res.status(200).json({ + success: true, + message: "VA logs retrieved successfully", + data: { + vaDetails: { + id: vaUser._id, + name: `${vaUser.userDetails?.firstName || ''} ${vaUser.userDetails?.lastName || ''}`.trim(), + email: vaUser.userDetails?.email, + status: vaUser.status, + schedule: vaUser.schedule + }, + dateRange: { + startDate: startDate.toISOString().split('T')[0], + endDate: endDate.toISOString().split('T')[0], + days: 15 + }, + projectLogs: projectLogs, + summary: { + totalProjects: sharedProjects.length, + totalWorkTracks: allTracks.length, + totalHours: totalHoursAllProjects, + averageDailyHours: totalHoursAllProjects / 15, + projectsWithActivity: Object.values(projectLogs) + .filter(project => project.summary.totalWorkTracks > 0).length + } + } + }); + + } catch (error) { + console.error('Error fetching VA logs:', error); + return res.status(500).json({ + success: false, + message: 'Failed to load VA logs', + error: process.env.NODE_ENV === 'development' ? error.message : undefined + }); + } +} + +export { + clientDashboardGetVaLogsController +}; \ No newline at end of file diff --git a/src/routes/v1/client.routes.js b/src/routes/v1/client.routes.js index 97f95e1..56b6ed1 100644 --- a/src/routes/v1/client.routes.js +++ b/src/routes/v1/client.routes.js @@ -14,6 +14,7 @@ import { client_verify_email } from "../../controllers/client/vaClientEmailVerif import { client_dashboard_projects } from "../../controllers/client/dashboard/clientDashboardProjects.controller.js"; import { client_logout_callback } from "../../controllers/client/dashboard/clientLogout.controller.js"; import { va_tips_delete, va_tips_get, va_tips_manage_function, va_tips_update } from "../../controllers/va/vaTips/vaTipsManage.controller.js"; +import { clientDashboardGetVaLogsController } from "../../controllers/client/dashboard/clientDashboardGetVaLogs.controller.js"; // clients routes clientRoutes.post("/register", va_register_client); // Register a new VA @@ -28,6 +29,8 @@ clientRoutes.get("/tips", clientAuthValidator, va_tips_get); clientRoutes.patch("/tips/:tipId", clientAuthValidator, va_tips_update); clientRoutes.delete("/tips/:tipId", clientAuthValidator, va_tips_delete); +clientRoutes.get("/vaLogsData/:vaId", clientAuthValidator, clientDashboardGetVaLogsController); + // clientRoutes.get("/:clientID?", va_clients_callback); // VA Clients Callback for getting all VA's or specific client by ID (NC) clientRoutes.get("/:clientId/projects/:projectId", va_clients_projects_callback); // VA Clients Projects Callback (NC) diff --git a/src/server.js b/src/server.js index 73a8f10..5c27bb6 100644 --- a/src/server.js +++ b/src/server.js @@ -175,6 +175,15 @@ app.get('/client/virtual-assistants/manage-tips', async (req, res) => { } }); +app.get('/client/virtual-assistants/view-logs', async (req, res) => { + try { + res.render('client/dashboard-va-logs'); + } catch (error) { + console.error('Projects error:', error); + res.status(500).render('client/dashboard-va-logs', { error: 'Failed to load profile' }); + } +}); + app.get("/live/:projectId/:vaId", async (req, res) => { const { projectId, vaId } = req.params; diff --git a/views/client/dashboard-va-logs.ejs b/views/client/dashboard-va-logs.ejs new file mode 100644 index 0000000..0a98804 --- /dev/null +++ b/views/client/dashboard-va-logs.ejs @@ -0,0 +1,334 @@ + + + + + + + DocVA Client | Dashboard + + + + + + + +
+ +
+ +
+
+ +
+
+ Back +
+
+
+ + + + + + + + + + + + + + +
TitleApp NameLinkFromToTotal Time
+
+ +
+
+
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/views/client/dashboard-virtual-assistants.ejs b/views/client/dashboard-virtual-assistants.ejs index 6dfddb5..be1809c 100644 --- a/views/client/dashboard-virtual-assistants.ejs +++ b/views/client/dashboard-virtual-assistants.ejs @@ -448,7 +448,7 @@ const activeProject = va?.lastProject?.name ? va.lastProject.name : '-'; const workedTime = formatTime(va.currentSessionDuration); const projectsAssigned = va.projectsAssigned || 0; - const viewLink = va.isCurrentlyActive ? `Live Tracking` : '

Logs

'; + const viewLink = va.isCurrentlyActive ? `Live Tracking` : `Logs`; return `