diff --git a/public/css/dashboard.css b/public/css/dashboard.css
index c56f08c..7ffb796 100644
--- a/public/css/dashboard.css
+++ b/public/css/dashboard.css
@@ -961,4 +961,4 @@ tr[data-vaid]:hover {
.detail-item span {
text-align: left;
}
-}
\ No newline at end of file
+}
diff --git a/src/controllers/client/dashboard/clientDashboardProjects.controller.js b/src/controllers/client/dashboard/clientDashboardProjects.controller.js
new file mode 100644
index 0000000..d7b77cc
--- /dev/null
+++ b/src/controllers/client/dashboard/clientDashboardProjects.controller.js
@@ -0,0 +1,100 @@
+import { ClientModel, ProjectModel, VAModel } from "../../../models/vaModel.js";
+import jwt from "jsonwebtoken";
+
+function formatHoursToReadable(hours) {
+ const wholeHours = Math.floor(hours);
+ const decimalMinutes = (hours - wholeHours) * 60;
+ const wholeMinutes = Math.round(decimalMinutes);
+
+ if (wholeHours > 0 && wholeMinutes > 0) {
+ return `${wholeHours}h ${wholeMinutes}m`;
+ } else if (wholeHours > 0) {
+ return `${wholeHours}h`;
+ } else {
+ return `${wholeMinutes}m`;
+ }
+}
+
+// Getting client dashboard data - all VAs and projects
+async function client_dashboard_projects(req, res) {
+ 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" });
+ }
+
+ // Get all VA IDs and Project IDs assigned to this client
+ const clientVaIds = clientUser.vaIds || [];
+ const clientProjectIds = clientUser.projectIds || [];
+
+ // Get all VAs assigned to this client (with required fields only)
+ const allAssignedVAs = await VAModel.find({
+ _id: { $in: clientVaIds }
+ })
+ .select('userDetails.firstName userDetails.lastName userDetails.email status')
+ .lean()
+ .exec();
+
+ // Get All Projects of client with required fields
+ const clientProjects = await ProjectModel.find({
+ _id: { $in: clientProjectIds }
+ })
+ .select('name hourlyRate vaAssigned dedicatedHours status')
+ .lean()
+ .exec();
+
+ // Format VAs with only required fields
+ const vasFormatted = allAssignedVAs.map((va) => ({
+ id: va._id,
+ firstName: va.userDetails?.firstName || '',
+ lastName: va.userDetails?.lastName || '',
+ email: va.userDetails?.email || '',
+ status: va.status || ''
+ }));
+
+ // Format projects with only required fields
+ const projectsFormatted = clientProjects.map((project) => ({
+ id: project._id,
+ name: project.name,
+ hourlyRate: project.hourlyRate,
+ vaAssigned: project.vaAssigned.length || 0,
+ dedicatedHours: formatHoursToReadable(project.dedicatedHours) || 0,
+ status: project.status || ''
+ }));
+
+ return res.status(200).json({
+ success: true,
+ data: {
+ vas: vasFormatted,
+ projects: projectsFormatted
+ },
+ counts: {
+ totalVAs: vasFormatted.length,
+ totalProjects: projectsFormatted.length
+ }
+ });
+
+ } catch (error) {
+ console.error("Client dashboard error:", error);
+ return res.status(500).json({
+ success: false,
+ message: "Internal Server Error",
+ error: error.message
+ });
+ }
+}
+
+export { client_dashboard_projects };
\ No newline at end of file
diff --git a/src/routes/v1/client.routes.js b/src/routes/v1/client.routes.js
index 79bc669..382d722 100644
--- a/src/routes/v1/client.routes.js
+++ b/src/routes/v1/client.routes.js
@@ -11,6 +11,7 @@ import { clientAuthValidator } from "../../middlewares/auth.middleware.js";
import { client_dashboard } from "./../../controllers/client/dashboard/clientDashboard.controller.js";
import { client_dashboard_va_log } from "../../controllers/client/dashboard/clientDashboardVaLog.controller.js";
import { client_verify_email } from "../../controllers/client/vaClientEmailVerify.controller.js";
+import { client_dashboard_projects } from "../../controllers/client/dashboard/clientDashboardProjects.controller.js"
// clients routes
clientRoutes.post("/register", va_register_client); // Register a new VA
@@ -26,7 +27,8 @@ clientRoutes.get("/:clientId/projects/:projectId", va_clients_projects_callback)
// dashboard API's
clientRoutes.get("/dashboard", clientAuthValidator, client_dashboard); // VA Clients Projects Callback
-clientRoutes.post("/dashboard/va-report", clientAuthValidator, client_dashboard_va_log); // VA Clients Projects Callback
+clientRoutes.post("/dashboard/va-report", clientAuthValidator, client_dashboard_va_log); // VA details
+clientRoutes.get("/dashboard-projects", clientAuthValidator, client_dashboard_projects); // VA Clients Projects Callback
diff --git a/src/server.js b/src/server.js
index 17cc29a..83552a0 100644
--- a/src/server.js
+++ b/src/server.js
@@ -159,6 +159,16 @@ app.get('/client/virtual-assistants', async (req, res) => {
}
});
+app.get('/client/projects', async (req, res) => {
+ try {
+ // const vaData = await virtualAssistantService.getClientVAs(req.user.id);
+ res.render('client/dashboard-projects');
+ } catch (error) {
+ console.error('Projects error:', error);
+ res.status(500).render('client/dashboard-projects', { error: 'Failed to load virtual assistants' });
+ }
+});
+
app.get("/live/:projectId/:vaId", async (req, res) => {
const { projectId, vaId } = req.params;
diff --git a/views/client/dashboard-dashboard.ejs b/views/client/dashboard-dashboard.ejs
index 640438c..0dede01 100644
--- a/views/client/dashboard-dashboard.ejs
+++ b/views/client/dashboard-dashboard.ejs
@@ -29,12 +29,12 @@
Virtual Assistants
-
+
+
+
TT
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Name |
+ Hourly Rate |
+ Total VA Assigned |
+ Dedicated Hours |
+ Status |
+
+
+
+
+
+
+
+
+
+
+
No Projects
+
You don't have any projects yet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Name |
+ Email |
+ Status |
+
+
+
+
+
+
+
+
+
+
+
No Virtual Assistants
+
You don't have any virtual assistants assigned yet.
+
+
+
+
+
+
+
+
+
+