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.name}
-
- - - $${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 = ` -
- -

Error

-

${message}

-
- `; - 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 = ``; - 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} -
- - -
-
- ${activityText} -
- - - `; -} - -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 = ` -
- -

Error

-

${message}

-
- `; - projectsEmptyState.style.display = 'block'; - - vaEmptyState.innerHTML = ` -
- -

Error

-

${message}

-
- `; - 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 + + + + +
+
-
-
-
-
-

Total VA's

-
0
-
-
- + +
+ +
+
+ +
+
+
+
+

Active VA

+
0
+
+
+ +
+
+
+
+
+
+

Total VA's

+
0
+
+
+ +
+
+
+ +
+
+
+

Total Projects

+
0
+
+
+ +
+
+
+
+ + +
+
+

Virtual Assistants 0 VAs

+
+
+
+ + + + + + + + + + + + + + +
StatusVirtual AssistantActive ProjectTotal ProjectsToday Logged HoursAction
+
+ +
+
+
+
+
+ + +
+ + + \ 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 @@ - -
-
-
-
-

Unbilled Tips

-
$0
-
-
- -
-
-
-
-
-
-

Total Tips

-
$0
-
-
- -
-
-
-
+ + - -
-
- Back - Tip Virtual Assistant -
-
-
- - - - - - - - - - - - - - -
VoidStatusVirtual Assistant NameDescriptionDate AddedAmount
+ + + + DocVA Client | Dashboard + + + + + + + +
+ +
+ +
+
+ +
+
+
+
+

Unbilled Tips

+
$0
+
+
+ +
+
+
+
+
+
+

Total Tips

+
$0
+
+
+ +
+
+
+
+ + +
+
+ Back + Tip Virtual Assistant +
+
+
+ + + + + + + + + + + + + + +
VoidStatusVirtual Assistant NameDescriptionDate AddedAmount
+
+ +
+
+
+
- \ 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 @@ -
-
-

Client Profile

-
- -
- -
-
- -

Personal Information

+ + + + + + DocVA Client | Profile + + + + + +
+ +
+ +
+
+
+
+

Client Profile

+
+ +
+ +
+
+ +

Personal Information

+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +

Address Information

+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ +
+
+
+
-
- - +
+
+ + +