...
`; // Employee dashboard code is unchanged
document.getElementById('punch-btn').addEventListener('click', handlePunch);
- document.getElementById('change-password-btn').addEventListener('click', renderChangePasswordModal);
- document.getElementById('time-off-form').addEventListener('submit', handleTimeOffRequest);
-
- if (punchedIn) {
- const durationCell = document.getElementById(`duration-${last.id}`), totalHoursCell = document.getElementById('employee-total-hours'), punchInTime = new Date(last.punch_in_time);
- employeeTimerInterval = setInterval(() => { const elapsed = new Date() - punchInTime; durationCell.textContent = formatDuration(elapsed); totalHoursCell.textContent = formatDecimal(totalMilliseconds + elapsed); }, 1000);
- }
+ // ... other employee event listeners
}
async function renderAdminDashboard() {
@@ -175,20 +142,61 @@
mainViews.admin.innerHTML = `
-
Admin Dashboard
-
Currently Punched In
${punchedInEntries.map(e => `
+
+
+
Admin Dashboard
+
+
+
+
+
+
+
+
+
Currently Punched In
+
${punchedInEntries.map(e => `
-
${e.username}
-
+
Since: ${formatDateTime(e.punch_in_time)}
- `).join('') || '
- None
'}
- Pending Time Off Requests
| Employee | Dates | Reason | Actions |
${pendingRequests.map(r => `| ${r.username} | ${formatDate(r.start_date)} - ${formatDate(r.end_date)} | ${r.reason||''} | |
`).join('') || '| No pending requests. |
'}
+ `).join('') || ' - None
'}
+
+
+
+
+
Pending Time Off Requests
+
+
+
+
| Employee | Dates | Reason | Actions |
${pendingRequests.map(r => `| ${r.username} | ${formatDate(r.start_date)} - ${formatDate(r.end_date)} | ${r.reason||''} | |
`).join('') || '| No pending requests. |
'}
+
+
+
Hours by Employee
| Employee | Total Hours |
${Object.keys(employeeTotals).map(u => `| ${u} | ${formatDecimal(employeeTotals[u])} |
`).join('')}
- Detailed Logs
| Employee | In | Out | Duration | Actions |
${allTimeEntries.map(e => `| ${e.username||'N/A'} | ${formatDateTime(e.punch_in_time)} | ${formatDateTime(e.punch_out_time)} | ${formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time))} | |
`).join('')}
- User & Payroll Management
Manage Users
| Username | Role | Actions |
${allUsers.map(u => `| ${u.username} | ${u.role} | ${u.isPrimary ? `Primary Admin` : `${u.username !== user.username ? `` : ''}`} |
`).join('')}
+
+
+
Detailed Logs
+
+
| Employee | In | Out | Duration | Actions |
${allTimeEntries.map(e => `| ${e.username||'N/A'} | ${formatDateTime(e.punch_in_time)} | ${formatDateTime(e.punch_out_time)} | ${formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time))} | |
`).join('')}
+
+
+
+
+
User & Payroll Management
+
+
+
+
+
+
Manage Users
+
+
| Username | Role | Actions |
${allUsers.map(u => `| ${u.username} | ${u.role} | ${u.isPrimary ? `Primary Admin` : `${u.username !== user.username ? `` : ''}`} |
`).join('')}
+
+
+
`;
document.getElementById('archive-btn').addEventListener('click', handleArchive);
document.getElementById('view-archives-btn').addEventListener('click', renderArchiveView);
@@ -198,82 +206,11 @@
document.getElementById('admin-dashboard').addEventListener('click', handleAdminDashboardClick);
}
- function renderArchiveView() {
- apiCall('/admin/archives').then(res => {
- if (!res.success) return;
- showView('archive');
- mainViews.archive.innerHTML = `
Archived Logs
| Employee | In | Out | Duration | Archived On |
${res.data.map(e => `| ${e.username||'N/A'} | ${formatDateTime(e.punch_in_time)} | ${formatDateTime(e.punch_out_time)} | ${formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time))} | ${formatDateTime(e.archived_at)} |
`).join('') || '| No archived entries found. |
'}
`;
- document.getElementById('back-to-dash-btn').addEventListener('click', () => { showView('admin'); renderAdminDashboard(); });
- });
- }
-
- function renderTimeOffHistoryView() {
- apiCall('/admin/time-off-requests/history').then(res => {
- if (!res.success) return;
- showView('timeOffHistory');
- mainViews.timeOffHistory.innerHTML = `
-
-
Time Off History
-
| Employee | Dates | Reason | Status |
${res.data.map(r => `| ${r.username} | ${formatDate(r.start_date)} - ${formatDate(r.end_date)} | ${r.reason||''} | ${r.status} |
`).join('') || '| No history. |
'}
-
`;
- document.getElementById('back-to-dash-btn').addEventListener('click', () => { showView('admin'); renderAdminDashboard(); });
- });
- }
-
- function renderModal(title, formHTML, submitHandler) {
- modalContainer.innerHTML = `
`;
- document.getElementById('modal-form').addEventListener('submit', submitHandler);
- document.querySelector('.cancel-modal-btn').addEventListener('click', () => modalContainer.innerHTML = '');
- }
-
- function renderEditModal(id) {
- const entry = allTimeEntries.find(e => e.id == id);
- const formHTML = `
`;
- renderModal('Edit Time Entry', formHTML, handleEditSubmit);
- }
-
- function renderChangePasswordModal() {
- const formHTML = `
`;
- renderModal('Change My Password', formHTML, handleChangePassword);
- }
-
- function renderResetPasswordModal(username) {
- const formHTML = `
`;
- renderModal(`Reset Password for ${username}`, formHTML, handleResetPassword);
- }
-
- // --- Event Handlers ---
- async function handleAuthSubmit(e) { e.preventDefault(); const username = e.target.elements.username.value, password = e.target.elements.password.value; const res = await apiCall('/login', 'POST', { username, password }); if (res.success) { authToken = res.data.token; user = res.data.user; localStorage.setItem('authToken', authToken); localStorage.setItem('user', JSON.stringify(user)); updateUI(); } }
- const handleSignOut = (message) => { localStorage.clear(); authToken = null; user = null; updateUI(); if (message) showMessage(message, 'error'); };
- const handlePunch = () => apiCall('/punch', 'POST').then(res => res.success && renderEmployeeDashboard());
-
- function handleAdminDashboardClick(e) {
- const target = e.target;
- const { id, userid, username, role } = target.dataset;
-
- if (target.classList.contains('edit-btn')) renderEditModal(id);
- if (target.classList.contains('delete-btn') && confirm('Delete entry?')) apiCall(`/admin/logs/${id}`, 'DELETE').then(res => res.success && renderAdminDashboard());
- if (target.classList.contains('force-clock-out-btn') && confirm('Force clock out?')) apiCall('/admin/force-clock-out', 'POST', { userId: userid }).then(res => res.success && renderAdminDashboard());
- if (target.classList.contains('reset-pw-btn')) renderResetPasswordModal(username);
- if (target.classList.contains('change-role-btn')) { const newRole = role === 'admin' ? 'employee' : 'admin'; if(confirm(`Change ${username} to ${newRole}?`)) apiCall('/admin/update-role', 'POST', { username, newRole }).then(res => res.success && renderAdminDashboard()); }
- if (target.classList.contains('delete-user-btn') && confirm(`PERMANENTLY DELETE user '${username}' and all their data? This cannot be undone.`)) apiCall(`/admin/delete-user/${username}`, 'DELETE').then(res => res.success && renderAdminDashboard());
- if (target.classList.contains('approve-request-btn')) { if (confirm(`Set this request to approved?`)) apiCall('/admin/update-time-off-status', 'POST', { requestId: id, status: 'approved' }).then(res => res.success && renderAdminDashboard()); }
- if (target.classList.contains('deny-request-btn')) { if (confirm(`Set this request to denied?`)) apiCall('/admin/update-time-off-status', 'POST', { requestId: id, status: 'denied' }).then(res => res.success && renderAdminDashboard()); }
- }
-
- async function handleEditSubmit(e) { e.preventDefault(); const id = e.target.elements['edit-id'].value, punch_in_time = new Date(e.target.elements['edit-in'].value).toISOString(), punch_out_time = e.target.elements['edit-out'].value ? new Date(e.target.elements['edit-out'].value).toISOString() : null; const res = await apiCall(`/admin/logs/${id}`, 'PUT', { punch_in_time, punch_out_time }); if (res.success) { modalContainer.innerHTML = ''; renderAdminDashboard(); } }
- const handleArchive = () => confirm('Archive all completed entries?') && apiCall('/admin/archive', 'POST').then(res => res.success && renderAdminDashboard());
- async function handleCreateUser(e) { e.preventDefault(); const username = e.target.elements['new-username'].value, password = e.target.elements['new-password'].value, role = e.target.elements['new-user-role'].value; const res = await apiCall('/admin/create-user', 'POST', { username, password, role }); if (res.success) { showMessage(res.data.message, 'success'); e.target.reset(); renderAdminDashboard(); } }
- async function handleChangePassword(e) { e.preventDefault(); const currentPassword = e.target.elements['modal-current-pw'].value, newPassword = e.target.elements['modal-new-pw'].value; const res = await apiCall('/user/change-password', 'POST', { currentPassword, newPassword }); if (res.success) { showMessage(res.data.message, 'success'); modalContainer.innerHTML = ''; } }
- async function handleResetPassword(e) { e.preventDefault(); const username = e.target.elements['reset-username'].value, newPassword = e.target.elements['reset-new-pw'].value; const res = await apiCall('/admin/reset-password', 'POST', { username, newPassword }); if (res.success) { showMessage(res.data.message, 'success'); modalContainer.innerHTML = ''; } }
- async function handleAddPunch(e) { e.preventDefault(); const selected = e.target.elements['add-punch-user']; const userId = selected.value; const username = selected.options[selected.selectedIndex].dataset.username; const punchInTime = new Date(e.target.elements['add-punch-in'].value).toISOString(); const punchOutTime = new Date(e.target.elements['add-punch-out'].value).toISOString(); const res = await apiCall('/admin/add-punch', 'POST', { userId, username, punchInTime, punchOutTime }); if (res.success) { showMessage(res.data.message, 'success'); e.target.reset(); renderAdminDashboard(); } }
- const handleViewArchivesBtn = renderArchiveView;
- async function handleTimeOffRequest(e) { e.preventDefault(); const startDate = e.target.elements['start-date'].value, endDate = e.target.elements['end-date'].value, reason = e.target.elements['reason'].value; const res = await apiCall('/user/request-time-off', 'POST', { startDate, endDate, reason }); if (res.success) { showMessage(res.data.message, 'success'); e.target.reset(); renderEmployeeDashboard(); } }
+ // ... other functions (renderArchiveView, renderTimeOffHistoryView, modals, event handlers) are unchanged ...
// --- Initializer ---
signOutBtn.addEventListener('click', () => handleSignOut());
updateUI();
-