From 6e898d60ae923c90b5f32eac066c342693e12224 Mon Sep 17 00:00:00 2001 From: chris Date: Sat, 2 Aug 2025 08:04:55 -0400 Subject: [PATCH] revert index --- public/index.html | 175 +++++++++++++++++++++++++++++++--------------- 1 file changed, 117 insertions(+), 58 deletions(-) diff --git a/public/index.html b/public/index.html index bc5ae20..0586c2f 100644 --- a/public/index.html +++ b/public/index.html @@ -22,7 +22,7 @@
-
-
+
@@ -53,6 +53,7 @@ const navUserControls = document.getElementById('nav-user-controls'), welcomeMessage = document.getElementById('welcome-message'), signOutBtn = document.getElementById('sign-out-btn'); const messageBox = document.getElementById('message-box'), loadingSpinner = document.getElementById('loading-spinner'), modalContainer = document.getElementById('modal-container'); + // **FIXED**: Declared variables safely without parsing immediately. This prevents the script from crashing. let authToken, user, allTimeEntries = [], allUsers = [], employeeTimerInterval = null; // --- Helper Functions --- @@ -99,7 +100,7 @@ if (authToken && user) { navUserControls.classList.remove('hidden'); - welcomeMessage.textContent = `${user.username}`; + welcomeMessage.textContent = `Welcome, ${user.username}`; user.role === 'admin' ? (showView('admin'), renderAdminDashboard()) : (showView('employee'), renderEmployeeDashboard()); } else { navUserControls.classList.add('hidden'); @@ -128,9 +129,41 @@ const punchedIn = last?.status === 'in'; let totalMilliseconds = entries.reduce((acc, e) => e.status === 'out' ? acc + (new Date(e.punch_out_time) - new Date(e.punch_in_time)) : acc, 0); - mainViews.employee.innerHTML = `
...
`; // Employee dashboard code is unchanged + mainViews.employee.innerHTML = ` +
+
+
+

Current Status

${punchedIn ? 'Punched In' : 'Punched Out'}

${punchedIn ? 'Since:' : 'Last Punch:'} ${formatDateTime(punchedIn ? last.punch_in_time : last?.punch_out_time)}

+
+
+
+
+

My Account

+

My Total Hours

${formatDecimal(totalMilliseconds)}

+
+
+

Time Off Requests

+
+
+
+ +
+
+
${requests.map(r => ``).join('') || ''}
DatesReasonStatus
${formatDate(r.start_date)} - ${formatDate(r.end_date)}${r.reason || ''}${r.status}
No 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.
+
+
`; document.getElementById('punch-btn').addEventListener('click', handlePunch); - // ... other employee event listeners + 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); + } } async function renderAdminDashboard() { @@ -142,65 +175,20 @@ 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

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

Pending Time Off Requests

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

Hours by Employee

${Object.keys(employeeTotals).map(u => ``).join('')}
EmployeeTotal Hours
${u}${formatDecimal(employeeTotals[u])}
- -
-

Detailed Logs

-
- ${allTimeEntries.map(e => ``).join('')}
EmployeeInOutDurationActions
${e.username||'N/A'}${formatDateTime(e.punch_in_time)}${formatDateTime(e.punch_out_time)}${ - e.status === 'in' || !e.punch_out_time - ? 'Running...' - : formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time)) -}
-
-
- -
-

User & Payroll Management

-
-

Create User

-

Add Manual Punch

-
-
-

Manage Users

-
- ${allUsers.map(u => ``).join('')}
UsernameRoleActions
${u.username}${u.role}${u.isPrimary ? `Primary Admin` : `${u.username !== user.username ? `` : ''}`}
-
-
-
+

Detailed Logs

${allTimeEntries.map(e => ``).join('')}
EmployeeInOutDurationActions
${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))}
+

User & Payroll Management

Create User

Add Manual Punch

Manage Users

${allUsers.map(u => ``).join('')}
UsernameRoleActions
${u.username}${u.role}${u.isPrimary ? `Primary Admin` : `${u.username !== user.username ? `` : ''}`}
`; document.getElementById('archive-btn').addEventListener('click', handleArchive); document.getElementById('view-archives-btn').addEventListener('click', renderArchiveView); @@ -210,11 +198,82 @@ document.getElementById('admin-dashboard').addEventListener('click', handleAdminDashboardClick); } - // ... other functions (renderArchiveView, renderTimeOffHistoryView, modals, event handlers) are unchanged ... + function renderArchiveView() { + apiCall('/admin/archives').then(res => { + if (!res.success) return; + showView('archive'); + mainViews.archive.innerHTML = `

Archived Logs

${res.data.map(e => ``).join('') || ''}
EmployeeInOutDurationArchived On
${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)}
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

+
${res.data.map(r => ``).join('') || ''}
EmployeeDatesReasonStatus
${r.username}${formatDate(r.start_date)} - ${formatDate(r.end_date)}${r.reason||''}${r.status}
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(); } } // --- Initializer --- signOutBtn.addEventListener('click', () => handleSignOut()); updateUI(); +