303 lines
12 KiB
JavaScript
303 lines
12 KiB
JavaScript
/**
|
|
* dashboard.js - Main dashboard controller
|
|
*
|
|
* This module serves as the main entry point and orchestrator for the dashboard,
|
|
* importing and initializing all the specialized modules.
|
|
*/
|
|
|
|
// Import all the modules
|
|
import AppState from './state.js';
|
|
import * as DateUtils from './dateUtils.js';
|
|
import * as Filters from './filters.js';
|
|
import * as TableManager from './tableManager.js';
|
|
import * as UserStates from './userStates.js';
|
|
import * as AutoRefresh from './autoRefresh.js';
|
|
import * as UserActivity from './userActivity.js';
|
|
import * as Api from './api.js';
|
|
|
|
// Main initialization function
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// DOM element references
|
|
const filterButtons = document.querySelectorAll('.btn-filter');
|
|
const userFilterInput = document.getElementById('userFilter');
|
|
const clearUserFilterBtn = document.getElementById('clearUserFilter');
|
|
const loadingSpinner = document.getElementById('loadingSpinner');
|
|
const errorMessage = document.getElementById('errorMessage');
|
|
const weekDaySelector = document.getElementById('weekDaySelector');
|
|
const weekDaySelect = document.getElementById('weekDaySelect');
|
|
const refreshStatesBtn = document.getElementById('refreshStates');
|
|
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 if AppState and getSelectedDate are defined
|
|
if (AppState && typeof AppState.getSelectedDate === 'function') {
|
|
const selectedDate = AppState.getSelectedDate();
|
|
if (selectedDate) {
|
|
dateSelector.value = selectedDate;
|
|
}
|
|
}
|
|
|
|
// Initialize all components
|
|
initializeNavigation();
|
|
initializeFilters();
|
|
initializeWeekDaySelector();
|
|
|
|
// Initialize sorting, state filters, and auto-refresh
|
|
Filters.initializeSortingHeaders();
|
|
Filters.initializeStateFilters();
|
|
AutoRefresh.initializeAutoRefresh();
|
|
|
|
// Set initial sort indicator based on AppState default sort settings
|
|
setInitialSortIndicator();
|
|
|
|
// Initialize user activity modal
|
|
UserActivity.initializeUserActivityModal();
|
|
|
|
// First load user states, then load report data
|
|
UserStates.refreshUserStates(true).then(() => {
|
|
// Load initial report data
|
|
loadReportData();
|
|
}).catch(error => {
|
|
console.error('Error loading initial user states:', error);
|
|
// Still load report data even if states fail.
|
|
loadReportData();
|
|
});
|
|
|
|
/**
|
|
* Set the initial sort indicator on the table header based on AppState default values
|
|
*/
|
|
function setInitialSortIndicator() {
|
|
const sortColumn = AppState.getCurrentSortColumn();
|
|
const sortDirection = AppState.getCurrentSortDirection();
|
|
if (sortColumn && sortDirection) {
|
|
const header = document.querySelector(`.sortable[data-sort="${sortColumn}"]`);
|
|
if (header) {
|
|
header.setAttribute('data-dir', sortDirection);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the visibility of UI controls based on the selected reporting period.
|
|
* @param {string} period - The current reporting period ('daily', 'weekly', 'monthly').
|
|
*/
|
|
function updatePeriodControlsVisibility(period) {
|
|
if (period === 'weekly') {
|
|
weekDaySelector.classList.remove('d-none');
|
|
populateWeekDaySelector(); // Populate selector when shown
|
|
dateNavControls.classList.add('d-none');
|
|
} else if (period === 'daily') {
|
|
weekDaySelector.classList.add('d-none');
|
|
dateNavControls.classList.remove('d-none');
|
|
updateDateDisplay(); // Update date display for daily view
|
|
} else { // monthly or any other period
|
|
weekDaySelector.classList.add('d-none');
|
|
dateNavControls.classList.add('d-none');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize date navigation controls.
|
|
* Sets up event listeners and initial visibility.
|
|
*/
|
|
function initializeNavigation() {
|
|
// Update the date display
|
|
updateDateDisplay();
|
|
|
|
// Show date navigation controls if daily view is active
|
|
if (AppState.getCurrentPeriod() === '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);
|
|
}
|
|
|
|
/**
|
|
* Initialize filter buttons (Daily, Weekly, Monthly) and the user text filter.
|
|
* Sets up event listeners for period changes and user input.
|
|
*/
|
|
function initializeFilters() {
|
|
// Add event listeners to filter buttons
|
|
filterButtons.forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
filterButtons.forEach(btn => btn.classList.remove('active'));
|
|
this.classList.add('active');
|
|
|
|
const period = this.getAttribute('data-period');
|
|
AppState.setCurrentPeriod(period);
|
|
|
|
// Update visibility of period-specific controls
|
|
updatePeriodControlsVisibility(period);
|
|
|
|
loadReportData(); // Reload data when period changes
|
|
});
|
|
});
|
|
|
|
// User filter input (with debounce)
|
|
let userFilterTimeout = null;
|
|
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() {
|
|
AppState.setUserFilterText(userFilterInput.value.trim());
|
|
loadReportData();
|
|
}, 300);
|
|
});
|
|
|
|
// Clear user filter button
|
|
clearUserFilterBtn.addEventListener('click', function() {
|
|
userFilterInput.value = '';
|
|
AppState.setUserFilterText('');
|
|
loadReportData();
|
|
});
|
|
|
|
// Refresh states button
|
|
refreshStatesBtn.addEventListener('click', function() {
|
|
UserStates.refreshUserStates(true); // Force refresh
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Initialize the week day selector for the weekly view.
|
|
* Sets up event listener for day selection changes.
|
|
*/
|
|
function initializeWeekDaySelector() {
|
|
// Add event listener for week day selector
|
|
weekDaySelect.addEventListener('change', function() {
|
|
AppState.setSelectedWeekDay(this.value);
|
|
loadReportData(); // Reload data when day selection changes
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Populate the week day selector dropdown with days of the current week.
|
|
* Fetches days from DateUtils.
|
|
*/
|
|
function populateWeekDaySelector() {
|
|
weekDaySelect.innerHTML = '<option value="">All Week</option>';
|
|
const days = DateUtils.getDaysOfWeek();
|
|
|
|
days.forEach(day => {
|
|
const option = document.createElement('option');
|
|
option.value = day.date;
|
|
option.textContent = `${day.dayName} (${day.displayDate})`;
|
|
weekDaySelect.appendChild(option);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update the current date display in the UI (e.g., "May 10, 2024").
|
|
* Uses the selected date from AppState.
|
|
*/
|
|
function updateDateDisplay() {
|
|
const currentDateDisplay = document.getElementById('currentDateDisplay');
|
|
currentDateDisplay.textContent = DateUtils.formatDateForDisplay(AppState.getSelectedDate());
|
|
}
|
|
|
|
/**
|
|
* Navigate to the previous day and reload report data.
|
|
* Updates AppState and the UI display.
|
|
*/
|
|
function goToPreviousDay() {
|
|
const newDate = DateUtils.getPreviousDay(AppState.getCurrentDate());
|
|
AppState.setCurrentDate(newDate);
|
|
updateDateDisplay();
|
|
loadReportData();
|
|
}
|
|
|
|
/**
|
|
* Navigate to the next day and reload report data.
|
|
* Updates AppState and the UI display.
|
|
*/
|
|
function goToNextDay() {
|
|
const newDate = DateUtils.getNextDay(AppState.getCurrentDate());
|
|
AppState.setCurrentDate(newDate);
|
|
updateDateDisplay();
|
|
loadReportData();
|
|
}
|
|
|
|
/**
|
|
* Handle date selection from the calendar input.
|
|
* Updates AppState, UI display, and reloads report data.
|
|
*/
|
|
function handleDateSelection() {
|
|
if (dateSelector.value) {
|
|
AppState.setSelectedDate(dateSelector.value);
|
|
updateDateDisplay();
|
|
dateSelector.style.display = 'none';
|
|
loadReportData();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle the visibility of the calendar date input.
|
|
*/
|
|
function toggleCalendar() {
|
|
if (dateSelector.style.display === 'none') {
|
|
dateSelector.style.display = 'inline-block';
|
|
dateSelector.value = AppState.getSelectedDate();
|
|
dateSelector.focus();
|
|
} else {
|
|
dateSelector.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load report data from the API and update the table.
|
|
* Manages loading spinner and error messages.
|
|
* @param {boolean} [isAutoRefresh=false] - Indicates if the load is triggered by auto-refresh.
|
|
* If true, loading spinner is not shown.
|
|
* @async
|
|
*/
|
|
async function loadReportData(isAutoRefresh = false) {
|
|
if (isAutoRefresh) {
|
|
// For auto-refresh, don't show spinner to avoid UI flicker
|
|
// Just update the last refresh time
|
|
if (AppState && typeof AppState.setLastRefreshTime === 'function') {
|
|
AppState.setLastRefreshTime(Date.now());
|
|
}
|
|
} else {
|
|
loadingSpinner.classList.remove('d-none'); // Show spinner for manual refresh
|
|
}
|
|
|
|
errorMessage.classList.add('d-none'); // Hide error message
|
|
|
|
try {
|
|
// Use the API module to load the report data
|
|
const data = await Api.loadReportData(isAutoRefresh);
|
|
|
|
// Apply default sorting if needed
|
|
const sortedData = AppState.getCurrentSortColumn() && AppState.getCurrentSortDirection()
|
|
? Filters.sortReportTable(data)
|
|
: data;
|
|
|
|
// Use the TableManager module to populate the table
|
|
TableManager.populateTable(sortedData, isAutoRefresh);
|
|
|
|
// If there's an active state filter, reapply it
|
|
if (AppState && typeof AppState.getCurrentStateFilter === 'function' && AppState.getCurrentStateFilter() !== 'all') {
|
|
Filters.filterTableByState(AppState.getCurrentStateFilter());
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error fetching report data:', error);
|
|
errorMessage.textContent = `Error: ${error.message || 'Network error or failed to fetch data.'}`;
|
|
errorMessage.classList.remove('d-none');
|
|
// Use TableManager to display the error in the table area
|
|
TableManager.showReportTableMessage('Failed to load data. See console for details.', true);
|
|
} finally {
|
|
loadingSpinner.classList.add('d-none'); // Hide spinner
|
|
}
|
|
}
|
|
});
|