work-tracing/static/js/dashboard.js
2025-05-16 17:55:30 +04:00

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
}
}
});