document.addEventListener('DOMContentLoaded', function() { const filterButtons = document.querySelectorAll('.btn-filter'); const userFilterInput = document.getElementById('userFilter'); const clearUserFilterBtn = document.getElementById('clearUserFilter'); const reportBody = document.getElementById('reportBody'); const periodHeader = document.getElementById('periodHeader'); const loadingSpinner = document.getElementById('loadingSpinner'); const errorMessage = document.getElementById('errorMessage'); const weekDaySelector = document.getElementById('weekDaySelector'); const weekDaySelect = document.getElementById('weekDaySelect'); // User activity modal elements const userActivityModal = document.getElementById('userActivityModal'); const modalUsername = document.getElementById('modalUsername'); const startDateInput = document.getElementById('startDate'); const endDateInput = document.getElementById('endDate'); const applyDateRangeBtn = document.getElementById('applyDateRange'); const userActivityBody = document.getElementById('userActivityBody'); const modalLoadingSpinner = document.getElementById('modalLoadingSpinner'); const modalErrorMessage = document.getElementById('modalErrorMessage'); // Initialize date inputs with current date const today = new Date().toISOString().split('T')[0]; startDateInput.value = today; endDateInput.value = today; let currentPeriod = 'daily'; // Default period let userActivityModalInstance = null; let selectedWeekDay = null; let currentDate = new Date(); // Track the currently selected date let selectedDate = formatDateForAPI(new Date()); // Current date in YYYY-MM-DD format // User filter debounce timer let userFilterTimeout = null; // Format date as YYYY-MM-DD for API function formatDateForAPI(date) { return date.toISOString().split('T')[0]; } // Format date for display (DD/MM/YYYY) function formatDateForDisplay(dateStr) { const date = new Date(dateStr); return date.toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }); } // Update the current date display in the UI function updateDateDisplay() { const currentDateDisplay = document.getElementById('currentDateDisplay'); currentDateDisplay.textContent = formatDateForDisplay(selectedDate); } // Function to navigate to previous day function goToPreviousDay() { currentDate.setDate(currentDate.getDate() - 1); selectedDate = formatDateForAPI(currentDate); updateDateDisplay(); loadReportData(); } // Function to navigate to next day function goToNextDay() { currentDate.setDate(currentDate.getDate() + 1); selectedDate = formatDateForAPI(currentDate); updateDateDisplay(); loadReportData(); } // Function to handle date selection from calendar function handleDateSelection() { const dateSelector = document.getElementById('dateSelector'); if (dateSelector.value) { selectedDate = dateSelector.value; currentDate = new Date(selectedDate); updateDateDisplay(); dateSelector.style.display = 'none'; loadReportData(); } } // Function to toggle calendar visibility function toggleCalendar() { const dateSelector = document.getElementById('dateSelector'); if (dateSelector.style.display === 'none') { dateSelector.style.display = 'inline-block'; dateSelector.value = selectedDate; dateSelector.focus(); } else { dateSelector.style.display = 'none'; } } // Function to get days of the current week (Monday to Sunday) function getDaysOfWeek() { const today = new Date(); const currentDay = today.getDay(); // 0 is Sunday, 1 is Monday, ... const mondayOffset = currentDay === 0 ? -6 : 1 - currentDay; // Calculate days from today to Monday const days = []; const monday = new Date(today); monday.setDate(today.getDate() + mondayOffset); // Generate array with dates for Monday through Sunday for (let i = 0; i < 7; i++) { const date = new Date(monday); date.setDate(monday.getDate() + i); days.push({ date: date.toISOString().split('T')[0], // YYYY-MM-DD format dayName: date.toLocaleDateString('en-US', { weekday: 'long' }), // Monday, Tuesday, etc. displayDate: date.toLocaleDateString('en-GB') // DD/MM/YYYY format for display }); } return days; } // Function to populate the week day selector dropdown function populateWeekDaySelector() { weekDaySelect.innerHTML = ''; const days = getDaysOfWeek(); days.forEach(day => { const option = document.createElement('option'); option.value = day.date; option.textContent = `${day.dayName} (${day.displayDate})`; weekDaySelect.appendChild(option); }); } // Function to fetch and display report data async function loadReportData() { loadingSpinner.classList.remove('d-none'); // Show spinner errorMessage.classList.add('d-none'); // Hide error message reportBody.innerHTML = ''; // Clear previous data const user = userFilterInput.value.trim(); let apiUrl = `/api/reports/${currentPeriod}`; const params = new URLSearchParams(); if (user) { params.append('user', user); } // Add date parameter for daily view if (currentPeriod === 'daily') { params.append('date', selectedDate); } // Add selected day parameter for weekly view if a specific day is selected if (currentPeriod === 'weekly' && selectedWeekDay) { params.append('day', selectedWeekDay); } if (params.toString()) { apiUrl += `?${params.toString()}`; } try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { populateTable(result.data); } else { showError(result.message || 'Failed to load data.'); } } catch (error) { console.error('Error fetching report data:', error); showError('Network error or failed to fetch data.'); } finally { loadingSpinner.classList.add('d-none'); // Hide spinner } } // Function to populate the table with data function populateTable(data) { // Update table header based on period switch (currentPeriod) { case 'daily': periodHeader.textContent = 'Day'; break; case 'weekly': periodHeader.textContent = 'Week'; break; case 'monthly': periodHeader.textContent = 'Month Starting'; break; default: periodHeader.textContent = 'Period'; } // Show/hide First Login Time column based on period and day selection const firstLoginHeader = document.querySelector('#reportTable th:nth-child(4)'); // Show First Login Time for daily view or when a specific day is selected in weekly view const hideLoginTime = currentPeriod === 'monthly' || (currentPeriod === 'weekly' && !selectedWeekDay); if (hideLoginTime) { // Hide First Login Time column for monthly view and weekly view without day selection firstLoginHeader.style.display = 'none'; } else { // Show First Login Time column for daily view or when a specific day is selected firstLoginHeader.style.display = ''; } if (data.length === 0) { reportBody.innerHTML = 'No data found for the selected filters.'; return; } data.forEach(entry => { const row = document.createElement('tr'); let periodValue = ''; // Determine which period key to use based on currentPeriod switch (currentPeriod) { case 'daily': periodValue = entry.day; break; case 'weekly': periodValue = entry.week_start; break; case 'monthly': periodValue = entry.month_start; break; } // Format date string from ISO (YYYY-MM-DD) to DD/MM/YYYY let formattedPeriod = periodValue; if (periodValue && periodValue.match(/^\d{4}-\d{2}-\d{2}/)) { const dateParts = periodValue.substring(0, 10).split('-'); formattedPeriod = `${dateParts[2]}/${dateParts[1]}/${dateParts[0]}`; } // Format first login time for display (from ISO to local time) let firstLoginTime = 'N/A'; if (entry.first_login_time) { const loginDate = new Date(entry.first_login_time); firstLoginTime = loginDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true }); } if (hideLoginTime) { // Don't include First Login Time cell in monthly and weekly view without day selection row.innerHTML = ` ${entry.user} ${formattedPeriod || 'N/A'} ${entry.duration_hours !== null ? entry.duration_hours.toFixed(2) : 'N/A'} `; } else { row.innerHTML = ` ${entry.user} ${formattedPeriod || 'N/A'} ${entry.duration_hours !== null ? entry.duration_hours.toFixed(2) : 'N/A'} ${firstLoginTime} `; } reportBody.appendChild(row); }); // Add event listeners to user links document.querySelectorAll('.user-link').forEach(link => { link.addEventListener('click', function() { const username = this.getAttribute('data-user'); showUserActivityModal(username); }); }); } // Function to show error messages function showError(message) { errorMessage.textContent = `Error: ${message}`; errorMessage.classList.remove('d-none'); reportBody.innerHTML = 'Failed to load data.'; } // Function to show user activity modal function showUserActivityModal(username) { modalUsername.textContent = username; userActivityBody.innerHTML = ''; modalErrorMessage.classList.add('d-none'); // Initialize and show modal with Bootstrap 5 if (!userActivityModalInstance) { userActivityModalInstance = new bootstrap.Modal(userActivityModal); } userActivityModalInstance.show(); // Load user activity data loadUserActivityData(username); } // Function to load user activity data async function loadUserActivityData(username) { modalLoadingSpinner.classList.remove('d-none'); userActivityBody.innerHTML = ''; modalErrorMessage.classList.add('d-none'); const startDate = startDateInput.value; const endDate = endDateInput.value; try { const response = await fetch(`/api/user-activity/${encodeURIComponent(username)}?start_date=${startDate}&end_date=${endDate}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { populateUserActivityTable(result.data.activities); } else { showModalError(result.message || 'Failed to load activity data'); } } catch (error) { console.error('Error fetching user activity data:', error); showModalError('Network error or failed to fetch activity data'); } finally { modalLoadingSpinner.classList.add('d-none'); } } // Function to populate user activity table function populateUserActivityTable(activities) { if (activities.length === 0) { userActivityBody.innerHTML = 'No activity found for the selected date range.'; return; } activities.forEach(activity => { const row = document.createElement('tr'); // Format date and times for display const date = new Date(activity.date).toLocaleDateString(); const startTime = new Date(activity.start_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true }); const endTime = new Date(activity.end_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true }); row.innerHTML = ` ${date} ${startTime} ${endTime} ${activity.duration_hours.toFixed(2)} `; userActivityBody.appendChild(row); }); } // Function to show modal error message function showModalError(message) { modalErrorMessage.textContent = `Error: ${message}`; modalErrorMessage.classList.remove('d-none'); userActivityBody.innerHTML = 'Failed to load activity data.'; } // Add event listeners to filter buttons filterButtons.forEach(button => { button.addEventListener('click', function() { // Update active button state filterButtons.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); currentPeriod = this.getAttribute('data-period'); // Show/hide week day selector based on period if (currentPeriod === 'weekly') { weekDaySelector.classList.remove('d-none'); dateNavControls.classList.add('d-none'); populateWeekDaySelector(); } else if (currentPeriod === 'daily') { weekDaySelector.classList.add('d-none'); dateNavControls.classList.remove('d-none'); updateDateDisplay(); } else { weekDaySelector.classList.add('d-none'); dateNavControls.classList.add('d-none'); } loadReportData(); // Reload data when period changes }); }); // Add event listener for user filter input (typing with debounce) userFilterInput.addEventListener('input', function() { // Clear any existing timeout if (userFilterTimeout) { clearTimeout(userFilterTimeout); } // Set a new timeout to delay the filter application (300ms debounce) userFilterTimeout = setTimeout(function() { loadReportData(); }, 300); }); // Add event listener for clear user filter button clearUserFilterBtn.addEventListener('click', function() { userFilterInput.value = ''; loadReportData(); }); // Add event listener for Apply Date Range button applyDateRangeBtn.addEventListener('click', function() { const username = modalUsername.textContent; loadUserActivityData(username); }); // Add event listener for week day selector weekDaySelect.addEventListener('change', function() { selectedWeekDay = this.value; loadReportData(); // Reload data when day selection changes }); // Initialize the dashboard function initializeDashboard() { // Initialize date navigation elements const dateNavControls = document.getElementById('dateNavControls'); const prevDateBtn = document.getElementById('prevDateBtn'); const nextDateBtn = document.getElementById('nextDateBtn'); const calendarBtn = document.getElementById('calendarBtn'); const dateSelector = document.getElementById('dateSelector'); // Set initial value for date selector dateSelector.value = selectedDate; // Update the date display updateDateDisplay(); // Show date navigation controls if daily view is active if (currentPeriod === 'daily') { dateNavControls.classList.remove('d-none'); } // Add event listeners for date navigation prevDateBtn.addEventListener('click', goToPreviousDay); nextDateBtn.addEventListener('click', goToNextDay); calendarBtn.addEventListener('click', toggleCalendar); dateSelector.addEventListener('change', handleDateSelection); // Load initial report data loadReportData(); } // Initial load initializeDashboard(); });