514 lines
22 KiB
JavaScript
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;
|