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}`);
}
// Record actions
function viewRecordDetails(recordId) {
const record = attendanceData.find(r => r.id == recordId);
if (!record) return;
const modal = document.getElementById('recordModal');
const modalTitle = document.getElementById('modalTitle');
const modalBody = document.getElementById('modalBody');
if (!modal || !modalTitle || !modalBody) return;
modalTitle.textContent = `Attendance Record - ${record.employeeId}`;
modalBody.innerHTML = `
Employee ID:${record.employeeId}
Location:${record.location}
Event:${record.event}
Date:${record.date}
Time:${record.time}
Device:${record.device}
Status:
${record.status}
`;
modal.style.display = 'flex';
setTimeout(() => modal.classList.add('show'), 10);
}
function editRecord(recordId) {
// Placeholder for edit functionality
alert(`Edit functionality for record ${recordId} would be implemented here.`);
}
function deleteRecord(recordId) {
const record = attendanceData.find(r => r.id == recordId);
if (!record) return;
const confirmed = confirm(
`Are you sure you want to delete the attendance record for ${record.employeeId}?\n\n` +
`Date: ${record.date}\n` +
`Time: ${record.time}\n` +
`Location: ${record.location}\n\n` +
'This action cannot be undone.'
);
if (confirmed) {
// Here you would make an API call to delete the record
console.log(`Deleting record ${recordId}`);
// For demo purposes, just remove from current data
const index = attendanceData.findIndex(r => r.id == recordId);
if (index > -1) {
// Remove from DOM
if (attendanceData[index].element) {
attendanceData[index].element.remove();
}
// Remove from data arrays
attendanceData.splice(index, 1);
const filteredIndex = filteredData.findIndex(r => r.id == recordId);
if (filteredIndex > -1) {
filteredData.splice(filteredIndex, 1);
}
// Update display
updateTable();
updatePagination();
showToast('Record deleted successfully', 'success');
}
}
}
function closeRecordModal() {
const modal = document.getElementById('recordModal');
if (modal) {
modal.classList.remove('show');
setTimeout(() => {
modal.style.display = 'none';
}, 200);
}
}
// Export functionality
function exportAttendance() {
const exportData = filteredData.map(record => ({
'Employee ID': record.employeeId,
'Location': record.location,
'Event': record.event,
'Date': record.date,
'Time': record.time,
'Device': record.device,
'Status': record.status
}));
const csv = convertToCSV(exportData);
downloadCSV(csv, `attendance_report_${new Date().toISOString().split('T')[0]}.csv`);
showToast('Attendance data exported successfully', 'success');
}
function convertToCSV(data) {
if (!data.length) return '';
const headers = Object.keys(data[0]);
const csvContent = [
headers.join(','),
...data.map(row =>
headers.map(header => {
const value = row[header];
// Escape commas and quotes
return typeof value === 'string' && (value.includes(',') || value.includes('"'))
? `"${value.replace(/"/g, '""')}"`
: value;
}).join(',')
)
].join('\n');
return csvContent;
}
function downloadCSV(csv, filename) {
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
if (link.download !== undefined) {
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
function refreshReport() {
showToast('Refreshing report...', 'info');
setTimeout(() => {
window.location.reload();
}, 500);
}
// Charts initialization
function initializeCharts() {
loadAttendanceStats();
}
function loadAttendanceStats() {
fetch('/api/attendance/stats')
.then(response => response.json())
.then(data => {
createDailyChart(data.daily_stats || []);
createLocationChart(data.location_stats || []);
})
.catch(error => {
console.error('Error loading attendance stats:', error);
});
}
function createDailyChart(dailyStats) {
const ctx = document.getElementById('dailyChart');
if (!ctx) return;
if (dailyChart) {
dailyChart.destroy();
}
dailyChart = new Chart(ctx, {
type: 'line',
data: {
labels: dailyStats.map(stat => stat.date),
datasets: [{
label: 'Check-ins',
data: dailyStats.map(stat => stat.checkins),
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
tension: 0.4,
fill: true
}, {
label: 'Unique Employees',
data: dailyStats.map(stat => stat.employees),
borderColor: '#059669',
backgroundColor: 'rgba(5, 150, 105, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top'
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
}
function createLocationChart(locationStats) {
const ctx = document.getElementById('locationChart');
if (!ctx) return;
if (locationChart) {
locationChart.destroy();
}
locationChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: locationStats.map(stat => stat.location),
datasets: [{
data: locationStats.map(stat => stat.checkins),
backgroundColor: [
'#2563eb',
'#059669',
'#d97706',
'#dc2626',
'#7c3aed',
'#0891b2',
'#65a30d',
'#c2410c'
]
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'right'
}
}
}
});
}
// Utility functions
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.innerHTML = `