Files

514 lines
22 KiB
JavaScript

/**
* Date Filter Component
* Provides a reusable date filtering UI using seasons data
*/
class DateFilter {
constructor(containerId, options = {}) {
this.container = document.getElementById(containerId);
if (!this.container) {
console.error(`[Date Filter] Container #${containerId} not found`);
return;
}
this.options = {
onFilterChange: options.onFilterChange || (() => {}),
includeCustomRange: options.includeCustomRange !== false,
includeCumulative: options.includeCumulative !== false,
defaultFilter: options.defaultFilter || 'all-time',
...options
};
this.seasonsFilter = window.seasonsFilter;
this.currentFilter = {
type: 'all-time', // 'all-time', 'season', 'week', 'custom', 'cumulative'
startDate: null,
endDate: null,
season: null,
week: null
};
this.init();
}
t(key, params = {}) {
let value = (window.__t && window.__t(key)) || key;
Object.entries(params).forEach(([name, replacement]) => {
value = value.replace(`{${name}}`, replacement);
});
return value;
}
async init() {
// Load seasons data
await this.seasonsFilter.loadSeasons();
// Render the UI
this.render();
// Apply default filter
if (this.options.defaultFilter !== 'all-time') {
this.applyPresetFilter(this.options.defaultFilter);
}
}
render() {
const html = `
<div class="date-filter-container">
<div class="flex flex-col space-y-4">
<!-- Filter Type Tabs -->
<div class="flex flex-wrap gap-2">
<button class="filter-button filter-button-active" data-filter-type="all-time">
<i class="fas fa-infinity mr-2"></i>${this.t('dateFilter.allTime')}
</button>
<button class="filter-button" data-filter-type="current-season">
<i class="fas fa-calendar-alt mr-2"></i>${this.t('dateFilter.currentSeason')}
</button>
<button class="filter-button" data-filter-type="season">
<i class="fas fa-calendar mr-2"></i>${this.t('dateFilter.bySeason')}
</button>
${this.options.includeCumulative ? `
<button class="filter-button" data-filter-type="cumulative">
<i class="fas fa-chart-line mr-2"></i>${this.t('dateFilter.cumulative')}
</button>
` : ''}
${this.options.includeCustomRange ? `
<button class="filter-button" data-filter-type="custom">
<i class="fas fa-calendar-day mr-2"></i>${this.t('dateFilter.customRange')}
</button>
` : ''}
</div>
<!-- Season Selection (hidden by default) -->
<div id="season-selector" class="hidden space-y-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-white/80 mb-2">
<i class="fas fa-trophy mr-1 text-primary-400"></i>${this.t('dateFilter.selectSeason')}
</label>
<select id="season-select" class="select-field">
<option value="">${this.t('dateFilter.selectSeasonDots')}</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-white/80 mb-2">
<i class="fas fa-calendar-week mr-1 text-primary-400"></i>${this.t('dateFilter.selectWeek')}
</label>
<select id="week-select" class="select-field" disabled>
<option value="all">${this.t('dateFilter.entireSeason')}</option>
</select>
</div>
</div>
<button id="apply-season-filter" class="btn-primary w-full md:w-auto">
<i class="fas fa-check mr-2"></i>${this.t('dateFilter.applyFilter')}
</button>
</div>
<!-- Cumulative Selection (hidden by default) -->
<div id="cumulative-selector" class="hidden space-y-3">
<div class="bg-primary-400/10 border border-primary-400/30 rounded-lg p-4 mb-3">
<p class="text-sm text-white/80">
<i class="fas fa-info-circle mr-2 text-primary-400"></i>
${this.t('dateFilter.cumulativeHelp')}
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-white/80 mb-2">${this.t('dateFilter.season')}</label>
<select id="cumulative-season-select" class="select-field">
<option value="">${this.t('dateFilter.selectSeasonDots')}</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-white/80 mb-2">${this.t('dateFilter.upToWeek')}</label>
<select id="cumulative-week-select" class="select-field" disabled>
<option value="">${this.t('dateFilter.selectWeekDots')}</option>
</select>
</div>
</div>
<button id="apply-cumulative-filter" class="btn-primary w-full md:w-auto">
<i class="fas fa-chart-line mr-2"></i>${this.t('dateFilter.applyCumulativeFilter')}
</button>
</div>
<!-- Custom Range Selection (hidden by default) -->
<div id="custom-range-selector" class="hidden space-y-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-white/80 mb-2">
<i class="fas fa-calendar-plus mr-1 text-primary-400"></i>${this.t('dateFilter.startDate')}
</label>
<input type="date" id="start-date" class="input-field">
</div>
<div>
<label class="block text-sm font-medium text-white/80 mb-2">
<i class="fas fa-calendar-minus mr-1 text-primary-400"></i>${this.t('dateFilter.endDate')}
</label>
<input type="date" id="end-date" class="input-field">
</div>
</div>
<button id="apply-custom-filter" class="btn-primary w-full md:w-auto">
<i class="fas fa-filter mr-2"></i>${this.t('dateFilter.applyCustomRange')}
</button>
</div>
<!-- Current Filter Display -->
<div id="current-filter-display" class="hidden">
<div class="bg-primary-400/10 border border-primary-400/30 rounded-lg p-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<i class="fas fa-filter text-primary-400 text-xl"></i>
<div>
<p class="text-sm text-white/60">${this.t('dateFilter.activeFilter')}</p>
<p id="filter-description" class="text-white font-semibold"></p>
</div>
</div>
<button id="clear-filter" class="text-red-400 hover:text-red-300 transition-colors">
<i class="fas fa-times-circle mr-1"></i>${this.t('dateFilter.clear')}
</button>
</div>
</div>
</div>
</div>
</div>
`;
this.container.innerHTML = html;
this.attachEventListeners();
this.populateSelectors();
}
attachEventListeners() {
// Filter type buttons
const filterButtons = this.container.querySelectorAll('[data-filter-type]');
filterButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
const filterType = e.currentTarget.dataset.filterType;
this.showFilterSection(filterType);
});
});
// Season selection
const seasonSelect = this.container.querySelector('#season-select');
if (seasonSelect) {
seasonSelect.addEventListener('change', (e) => {
const weekSelect = this.container.querySelector('#week-select');
if (e.target.value) {
this.seasonsFilter.populateWeekSelect(weekSelect, e.target.value, true);
weekSelect.disabled = false;
} else {
weekSelect.disabled = true;
weekSelect.innerHTML = `<option value="all">${this.t('dateFilter.entireSeason')}</option>`;
}
});
}
// Cumulative season selection
const cumulativeSeasonSelect = this.container.querySelector('#cumulative-season-select');
if (cumulativeSeasonSelect) {
cumulativeSeasonSelect.addEventListener('change', (e) => {
const weekSelect = this.container.querySelector('#cumulative-week-select');
if (e.target.value) {
this.seasonsFilter.populateWeekSelect(weekSelect, e.target.value, false);
weekSelect.disabled = false;
} else {
weekSelect.disabled = true;
weekSelect.innerHTML = `<option value="">${this.t('dateFilter.selectWeekDots')}</option>`;
}
});
}
// Apply buttons
const applySeasonBtn = this.container.querySelector('#apply-season-filter');
if (applySeasonBtn) {
applySeasonBtn.addEventListener('click', () => this.applySeasonFilter());
}
const applyCumulativeBtn = this.container.querySelector('#apply-cumulative-filter');
if (applyCumulativeBtn) {
applyCumulativeBtn.addEventListener('click', () => this.applyCumulativeFilter());
}
const applyCustomBtn = this.container.querySelector('#apply-custom-filter');
if (applyCustomBtn) {
applyCustomBtn.addEventListener('click', () => this.applyCustomFilter());
}
// Clear filter
const clearBtn = this.container.querySelector('#clear-filter');
if (clearBtn) {
clearBtn.addEventListener('click', () => this.clearFilter());
}
}
populateSelectors() {
const seasonSelect = this.container.querySelector('#season-select');
const cumulativeSeasonSelect = this.container.querySelector('#cumulative-season-select');
if (seasonSelect) {
this.seasonsFilter.populateSeasonSelect(seasonSelect, false);
}
if (cumulativeSeasonSelect) {
this.seasonsFilter.populateSeasonSelect(cumulativeSeasonSelect, false);
}
}
showFilterSection(filterType) {
// Update button states
const buttons = this.container.querySelectorAll('[data-filter-type]');
buttons.forEach(btn => {
if (btn.dataset.filterType === filterType) {
btn.classList.add('filter-button-active');
btn.classList.remove('filter-button');
} else {
btn.classList.remove('filter-button-active');
btn.classList.add('filter-button');
}
});
// Hide all sections
this.container.querySelector('#season-selector')?.classList.add('hidden');
this.container.querySelector('#cumulative-selector')?.classList.add('hidden');
this.container.querySelector('#custom-range-selector')?.classList.add('hidden');
// Show appropriate section
switch (filterType) {
case 'all-time':
this.applyAllTimeFilter();
break;
case 'current-season':
this.applyCurrentSeasonFilter();
break;
case 'season':
this.container.querySelector('#season-selector')?.classList.remove('hidden');
break;
case 'cumulative':
this.container.querySelector('#cumulative-selector')?.classList.remove('hidden');
break;
case 'custom':
this.container.querySelector('#custom-range-selector')?.classList.remove('hidden');
break;
}
}
applyAllTimeFilter() {
this.currentFilter = {
type: 'all-time',
startDate: null,
endDate: null,
season: null,
week: null
};
this.updateFilterDisplay(this.t('dateFilter.allTimeStatistics'));
this.options.onFilterChange(this.currentFilter);
}
applyCurrentSeasonFilter() {
const currentSeasonInfo = this.seasonsFilter.getCurrentSeason();
if (!currentSeasonInfo) {
console.error('[Date Filter] No current season found');
return;
}
const dateRange = this.seasonsFilter.getSeasonDateRange(currentSeasonInfo.name);
if (!dateRange) return;
this.currentFilter = {
type: 'season',
startDate: dateRange.startDate,
endDate: dateRange.endDate,
season: currentSeasonInfo.name,
week: null
};
this.updateFilterDisplay(this.t('dateFilter.currentSeasonValue', { season: currentSeasonInfo.name }));
this.options.onFilterChange(this.currentFilter);
}
applySeasonFilter() {
const seasonSelect = this.container.querySelector('#season-select');
const weekSelect = this.container.querySelector('#week-select');
const seasonName = seasonSelect.value;
if (!seasonName) {
alert(this.t('dateFilter.alertSelectSeason'));
return;
}
const weekValue = weekSelect.value;
let dateRange;
let description;
if (weekValue === 'all') {
// Entire season
dateRange = this.seasonsFilter.getSeasonDateRange(seasonName);
description = this.t('dateFilter.seasonValue', { season: seasonName });
this.currentFilter = {
type: 'season',
startDate: dateRange.startDate,
endDate: dateRange.endDate,
season: seasonName,
week: null
};
} else {
// Specific week
const weekNumber = weekValue === 'final' ? null : parseInt(weekValue);
dateRange = this.seasonsFilter.getWeekDateRange(seasonName, weekNumber);
const season = this.seasonsFilter.getSeason(seasonName);
const week = season.weeks.find(w =>
(w.weekNumber === weekNumber) || (weekValue === 'final' && w.weekNumber === null)
);
description = `${seasonName} - ${week.displayName}`;
this.currentFilter = {
type: 'week',
startDate: dateRange.startDate,
endDate: dateRange.endDate,
season: seasonName,
week: weekNumber
};
}
this.updateFilterDisplay(description);
this.options.onFilterChange(this.currentFilter);
}
applyCumulativeFilter() {
const seasonSelect = this.container.querySelector('#cumulative-season-select');
const weekSelect = this.container.querySelector('#cumulative-week-select');
const seasonName = seasonSelect.value;
const weekValue = weekSelect.value;
if (!seasonName || !weekValue) {
alert(this.t('dateFilter.alertSelectSeasonWeek'));
return;
}
const weekNumber = weekValue === 'final' ? null : parseInt(weekValue);
const dateRange = this.seasonsFilter.getWeekDateRange(seasonName, weekNumber);
if (!dateRange) return;
const season = this.seasonsFilter.getSeason(seasonName);
const week = season.weeks.find(w =>
(w.weekNumber === weekNumber) || (weekValue === 'final' && w.weekNumber === null)
);
this.currentFilter = {
type: 'cumulative',
startDate: null, // No start date for cumulative
endDate: dateRange.endDate,
season: seasonName,
week: weekNumber
};
this.updateFilterDisplay(this.t('dateFilter.cumulativeValue', { season: seasonName, week: week.displayName }));
this.options.onFilterChange(this.currentFilter);
}
applyCustomFilter() {
const startDateInput = this.container.querySelector('#start-date');
const endDateInput = this.container.querySelector('#end-date');
const startDate = startDateInput.value ? new Date(startDateInput.value) : null;
const endDate = endDateInput.value ? new Date(endDateInput.value) : null;
if (!startDate && !endDate) {
alert(this.t('dateFilter.alertSelectDate'));
return;
}
if (startDate && endDate && startDate > endDate) {
alert(this.t('dateFilter.alertStartBeforeEnd'));
return;
}
this.currentFilter = {
type: 'custom',
startDate: startDate,
endDate: endDate,
season: null,
week: null
};
let description = this.t('dateFilter.customRangePrefix') + ' ';
if (startDate && endDate) {
description += `${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}`;
} else if (startDate) {
description += this.t('dateFilter.fromDate', { date: startDate.toLocaleDateString() });
} else {
description += this.t('dateFilter.upToDate', { date: endDate.toLocaleDateString() });
}
this.updateFilterDisplay(description);
this.options.onFilterChange(this.currentFilter);
}
clearFilter() {
this.applyAllTimeFilter();
}
updateFilterDisplay(description) {
const filterDisplay = this.container.querySelector('#current-filter-display');
const filterDescription = this.container.querySelector('#filter-description');
if (this.currentFilter.type === 'all-time') {
filterDisplay?.classList.add('hidden');
} else {
filterDisplay?.classList.remove('hidden');
if (filterDescription) {
filterDescription.textContent = description;
}
}
}
applyPresetFilter(preset) {
// Apply a preset filter (e.g., 'current-season', 'all-time')
this.showFilterSection(preset);
}
getCurrentFilter() {
return this.currentFilter;
}
getAPIQueryParams() {
// Generate query parameters for API requests
const params = new URLSearchParams();
if (this.currentFilter.type === 'all-time') {
// No params needed for all-time
return '';
}
if (this.currentFilter.startDate) {
const startDateISO = this.currentFilter.startDate.toISOString();
params.append('start_date', startDateISO);
console.log('[Date Filter] Start Date:', this.currentFilter.startDate, '→', startDateISO);
}
if (this.currentFilter.endDate) {
const endDate = new Date(this.currentFilter.endDate);
endDate.setHours(23, 59, 59, 999);
const endDateISO = endDate.toISOString();
params.append('end_date', endDateISO);
console.log('[Date Filter] End Date:', endDate, '→', endDateISO);
}
if (this.currentFilter.season) {
params.append('season', this.currentFilter.season);
}
if (this.currentFilter.week !== null) {
params.append('week', this.currentFilter.week);
}
const queryString = params.toString() ? '?' + params.toString() : '';
console.log('[Date Filter] API Query Params:', queryString);
return queryString;
}
}
// Export for use in other scripts
window.DateFilter = DateFilter;