diff --git a/public/js/main.js b/public/js/main.js
index 659efb7..5caf147 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -204,20 +204,19 @@ export function attachEmployeeDashboardListeners() {
document.getElementById('view-request-history-btn').addEventListener('click', handleViewRequestHistoryClick);
}
+// In js/main.js
+
export function attachAdminDashboardListeners() {
- // Event delegation for all buttons
+ // This master listener handles most buttons in the admin view via event delegation
document.getElementById('admin-dashboard').addEventListener('click', handleAdminDashboardClick);
- // Specific form handlers
+ // Listeners for specific forms that need to prevent default submission behavior
document.getElementById('create-user-form').addEventListener('submit', handleCreateUser);
document.getElementById('add-punch-form').addEventListener('submit', handleAddPunch);
document.getElementById('add-note-form').addEventListener('submit', handleAddNote);
-
- // Other top-level buttons
- document.getElementById('archive-btn').addEventListener('click', handleArchive);
- document.getElementById('view-archives-btn').addEventListener('click', renderArchiveView);
- document.getElementById('view-time-off-history-btn').addEventListener('click', renderTimeOffHistoryView);
- document.getElementById('view-notes-btn').addEventListener('click', handleViewNotesClick);
+
+ // Call the function to make the new tabs work
+ setupTabbedInterface();
}
// --- APP INITIALIZER ---
@@ -241,6 +240,36 @@ function initializeApp() {
}
}
+// This function handles the logic for switching between tabs
+function setupTabbedInterface() {
+ const tabsContainer = document.getElementById('admin-tabs-nav');
+ const contentContainer = document.getElementById('admin-tabs-content');
+
+ // Exit if the tab elements aren't on the page
+ if (!tabsContainer || !contentContainer) return;
+
+ // Use event delegation on the tab navigation container
+ tabsContainer.addEventListener('click', (e) => {
+ const clickedTab = e.target.closest('.tab-btn');
+ // Ignore clicks that aren't on a tab button
+ if (!clickedTab) return;
+
+ const tabTarget = clickedTab.dataset.tab;
+
+ // Update the active state on tab buttons
+ tabsContainer.querySelectorAll('.tab-btn').forEach(btn => {
+ btn.classList.remove('active-tab');
+ });
+ clickedTab.classList.add('active-tab');
+
+ // Show the correct content panel and hide the others
+ contentContainer.querySelectorAll('[id^="tab-content-"]').forEach(panel => {
+ panel.classList.add('hidden');
+ });
+ document.getElementById(`tab-content-${tabTarget}`).classList.remove('hidden');
+ });
+}
+
// --- START THE APP ---
// Attach global listeners that are always present.
document.getElementById('sign-out-btn').addEventListener('click', () => handleSignOut('You have been signed out.'));
diff --git a/public/js/ui.js b/public/js/ui.js
index 0ca95a3..e03526e 100644
--- a/public/js/ui.js
+++ b/public/js/ui.js
@@ -147,42 +147,70 @@ export async function renderEmployeeDashboard() {
}
}
+// In js/ui.js
+
export async function renderAdminDashboard() {
showView('admin');
const [logsRes, usersRes, requestsRes] = await Promise.all([apiCall('/admin/logs'), apiCall('/admin/users'), apiCall('/admin/time-off-requests/pending')]);
if (!logsRes.success || !usersRes.success || !requestsRes.success) return;
- // Update module-level state
+ // --- Existing data processing logic (no changes here) ---
allTimeEntries = logsRes.data;
allUsers = usersRes.data;
const pendingRequests = requestsRes.data;
const user = JSON.parse(localStorage.getItem('user'));
-
const employeeTotals = allTimeEntries.reduce((acc, entry) => { const dur = entry.punch_out_time ? (new Date(entry.punch_out_time) - new Date(entry.punch_in_time)) : (Date.now() - new Date(entry.punch_in_time)); acc[entry.username] = (acc[entry.username] || 0) + dur; return acc; }, {});
const punchedInEntries = allTimeEntries.filter(e => e.status === 'in');
const employeesOnly = allUsers.filter(u => u.role === 'employee');
-
- mainViews.admin.innerHTML = `
-
-
Admin Dashboard View Archives Archive Records
-
-
-
Pending Time Off Requests View History Employee Dates Reason Actions ${pendingRequests.map(r => `${r.username} ${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)} ${r.reason||''} Approve Deny
`).join('') || 'No pending requests. '}
-
Hours by Employee Employee Total Hours ${Object.entries(employeeTotals).map(([username, totalMs]) => `${username} ${utils.formatDecimal(totalMs)} `).join('') || 'No data. '}
-
Detailed Logs Employee In Out Duration Actions ${allTimeEntries.map(e => `${e.username||'N/A'} ${utils.formatDateTime(e.punch_in_time)} ${utils.formatDateTime(e.punch_out_time)} ${e.punch_out_time ? utils.formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time)) + ' hrs' : '...'} Edit Delete
`).join('')}
-
-
`;
-
- attachAdminDashboardListeners(); // Attach all listeners for this view
+ // --- New Tabbed HTML Structure ---
+ mainViews.admin.innerHTML = `
+
+
+
Admin Dashboard
+
+
+
+ Overview
+ Time Logs
+ User Management
+
+
+
+
+
+
+
Pending Time Off Requests View History Employee Dates Reason Actions ${pendingRequests.map(r => `${r.username} ${utils.formatDate(r.start_date)} - ${utils.formatDate(r.end_date)} ${r.reason||''} Approve Deny
`).join('') || 'No pending requests. '}
+
+
Employee Notes
+
+ -- Select an Employee -- ${employeesOnly.map(u => `${u.username} `).join('')}
+
+ Submit Note View Notes
+
+
+
+
+
+
+
View Archives Archive All Completed Records
+
Hours by Employee Employee Total Hours ${Object.entries(employeeTotals).map(([username, totalMs]) => `${username} ${utils.formatDecimal(totalMs)} `).join('') || 'No data. '}
+
Detailed Logs Employee In Out Duration Actions ${allTimeEntries.map(e => `${e.username||'N/A'} ${utils.formatDateTime(e.punch_in_time)} ${utils.formatDateTime(e.punch_out_time)} ${e.punch_out_time ? utils.formatDecimal(new Date(e.punch_out_time) - new Date(e.punch_in_time)) + ' hrs' : '...'} Edit Delete
`).join('')}
+
+
+
+
+
+ `;
+
+ // The rest of the function remains the same
+ attachAdminDashboardListeners();
punchedInEntries.forEach(entry => {
const durationCell = document.getElementById(`admin-duration-${entry.id}`);
if (durationCell) {
diff --git a/public/style/style.css b/public/style/style.css
index e3c59df..9a3f9f5 100644
--- a/public/style/style.css
+++ b/public/style/style.css
@@ -28,4 +28,20 @@ body {
border-radius: 0.5rem;
width: 90%;
max-width: 500px;
+}
+
+/* Tab Styles */
+.tab-btn {
+ /* Smooth transition for color and border changes */
+ transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
+ /* A transparent bottom border on all tabs to prevent layout shift */
+ border-bottom: 3px solid transparent;
+}
+
+.active-tab {
+ /* The blue bottom border for the active tab */
+ border-color: #2563EB; /* Tailwind's blue-600 */
+ /* A darker text color for the active tab to make it stand out */
+ color: #1E40AF; /* Tailwind's blue-800 */
+ font-weight: 500;
}
\ No newline at end of file