240 lines
8.3 KiB
JavaScript
240 lines
8.3 KiB
JavaScript
// 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 `
|
|
<tr>
|
|
<td>
|
|
<div class="va-name">
|
|
<div class="va-avatar">${initials}</div>
|
|
<div class="va-details">
|
|
<span class="va-fullname">${va.firstName} ${va.lastName}</span>
|
|
<span class="va-email">${va.email}</span>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class=" ${statusClass}">
|
|
${statusText}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<div class="activity-status">
|
|
<div class="activity-dot ${activityClass}"></div>
|
|
<span>${activityText}</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
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 `
|
|
<tr class="project-row" data-project-id="${project.id}">
|
|
<td>
|
|
<div class="project-info">
|
|
<div class="project-name">${project.name || 'Unnamed Project'}</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="hourly-rate">${hourlyRate}</span>
|
|
</td>
|
|
<td>
|
|
<span class="va-count">${project.vaAssigned || 0}</span>
|
|
</td>
|
|
<td>
|
|
<span class="dedicated-hours">${dedicatedHours}</span>
|
|
</td>
|
|
</tr>
|
|
`;
|
|
}
|
|
|
|
// 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 = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<h3>Error</h3>
|
|
<p>${message}</p>
|
|
</div>
|
|
`;
|
|
projectsEmptyState.style.display = 'block';
|
|
|
|
vaEmptyState.innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<h3>Error</h3>
|
|
<p>${message}</p>
|
|
</div>
|
|
`;
|
|
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); |