registerPage('dashboard', async (content) => {
document.getElementById('topbar-title').textContent = 'Dashboard';
const r = await api('dashboard.php');
if (!r.success) { content.innerHTML = emptyHTML('Failed to load dashboard'); return; }
const { stats, booking_statuses, recent_bookings } = r;
const statItems = [
{ label: 'Clients', value: stats.clients, icon: '🏢', page: 'clients' },
{ label: 'Employees', value: stats.employees, icon: '👷', page: 'employees' },
{ label: 'Bookings', value: stats.bookings, icon: '📅', page: 'bookings' },
{ label: 'Assessments', value: stats.assessments, icon: '📋', page: 'assessments' },
{ label: 'Tests', value: stats.tests, icon: '📝', page: 'tests' },
{ label: 'Users', value: stats.users, icon: '👤', page: 'users' },
{ label: 'Certificates', value: stats.certificates, icon: '🏅', page: 'courses' },
];
const statsHtml = statItems.map(s => `
${s.icon}
${s.value}
${s.label}
`).join('');
const recentHtml = recent_bookings.length === 0 ? emptyHTML('No recent bookings') :
`
| # | Date | Client | Assessor | Status | |
${recent_bookings.map(b => `
| ${b.booking_number || b.record_id} |
${b.date_booked} |
${b.clients_name || '—'} |
${b.assessor || '—'} |
${statusBadge(b.status)} |
|
`).join('')}
`;
// Load expiry data
const er = await api('expiry.php', { action: 'dashboard' });
const expiryGrouped = er.success ? er.grouped : {};
const expiryTotal = er.success ? er.total : 0;
function daysBadge(d) {
const n = parseInt(d);
if (isNaN(n)) return `Unknown`;
const abs = Math.abs(n);
const months = Math.round(abs / 30.44);
const label = abs < 30 ? `${abs}d` : `${months}mo`;
if (n < 0) return `Expired ${label} ago`;
if (n === 0) return `Expires today`;
if (n <= 30) return `${label} left`;
if (n <= 60) return `${label} left`;
if (n <= 180) return `${label} left`;
return `${label} left`;
}
const expiryHtml = Object.keys(expiryGrouped).length === 0
? `✓ Nothing expiring within 60 days
`
: Object.entries(expiryGrouped).map(([client, rows], ci) => `
${client}
${rows.length}
${rows.filter(r => parseInt(r.days_left) < 0).length ? `
${rows.filter(r => parseInt(r.days_left) < 0).length} expired` : ''}
${rows.filter(r => parseInt(r.days_left) >= 0 && parseInt(r.days_left) <= 30).length ? `
${rows.filter(r => parseInt(r.days_left) >= 0 && parseInt(r.days_left) <= 30).length} critical` : ''}
| Name |
ID |
Assessment / Test |
Done |
Expires |
Status |
${rows.map(r => `
| ${r.client_employees_name} ${r.surname} |
${r.i_doc_passport || '—'} |
${r.rec_type === 'assessment' ? 'A' : 'T'}
${r.name}
|
${(r.done_date || '').slice(0, 10)} |
${r.expiry_date || '—'} |
${daysBadge(r.days_left)} |
`).join('')}
`).join('');
// Toggle open/close for collapsible groups
function initExpiry() {
document.querySelectorAll('.exp-body').forEach(b => {
b.style.display = 'block'; // start open
});
document.querySelectorAll('[onclick*="exp-body"]').forEach(h => {
h.addEventListener('click', function () {
const arrow = this.querySelector('.exp-arrow');
if (arrow) arrow.style.transform = arrow.style.transform === 'rotate(180deg)' ? '' : 'rotate(180deg)';
});
});
}
content.innerHTML = `
${statsHtml}
${expiryHtml}
${recentHtml}
`;
initExpiry();
});