Compare commits

..

No commits in common. "2ca51468460be4ffb8e124e65a9599657abc046e" and "6c8e85336ceffcd02285e925d48658c64e2e9ca9" have entirely different histories.

5 changed files with 95 additions and 693 deletions

View File

@ -1461,7 +1461,6 @@ tr[data-vaid]:hover {
background: var(--bg-primary); background: var(--bg-primary);
color: var(--text-primary); color: var(--text-primary);
transition: border-color 0.2s; transition: border-color 0.2s;
resize: vertical;
} }
.modal-form .form-input:focus { .modal-form .form-input:focus {
@ -1478,19 +1477,6 @@ tr[data-vaid]:hover {
border-color: var(--color-danger); border-color: var(--color-danger);
} }
.select-field {
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 12px center;
background-size: 16px;
border-radius: 6px;
}
[data-theme="dark"] .select-field {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d1d5db' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
}
.error-message { .error-message {
color: var(--color-danger); color: var(--color-danger);
font-size: 0.875rem; font-size: 0.875rem;

View File

@ -64,12 +64,11 @@ body {
display: none; display: none;
flex-direction: column; flex-direction: column;
width: 388px; width: 388px;
background: var(--bg-sidebar); background: linear-gradient(to bottom, #f4f4f5, #d0f9f9, #cac5f7);
padding: 24px; padding: 24px;
border-radius: 14px; border-radius: 14px;
margin: 20px; margin: 20px;
box-shadow: var(--shadow); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
} }
.logo { .logo {
@ -81,24 +80,18 @@ body {
width: 100%; width: 100%;
height: auto; height: auto;
max-width: 196px; max-width: 196px;
filter: brightness(var(--logo-brightness, 1));
} }
[data-theme="dark"] .logo-placeholder img {
--logo-brightness: 0.9;
}
.logo-placeholder h2 { .logo-placeholder h2 {
font-size: 32px; font-size: 32px;
font-weight: 700; font-weight: 700;
color: var(--text-primary); color: #000;
text-align: center; text-align: center;
} }
.form-title-sec { .form-title-sec {
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
color:var(--text-primary); color: #000;
text-align: center; text-align: center;
margin-bottom: 24px; margin-bottom: 24px;
} }
@ -118,25 +111,15 @@ body {
border: 2px solid transparent; border: 2px solid transparent;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.1);
}
[data-theme="dark"] .step {
background: rgba(255, 255, 255, 0.05);
} }
.step.active { .step.active {
border-color: var(--accent-color); border-color: #000000;
}
[data-theme="dark"] .step.active {
background: rgba(255, 255, 255, 0.1);
} }
.step.completed .step-number { .step.completed .step-number {
background-color: var(--accent-color); background-color: #000000;
border-color: var(--accent-color); border-color: #000000;
color: white; color: white;
} }
@ -147,43 +130,30 @@ body {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 2px solid var(--accent-color); border: 2px solid #00000070;
font-weight: 600; font-weight: 600;
color: var(--accent-color); color: #000000b5;
transition: all 0.3s ease; transition: all 0.3s ease;
background: rgba(255, 255, 255, 0.8);
}
[data-theme="dark"] .step-number {
background: rgba(255, 255, 255, 0.1);
border-color: var(--accent-color);
color: var(--text-primary);
} }
.step.active .step-number { .step.active .step-number {
border-color:var(--text-primary); border-color: #000;
color:var(--text-primary); color: #000;
opacity: 1;
background: rgba(255, 255, 255, 0.9);
}
[data-theme="dark"] .step.active .step-number {
background: rgba(255, 255, 255, 0.2);
} }
.step-text { .step-text {
font-size: 14px; font-size: 14px;
color: var(--text-primary); color: #000000b5;
} }
.step.active .step-text { .step.active .step-text {
color:var(--text-primary); color: #000;
} }
.step-connector { .step-connector {
height: 24px; height: 24px;
width: 2px; width: 2px;
background-color: var(--accent-color); background-color: #00000070;
margin-left: 16px; margin-left: 16px;
position: absolute; position: absolute;
top: 46px; top: 46px;
@ -191,7 +161,7 @@ body {
} }
.step.active .step-connector { .step.active .step-connector {
background-color:var(--text-primary); background-color: #000;
} }
/* Main Content Styles */ /* Main Content Styles */
@ -201,17 +171,15 @@ body {
flex-direction: column; flex-direction: column;
padding: 20px; padding: 20px;
overflow-y: auto; overflow-y: auto;
background-color: var(--bg-primary);
} }
.mobile-progress { .mobile-progress {
background-color: var(--bg-secondary); background-color: #f4f4f5;
padding: 16px; padding: 16px;
border-radius: 12px; border-radius: 12px;
margin-bottom: 20px; margin-bottom: 20px;
box-shadow: var(--shadow); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow-x: auto; overflow-x: auto;
border: 1px solid var(--border-color);
} }
.mobile-progress ol { .mobile-progress ol {
@ -236,12 +204,11 @@ body {
.form-card { .form-card {
width: 100%; width: 100%;
max-width: 450px; max-width: 450px;
background: var(--bg-card); background: white;
border: 1px solid var(--border-color); border: 1px solid #e5e7eb;
border-radius: 12px; border-radius: 12px;
box-shadow: var(--shadow-lg); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
padding: 24px; padding: 24px;
transition: all 0.3s ease;
} }
.form-title { .form-title {
@ -249,7 +216,7 @@ body {
font-size: 20px; font-size: 20px;
font-weight: 600; font-weight: 600;
margin-bottom: 24px; margin-bottom: 24px;
color:var(--text-primary); color: #000;
} }
.form-step { .form-step {
@ -267,17 +234,10 @@ body {
.input-container { .input-container {
position: relative; position: relative;
background-color: var(--bg-secondary); background-color: #f9fafb;
border-radius: 6px; border-radius: 6px;
overflow: hidden; overflow: hidden;
padding: 2px 0; padding: 2px 0;
border: 1px solid transparent;
transition: all 0.3s ease;
}
.input-container:focus-within {
border-color: var(--accent-light);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
} }
.input-label { .input-label {
@ -286,7 +246,7 @@ body {
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
font-size: 14px; font-size: 14px;
color: var(--text-secondary); color: #6b7280;
transition: all 0.2s ease; transition: all 0.2s ease;
pointer-events: none; pointer-events: none;
} }
@ -299,16 +259,6 @@ body {
outline: none; outline: none;
font-size: 14px; font-size: 14px;
border-radius: 6px; border-radius: 6px;
color: var(--text-primary);
transition: all 0.3s ease;
}
[data-theme="dark"] .input-field, [data-theme="dark"] .select-field {
background-color: #ffffff1a;
}
.input-field::placeholder {
color: var(--text-muted);
} }
.select-field { .select-field {
@ -320,10 +270,6 @@ body {
border-radius: 6px; border-radius: 6px;
} }
[data-theme="dark"] .select-field {
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23d1d5db' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
}
.input-field:focus + .input-label, .input-field:focus + .input-label,
.input-field:not(:placeholder-shown) + .input-label, .input-field:not(:placeholder-shown) + .input-label,
.select-field:focus + .input-label, .select-field:focus + .input-label,
@ -331,7 +277,6 @@ body {
top: 2px; top: 2px;
font-size: 12px; font-size: 12px;
transform: none; transform: none;
color: var(--accent-light);
} }
.checkbox-group { .checkbox-group {
@ -344,13 +289,10 @@ body {
.checkbox-group input { .checkbox-group input {
width: 16px; width: 16px;
height: 16px; height: 16px;
accent-color: var(--accent-color);
} }
.checkbox-group label { .checkbox-group label {
font-size: 14px; font-size: 14px;
color: var(--text-primary);
cursor: pointer;
} }
.form-actions { .form-actions {
@ -370,33 +312,31 @@ body {
} }
.btn-primary { .btn-primary {
background-color: var(--accent-color); background-color: #000000;
color: white; color: white;
} }
.btn-primary:hover { .btn-primary:hover {
opacity: 0.9; opacity: 0.9;
transform: translateY(-1px);
} }
.btn-secondary { .btn-secondary {
background-color: var(--bg-secondary); background-color: #f3f4f6;
color: var(--text-primary); color: #374151;
border: 1px solid var(--border-color);
} }
.btn-secondary:hover { .btn-secondary:hover {
background-color: var(--border-color); background-color: #e5e7eb;
} }
.btn-outline { .btn-outline {
background-color: transparent; background-color: transparent;
border: 1px solid var(--accent-light); border: 1px solid #070065;
color: var(--accent-light); color: #070065;
} }
.btn-outline:hover { .btn-outline:hover {
background-color: var(--accent-light); background-color: #070065;
color: white; color: white;
} }
@ -404,7 +344,6 @@ body {
.btn:disabled { .btn:disabled {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed; cursor: not-allowed;
transform: none !important;
} }
.btn-sm { .btn-sm {
@ -421,109 +360,31 @@ body {
} }
.payment-option.selected { .payment-option.selected {
border-color: var(--border-color); border-color: #000000;
transition: all 0.3s ease; background-color: #f0f9ff;
background: var(--bg-card);
}
.payment-option:hover {
border-color: var(--accent-light);
transform: translateY(-1px);
}
.payment-option.selected {
border-color: var(--accent-color);
background-color: rgba(99, 102, 241, 0.1);
}
[data-theme="dark"] .payment-option.selected {
background-color: rgba(99, 102, 241, 0.2);
}
/* Button Dark Mode Styles */
[data-theme="dark"] .btn-primary {
background-color: #6366f1;
color: white;
border: 1px solid #6366f1;
}
[data-theme="dark"] .btn-primary:hover {
background-color: #4f46e5;
border-color: #4f46e5;
opacity: 1;
}
[data-theme="dark"] .btn-secondary {
background-color: #374151;
color: #d1d5db;
border: 1px solid #4b5563;
}
[data-theme="dark"] .btn-secondary:hover {
background-color: #4b5563;
border-color: #6b7280;
}
[data-theme="dark"] .btn-outline {
background-color: transparent;
border: 1px solid #6366f1;
color: #6366f1;
}
[data-theme="dark"] .btn-outline:hover {
background-color: #6366f1;
color: white;
}
[data-theme="dark"] .btn:disabled {
background-color: #374151;
color: #6b7280;
border-color: #4b5563;
opacity: 0.5;
}
/* Action button dark mode styles */
[data-theme="dark"] .password-toggle,
[data-theme="dark"] .password-generate,
[data-theme="dark"] .detect-location {
color: #818cf8;
}
[data-theme="dark"] .password-toggle:hover,
[data-theme="dark"] .password-generate:hover,
[data-theme="dark"] .detect-location:hover {
color: #6366f1;
}
/* Focus states for dark mode */
[data-theme="dark"] .btn:focus-visible {
outline: 2px solid #818cf8;
outline-offset: 2px;
} }
.payment-option h4 { .payment-option h4 {
margin-bottom: 8px; margin-bottom: 8px;
font-size: 16px; font-size: 16px;
color: var(--text-primary);
} }
.payment-option p { .payment-option p {
font-size: 14px; font-size: 14px;
color: var(--text-secondary); color: #6b7280;
} }
.payment-details { .payment-details {
display: none; display: none;
margin: 4px 0 16px; margin: 4px 0 16px;
padding: 16px; padding: 16px;
border: 1px solid var(--border-color); border: 1px solid #e5e7eb;
border-radius: 8px; border-radius: 8px;
transition: all 0.3s ease; background-color: #bdbdbd;
} }
.payment-details.active { .payment-details.active {
display: block; display: block;
animation: slideDown 0.3s ease-out;
} }
.success-message { .success-message {
@ -533,25 +394,24 @@ body {
.success-icon { .success-icon {
font-size: 48px; font-size: 48px;
color: var(--success-color); color: #10b981;
margin-bottom: 16px; margin-bottom: 16px;
} }
.success-message h3 { .success-message h3 {
font-size: 24px; font-size: 24px;
margin-bottom: 8px; margin-bottom: 8px;
color: var(--text-primary);
} }
.success-message p { .success-message p {
color: var(--text-secondary); color: #6b7280;
margin-bottom: 24px; margin-bottom: 24px;
} }
.location-loading { .location-loading {
display: none; display: none;
font-size: 12px; font-size: 12px;
color: var(--text-secondary); color: #6b7280;
margin-top: 4px; margin-top: 4px;
margin-left: 12px; margin-left: 12px;
position: absolute; position: absolute;
@ -568,17 +428,17 @@ body {
} }
.profile-incomplete { .profile-incomplete {
background: var(--warning-bg); background: #fff3cd;
border: 1px solid var(--warning-border); border: 1px solid #ffeaa7;
border-radius: 8px; border-radius: 8px;
padding: 1rem; padding: 1rem;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
color: var(--warning-text); color: #856404;
} }
.password-strength { .password-strength {
height: 5px; height: 5px;
background: var(--bg-secondary); background: #e9ecef;
border-radius: 5px; border-radius: 5px;
margin-top: 5px; margin-top: 5px;
overflow: hidden; overflow: hidden;
@ -592,7 +452,7 @@ body {
} }
.password-weak { .password-weak {
background: var(--error-color); background: #dc3545;
width: 25%; width: 25%;
} }
@ -602,7 +462,7 @@ body {
} }
.password-strong { .password-strong {
background: var(--success-color); background: #28a745;
width: 100%; width: 100%;
} }
@ -613,127 +473,38 @@ body {
margin-top: 8px; margin-top: 8px;
} }
.password-toggle, .password-generate, .detect-location { .password-toggle {
background: none; background: none;
border: none; border: none;
color: var(--accent-light); color: #070065;
cursor: pointer; cursor: pointer;
font-size: 12px; font-size: 12px;
transition: color 0.3s ease;
} }
.password-toggle:hover, .password-generate:hover, .detect-location:hover { .password-generate {
color: var(--accent-color); background: none;
border: none;
color: #070065;
cursor: pointer;
font-size: 12px;
} }
.form-text { .form-text {
font-size: 12px; font-size: 12px;
color: var(--text-secondary); color: #6b7280;
margin-top: 4px; margin-top: 4px;
} }
/* Error States */ .detect-location {
.step-error-container { background: none;
background-color: rgba(220, 38, 38, 0.1); border: none;
border: 1px solid rgba(220, 38, 38, 0.3); color: #070065;
border-radius: 8px; cursor: pointer;
padding: 12px 16px; font-size: 12px;
margin-bottom: 20px; text-decoration: underline;
animation: slideDown 0.3s ease-out; margin-top: 5px;
} }
[data-theme="dark"] .step-error-container {
background-color: rgba(220, 38, 38, 0.2);
border-color: rgba(220, 38, 38, 0.4);
}
.error-message {
color: var(--error-color);
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
gap: 8px;
}
.error-message::before {
content: "⚠";
font-size: 16px;
}
.field-error {
border: 1px solid var(--error-color) !important;
background-color: rgba(220, 38, 38, 0.05) !important;
}
[data-theme="dark"] .field-error {
background-color: rgba(220, 38, 38, 0.1) !important;
}
/* Animations */
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Scrollbar Styling */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
[data-theme="dark"] ::-webkit-scrollbar-thumb {
background: var(--border-dark);
}
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
.btn:focus-visible,
.input-field:focus-visible,
.select-field:focus-visible,
.payment-option:focus-visible {
outline: 2px solid var(--accent-light);
outline-offset: 2px;
}
/* Selection Color */
::selection {
background-color: rgba(99, 102, 241, 0.3);
color: var(--text-primary);
}
/* Responsive styles */ /* Responsive styles */
@media (min-width: 1024px) { @media (min-width: 1024px) {
.sidebar { .sidebar {
@ -749,35 +520,41 @@ body {
} }
} }
@media (max-width: 1023px) { .step-error-container {
[data-theme="dark"] .mobile-progress { background-color: #fef2f2;
background-color: var(--bg-card); border: 1px solid #fecaca;
border: 1px solid var(--border-dark); border-radius: 8px;
} padding: 12px 16px;
margin-bottom: 20px;
.mobile-progress .step { animation: slideDown 0.3s ease-out;
background: rgba(255, 255, 255, 0.05);
}
[data-theme="dark"] .mobile-progress .step {
background: rgba(255, 255, 255, 0.1);
}
} }
@media print { .error-message {
[data-theme="dark"] { color: #dc2626;
--bg-primary: #ffffff; font-size: 14px;
--bg-card: #ffffff; font-weight: 500;
--text-primary: #000000; display: flex;
--text-secondary: #333333; align-items: center;
--border-color: #cccccc; gap: 8px;
} }
.sidebar { .error-message::before {
display: none; content: "⚠";
} font-size: 16px;
}
.btn { .field-error {
display: none; border: 1px solid #dc2626 !important;
background-color: #fef2f2;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
} }
} }

View File

@ -168,16 +168,6 @@ app.get('/client/profile', async (req, res) => {
} }
}); });
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) => { app.get("/live/:projectId/:vaId", async (req, res) => {
const { projectId, vaId } = req.params; const { projectId, vaId } = req.params;

View File

@ -1,350 +0,0 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DocVA Client | Dashboard</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="/public/css/dashboard.css">
</head>
<body class="dashboard-body">
<div id="root">
<nav class="dashboard-header">
<div class="header-content">
<div class="header-logo">
<div class="logo-container">
<h2 class="sidebar-title">DocVA Client Portal</h2>
</div>
</div>
<div class="header-nav">
<ul class="nav-list">
<li class="nav-item">
<a href="/client/dashboard" class="nav-button">
<i class="fas fa-chart-bar"></i>Dashboard
</a>
</li>
<li class="nav-item">
<a href="/client/virtual-assistants" class="nav-button active">
<i class="fas fa-users"></i>Virtual Assistants
</a>
</li>
<li class="nav-item">
<a href="/client/projects" class="nav-button">
<i class="fas fa-file-invoice"></i>Projects
</a>
</li>
</ul>
<button class="mobile-menu-button">
<i class="fas fa-bars"></i>
</button>
</div>
</div>
</nav>
<div class="dashboard-container">
<aside class="dashboard-sidebar">
<nav class="sidebar-nav">
<ul class="sidebar-list">
<li class="sidebar-item">
<a href="/client/profile" class="sidebar-link">
<i class="fas fa-user sidebar-icon"></i>
<span>Profile</span>
</a>
</li>
<li class="sidebar-item">
<a href="/client/dashboard" class="sidebar-link">
<i class="fas fa-chart-bar sidebar-icon"></i>
<span>Dashboard</span>
</a>
</li>
<li class="sidebar-item">
<a href="/client/virtual-assistants" class="sidebar-link active">
<i class="fas fa-users sidebar-icon"></i>
<span>Virtual Assistants</span>
</a>
</li>
<li class="sidebar-item">
<a href="/client/projects" class="sidebar-link">
<i class="fas fa-file-invoice sidebar-icon"></i>
<span>Projects</span>
</a>
</li>
<li class="sidebar-item">
<a href="javascript:void(0);" class="sidebar-link" id="logoutButton">
<i class="fas fa-sign-out-alt sidebar-icon"></i>
<span>Logout</span>
</a>
</li>
</ul>
</nav>
</aside>
<main class="dashboard-main">
<div class="dashboard-content">
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-content">
<div class="stat-info">
<h3>Unbilled Tips</h3>
<div class="stat-value" id="vaUnbilledTips">$0</div>
</div>
<div class="stat-icon blue">
<i class="fas fa-file-invoice fa-xl"></i>
</div>
</div>
</div>
<div class="stat-card">
<div class="stat-content">
<div class="stat-info">
<h3>Total Tips</h3>
<div class="stat-value" id="vaTotalTips">$0</div>
</div>
<div class="stat-icon blue">
<i class="fas fa-dollar fa-xl"></i>
</div>
</div>
</div>
</div>
<!-- Virtual Assistants List -->
<div class="content-section">
<div class="section-header" style="justify-content: space-between;align-items: center;">
<a href="/client/virtual-assistants" class="btn btn-secondary">Back</a>
<span class="btn btn-primary" id="tipVaAddBtn">Tip Virtual Assistant</span>
</div>
<div class="table-container">
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Void</th>
<th>Status</th>
<th>Virtual Assistant Name</th>
<th>Description</th>
<th>Date Added</th>
<th>Amount</th>
</tr>
</thead>
<tbody id="vaTipTableBody">
<!-- Data will be loaded here -->
</tbody>
</table>
</div>
<div id="vaEmptyTipState" style="display: none;">
<div class="empty-state">
<i class="fas fa-users"></i>
<h3>No Tip Found</h3>
<p>You haven't gave any Tip yet.</p>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<div id="tipModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Tip Virtual Assistant</h2>
<button class="modal-close" onclick="closeTipModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="modal-body">
<form class="modal-form" id="tipForm">
<div class="form-group">
<label class="form-label">Select Virtual Assistant</label>
<select name="vaForTip" class="form-input select-field" id="vaForTip" required>
<option value="">-- Select Virtual Assistant --</option>
<option value="va 1">VA 1</option>
<!-- Options will be populated dynamically -->
</select>
<div class="error-message" id="vaForTipError"></div>
</div>
<div class="form-group">
<label class="form-label">Tip Amount</label>
<input type="text" class="form-input" id="vaTipAmount" required>
<div class="error-message" id="vaTipAmountError"></div>
</div>
<div class="form-group">
<label class="form-label">Tip Description</label>
<textarea name="vaTipDescription" class="form-input" id="vaTipDescription"></textarea>
<div class="error-message" id="vaTipDescriptionError"></div>
</div>
<div class="modal-actions">
<button type="button" class="cancel-btn" onclick="closeTipModal()">Cancel</button>
<button type="submit" class="save-btn" id="savePasswordBtn">Submit Tip</button>
</div>
</form>
</div>
</div>
</div>
<div id="logoutModal" class="modal-overlay">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title">Confirm Logout</h2>
<button class="modal-close" onclick="closeLogoutModal()">
<i class="fas fa-times"></i>
</button>
</div>
<div class="logout-modal-body">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h3 class="logout-title">Are you sure you want to logout?</h3>
<p class="logout-message">You will be logged out of your account and redirected to the login page.</p>
<div class="logout-actions">
<button class="logout-cancel-btn" onclick="closeLogoutModal()">Cancel</button>
<button class="logout-confirm-btn" id="confirmLogoutBtn">Yes, Logout</button>
</div>
</div>
</div>
</div>
</div>
<script>
// Theme management
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
function getSystemTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
// Initialize theme
const savedTheme = getSystemTheme();
setTheme(savedTheme);
// API base URL
const API_BASE = '/api/v1/client';
// Auth token management
function getAuthToken() {
return localStorage.getItem('knoptpq');
}
async function fetchWithAuth(url, options = {}) {
const token = getAuthToken();
if (!token) {
window.location.href = '/client/login';
return;
}
const response = await fetch(url, {
...options,
headers: {
'token': `${token}`,
'Content-Type': 'application/json',
...options.headers,
}
});
if (response.status === 401) {
localStorage.removeItem('knoptpq');
localStorage.removeItem('refreshToken');
window.location.href = '/client/login';
return;
}
return response;
}
async function handleLogout() {
try {
// Call logout API
await fetchWithAuth(`${API_BASE}/logout`, {
method: 'POST'
});
} catch (error) {
console.error('Logout API error:', error);
} finally {
// Clear local storage and redirect
localStorage.removeItem('knoptpq');
localStorage.removeItem('refreshToken');
window.location.href = '/client/login';
}
}
// 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';
}
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);
});
</script>
</body>
</html>

View File

@ -121,9 +121,8 @@
<!-- Virtual Assistants List --> <!-- Virtual Assistants List -->
<div class="content-section"> <div class="content-section">
<div class="section-header" style="justify-content: space-between;align-items: center;"> <div class="section-header">
<h2 class="section-title">Virtual Assistants <span class="total-count" id="vaListCount">0 VAs</span></h2> <h2 class="section-title">Virtual Assistants <span class="total-count" id="vaListCount">0 VAs</span></h2>
<a href="/client/virtual-assistants/manage-tips" class="btn btn-primary">Manage Tips</a>
</div> </div>
<div class="table-container"> <div class="table-container">
<div class="table-wrapper"> <div class="table-wrapper">