diff --git a/public/js/main.js b/public/js/main.js index bc0df10..7338f61 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -206,6 +206,40 @@ function handleAdminDashboardClick(e) { return; } + if (target.classList.contains('set-pending-btn')) { + if (confirm('Are you sure you want to move this request back to pending?')) { + apiCall('/admin/update-time-off-status', 'POST', { requestId: id, status: 'pending' }) + .then(res => { + if (res.success) { + showMessage('Request status set to pending.', 'success'); + renderTimeOffHistoryView(); + } + }); + } + return; + } + + if (target.classList.contains('admin-delete-request-btn')) { + if (confirm('Are you sure you want to permanently delete this request? This cannot be undone.')) { + apiCall(`/admin/time-off-requests/${id}`, 'DELETE') + .then(res => { + if (res.success) { + showMessage('Request deleted.', 'success'); + if (document.getElementById('tab-content-overview')) { + apiCall('/admin/time-off-requests/pending').then(requestsRes => { + if (requestsRes.success) { + updatePendingRequestsList(requestsRes.data); + } + }); + } else { + renderTimeOffHistoryView(); + } + } + }); + } + return; + } + if (target.classList.contains('edit-btn')) renderEditModal(id, handleEditSubmit); if (target.classList.contains('delete-btn') && confirm('Delete this time entry?')) apiCall(`/admin/logs/${id}`, 'DELETE').then(res => res.success && renderAdminDashboard()); if (target.classList.contains('force-clock-out-btn') && confirm(`Force clock out ${username}?`)) apiCall('/admin/force-clock-out', 'POST', { userId: userid }).then(res => res.success && renderAdminDashboard()); @@ -215,7 +249,6 @@ function handleAdminDashboardClick(e) { if (target.classList.contains('delete-note-btn')) { if (confirm('Delete this note?')) { apiCall(`/admin/notes/${noteId}`, 'DELETE').then(res => { if (res.success) { showMessage('Note deleted.', 'success'); handleViewNotesClick(); }});}} } -// --- NEW: Handler for editing a time-off request --- async function handleEditTimeOffSubmit(e) { e.preventDefault(); const id = e.target.elements['edit-request-id'].value; @@ -231,7 +264,7 @@ async function handleEditTimeOffSubmit(e) { if (res.success) { showMessage(res.data.message, 'success'); document.getElementById('modal-container').innerHTML = ''; - renderEmployeeDashboard(); // Refresh the dashboard to show changes + renderEmployeeDashboard(); } } @@ -241,12 +274,10 @@ export function attachAuthFormListener() { if (form) form.addEventListener('submit', handleAuthSubmit); } -// --- UPDATED: Employee dashboard listeners now use event delegation --- export function attachEmployeeDashboardListeners() { const dashboard = document.getElementById('employee-dashboard'); if (!dashboard) return; - // Use one listener for the entire dashboard dashboard.addEventListener('click', async (e) => { const target = e.target; @@ -271,7 +302,6 @@ export function attachEmployeeDashboardListeners() { } if (target.classList.contains('edit-request-btn')) { const id = target.dataset.id; - // We need to get the full request data to pre-fill the form const res = await apiCall('/user/time-off-requests/history'); if (res.success) { const requestToEdit = res.data.find(r => r.id == id); @@ -282,7 +312,6 @@ export function attachEmployeeDashboardListeners() { } }); - // Handle form submissions separately const timeOffForm = document.getElementById('time-off-form'); if (timeOffForm) { timeOffForm.addEventListener('submit', handleTimeOffRequest); diff --git a/public/js/ui.js b/public/js/ui.js index 85a2014..4507c48 100644 --- a/public/js/ui.js +++ b/public/js/ui.js @@ -201,7 +201,30 @@ export async function renderAdminDashboard() {

Currently Punched In

    ${punchedInEntries.map(e => `
  • ${e.username}
    Since: ${utils.formatDateTime(e.punch_in_time)}
  • `).join('') || '
  • None
  • '}
-

Pending Time Off Requests

${pendingRequests.map(r => ``).join('') || ''}
EmployeeDatesReasonActions
${r.username}${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)}${r.reason||''}
No pending requests.
+
+

Pending Time Off Requests

+
+ + + + ${pendingRequests.map(r => ` + + + + + + + `).join('') || ''} + +
EmployeeDatesReasonActions
${r.username}${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)}${r.reason||''} +
+ + + +
+
No pending requests.
+
+

Employee Notes

@@ -263,7 +286,38 @@ export function renderTimeOffHistoryView() { if (!res.success) return; showView('timeOffHistory'); mainViews.timeOffHistory.innerHTML = ` -

Time Off History

${res.data.map(r => ``).join('') || ''}
EmployeeDatesReasonStatus
${r.username}${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)}${r.reason||''}${r.status}
No history.
`; +
+

Time Off History

+
+ + + + + + + + + + + + ${res.data.map(r => ` + + + + + + + + `).join('') || ''} + +
EmployeeDatesReasonStatusActions
${r.username}${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)}${r.reason||''}${r.status} +
+ + +
+
No history.
+
+
`; document.getElementById('back-to-dash-btn').addEventListener('click', renderAdminDashboard); }); } @@ -374,6 +428,7 @@ export function updatePendingRequestsList(requests) {
+
diff --git a/server.js b/server.js index ac49e7b..dea41e1 100644 --- a/server.js +++ b/server.js @@ -385,6 +385,19 @@ function setupRoutes() { } }); + app.delete('/api/admin/time-off-requests/:id', authenticateToken, requireRole('admin'), async (req, res) => { + try { + const { id } = req.params; + const result = await db.run('DELETE FROM time_off_requests WHERE id = ?', [id]); + if (result.changes === 0) { + return res.status(404).json({ message: "Request not found." }); + } + res.json({ message: 'Time off request permanently deleted.' }); + } catch (err) { + res.status(500).json({ message: 'Error deleting time off request.' }); + } +}); + app.post('/api/admin/notes', authenticateToken, requireRole('admin'), async (req, res) => { try { const { userId, noteText } = req.body;