diff --git a/public/js/main.js b/public/js/main.js
index 683cc48..36d41d2 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -301,6 +301,7 @@ function handleAdminDashboardClick(e) {
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('archive-log-btn') && confirm('Archive this time entry?')) apiCall(`/admin/logs/archive/${id}`, 'POST').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());
if (target.classList.contains('reset-pw-btn')) renderResetPasswordModal(username, handleResetPassword);
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()); }
diff --git a/public/js/ui.js b/public/js/ui.js
index 55a97ad..963a599 100644
--- a/public/js/ui.js
+++ b/public/js/ui.js
@@ -242,7 +242,8 @@ export async function renderAdminDashboard() {
diff --git a/server.js b/server.js
index 60d9cc7..4e88547 100644
--- a/server.js
+++ b/server.js
@@ -452,6 +452,35 @@ app.get('/api/user/notes', authenticateToken, async (req, res) => {
res.status(500).json({ message: 'Failed to delete time entry.' });
}
});
+
+ app.post('/api/admin/logs/archive/:id', authenticateToken, requireRole('admin'), async (req, res) => {
+ try {
+ const { id } = req.params;
+ const entry = await db.get("SELECT * FROM time_entries WHERE id = ?", [id]);
+
+ if (!entry) {
+ return res.status(404).json({ message: "Time entry not found." });
+ }
+
+ if (entry.status !== 'out') {
+ return res.status(400).json({ message: "Only completed (punched-out) entries can be archived." });
+ }
+
+ const archivedAt = new Date().toISOString();
+ await db.exec('BEGIN TRANSACTION');
+ await db.run('INSERT INTO archived_time_entries (id, user_id, username, punch_in_time, punch_out_time, status, archived_at) VALUES (?, ?, ?, ?, ?, ?, ?)',
+ [entry.id, entry.user_id, entry.username, entry.punch_in_time, entry.punch_out_time, entry.status, archivedAt]
+ );
+ await db.run('DELETE FROM time_entries WHERE id = ?', [id]);
+ await db.exec('COMMIT');
+
+ res.json({ message: 'Time entry archived successfully.' });
+ } catch (err) {
+ await db.exec('ROLLBACK');
+ console.error("Archiving error:", err)
+ res.status(500).json({ message: 'Failed to archive time entry.' });
+ }
+ });
app.post('/api/admin/force-clock-out', authenticateToken, requireRole('admin'), async (req, res) => {
try {