From 68ba7c4a5e68dcf01d0d2327f68ce124c19bac1d Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 2 Aug 2025 11:17:21 -0400 Subject: [PATCH] time-off history --- public/index.html | 75 ++++++++++++++++++++++++++++++++++------------- server.js | 32 ++++++++++++++++---- 2 files changed, 82 insertions(+), 25 deletions(-) diff --git a/public/index.html b/public/index.html index a9af8eb..f5165f3 100644 --- a/public/index.html +++ b/public/index.html @@ -151,13 +151,12 @@ async function renderEmployeeDashboard() { clearInterval(employeeTimerInterval); - // Fetch notes along with other data const [statusRes, timeOffRes, notesRes] = await Promise.all([apiCall('/status'), apiCall('/user/time-off-requests'), apiCall('/user/notes')]); if (!statusRes.success || !timeOffRes.success || !notesRes.success) return; const entries = statusRes.data; const requests = timeOffRes.data; - const notes = notesRes.data; // Get notes data + const notes = notesRes.data; const last = entries[0]; const punchedIn = last?.status === 'in'; let totalMilliseconds = entries.reduce((acc, e) => { @@ -181,36 +180,28 @@

Notes from Admin

- +
-
-

My Account

- -
-
-

My Total Hours (This Pay Period)

-

${formatDecimal(totalMilliseconds)}

-
+

My Account

+

My Total Hours (This Pay Period)

${formatDecimal(totalMilliseconds)}

+
-

Time Off Requests

+
+

Time Off Requests

+ +
-
${requests.map(r => ``).join('') || ''}
DatesReasonStatus
${formatDate(r.start_date)} - ${formatDate(r.end_date)}${r.reason || ''}${r.status}
No requests.
+
${requests.map(r => ``).join('') || ''}
DatesReasonStatus
${formatDate(r.start_date)} - ${formatDate(r.end_date)}${r.reason || ''}${r.status}
No upcoming or pending requests.
+

My Time Log

${entries.map(e => ``).join('') || ''}
InOutDuration (Hours)
${formatDateTime(e.punch_in_time)}${formatDateTime(e.punch_out_time)}${e.status === 'in' ? 'Running...' : formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time))}
No entries.
@@ -219,6 +210,8 @@ document.getElementById('punch-btn').addEventListener('click', handlePunch); document.getElementById('change-password-btn').addEventListener('click', renderChangePasswordModal); document.getElementById('time-off-form').addEventListener('submit', handleTimeOffRequest); + document.getElementById('view-request-history-btn').addEventListener('click', handleViewRequestHistoryClick); // Attach handler to new button + if (punchedIn) { const durationCell = document.getElementById(`duration-${last.id}`); const totalHoursCell = document.getElementById('employee-total-hours'); @@ -400,6 +393,48 @@ async function renderAdminDashboard() { renderAdminDashboard(); } } + +function renderRequestHistoryModal(requests) { + const modalBody = ` +
+ + + + + + ${requests.map(r => ` + + + + + + `).join('') || ''} + +
DatesReasonStatus
${formatDate(r.start_date)} - ${formatDate(r.end_date)}${r.reason || ''}${r.status}
No history found.
+
`; + + // Using a simplified modal since we only need a "Close" button + modalContainer.innerHTML = ` + `; + + document.querySelector('.cancel-modal-btn').addEventListener('click', () => modalContainer.innerHTML = ''); + document.querySelector('.modal-overlay').addEventListener('click', (e) => { if (e.target === e.currentTarget) modalContainer.innerHTML = ''; }); +} + +async function handleViewRequestHistoryClick() { + const res = await apiCall('/user/time-off-requests/history'); + if (res.success) { + renderRequestHistoryModal(res.data); + } +} async function handleAddNote(e) { e.preventDefault(); const userId = e.target.elements['note-user-select'].value; diff --git a/server.js b/server.js index 9f2bdfc..289e365 100644 --- a/server.js +++ b/server.js @@ -177,12 +177,34 @@ function setupRoutes() { app.get('/api/user/time-off-requests', authenticateToken, async (req, res) => { try { - const rows = await db.all("SELECT * FROM time_off_requests WHERE user_id = ? ORDER BY start_date DESC", [req.user.id]); - res.json(rows); - } catch { - res.status(500).json({ message: "Failed to fetch requests." }); + // This query now only gets requests that are pending OR have an end date in the future. + const rows = await db.all( + `SELECT * FROM time_off_requests + WHERE user_id = ? AND (status = 'pending' OR end_date >= date('now')) + ORDER BY start_date ASC`, + [req.user.id] + ); + res.json(rows); + } catch (err) { + console.error("Error fetching time off requests:", err); + res.status(500).json({ message: "Failed to fetch requests." }); } - }); +}); + +app.get('/api/user/time-off-requests/history', authenticateToken, async (req, res) => { + try { + // This query gets ALL requests for the user, sorted by newest first. + const rows = await db.all( + "SELECT * FROM time_off_requests WHERE user_id = ? ORDER BY start_date DESC", + [req.user.id] + ); + res.json(rows); + } catch (err) { + console.error("Error fetching time off history:", err); + res.status(500).json({ message: "Failed to fetch request history." }); + } +}); + // --- Admin Tools --- app.post('/api/admin/force-clock-out', authenticateToken, requireRole('admin'), async (req, res) => {