diff --git a/package-lock.json b/package-lock.json
index 98c8b48..6b6caba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,7 +20,6 @@
"dotenv": "^16.4.7",
"ejs": "^3.1.10",
"express": "^4.19.2",
- "express-ejs-layouts": "^2.5.1",
"express-minify": "^1.0.0",
"helmet": "^8.1.0",
"html-minifier": "^4.0.0",
@@ -4233,11 +4232,6 @@
"node": ">= 0.10.0"
}
},
- "node_modules/express-ejs-layouts": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/express-ejs-layouts/-/express-ejs-layouts-2.5.1.tgz",
- "integrity": "sha512-IXROv9n3xKga7FowT06n1Qn927JR8ZWDn5Dc9CJQoiiaaDqbhW5PDmWShzbpAa2wjWT1vJqaIM1S6vJwwX11gA=="
- },
"node_modules/express-minify": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/express-minify/-/express-minify-1.0.0.tgz",
diff --git a/package.json b/package.json
index afe4b12..5b7e2e0 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"scripts": {
"start": "nodemon --inspect src/server.js",
"test": "echo \"Error: no test specified\" && exit 1",
- "deploy": "curl -X POST http://35.162.222.18:9000/hooks/deploy-va-middleware",
+ "deploy": "curl -X POST http://35.95.209.161:9000/hooks/deploy-va-middleware",
"build": "npx webpack --mode production",
"dev": "npx webpack --mode development"
},
@@ -25,7 +25,6 @@
"dotenv": "^16.4.7",
"ejs": "^3.1.10",
"express": "^4.19.2",
- "express-ejs-layouts": "^2.5.1",
"express-minify": "^1.0.0",
"helmet": "^8.1.0",
"html-minifier": "^4.0.0",
diff --git a/public/css/dashboard.css b/public/css/dashboard.css
index 4b2c186..1921186 100644
--- a/public/css/dashboard.css
+++ b/public/css/dashboard.css
@@ -15,7 +15,7 @@
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
-
+
--bg-primary: #ffffff;
--bg-secondary: #f8fafc;
--bg-card: #ffffff;
@@ -213,7 +213,6 @@ body {
transform: translateX(-100%);
transition: .3s ease;
}
-
.dashboard-sidebar.active {
transform: translateX(0);
}
@@ -261,54 +260,6 @@ body {
color: var(--text-primary);
}
-/* ===== FIX DUPLICATE SELECT ICON ISSUE ===== */
-select,
-.select-field {
- appearance: none !important;
- -webkit-appearance: none !important;
- -moz-appearance: none !important;
-
- background-image: none !important;
- background-repeat: no-repeat !important;
- background-position: unset !important;
-}
-
-/* Custom select wrapper */
-.select-wrapper {
- position: relative;
- width: 100%;
-}
-
-.void-btn-a {
- background: tomato;
- text-decoration: none;
- color: #ffffff !important;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 5px;
- font-weight: bold;
- text-decoration: none;
- cursor: pointer;
-}
-
-/* Add spacing for icon */
-.select-wrapper select {
- width: 100%;
- padding-right: 44px;
-}
-
-/* Single dropdown icon */
-.select-icon {
- position: absolute;
- right: 14px;
- top: 50%;
- transform: translateY(-50%);
- pointer-events: none;
- color: #aaa;
- font-size: 14px;
-}
-
-
[data-theme="dark"] .sidebar-link:hover {
background-color: var(--color-gray-800);
}
@@ -748,7 +699,6 @@ select,
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
-
to {
opacity: 1;
transform: scale(1) translateY(0);
@@ -994,17 +944,17 @@ tr[data-vaid]:hover {
align-items: flex-start;
gap: 0.5rem;
}
-
+
.project-time {
align-self: flex-start;
}
-
+
.detail-item {
flex-direction: column;
align-items: flex-start;
gap: 0.25rem;
}
-
+
.detail-item span {
text-align: left;
}
@@ -1265,17 +1215,9 @@ tr[data-vaid]:hover {
}
@keyframes pulse {
- 0% {
- opacity: 1;
- }
-
- 50% {
- opacity: 0.5;
- }
-
- 100% {
- opacity: 1;
- }
+ 0% { opacity: 1; }
+ 50% { opacity: 0.5; }
+ 100% { opacity: 1; }
}
.no-vas-message {
@@ -1673,11 +1615,11 @@ tr[data-vaid]:hover {
.form-grid {
grid-template-columns: 1fr;
}
-
+
.modal-actions {
flex-direction: column;
}
-
+
.logout-actions {
flex-direction: column;
}
@@ -1685,49 +1627,41 @@ tr[data-vaid]:hover {
/* Additional Dark Mode Styles for Elements Not Covered */
[data-theme="dark"] {
-
/* Ensure all text colors are properly set */
- h1,
- h2,
- h3,
- h4,
- h5,
- h6 {
+ h1, h2, h3, h4, h5, h6 {
color: var(--text-primary);
}
-
- p,
- span,
- div {
+
+ p, span, div {
color: var(--text-primary);
}
-
+
/* Form elements */
input::placeholder,
textarea::placeholder {
color: var(--text-muted);
}
-
+
/* Selection color */
::selection {
background: rgba(59, 130, 246, 0.3);
color: var(--text-primary);
}
-
+
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
}
-
+
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
-
+
::-webkit-scrollbar-thumb {
background: var(--color-gray-600);
border-radius: 4px;
}
-
+
::-webkit-scrollbar-thumb:hover {
background: var(--color-gray-500);
}
@@ -1774,167 +1708,4 @@ tr[data-vaid]:hover {
.va-action:hover {
transform: scale(1);
-}
-
-
-/* ================================
- PROFILE DROPDOWN (GLASS STYLE)
-================================ */
-.header-profile {
- position: relative;
- display: flex;
- align-items: center;
-}
-
-.profile-trigger {
- display: flex;
- align-items: center;
- gap: 0.5rem;
-
- padding: 0.70rem 0.75rem;
-
- border-radius: 999px;
-
- cursor: pointer;
-
- /* NEW */
- border: 1px solid var(--border-color);
- background: linear-gradient(135deg, rgba(255, 0, 150, 0.3), rgba(0, 204, 255, 0.3));
-
- transition:
- background-color 0.2s ease,
- border-color 0.2s ease,
- box-shadow 0.2s ease;
-}
-
-/* Hover effect */
-.profile-trigger:hover {
- background-color: rgba(0, 0, 0, 0.06);
- border-color: var(--primary-color);
-}
-
-/* Active (clicked/open) state */
-.profile-trigger.active {
- background-color: rgba(0, 123, 255, 0.1);
- border-color: var(--primary-color);
- box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.15);
-}
-
-
-[data-theme="dark"] .profile-trigger:hover {
- background-color: rgba(255, 255, 255, 0.08);
-}
-
-/* Avatar */
-.profile-avatar {
- width: 36px;
- height: 36px;
- border-radius: 50%;
- object-fit: cover;
- border: 2px solid #3b82f6;
-}
-
-/* Name */
-.profile-name {
- font-size: 0.875rem;
- font-weight: 500;
- color: var(--text-color, #111827);
-}
-
-[data-theme="dark"] .profile-name {
- color: #e5e7eb;
-}
-
-.profile-trigger i {
- font-size: 0.7rem;
- opacity: 0.7;
-}
-
-/* ================================
- DROPDOWN MENU
-================================ */
-.profile-menu {
- position: absolute;
- top: calc(100% + 0.45rem);
- right: 0;
- width: 240px;
- border-radius: 12px;
- backdrop-filter: blur(16px);
- background: rgba(255, 255, 255, 0.85);
- border: 1px solid var(--border-color);
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
- display: none;
- flex-direction: column;
- overflow: hidden;
- z-index: 50;
-}
-
-[data-theme="dark"] .profile-menu {
- background: rgba(30, 30, 30, 0.9);
-}
-
-/* Show menu on hover */
-.profile-menu {
- display: none;
-}
-
-.profile-menu.active {
- display: flex;
-}
-
-
-/* Menu items */
-.profile-menu a,
-.logout-btn {
- padding: 0.75rem 1rem;
- display: flex;
- align-items: center;
- gap: 0.75rem;
- font-size: 0.875rem;
- color: var(--text-color, #111827);
- background: none;
- border: none;
- cursor: pointer;
- text-decoration: none;
- transition: background-color 0.15s ease;
-}
-
-.profile-menu a:hover,
-.logout-btn:hover {
- background-color: rgba(0, 0, 0, 0.05);
-}
-
-[data-theme="dark"] .profile-menu a,
-[data-theme="dark"] .logout-btn {
- color: #e5e7eb;
-}
-
-[data-theme="dark"] .profile-menu a:hover,
-[data-theme="dark"] .logout-btn:hover {
- background-color: rgba(255, 255, 255, 0.08);
-}
-
-/* Divider */
-.menu-divider {
- height: 1px;
- background: var(--border-color);
- margin: 0.25rem 0;
-}
-
-/* Logout */
-.logout-btn {
- color: #dc2626;
-}
-
-.logout-btn i {
- color: #dc2626;
-}
-
-/* ================================
- RESPONSIVE
-================================ */
-@media (max-width: 768px) {
- .profile-name {
- display: none;
- }
}
\ No newline at end of file
diff --git a/public/images/favicon.ico b/public/images/favicon.ico
deleted file mode 100644
index 582df3a..0000000
Binary files a/public/images/favicon.ico and /dev/null differ
diff --git a/public/images/favicon.svg b/public/images/favicon.svg
deleted file mode 100644
index feb5a41..0000000
--- a/public/images/favicon.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/js/common/auth.js b/public/js/common/auth.js
deleted file mode 100644
index 825bc31..0000000
--- a/public/js/common/auth.js
+++ /dev/null
@@ -1,29 +0,0 @@
-window.getAuthToken = function () {
- return localStorage.getItem("knoptpq");
-};
-
-window.fetchWithAuth = async function (url, options = {}) {
- const token = getAuthToken();
-
- if (!token) {
- window.location.href = "/client/login";
- return;
- }
-
- const res = await fetch(url, {
- ...options,
- headers: {
- token,
- "Content-Type": "application/json",
- ...options.headers,
- },
- });
-
- if (res.status === 401) {
- localStorage.clear();
- window.location.href = "/client/login";
- return;
- }
-
- return res;
-};
diff --git a/public/js/common/header.js b/public/js/common/header.js
deleted file mode 100644
index 7939feb..0000000
--- a/public/js/common/header.js
+++ /dev/null
@@ -1,29 +0,0 @@
-document.addEventListener('DOMContentLoaded', () => {
- const profileTrigger = document.querySelector('.profile-trigger');
- const profileMenu = document.querySelector('.profile-menu');
-
- if (!profileTrigger || !profileMenu) return;
-
- // Toggle on click
- profileTrigger.addEventListener('click', (e) => {
- e.stopPropagation();
- profileMenu.classList.toggle('active');
- });
-
- // Close on outside click
- document.addEventListener('click', () => {
- profileMenu.classList.remove('active');
- });
-
- // Prevent closing when clicking inside menu
- profileMenu.addEventListener('click', (e) => {
- e.stopPropagation();
- });
-
- // Close on ESC
- document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape') {
- profileMenu.classList.remove('active');
- }
- });
-});
\ No newline at end of file
diff --git a/public/js/common/logout.js b/public/js/common/logout.js
deleted file mode 100644
index 76289c7..0000000
--- a/public/js/common/logout.js
+++ /dev/null
@@ -1,23 +0,0 @@
-function openLogoutModal() {
- document.getElementById("logoutModal").style.display = "flex";
- document.body.style.overflow = "hidden";
-}
-
-function closeLogoutModal() {
- document.getElementById("logoutModal").style.display = "none";
- document.body.style.overflow = "auto";
-}
-
-async function handleLogout() {
- try {
- await fetchWithAuth("/api/v1/client/logout", { method: "POST" });
- } finally {
- localStorage.clear();
- window.location.href = "/client/login";
- }
-}
-
-document.addEventListener("DOMContentLoaded", () => {
- document.getElementById("logoutButton")?.addEventListener("click", openLogoutModal);
- document.getElementById("confirmLogoutBtn")?.addEventListener("click", handleLogout);
-});
diff --git a/public/js/common/sidebar.js b/public/js/common/sidebar.js
deleted file mode 100644
index c6922e5..0000000
--- a/public/js/common/sidebar.js
+++ /dev/null
@@ -1,90 +0,0 @@
-const currentPath = window.location.pathname;
-document.querySelectorAll('.nav-button').forEach(link => {
- if (link.getAttribute('href') === currentPath) {
- link.classList.add('active');
- } else {
- link.classList.remove('active');
- }
- });
-
-
- document.querySelectorAll('.sidebar-link').forEach(link => {
- if (link.getAttribute('href') === currentPath) {
- link.classList.add('active');
- } else {
- link.classList.remove('active');
- }
- });
-
-// Auth helpers
-function getAuthToken() {
- return localStorage.getItem("knoptpq");
-}
-
-async function fetchWithAuth(url, options = {}) {
- const token = getAuthToken();
- if (!token) {
- window.location.href = "/client/login";
- return;
- }
-
- const res = await fetch(url, {
- ...options,
- headers: {
- "token": token,
- "Content-Type": "application/json",
- ...options.headers,
- }
- });
-
- if (res.status === 401) {
- localStorage.clear();
- window.location.href = "/client/login";
- return;
- }
-
- return res;
-}
-
-// Logout modal
-function openLogoutModal() {
- document.getElementById("logoutModal").style.display = "flex";
- document.body.style.overflow = "hidden";
-}
-
-function closeLogoutModal() {
- document.getElementById("logoutModal").style.display = "none";
- document.body.style.overflow = "auto";
-}
-
-// Final logout
-async function handleLogout() {
- try {
- await fetchWithAuth("/api/v1/client/logout", { method: "POST" });
- } catch (e) {
- console.error("Logout error", e);
- } finally {
- localStorage.clear();
- window.location.href = "/client/login";
- }
-}
-
-// DOM bindings
-document.addEventListener("DOMContentLoaded", () => {
-
- // Logout button
- const logoutBtn = document.getElementById("logoutButton");
- if (logoutBtn) logoutBtn.addEventListener("click", openLogoutModal);
-
- // Confirm logout
- const confirmBtn = document.getElementById("confirmLogoutBtn");
- if (confirmBtn) confirmBtn.addEventListener("click", handleLogout);
-
- // Click outside modal
- const modal = document.getElementById("logoutModal");
- if (modal) {
- modal.addEventListener("click", (e) => {
- if (e.target === modal) closeLogoutModal();
- });
- }
-});
diff --git a/public/js/common/theme.js b/public/js/common/theme.js
deleted file mode 100644
index 554ff85..0000000
--- a/public/js/common/theme.js
+++ /dev/null
@@ -1,24 +0,0 @@
-(function () {
- const root = document.documentElement;
-
- function applyTheme(theme) {
- root.setAttribute("data-theme", theme);
- localStorage.setItem("theme", theme);
- }
-
- function getSystemTheme() {
- return window.matchMedia("(prefers-color-scheme: dark)").matches
- ? "dark"
- : "light";
- }
-
- function initTheme() {
- const savedTheme = localStorage.getItem("theme");
- applyTheme(savedTheme || getSystemTheme());
- }
-
- // expose globally
- window.setTheme = applyTheme;
-
- document.addEventListener("DOMContentLoaded", initTheme);
-})();
diff --git a/public/js/pages/dashboard-virtual-assistants.js b/public/js/pages/dashboard-virtual-assistants.js
deleted file mode 100644
index 9a702c1..0000000
--- a/public/js/pages/dashboard-virtual-assistants.js
+++ /dev/null
@@ -1,263 +0,0 @@
-const API_BASE = "/api/v1/client";
-
-// Format time function
-function formatTime(seconds) {
- if (!seconds) return 'No Record Found';
-
- const hours = Math.floor(seconds / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
-
- if (hours > 0) {
- return `${hours}h ${minutes}m`;
- } else if (minutes > 0) {
- return `${minutes}m`;
- } else {
- return 'Just started';
- }
-}
-
-const vaModal = document.getElementById('vaModal');
-
-function openVAModal(va) {
- // Set basic VA info
- document.getElementById('modalVAName').textContent = va.name;
-
- // Set projects
- const projects = va.projects || [];
- document.getElementById('projectsCountBadge').textContent = projects.length;
-
- const projectsTableBody = document.getElementById('modalProjectsTableBody');
- const noProjectsMessage = document.getElementById('modalNoProjects');
-
- if (projects.length > 0) {
- projectsTableBody.innerHTML = projects.map(project => createProjectTableRow(project)).join('');
- projectsTableBody.style.display = '';
- noProjectsMessage.style.display = 'none';
- } else {
- projectsTableBody.innerHTML = '';
- projectsTableBody.style.display = 'none';
- noProjectsMessage.style.display = 'block';
- }
-
- // Set action button
- const modalActions = document.getElementById('modalActions');
- const viewSessionBtn = document.getElementById('viewSessionBtn');
-
- if (va.isCurrentlyActive && va.lastProjectId) {
- viewSessionBtn.href = `/live/${va.lastProjectId}/${va.id}`;
- modalActions.style.display = 'block';
- } else {
- modalActions.style.display = 'none';
- }
-
- // Show modal
- vaModal.style.display = 'flex';
- document.body.style.overflow = 'hidden';
-}
-
-function closeVAModal() {
- vaModal.style.display = 'none';
- document.body.style.overflow = 'auto';
-}
-
-// Create project table row for modal
-function createProjectTableRow(project) {
- const statusClass = project.isCurrentlyWorking ? 'active' : 'inactive';
- const statusText = project.isCurrentlyWorking ? 'Working' : 'Not Active';
-
- return `
-
-
-
-
-
- $${project.hourlyRate}/hr
-
-
- ${project.totalWorkedTime}
-
-
-
- ${statusText}
-
-
-
- `;
-}
-
-// Create VA table row for main table
-function createVATableRow(va) {
- const statusClass = va.isCurrentlyActive ? 'active' : 'inactive';
- const statusText = va.isCurrentlyActive ? 'Active' : 'Inactive';
- 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
';
-
- return `
-
-
-
- ${statusText}
-
-
-
-
-
${va.name}
-
${va.email || 'N/A'}
-
-
-
- ${activeProject}
-
-
- ${projectsAssigned} Projects
-
-
- ${workedTime}
-
-
- ${viewLink}
-
-
- `;
-}
-
-// Load dashboard data
-async function loadDashboardData() {
- try {
- const response = await fetchWithAuth(`${API_BASE}/dashboard`);
- if (!response) return;
- const result = await response.json();
-
- if (result.success) {
- const data = result.data;
- const summary = result.summary;
-
- // Update stats cards
- document.getElementById('activeVaCount').textContent = summary.activeVAs || 0;
- document.getElementById('totalVaCount').textContent = summary.totalVAs || 0;
- document.getElementById('totalProjects').textContent = summary.totalProjects || 0;
-
- // Update VA list count
- document.getElementById('vaListCount').textContent = `${summary.totalVAs || 0} VAs`;
-
- // Update VA table
- const vaTableBody = document.getElementById('vaTableBody');
- const vaEmptyState = document.getElementById('vaEmptyState');
- const allVAs = data.allVAs || [];
-
- if (allVAs.length > 0) {
- vaTableBody.innerHTML = allVAs.map(va => createVATableRow(va)).join('');
- vaTableBody.style.display = '';
- vaEmptyState.style.display = 'none';
-
- // Store VA data globally for modal access
- window.vaData = allVAs;
-
- // Add click event listeners to VA rows
- document.querySelectorAll('.va-row').forEach((row, index) => {
- row.addEventListener('click', function (e) {
- // Don't open modal if clicking on view link
- if (!e.target.closest('.view-link')) {
- const va = allVAs[index];
- openVAModal(va);
- }
- });
- });
- } else {
- vaTableBody.innerHTML = '';
- vaTableBody.style.display = 'none';
- vaEmptyState.style.display = 'block';
- }
-
- } else {
- console.error('Failed to load dashboard data:', result.message);
- showError('Failed to load dashboard data');
- }
-
- } catch (error) {
- console.error('Error loading dashboard data:', error);
- showError('Error loading dashboard data');
- }
-}
-
-// Show error message
-function showError(message) {
- const vaTableBody = document.getElementById('vaTableBody');
- const vaEmptyState = document.getElementById('vaEmptyState');
-
- vaTableBody.style.display = 'none';
- vaEmptyState.innerHTML = `
-
- `;
- vaEmptyState.style.display = 'block';
-}
-
-// Close modal when clicking outside
-vaModal.addEventListener('click', (e) => {
- if (e.target === vaModal) {
- closeVAModal();
- }
-});
-
-// Close modal with escape key
-document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape' && vaModal.style.display === 'flex') {
- closeVAModal();
- }
-});
-
-// Optional: Auto-refresh data every 30 seconds
-setInterval(loadDashboardData, 30000);
-
-// Logout Modal Functions
-function openLogoutModal() {
- const modal = document.getElementById('logoutModal');
- modal.style.display = 'flex';
- document.body.style.overflow = 'hidden';
-}
-
-function closeLogoutModal() {
- const modal = document.getElementById('logoutModal');
- modal.style.display = 'none';
- document.body.style.overflow = 'auto';
-}
-
-document.addEventListener('DOMContentLoaded', function () {
- // Load dashboard data
- loadDashboardData();
-
- // Mobile menu toggle
- const menuToggle = document.querySelector('.mobile-menu-button');
- const sidebar = document.querySelector('.dashboard-sidebar');
- menuToggle.addEventListener('click', () => {
- sidebar.classList.toggle('active');
- });
-
- // Close modal with escape key
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape') {
- closeLogoutModal();
- }
- });
-
- document.getElementById('logoutModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closeLogoutModal();
- }
- });
-
- // Logout button - opens confirmation modal instead of directly logging out
- document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
-
- // Confirm logout button
- document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
-});
-
diff --git a/public/js/pages/dashboard.js b/public/js/pages/dashboard.js
deleted file mode 100644
index 38ae32c..0000000
--- a/public/js/pages/dashboard.js
+++ /dev/null
@@ -1,142 +0,0 @@
-const API_BASE = "/api/v1/client";
-
-/* ------------------ Utils ------------------ */
-
-function formatTime(seconds) {
- if (!seconds) return "No Record";
- const h = Math.floor(seconds / 3600);
- const m = Math.floor((seconds % 3600) / 60);
- return h ? `${h}h ${m}m` : `${m}m`;
-}
-
-/* ------------------ DOM Elements ------------------ */
-
-const vaModal = document.getElementById("vaModal");
-const modalVAName = document.getElementById("modalVAName");
-const modalProjectsTableBody = document.getElementById("modalProjectsTableBody");
-const modalNoProjects = document.getElementById("modalNoProjects");
-
-const vaTableBody = document.getElementById("vaTableBody");
-const vaEmptyState = document.getElementById("vaEmptyState");
-
-/* ------------------ Modal Controls ------------------ */
-
-function openVAModal(va) {
- modalVAName.textContent = va.name || "VA Details";
-
- const projects = va.projects || [];
-
- if (projects.length === 0) {
- modalProjectsTableBody.innerHTML = "";
- modalNoProjects.style.display = "block";
- } else {
- modalNoProjects.style.display = "none";
- modalProjectsTableBody.innerHTML = projects
- .map(createProjectRow)
- .join("");
- }
-
- vaModal.style.display = "flex";
- document.body.style.overflow = "hidden";
-}
-
-function closeVAModal() {
- vaModal.style.display = "none";
- document.body.style.overflow = "auto";
-}
-
-/* Outside click close */
-vaModal.addEventListener("click", (e) => {
- if (e.target === vaModal) closeVAModal();
-});
-
-/* ESC key close */
-document.addEventListener("keydown", (e) => {
- if (e.key === "Escape") closeVAModal();
-});
-
-/* ------------------ Table Builders ------------------ */
-
-function createProjectRow(project) {
- return `
-
- ${project.name}
- $${project.hourlyRate}/hr
- ${project.totalWorkedTime}
- ${project.isCurrentlyWorking ? "Working" : "Inactive"}
-
- `;
-}
-
-function createVATableRow(va, index) {
- return `
-
-
-
- ${va.isCurrentlyActive ? "Active" : "Inactive"}
-
-
- ${va.name}
- ${va.lastProject?.name || "-"}
- ${va.projectsAssigned || 0}
- ${va.currentSessionDuration || "No Record Found"}
-
- ${
- va.isCurrentlyActive
- ? `Live `
- : "-"
- }
-
-
- `;
-}
-
-
- setInterval(loadDashboardData, 30000);
-
-/* ------------------ Load Dashboard ------------------ */
-
-async function loadDashboardData() {
- const res = await fetchWithAuth(`${API_BASE}/dashboard`);
- if (!res) return;
-
- const result = await res.json();
- if (!result.success) return;
-
- const { summary, data } = result;
-
- document.getElementById("activeVaCount").textContent = summary.activeVAs;
- document.getElementById("totalVaCount").textContent = summary.totalVAs;
- document.getElementById("totalProjects").textContent = summary.totalProjects;
- document.getElementById("vaListCount").textContent = `${summary.totalVAs} VAs`;
-
- const allVAs = data.allVAs || [];
-
- if (allVAs.length === 0) {
- vaTableBody.innerHTML = "";
- vaEmptyState.style.display = "block";
- return;
- }
-
- vaEmptyState.style.display = "none";
-
- /* store globally for modal */
- window.allVAs = allVAs;
-
- vaTableBody.innerHTML = allVAs
- .map((va, index) => createVATableRow(va, index))
- .join("");
-
- /* Row click handlers */
- document.querySelectorAll(".va-row").forEach(row => {
- row.addEventListener("click", (e) => {
- if (e.target.tagName === "A") return; // ignore Live click
- const index = row.dataset.index;
- openVAModal(window.allVAs[index]);
- });
- });
-}
-
-/* ------------------ Init ------------------ */
-
-document.addEventListener("DOMContentLoaded", loadDashboardData);
diff --git a/public/js/pages/manage-tips.js b/public/js/pages/manage-tips.js
deleted file mode 100644
index 0017a55..0000000
--- a/public/js/pages/manage-tips.js
+++ /dev/null
@@ -1,248 +0,0 @@
-
-
- // API base URL
- const API_BASE = '/api/v1/client';
-
-
-
- function openTipModal() {
- const modal = document.getElementById('tipModal');
- modal.style.display = 'flex';
- document.body.style.overflow = 'hidden';
-
- // Clear form
- document.getElementById('tipForm').reset();
- clearErrorMessages();
- }
-
- function closeTipModal() {
- const modal = document.getElementById('tipModal');
- modal.style.display = 'none';
- document.body.style.overflow = 'auto';
-
- // Clear form and errors
- document.getElementById('tipForm').reset();
- clearErrorMessages();
- }
-
- function clearErrorMessages() {
- document.getElementById('vaForTipError').textContent = '';
- document.getElementById('vaTipAmountError').textContent = '';
- document.getElementById('vaTipDescriptionError').textContent = '';
- }
-
- document.addEventListener('DOMContentLoaded', function () {
-
- // Mobile menu toggle
- const menuToggle = document.querySelector('.mobile-menu-button');
- const sidebar = document.querySelector('.dashboard-sidebar');
- menuToggle.addEventListener('click', () => {
- sidebar.classList.toggle('active');
- });
-
- document.getElementById('tipVaAddBtn').addEventListener('click', openTipModal);
-
- document.getElementById('tipModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closeTipModal();
- }
- });
-
- // Close modal with escape key
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape') {
- closeLogoutModal();
- closeTipModal();
- }
- });
-
- document.getElementById('logoutModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closeLogoutModal();
- }
- });
-
- // Logout button - opens confirmation modal instead of directly logging out
- document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
-
- // Confirm logout button
- document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
- });
-
- // ------------------------------
- // TIP FORM SUBMISSION HANDLER
- // ------------------------------
-
- document.getElementById('tipForm').addEventListener('submit', async function (e) {
- e.preventDefault();
-
- // clearErrorMessages();
-
- const vaId = document.getElementById('vaForTip').value;
- const amount = document.getElementById('vaTipAmount').value.trim();
- const description = document.getElementById('vaTipDescription').value.trim();
-
- let isValid = true;
-
- // Validation
- if (!vaId) {
- document.getElementById('vaForTipError').textContent = "Please select a virtual assistant.";
- isValid = false;
- }
-
- if (!amount || isNaN(amount) || Number(amount) <= 0) {
- document.getElementById('vaTipAmountError').textContent = "Enter a valid tip amount.";
- isValid = false;
- }
-
- if (!isValid) return;
-
- // Prepare API body
- const payload = {
- vaId,
- amount: Number(amount),
- description
- };
-
- try {
- document.getElementById('submitTipBtn').disabled = true;
- document.getElementById('submitTipBtn').textContent = "Submitting...";
-
- const response = await fetchWithAuth(`/api/v1/client/tips`, {
- method: "POST",
- body: JSON.stringify(payload)
- });
-
- const data = await response.json();
-
- if (!response.ok) {
- alert(data.message || "Failed to submit tip.");
- return;
- }
-
- alert("Tip submitted successfully!");
- closeTipModal();
-
- // Optionally refresh page or reload tips list
- window.location.reload();
-
- } catch (err) {
- console.error("Tip submit error:", err);
- alert("Something went wrong while submitting tip.");
- } finally {
- document.getElementById('submitTipBtn').disabled = false;
- document.getElementById('submitTipBtn').textContent = "Submit Tip";
- }
- });
-
-
- document.addEventListener('DOMContentLoaded', function () {
- // Load VAs for tip selection
- async function loadVAs() {
- try {
- const response = await fetchWithAuth(`${API_BASE}/dashboard`);
- const data = await response.json();
-
- if (!response.ok) {
- console.error("Failed to load VAs:", data.message);
- return;
- }
-
- const vaSelect = document.getElementById('vaForTip');
- data.data.allVAs.forEach(va => {
- const option = document.createElement('option');
- option.value = va.id;
- option.textContent = va.name;
- vaSelect.appendChild(option);
- });
-
- } catch (error) {
- console.error("Error loading VAs:", error);
- }
- }
-
- loadVAs();
- });
-
- document.addEventListener('DOMContentLoaded', function () {
- // Load VAs for tip selection
- async function loadTips() {
- try {
- const response = await fetchWithAuth(`${API_BASE}/tips`);
-
- if (!response.ok) {
- console.error("Failed to load VAs:", data.message);
- return;
- }
- const data = await response.json();
-
- const vaSelect = document.getElementById('vaTipTableBody');
- data.data.forEach(tip => {
- const row = document.createElement('tr');
- const cellVoid = document.createElement('td');
- const cellStatus = document.createElement('td');
- const cellName = document.createElement('td');
- const cellDescription = document.createElement('td');
- const cellDate = document.createElement('td');
- const cellAmount = document.createElement('td');
-
- cellVoid.innerHTML = `Void `;
- cellVoid.classList.add('void-btn');
-
- cellStatus.textContent = tip.status;
- cellName.textContent = tip.vaName;
- cellDescription.textContent = tip.description;
- cellDate.textContent = tip.createdAt ? new Date(tip.createdAt).toLocaleDateString() : '';
- cellAmount.textContent = `$${parseFloat(tip.amount).toFixed(2)}`;
-
- row.appendChild(cellVoid);
- row.appendChild(cellStatus);
- row.appendChild(cellName);
- row.appendChild(cellDescription);
- row.appendChild(cellDate);
- row.appendChild(cellAmount);
-
- vaSelect.appendChild(row);
- });
-
- document.querySelectorAll('.void-btn-a').forEach(button => {
- button.addEventListener('click', async function () {
- const tipId = this.getAttribute('data-id');
- if (confirm('Are you sure you want to void this tip?')) {
- try {
- const response = await fetchWithAuth(`${API_BASE}/tips/${tipId}`, {
- method: 'DELETE'
- });
- const result = await response.json();
-
- if (!response.ok) {
- alert(result.message || 'Failed to void tip.');
- return;
- }
-
- alert('Tip voided successfully!');
- window.location.reload();
- } catch (error) {
- console.error('Error voiding tip:', error);
- alert('Something went wrong while voiding the tip.');
- }
- }
- });
- });
-
- } catch (error) {
- console.error("Error loading VAs:", error);
- }
- }
-
- loadTips();
-
-
-
-
-
- });
-
-
-
-
diff --git a/public/js/pages/profile.js b/public/js/pages/profile.js
deleted file mode 100644
index 24b0ad1..0000000
--- a/public/js/pages/profile.js
+++ /dev/null
@@ -1,195 +0,0 @@
-// API base URL
-const API_BASE = '/api/v1/client';
-
-
-// Format empty fields
-function formatEmptyFields() {
- const inputs = document.querySelectorAll('.form-input');
- inputs.forEach(input => {
- if (!input.value.trim()) {
- input.value = 'Not provided';
- input.classList.add('empty-field');
- } else {
- input.classList.remove('empty-field');
- }
- });
-}
-
-// Load profile data
-async function loadProfileData() {
- try {
- const response = await fetchWithAuth(`${API_BASE}/profile`);
- if (!response) return;
-
- const result = await response.json();
- if (result.success) {
- const data = result.data;
-
- // Populate only personal and address information
- const fieldsToShow = ['name', 'email', 'position', 'address', 'city', 'state', 'zip'];
-
- fieldsToShow.forEach(key => {
- const element = document.getElementById(key);
- if (element && data[key]) {
- element.value = data[key];
- }
- });
-
- formatEmptyFields();
-
- } else {
- console.error('Failed to load profile data:', result.message);
- alert('Failed to load profile data');
- }
- } catch (error) {
- console.error('Error loading profile data:', error);
- alert('Error loading profile data');
- }
-}
-
-// Password Modal Functions
-function openPasswordModal() {
- const modal = document.getElementById('passwordModal');
- modal.style.display = 'flex';
- document.body.style.overflow = 'hidden';
-
- // Clear form
- document.getElementById('passwordForm').reset();
- clearErrorMessages();
-}
-
-function closePasswordModal() {
- const modal = document.getElementById('passwordModal');
- modal.style.display = 'none';
- document.body.style.overflow = 'auto';
-
- // Clear form and errors
- document.getElementById('passwordForm').reset();
- clearErrorMessages();
-}
-
-function clearErrorMessages() {
- document.getElementById('currentPasswordError').textContent = '';
- document.getElementById('newPasswordError').textContent = '';
- document.getElementById('confirmPasswordError').textContent = '';
-}
-
-// Change password
-async function changePassword(event) {
- event.preventDefault();
-
- const currentPassword = document.getElementById('currentPassword').value;
- const newPassword = document.getElementById('newPassword').value;
- const confirmPassword = document.getElementById('confirmPassword').value;
-
- // Reset errors
- clearErrorMessages();
-
- let hasError = false;
-
- if (!currentPassword) {
- document.getElementById('currentPasswordError').textContent = 'Current password is required';
- hasError = true;
- }
-
- if (!newPassword) {
- document.getElementById('newPasswordError').textContent = 'New password is required';
- hasError = true;
- } else if (newPassword.length < 6) {
- document.getElementById('newPasswordError').textContent = 'Password must be at least 6 characters';
- hasError = true;
- }
-
- if (!confirmPassword) {
- document.getElementById('confirmPasswordError').textContent = 'Please confirm your password';
- hasError = true;
- } else if (newPassword !== confirmPassword) {
- document.getElementById('confirmPasswordError').textContent = 'Passwords do not match';
- hasError = true;
- }
-
- if (hasError) return;
-
- try {
- const saveBtn = document.getElementById('savePasswordBtn');
- saveBtn.disabled = true;
- saveBtn.textContent = 'Changing Password...';
-
- const response = await fetchWithAuth(`${API_BASE}/profile`, {
- method: 'PATCH',
- body: JSON.stringify({
- userDetails: {
- currentPassword,
- newPassword,
- confirmPassword
- }
- })
- });
-
- if (!response) {
- saveBtn.disabled = false;
- saveBtn.textContent = 'Change Password';
- return;
- }
-
- const result = await response.json();
- if (result.success) {
- alert('Password changed successfully!');
- closePasswordModal();
- } else {
- alert(result.message || 'Failed to change password');
- }
- } catch (error) {
- console.error('Error changing password:', error);
- alert('Error changing password');
- } finally {
- const saveBtn = document.getElementById('savePasswordBtn');
- saveBtn.disabled = false;
- saveBtn.textContent = 'Change Password';
- }
-}
-
-// Event listeners
-document.addEventListener('DOMContentLoaded', function () {
- loadProfileData();
-
- // Change password button
- document.getElementById('changePasswordBtn').addEventListener('click', openPasswordModal);
-
- // Password form submission
- document.getElementById('passwordForm').addEventListener('submit', changePassword);
-
- // Close modal when clicking outside
- document.getElementById('passwordModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closePasswordModal();
- }
- });
-
- // Close modal with escape key
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape') {
- closePasswordModal();
- closeLogoutModal();
- }
- });
-
- document.getElementById('logoutModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closeLogoutModal();
- }
- });
-
- // Logout button - opens confirmation modal instead of directly logging out
- document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
-
- // Confirm logout button
- document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
-
- // Mobile menu toggle
- const menuToggle = document.querySelector('.mobile-menu-button');
- const sidebar = document.querySelector('.dashboard-sidebar');
- menuToggle.addEventListener('click', () => {
- sidebar.classList.toggle('active');
- });
-});
\ No newline at end of file
diff --git a/public/js/pages/projects.js b/public/js/pages/projects.js
deleted file mode 100644
index d26b0b6..0000000
--- a/public/js/pages/projects.js
+++ /dev/null
@@ -1,240 +0,0 @@
-// API base URL
-const API_BASE = '/api/v1/client';
-
-// Modal management
-const projectModal = document.getElementById('projectModal');
-const modalClose = document.getElementById('modalClose');
-const modalProjectName = document.getElementById('modalProjectName');
-const modalVATableBody = document.getElementById('modalVATableBody');
-const modalNoVAs = document.getElementById('modalNoVAs');
-const vaCountBadge = document.getElementById('vaCountBadge');
-
-
-function openProjectModal(project) {
- // Set project name
- modalProjectName.textContent = project.name || 'Unnamed Project';
-
- // Set project info
- const hourlyRate = project.hourlyRate ? `$${project.hourlyRate}/hr` : 'Not set';
- const dedicatedHours = project.dedicatedHours || 'Not set';
- const statusText = project.status === 'ACTIVE' ? 'Active' : 'Inactive';
- const statusClass = project.status === 'ACTIVE' ? 'active' : 'inactive';
-
- // Set VA details
- const assignedVAs = project.assignedVADetails || [];
- vaCountBadge.textContent = assignedVAs.length;
-
- if (assignedVAs.length > 0) {
- modalVATableBody.innerHTML = assignedVAs.map(va => createVATableRow(va)).join('');
- modalVATableBody.style.display = '';
- modalNoVAs.style.display = 'none';
- } else {
- modalVATableBody.innerHTML = '';
- modalVATableBody.style.display = 'none';
- modalNoVAs.style.display = 'block';
- }
-
- // Show modal
- projectModal.style.display = 'block';
- document.body.style.overflow = 'hidden';
-}
-
-function createVATableRow(va) {
- const statusClass = va.status === 'FULLTIME' ? 'active' : 'inactive';
- const statusText = va.status === 'FULLTIME' ? 'Full Time' :
- va.status === 'PARTTIME' ? 'Part Time' : 'Inactive';
-
- const activityClass = va.isActive ? 'active' : 'inactive';
- const activityText = va.isActive ? 'Currently Active' : 'Not Active';
-
- // Get initials for avatar
- const initials = (va.firstName?.charAt(0) || '') + (va.lastName?.charAt(0) || '') || 'VA';
-
- return `
-
-
-
-
${initials}
-
- ${va.firstName} ${va.lastName}
- ${va.email}
-
-
-
-
-
- ${statusText}
-
-
-
-
-
-
- `;
-}
-
-function closeProjectModal() {
- projectModal.style.display = 'none';
- document.body.style.overflow = 'auto';
-}
-
-// Close modal when clicking outside
-projectModal.addEventListener('click', (e) => {
- if (e.target === projectModal) {
- closeProjectModal();
- }
-});
-
-// Close modal with escape key
-document.addEventListener('keydown', (e) => {
- if (e.key === 'Escape' && projectModal.style.display === 'block') {
- closeProjectModal();
- }
-});
-
-function createProjectTableRow(project) {
- const statusClass = project.status === 'ACTIVE' ? 'active' : 'inactive';
- const statusText = project.status === 'ACTIVE' ? 'Active' : 'Inactive';
- const hourlyRate = project.hourlyRate ? `$${project.hourlyRate}/hr` : '-';
- const dedicatedHours = project.dedicatedHours ? `${project.dedicatedHours}` : '-';
-
- return `
-
-
-
-
${project.name || 'Unnamed Project'}
-
-
-
- ${hourlyRate}
-
-
- ${project.vaAssigned || 0}
-
-
- ${dedicatedHours}
-
-
- `;
-}
-
-// Load dashboard data
-async function loadDashboardData() {
- try {
- const response = await fetchWithAuth(`${API_BASE}/dashboard-projects`);
- if (!response) return;
-
- const result = await response.json();
- if (result.success) {
- const data = result.data;
- const counts = result.counts;
-
- // Update counts
- document.getElementById('projectsCount').textContent = `${counts.totalProjects || 0} Projects`;
-
- // Update Projects table
- const projectsTableBody = document.getElementById('projectsTableBody');
- const projectsEmptyState = document.getElementById('projectsEmptyState');
- const projects = data.projects || [];
-
- if (projects.length > 0) {
- projectsTableBody.innerHTML = projects.map(project => createProjectTableRow(project)).join('');
- projectsTableBody.style.display = '';
- projectsEmptyState.style.display = 'none';
-
- // Add click event listeners to project rows
- document.querySelectorAll('.project-row').forEach((row, index) => {
- row.addEventListener('click', function () {
- const project = projects[index];
- openProjectModal(project);
- });
- });
- } else {
- projectsTableBody.innerHTML = '';
- projectsTableBody.style.display = 'none';
- projectsEmptyState.style.display = 'block';
- }
-
- } else {
- console.error('Failed to load dashboard data:', result.message);
- showError('Failed to load dashboard data');
- }
-
- } catch (error) {
- console.error('Error loading dashboard data:', error);
- showError('Error loading dashboard data');
- }
-}
-
-// Show error message
-function showError(message) {
- const projectsTableBody = document.getElementById('projectsTableBody');
- const projectsEmptyState = document.getElementById('projectsEmptyState');
- const vaTableBody = document.getElementById('vaTableBody');
- const vaEmptyState = document.getElementById('vaEmptyState');
-
- // Hide tables
- projectsTableBody.style.display = 'none';
- vaTableBody.style.display = 'none';
-
- // Show error states
- projectsEmptyState.innerHTML = `
-
- `;
- projectsEmptyState.style.display = 'block';
-
- vaEmptyState.innerHTML = `
-
- `;
- vaEmptyState.style.display = 'block';
-}
-
-
-// Optional: Auto-refresh data every 30 seconds
-setInterval(loadDashboardData, 30000);
-
-
-
-document.addEventListener('DOMContentLoaded', function () {
- // Load dashboard data
- loadDashboardData();
-
- // Mobile menu toggle
- const menuToggle = document.querySelector('.mobile-menu-button');
- const sidebar = document.querySelector('.dashboard-sidebar');
- menuToggle.addEventListener('click', () => {
- sidebar.classList.toggle('active');
- });
-
- // Close modal with escape key
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape') {
- closeLogoutModal();
- }
- });
-
- document.getElementById('logoutModal').addEventListener('click', function (e) {
- if (e.target === this) {
- closeLogoutModal();
- }
- });
-
- // Logout button - opens confirmation modal instead of directly logging out
- document.getElementById('logoutButton').addEventListener('click', openLogoutModal);
-
- // Confirm logout button
- document.getElementById('confirmLogoutBtn').addEventListener('click', handleLogout);
-});
-
-modalClose.addEventListener('click', closeProjectModal);
\ No newline at end of file
diff --git a/src/controllers/client/dashboard/clientDashboardProjects.controller.js b/src/controllers/client/dashboard/clientDashboardProjects.controller.js
index 19b6cf6..1d9372f 100644
--- a/src/controllers/client/dashboard/clientDashboardProjects.controller.js
+++ b/src/controllers/client/dashboard/clientDashboardProjects.controller.js
@@ -35,8 +35,6 @@ async function client_dashboard_projects(req, res) {
// STEP 1: Get client ID from token
const clientUser = await ClientModel.findOne({ "userDetails.email": clientEmail });
-
- console.log("Line 38" , clientUser)
if (!clientUser) {
return res.status(404).json({ success: false, message: "Client not found" });
}
diff --git a/src/controllers/va/vaAuth/vaLogin.controller.js b/src/controllers/va/vaAuth/vaLogin.controller.js
index 64a7469..dcb4b14 100644
--- a/src/controllers/va/vaAuth/vaLogin.controller.js
+++ b/src/controllers/va/vaAuth/vaLogin.controller.js
@@ -42,32 +42,32 @@ import authFetch from "../../../utils/authFetch.js";
// time: currentTime // Use same timestamp for consistent logging
// });
- // console.log(`Created logout log: ${logoutLog._id}`);
- // 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": lastLog.vaEmail,
- // "projectId": lastLog.projectId,
- // "time": logoutLog.time,
- // }
+// // console.log(`Created logout log: ${logoutLog._id}`);
+// 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": lastLog.vaEmail,
+// "projectId": lastLog.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 authFetch(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);
diff --git a/src/controllers/va/vaLeaves/vaLeavesApply.controller.js b/src/controllers/va/vaLeaves/vaLeavesApply.controller.js
index 719dd23..7e86e13 100644
--- a/src/controllers/va/vaLeaves/vaLeavesApply.controller.js
+++ b/src/controllers/va/vaLeaves/vaLeavesApply.controller.js
@@ -159,7 +159,7 @@ async function va_leave_apply_function(req, res) {
"dateTo": endDate.toISOString().split('T')[0],
"totalDays": totalDays,
"leaveType": leaveType,
- "comment": processedComment.plainText, // Send plain text to Salesforce
+ "comment": processedComment.plainText !== "" ? processedComment.plainText : "-", // Send plain text to Salesforce
"attachments": processedComment.extractedImages, // Send extracted images info
"status": "PENDING",
"rejectionReason": ""
diff --git a/src/controllers/va/vaProfile/vaProfile.controller.js b/src/controllers/va/vaProfile/vaProfile.controller.js
index 346be7e..6417dce 100644
--- a/src/controllers/va/vaProfile/vaProfile.controller.js
+++ b/src/controllers/va/vaProfile/vaProfile.controller.js
@@ -21,16 +21,12 @@ async function va_profile_callback(req, res) {
return res.status(400).json({ success: false, message: "Invalid token payload" });
}
- console.log(decoded.email , "Line 24")
-
-
const vaUser = await VAModel.findOne({ "userDetails.email": decoded.email })
.select("-userDetails.password") // hide password
.populate("clientIds", "userDetails.email userDetails.name") // optional populate
.populate("projectIds", "name totalLoggedHours") // optional populate
.lean();
- console.log("Line 33" , vaUser)
if (!vaUser) {
return res.status(404).json({ success: false, message: "VA not found" });
}
diff --git a/src/server.js b/src/server.js
index 791739e..73a8f10 100644
--- a/src/server.js
+++ b/src/server.js
@@ -18,7 +18,6 @@ import apiRoutes from './routes/index.js'
import { render_reset_form } from "./controllers/passwordReset.controllers.js";
-import expressLayouts from "express-ejs-layouts";
import jwt from "jsonwebtoken";
const app = express();
@@ -40,11 +39,6 @@ app.on('certificate-error', (event, webContents, url, error, certificate, callba
});
app.set("view engine", "ejs");
-
-app.use(expressLayouts);
-
-// DEFAULT layout for all pages
-app.set("layout", "layouts/dashboard");
const PORT = process.env.PORT || 8000;
app.use(cors({
@@ -120,19 +114,9 @@ app.get('/clientregister/:sf_token?', (req, res) => {
app.get('/auth/password/reset-form', render_reset_form);
-app.get('/health', (req, res) => {
- res.status(201).send({ status: "Server is healthy" });
-})
-
-
-
-
app.get("/client/login", (req, res) => {
- res.render("client/client_login", {
- layout: "layouts/auth", // ⬅️ override layout
- title: "DocVA | Login"
- });
-});
+ res.render("client_login")
+})
app.get("/client/forgot-password", (req, res) => {
res.render("forgot_password")
@@ -142,11 +126,8 @@ app.get("/client/forgot-password", (req, res) => {
app.get('/client/dashboard', async (req, res) => {
try {
// const dashboardData = await dashboardService.getClientDashboard(req.user.id);
- res.render('client/dashboard-dashboard', {
- title: "Client Dashboard",
- pageScript: "dashboard.js"
- });
- // res.render('layouts/dashboard');
+ // res.render('client/dashboard-dashboard', dashboardData);
+ res.render('client/dashboard-dashboard');
} catch (error) {
console.error('Dashboard error:', error);
res.status(500).render('client/dashboard-dashboard', { error: 'Failed to load dashboard' });
@@ -156,11 +137,7 @@ app.get('/client/dashboard', async (req, res) => {
app.get('/client/virtual-assistants', async (req, res) => {
try {
// const vaData = await virtualAssistantService.getClientVAs(req.user.id);
- res.render('client/dashboard-virtual-assistants', {
- title: "Virtual Assistants",
- pageScript:"dashboard-virtual-assistants.js"
-
- });
+ res.render('client/dashboard-virtual-assistants');
// res.render('client/dashboard-virtual-assistants', vaData);
} catch (error) {
console.error('Virtual assistants error:', error);
@@ -171,11 +148,7 @@ 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', {
- title: "Projects",
- pageScript:"projects.js"
-
- });
+ res.render('client/dashboard-projects');
} catch (error) {
console.error('Projects error:', error);
res.status(500).render('client/dashboard-projects', { error: 'Failed to load projects' });
@@ -185,25 +158,23 @@ app.get('/client/projects', async (req, res) => {
app.get('/client/profile', async (req, res) => {
try {
// const vaData = await virtualAssistantService.getClientVAs(req.user.id);
- res.render('client/dashboard-profile',{
- title: "Profile",
- pageScript:"profile.js"
-
- });
+ res.render('client/dashboard-profile');
} catch (error) {
console.error('Projects error:', error);
res.status(500).render('client/dashboard-profile', { error: 'Failed to load profile' });
}
});
-app.get('/client/virtual-assistants/manage-tips', (req, res) => {
- res.render('client/dashboard-manage-va-tips', {
- title: 'Manage VA Tips',
- pageScript: 'manage-tips.js'
- });
+app.get('/client/virtual-assistants/manage-tips', async (req, res) => {
+ try {
+ // const vaData = await virtualAssistantService.getClientVAs(req.user.id);
+ res.render('client/dashboard-manage-va-tips');
+ } catch (error) {
+ console.error('Projects error:', error);
+ res.status(500).render('client/dashboard-manage-va-tips', { error: 'Failed to load profile' });
+ }
});
-
app.get("/live/:projectId/:vaId", async (req, res) => {
const { projectId, vaId } = req.params;
@@ -440,66 +411,6 @@ io.on("connection", (socket) => {
socket.to(vaSocketRespectToClient.get(`${clientId}-${vaId}-${projectId}-va`)).emit("appToCapture", { appTitle });
});
- socket.on("getVisibleApps", async ({ token }) => {
- try {
- // Verify token
- const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN);
- const vaEmail = decoded.email;
-
- // Find VA in DB
- const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail });
- if (!vaUser) {
- console.log("❌ Unauthorized VA");
- return;
- }
-
- const vaId = vaUser._id.toString();
- console.log("🟢 Received Apps from VA:", vaId);
-
- // Broadcast image only to allowed clients
- for (let [_, clientSocket] of io.sockets.sockets) {
- if (
- clientSocket.data.role === "client" &&
- clientSocket.data.allowedVAs?.includes(vaId)
- ) {
- clientSocket.emit("captureAppsTracker");
- }
- }
- } catch (error) {
- console.error("❌ Token verification failed:", err.message);
- }
- });
-
- socket.on("requestVisibleApps", async ({ token }) => {
- try {
- // Verify token
- const decoded = jwt.verify(token, process.env.JWT_SECRET_ACCESS_TOKEN);
- const vaEmail = decoded.email;
-
- // Find VA in DB
- const vaUser = await VAModel.findOne({ "userDetails.email": vaEmail });
- if (!vaUser) {
- console.log("❌ Unauthorized VA");
- return;
- }
-
- const vaId = vaUser._id.toString();
- console.log("🟢 Received Apps from VA:", vaId);
-
- // Broadcast image only to allowed clients
- for (let [_, clientSocket] of io.sockets.sockets) {
- if (
- clientSocket.data.role === "client" &&
- clientSocket.data.allowedVAs?.includes(vaId)
- ) {
- clientSocket.emit("captureAppsTracker");
- }
- }
- } catch (error) {
- console.error("❌ Token verification failed:", err.message);
- }
- });
-
socket.on("requestVisibleApps", async ({ token }) => {
try {
// Verify token
@@ -574,15 +485,28 @@ io.on("connection", (socket) => {
);
if (vaSocketId) {
- io.to(vaSocketId).emit("stopSendAppsDetails");
+ // io.to(vaSocketId).emit("stopSendAppsDetails");
+ io.to(vaSocketRespectToClient.get(`${clientId}-${vaId}-${projectId}-va`)).emit("stopSendAppsDetails", { projId: projectId });
}
}
- console.log("❌ Client disconnected");
+
});
});
+app.get("/", (req, res) => {
+ res.render("client_login")
+})
+
+app.get("/forgot-password", (req, res) => {
+ res.render("forgot_password")
+})
+
+app.get("/health", (req, res) => {
+ return res.status(200).json({ "success": true, "message": "Server is Healthy" })
+})
+
app.use((req, res) => {
res.status(404).render("not_found", {
title: "404 - Page Not Found"
diff --git a/views/client/dashboard-dashboard.ejs b/views/client/dashboard-dashboard.ejs
index 7e1e9e4..a4d2eed 100644
--- a/views/client/dashboard-dashboard.ejs
+++ b/views/client/dashboard-dashboard.ejs
@@ -1,72 +1,557 @@
-
-
-
-
-
-
-
Active VA
-
0
+
+
+
+
+
+
DocVA Client | Dashboard
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Status
+ Virtual Assistant
+ Active Project
+ Total Projects
+ Today Logged Hours
+ Action
+
+
+
+
+
+
+
+
+
+
+
No Virtual Assistants
+
You haven't assigned any virtual assistants yet.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Projects
+ 0
+
+
+
+
+
+
+ Project Name
+ Hourly Rate
+ Worked Time
+ Status
+
+
+
+
+
+
+
+
+
No Projects
+
This VA is not assigned to any projects.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Are you sure you want to logout?
+
You will be logged out of your account and redirected to the login page.
+
+ Cancel
+ Yes, Logout
+
+
+
+
+
\ No newline at end of file
diff --git a/views/client/dashboard-manage-va-tips.ejs b/views/client/dashboard-manage-va-tips.ejs
index fa30e29..c73a03b 100644
--- a/views/client/dashboard-manage-va-tips.ejs
+++ b/views/client/dashboard-manage-va-tips.ejs
@@ -1,59 +1,548 @@
-
-
+
+
-
-
-
-
-
-
-
-
- Void
- Status
- Virtual Assistant Name
- Description
- Date Added
- Amount
-
-
-
-
-
-
+
+
+
+
DocVA Client | Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Void
+ Status
+ Virtual Assistant Name
+ Description
+ Date Added
+ Amount
+
+
+
+
+
+
+
+
+
+
+
No Tip Found
+
You haven't gave any Tip yet.
+
+
+
+
+
+
-
-
-
-
No Tip Found
-
You haven't gave any Tip yet.
+
+
+
+
+
+
+
+
+
+
Are you sure you want to logout?
+
You will be logged out of your account and redirected to the login page.
+
+
+ Cancel
+ Yes, Logout
+
+
-
\ No newline at end of file
+
+
+
+
\ No newline at end of file
diff --git a/views/client/dashboard-profile.ejs b/views/client/dashboard-profile.ejs
index 934137b..8dcb40b 100644
--- a/views/client/dashboard-profile.ejs
+++ b/views/client/dashboard-profile.ejs
@@ -1,65 +1,478 @@
-