/** * Enhanced Attendance Report JavaScript * Handles filtering, sorting, pagination, and new location/accuracy features */ // Global variables let currentPage = 1; let entriesPerPage = 50; let sortColumn = -1; let sortDirection = "asc"; let attendanceData = []; let filteredData = []; // Charts let dailyChart = null; let locationChart = null; // Initialize page when DOM is loaded document.addEventListener("DOMContentLoaded", function () { console.log("Enhanced Attendance Report page initialized"); initializeReport(); loadAttendanceData(); initializeCharts(); setupEventListeners(); initializeDateRangeFilters(); }); function initializeReport() { // Load data from table loadTableData(); // Initialize pagination updatePagination(); // Apply initial filters if any applyFilters(); } function loadTableData() { const table = document.getElementById("attendanceTable"); if (table) { const rows = table.querySelectorAll("tbody tr"); attendanceData = Array.from(rows).map((row, index) => { const cells = row.querySelectorAll("td"); // Debug: Log the actual cell content if (index < 3) { // Only log first 3 rows for debugging console.log(`=== DEBUGGING ROW ${index + 1} ===`); console.log( `Cell 7 (check-in address):`, cells[7] ? cells[7].innerHTML : "NOT FOUND" ); console.log( `Cell 8 (accuracy):`, cells[8] ? cells[8].innerHTML : "NOT FOUND" ); if (cells[8]) { const accuracyText = cells[8].textContent; console.log(`Accuracy text:`, accuracyText); const milesMatch = accuracyText.match(/(\d+\.?\d*)\s*mi/); const metersMatch = accuracyText.match(/(\d+\.?\d*)\s*m/); console.log(`Miles match:`, milesMatch); console.log(`Meters match:`, metersMatch); } } return { id: row.dataset.recordId, index: index + 1, employeeId: cells[1] ? cells[1].textContent.trim() : "", employeeName: cells[2] ? cells[2].textContent.trim() : "", location: cells[3] ? cells[3].textContent.trim() : "", event: cells[4] ? cells[4].textContent.trim() : "", date: cells[5] ? cells[5].textContent.trim() : "", time: cells[6] ? cells[6].textContent.trim() : "", qr_address: cells[7] ? cells[7].getAttribute("title") || cells[7].textContent.trim() : "", checked_in_address: cells[8] ? cells[8].getAttribute("title") || cells[8].textContent.trim() : "", location_accuracy: cells[9] ? extractLocationAccuracy(cells[9]) : null, accuracy_level: cells[9] ? extractLocationAccuracyLevel(cells[9]) : "unknown", device: cells[10] ? cells[10].textContent.trim() : "" }; }); filteredData = [...attendanceData]; console.log(`Loaded ${attendanceData.length} attendance records`); // Debug log for location accuracy data const recordsWithAccuracy = attendanceData.filter( (r) => r.location_accuracy !== null ); console.log( `Records with location accuracy: ${recordsWithAccuracy.length}` ); if (recordsWithAccuracy.length > 0) { console.log( `Sample records with accuracy:`, recordsWithAccuracy.slice(0, 3).map((r) => ({ employeeId: r.employeeId, location_accuracy: r.location_accuracy, accuracy_level: r.accuracy_level, qr_address: r.qr_address, checked_in_address: r.checked_in_address, })) ); } // Log all first 3 records for debugging console.log("First 3 attendance records:", attendanceData.slice(0, 3)); } } function extractAccuracyValue(cell) { const text = cell.textContent; const match = text.match(/(\d+\.?\d*)m/); return match ? parseFloat(match[1]) : null; } function extractAccuracyLevel(cell) { const text = cell.textContent; if (text.includes("high")) return "high"; if (text.includes("medium")) return "medium"; if (text.includes("low")) return "low"; return "unknown"; } function extractCoordinates(cell) { // This would need to be enhanced based on actual data structure // For now, return placeholder return "Coordinates available"; } function initializeDateRangeFilters() { const dateFromInput = document.getElementById("date_from"); const dateToInput = document.getElementById("date_to"); if (dateFromInput && dateToInput) { // Set max date to today const today = new Date().toISOString().split("T")[0]; dateFromInput.max = today; dateToInput.max = today; // Add validation to ensure 'from' date is not after 'to' date dateFromInput.addEventListener("change", function () { if (dateToInput.value && this.value > dateToInput.value) { dateToInput.value = this.value; } }); dateToInput.addEventListener("change", function () { if (dateFromInput.value && this.value < dateFromInput.value) { dateFromInput.value = this.value; } }); } } function setupEventListeners() { // Enhanced search and filter listeners const searchInput = document.getElementById("searchInput"); const locationFilter = document.getElementById("location"); const employeeFilter = document.getElementById("employee"); if (searchInput) { searchInput.addEventListener("input", debounce(applyFilters, 300)); } if (locationFilter) { locationFilter.addEventListener("change", applyFilters); } if (employeeFilter) { employeeFilter.addEventListener("input", debounce(applyFilters, 300)); } // Entries per page listener const entriesSelect = document.getElementById("entriesPerPage"); if (entriesSelect) { entriesSelect.addEventListener("change", changeEntriesPerPage); } // Modal close listeners window.addEventListener("click", function (event) { const recordModal = document.getElementById("recordModal"); const mapModal = document.getElementById("mapModal"); if (event.target === recordModal) { closeModal(); } if (event.target === mapModal) { closeMapModal(); } }); // Keyboard shortcuts document.addEventListener("keydown", function (event) { if (event.key === "Escape") { closeModal(); closeMapModal(); } }); } function applyFilters() { const searchTerm = document.getElementById("searchInput")?.value.toLowerCase() || ""; const locationFilter = document.getElementById("location")?.value || ""; const employeeFilter = document.getElementById("employee")?.value.toLowerCase() || ""; filteredData = attendanceData.filter((record) => { const matchesSearch = !searchTerm || record.employeeId.toLowerCase().includes(searchTerm) || record.location.toLowerCase().includes(searchTerm) || record.event.toLowerCase().includes(searchTerm); const matchesLocation = !locationFilter || record.location === locationFilter; const matchesEmployee = !employeeFilter || record.employeeId.toLowerCase().includes(employeeFilter); return matchesSearch && matchesLocation && matchesEmployee; }); currentPage = 1; updateTable(); updatePagination(); updateFilterStats(); } function sortTable(columnIndex) { if (sortColumn === columnIndex) { sortDirection = sortDirection === "asc" ? "desc" : "asc"; } else { sortColumn = columnIndex; sortDirection = "asc"; } const sortKey = getSortKey(columnIndex); filteredData.sort((a, b) => { let aVal = a[sortKey]; let bVal = b[sortKey]; // Handle numeric values for accuracy if (columnIndex === 8 && aVal !== null && bVal !== null) { aVal = parseFloat(aVal); bVal = parseFloat(bVal); } // Handle null values if (aVal === null || aVal === undefined) aVal = ""; if (bVal === null || bVal === undefined) bVal = ""; if (typeof aVal === "string") { aVal = aVal.toLowerCase(); bVal = bVal.toLowerCase(); } let result; if (aVal < bVal) result = -1; else if (aVal > bVal) result = 1; else result = 0; return sortDirection === "asc" ? result : -result; }); updateTable(); updateSortIndicators(columnIndex); } function getSortKey(columnIndex) { const sortKeys = [ "index", "employeeId", "location", "event", "date", "time", "qr_address", "checked_in_address", "accuracy", "device", ]; return sortKeys[columnIndex] || "index"; } function updateSortIndicators(activeColumn) { // Update sort indicators in table headers const headers = document.querySelectorAll(".attendance-table th"); headers.forEach((header, index) => { const icon = header.querySelector("i"); if (icon) { icon.className = "fas fa-sort"; if (index === activeColumn) { icon.className = sortDirection === "asc" ? "fas fa-sort-up" : "fas fa-sort-down"; } } }); } function updateTable() { const table = document.getElementById("attendanceTable"); if (!table) return; const tbody = table.querySelector("tbody"); const startIndex = (currentPage - 1) * entriesPerPage; const endIndex = entriesPerPage === "all" ? filteredData.length : startIndex + entriesPerPage; const pageData = filteredData.slice(startIndex, endIndex); tbody.innerHTML = ""; pageData.forEach((record, index) => { const row = createTableRow(record, startIndex + index + 1); tbody.appendChild(row); }); // Update any dynamic elements updateFilterStats(); } function changeEntriesPerPage() { const select = document.getElementById("entriesPerPage"); entriesPerPage = select.value === "all" ? "all" : parseInt(select.value); currentPage = 1; updateTable(); updatePagination(); } function updatePagination() { const container = document.getElementById("paginationContainer"); if (!container || entriesPerPage === "all") { if (container) container.innerHTML = ""; return; } const totalPages = Math.ceil(filteredData.length / entriesPerPage); if (totalPages <= 1) { container.innerHTML = ""; return; } let paginationHTML = '"; // Add pagination info const startRecord = (currentPage - 1) * entriesPerPage + 1; const endRecord = Math.min(currentPage * entriesPerPage, filteredData.length); paginationHTML += `
Showing ${startRecord} to ${endRecord} of ${ filteredData.length } entries ${ filteredData.length !== attendanceData.length ? `(filtered from ${attendanceData.length} total entries)` : "" }
`; container.innerHTML = paginationHTML; } function goToPage(page) { const totalPages = Math.ceil(filteredData.length / entriesPerPage); if (page < 1 || page > totalPages) return; currentPage = page; updateTable(); updatePagination(); // Scroll to top of table const table = document.getElementById("attendanceTable"); if (table) { table.scrollIntoView({ behavior: "smooth", block: "start" }); } } function updateFilterStats() { // Update stats display if needed const totalRecords = filteredData.length; console.log(`Filtered records: ${totalRecords}`); } // Enhanced record actions function editRecord(recordId) { // Check permissions before allowing edit if (!hasEditPermission) { alert( "Access denied. Only administrators and payroll staff can edit attendance records." ); return; } console.log(`Edit record: ${recordId}`); // Log the action console.log(`[LOG] User attempting to edit attendance record: ${recordId}`); // Redirect to edit page window.location.href = `/attendance/${recordId}/edit`; } function deleteRecord(recordId, employeeId) { // Check permissions before allowing delete if (!hasEditPermission) { alert( "Access denied. Only administrators and payroll staff can delete attendance records." ); return; } console.log(`Delete record: ${recordId}`); // Confirmation dialog const confirmMessage = `Are you sure you want to delete the attendance record for employee "${employeeId}"?\n\nThis action cannot be undone.`; if (confirm(confirmMessage)) { console.log( `[LOG] User confirmed deletion of attendance record: ${recordId}` ); // Send delete request fetch(`/attendance/${recordId}/delete`, { method: "POST", headers: { "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", }, }) .then((response) => response.json()) .then((data) => { if (data.success) { console.log( `[LOG] Successfully deleted attendance record: ${recordId}` ); alert("Attendance record deleted successfully!"); window.location.reload(); } else { console.error( `[LOG] Failed to delete attendance record: ${recordId} - ${data.message}` ); alert(data.message || "Error deleting record. Please try again."); } }) .catch((error) => { console.error( `[LOG] Error during attendance record deletion: ${recordId}`, error ); alert("Error deleting record. Please try again."); }); } } function closeModal() { const modal = document.getElementById("recordModal"); if (modal) { modal.style.display = "none"; } } // Chart initialization (placeholder) function initializeCharts() { console.log("Initializing charts..."); // Chart implementation would go here } function loadAttendanceData() { console.log("Loading attendance data for charts..."); // Additional data loading for charts would go here } // Utility function function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Enhanced JavaScript functions for location accuracy features function loadTableData() { const table = document.getElementById("attendanceTable"); if (table) { const rows = table.querySelectorAll("tbody tr"); attendanceData = Array.from(rows).map((row, index) => { const cells = row.querySelectorAll("td"); return { id: row.dataset.recordId, index: index + 1, employeeId: cells[1] ? cells[1].textContent.trim() : "", employeeName: cells[2] ? cells[2].textContent.trim() : "", // NEW: Employee Name column location: cells[3] ? cells[3].textContent.trim() : "", // Updated from cells[2] event: cells[4] ? cells[4].textContent.trim() : "", // Updated from cells[3] date: cells[5] ? cells[5].textContent.trim() : "", // Updated from cells[4] time: cells[6] ? cells[6].textContent.trim() : "", // Updated from cells[5] qr_address: cells[7] // Updated from cells[6] ? cells[7].getAttribute("title") || cells[7].textContent.trim() : "", checked_in_address: cells[8] // Updated from cells[7] ? cells[8].getAttribute("title") || cells[8].textContent.trim() : "", // FIXED: Extract location accuracy for address display logic location_accuracy: cells[9] ? extractLocationAccuracy(cells[9]) : null, // Updated from cells[8] accuracy_level: cells[9] // Updated from cells[8] ? extractLocationAccuracyLevel(cells[9]) : "unknown", device: cells[10] // Updated from cells[9] ? cells[10].textContent.trim() : "", isModified: row.classList.contains('modified-record'), }; }); filteredData = [...attendanceData]; console.log(`Loaded ${attendanceData.length} attendance records`); // Debug log for location accuracy data const recordsWithAccuracy = attendanceData.filter( (r) => r.location_accuracy !== null ); console.log( `Records with location accuracy: ${recordsWithAccuracy.length}` ); if (recordsWithAccuracy.length > 0) { console.log( `Sample location accuracy values:`, recordsWithAccuracy.slice(0, 3).map((r) => r.location_accuracy) ); } } } function extractLocationAccuracy(cell) { const text = cell.textContent; console.log(`Extracting accuracy from: "${text}"`); // Look for miles pattern (e.g., "0.003 mi", "1.234 mi") const milesMatch = text.match(/(\d+\.?\d*)\s*mi/); if (milesMatch) { const value = parseFloat(milesMatch[1]); console.log(`Found miles: ${value}`); return value; } // Look for specific accuracy patterns in the HTML const accuracyMatch = text.match(/accuracy[:\s]*(\d+\.?\d*)/i); if (accuracyMatch) { const value = parseFloat(accuracyMatch[1]); console.log(`Found accuracy: ${value}`); return value; } // Check for data attributes const dataAccuracy = cell.getAttribute("data-accuracy"); if (dataAccuracy) { const value = parseFloat(dataAccuracy); console.log(`Found data-accuracy: ${value}`); return value; } // Fallback: look for GPS accuracy in meters and convert to miles (approximate) const metersMatch = text.match(/(\d+\.?\d*)\s*m/); if (metersMatch) { const meters = parseFloat(metersMatch[1]); const miles = meters * 0.000621371; // Convert meters to miles (approximate) console.log(`Found meters: ${meters}, converted to miles: ${miles}`); return miles; } console.log(`No accuracy found in: "${text}"`); return null; } function extractLocationAccuracyLevel(cell) { // Get the numerical accuracy value from the cell const accuracy = extractLocationAccuracy(cell); // Return 2-level accuracy based on 0.5-mile threshold if (accuracy !== null && accuracy !== undefined) { return accuracy < 0.3 ? "accurate" : "inaccurate"; } return "unknown"; } function createTableRow(record, displayIndex) { const row = document.createElement("tr"); row.dataset.recordId = record.id; // Apply highlighting if record was modified if (record.isModified) { row.classList.add('modified-record'); } // Debug logging for first few records if (displayIndex <= 3) { console.log(`=== CREATING ROW ${displayIndex} ===`); console.log(`Employee: ${record.employeeId}`); console.log(`Location accuracy: ${record.location_accuracy}`); console.log(`QR address: ${record.qr_address}`); console.log(`Check-in address: ${record.checked_in_address}`); } // Create location accuracy badge HTML const locationAccuracyBadge = record.location_accuracy !== null ? ` ${record.location_accuracy.toFixed(3)} mi (${record.accuracy_level}) ` : ` Unknown `; // Address display logic based on location accuracy let addressDisplayHTML = ""; let addressToShow = record.checked_in_address; let addressIcon = "fas fa-location-arrow"; let addressClass = "address-normal-accuracy"; let addressTitle = `Check-in Address: ${record.checked_in_address}`; // Apply 0.5-mile threshold logic if ( record.location_accuracy !== null && record.location_accuracy !== undefined ) { const accuracy = parseFloat(record.location_accuracy); if (displayIndex <= 3) { console.log(`Applying address logic for ${record.employeeId}:`); console.log(` Accuracy value: ${accuracy}`); console.log(` Is <= 0.3? ${accuracy <= 0.3}`); } if (!isNaN(accuracy) && accuracy <= 0.3) { // High accuracy - use QR address addressToShow = record.qr_address; addressIcon = "fas fa-check-circle"; addressClass = "address-high-accuracy"; addressTitle = `QR Address (High Accuracy ≤ 0.5 mi): ${record.qr_address}`; if (displayIndex <= 3) { console.log(` → Using QR address: ${addressToShow}`); } addressDisplayHTML = ` ${ addressToShow.length > 45 ? addressToShow.substring(0, 45) + "..." : addressToShow } `; } else { // Lower accuracy - use check-in address if (displayIndex <= 3) { console.log(` → Using check-in address: ${addressToShow}`); } addressDisplayHTML = ` ${ addressToShow.length > 45 ? addressToShow.substring(0, 45) + "..." : addressToShow } `; } } else { // No accuracy data - use check-in address if (displayIndex <= 3) { console.log( ` → No accuracy data, using check-in address: ${addressToShow}` ); } addressDisplayHTML = ` ${ addressToShow.length > 45 ? addressToShow.substring(0, 45) + "..." : addressToShow } `; } row.innerHTML = ` ${displayIndex}
${record.employeeId}
${record.employeeName || 'Unknown'}
${record.location}
${record.event}
${record.date}
${record.time}
${ record.qr_address.length > 50 ? record.qr_address.substring(0, 50) + "..." : record.qr_address }
${addressDisplayHTML}
${locationAccuracyBadge}
${ record.device.length > 20 ? record.device.substring(0, 20) + "..." : record.device }
${ hasEditPermission ? ` ` : ` ` }
`; return row; } function getSortKey(columnIndex) { const sortKeys = [ "index", "employeeId", "location", "event", "date", "time", "qr_address", "checked_in_address", "location_accuracy", "device", ]; return sortKeys[columnIndex] || "index"; } // Enhanced sorting for location accuracy (numeric sorting) function sortTable(columnIndex) { if (sortColumn === columnIndex) { sortDirection = sortDirection === "asc" ? "desc" : "asc"; } else { sortColumn = columnIndex; sortDirection = "asc"; } const sortKey = getSortKey(columnIndex); filteredData.sort((a, b) => { let aVal = a[sortKey]; let bVal = b[sortKey]; // Handle numeric values for location accuracy if (columnIndex === 8 && aVal !== null && bVal !== null) { aVal = parseFloat(aVal); bVal = parseFloat(bVal); } // Handle null values - put them at the end if (aVal === null || aVal === undefined) { return sortDirection === "asc" ? 1 : -1; } if (bVal === null || bVal === undefined) { return sortDirection === "asc" ? -1 : 1; } if (typeof aVal === "string") { aVal = aVal.toLowerCase(); bVal = bVal.toLowerCase(); } let result; if (aVal < bVal) result = -1; else if (aVal > bVal) result = 1; else result = 0; return sortDirection === "asc" ? result : -result; }); updateTable(); updateSortIndicators(columnIndex); } // Enhanced statistics display for location accuracy function updateFilterStats() { const totalRecords = filteredData.length; const recordsWithAccuracy = filteredData.filter( (r) => r.location_accuracy !== null ).length; const avgAccuracy = recordsWithAccuracy > 0 ? filteredData .filter((r) => r.location_accuracy !== null) .reduce((sum, r) => sum + r.location_accuracy, 0) / recordsWithAccuracy : 0; console.log(`Filtered records: ${totalRecords}`); console.log(`Records with location accuracy: ${recordsWithAccuracy}`); console.log(`Average location accuracy: ${avgAccuracy.toFixed(3)} miles`); } // Function to get accuracy level color for charts or displays function getAccuracyLevelColor(level) { const colors = { accurate: "#059669", // green inaccurate: "#dc2626", // red unknown: "#6b7280", // gray }; return colors[level] || colors["unknown"]; } // Enhanced export function to include location accuracy function exportAttendanceWithAccuracy() { // Build CSV header with location accuracy const headers = [ "#", "Employee ID", "Employee Name", "Location", "Event", "Date", "Time", "QR Address", "Check-in Address", "Location Accuracy (miles)", "Accuracy Level", "Device", ]; // Build CSV rows const rows = filteredData.map((record, index) => [ index + 1, record.employeeId, record.employeeName || "Unknown", record.location, record.event, record.date, record.time, record.qr_address, record.checked_in_address, record.location_accuracy ? record.location_accuracy.toFixed(3) : "Unknown", record.accuracy_level, record.device, ]); // Create CSV content const csvContent = [headers, ...rows] .map((row) => row.map((field) => `"${field}"`).join(",")) .join("\n"); // Download CSV const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const link = document.createElement("a"); const url = URL.createObjectURL(blob); link.setAttribute("href", url); link.setAttribute( "download", `attendance_report_with_accuracy_${ new Date().toISOString().split("T")[0] }.csv` ); link.style.visibility = "hidden"; document.body.appendChild(link); link.click(); document.body.removeChild(link); } function exportAttendance() { // Check user role before proceeding const userRole = window.userRole; // Read from global variable set in template console.log("Template - session.role:", '{{ session.role }}'); console.log("Template - window.userRole set to:", window.userRole); if (!['admin', 'payroll'].includes(userRole)) { console.log("Export access denied - insufficient privileges"); alert("Access denied. Only administrators and payroll staff can export data."); return; } // Log export action console.log(`Export button clicked by ${userRole} - redirecting to configuration page`); // Get current filters const currentFilters = getCurrentFilters(); // Build URL with current filters const params = new URLSearchParams(); if (currentFilters.date_from) params.append("date_from", currentFilters.date_from); if (currentFilters.date_to) params.append("date_to", currentFilters.date_to); if (currentFilters.location) params.append("location", currentFilters.location); if (currentFilters.employee) params.append("employee", currentFilters.employee); // Navigate to export configuration page const configUrl = "/export-configuration" + (params.toString() ? "?" + params.toString() : ""); window.location.href = configUrl; } function getCurrentFilters() { // Extract current filter values from the page return { date_from: document.getElementById("date_from")?.value || "", date_to: document.getElementById("date_to")?.value || "", location: document.getElementById("location")?.value || "", employee: document.getElementById("employee")?.value || "", project: document.getElementById("project")?.value || "", }; } // Add a quick CSV export function as backup (keep existing functionality) function exportAttendanceCSV() { // Check user role before proceeding const userRole = window.userRole; if (!['admin', 'payroll'].includes(userRole)) { console.log("CSV export access denied - insufficient privileges"); alert("Access denied. Only administrators and payroll staff can export data."); return; } // Build export URL with current filters for CSV const params = new URLSearchParams(); const filters = getCurrentFilters(); if (filters.date_from) params.append("date_from", filters.date_from); if (filters.date_to) params.append("date_to", filters.date_to); if (filters.location) params.append("location", filters.location); if (filters.employee) params.append("employee", filters.employee); if (filters.project) params.append("project", filters.project); params.append("export", "csv"); // Create a temporary link and click it to download const downloadUrl = window.location.pathname + "?" + params.toString(); window.open(downloadUrl, "_blank"); } // Enhanced export menu (if you want to add dropdown with multiple export options) function showExportMenu() { // Create export options menu const existingMenu = document.getElementById("exportMenu"); if (existingMenu) { existingMenu.remove(); return; } const exportBtn = document.querySelector( 'button[onclick="exportAttendance()"]' ); if (!exportBtn) return; const menu = document.createElement("div"); menu.id = "exportMenu"; menu.style.cssText = ` position: absolute; top: 100%; right: 0; background: white; border: 1px solid #e2e8f0; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); z-index: 1000; min-width: 200px; margin-top: 5px; `; menu.innerHTML = `
`; exportBtn.parentElement.style.position = "relative"; exportBtn.parentElement.appendChild(menu); // Close menu when clicking outside setTimeout(() => { document.addEventListener("click", function closeOnClickOutside(e) { if (!menu.contains(e.target) && e.target !== exportBtn) { closeExportMenu(); document.removeEventListener("click", closeOnClickOutside); } }); }, 100); } function closeExportMenu() { const menu = document.getElementById("exportMenu"); if (menu) { menu.remove(); } } // Initialize export functionality when page loads document.addEventListener("DOMContentLoaded", function () { // Update export button to use enhanced functionality const exportBtn = document.querySelector( 'button[onclick="exportAttendance()"]' ); if (exportBtn) { // You can modify the button to show a dropdown instead // exportBtn.onclick = showExportMenu; // exportBtn.innerHTML = ' Export Data '; } console.log("Enhanced export functionality initialized"); });