Filtering enhancements

This commit is contained in:
2025-12-19 08:53:47 -05:00
parent dc6b551325
commit bcd582aee5

View File

@@ -378,18 +378,26 @@
<div class="container"> <div class="container">
<!-- Filters Section --> <!-- Filters Section -->
<div class="filters-section"> <div class="filters-section">
<div class="filters-grid"> <div style="display: flex; gap: 0.5rem; align-items: flex-end; flex-wrap: wrap;">
<div class="filter-group"> <!-- Quick Filter Buttons -->
<label for="date-from">Date From:</label> <div style="display: flex; gap: 0.5rem;">
<input type="date" id="date-from"> <button class="btn btn-primary" id="filter-today" onclick="setDateRangeToday()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">📅 Today</button>
<button class="btn btn-secondary" id="filter-week" onclick="setDateRangeThisWeek()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">📆 This Week</button>
<button class="btn btn-secondary" id="filter-month" onclick="setDateRangeThisMonth()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">📊 This Month</button>
<button class="btn btn-secondary" id="filter-custom" onclick="toggleCustomRange()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">📋 Custom Range</button>
</div> </div>
<div class="filter-group">
<label for="date-to">Date To:</label> <!-- Custom Date Range (hidden by default) -->
<input type="date" id="date-to"> <div id="custom-range-container" style="display: none; display: flex; gap: 0.5rem; align-items: center;">
<input type="date" id="date-from" style="padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
<span style="font-weight: 600; color: #666;">to</span>
<input type="date" id="date-to" style="padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px;">
</div> </div>
<div class="filter-group">
<label for="status-filter">Status:</label> <!-- Status Filter -->
<select id="status-filter"> <div style="display: flex; flex-direction: column; gap: 0.3rem;">
<label for="status-filter" style="font-weight: 600; font-size: 0.85rem; color: #555;">Status:</label>
<select id="status-filter" style="padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; font-size: 0.9rem;">
<option value="">All Statuses</option> <option value="">All Statuses</option>
<option value="NEW">New</option> <option value="NEW">New</option>
<option value="CONFIRMED">Confirmed</option> <option value="CONFIRMED">Confirmed</option>
@@ -399,15 +407,19 @@
<option value="DELETED">Deleted</option> <option value="DELETED">Deleted</option>
</select> </select>
</div> </div>
<div class="filter-group">
<label for="search-input">Search:</label> <!-- Search Input -->
<input type="text" id="search-input" placeholder="Aircraft reg, callsign, captain, or airport..."> <div style="flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 0.3rem;">
<label for="search-input" style="font-weight: 600; font-size: 0.85rem; color: #555;">Search:</label>
<input type="text" id="search-input" placeholder="Aircraft reg, callsign, captain, or airport..." style="padding: 0.5rem; border: 1px solid #ddd; border-radius: 4px; font-size: 0.9rem;">
</div> </div>
<div class="filter-actions">
<button class="btn btn-primary" onclick="loadReports()"> <!-- Action Buttons -->
<div style="display: flex; gap: 0.5rem;">
<button class="btn btn-primary" onclick="loadReports()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">
🔍 Search 🔍 Search
</button> </button>
<button class="btn btn-secondary" onclick="clearFilters()"> <button class="btn btn-secondary" onclick="clearFilters()" style="font-size: 0.9rem; padding: 0.5rem 1rem; white-space: nowrap;">
🗑️ Clear 🗑️ Clear
</button> </button>
</div> </div>
@@ -417,67 +429,69 @@
<!-- Summary Box --> <!-- Summary Box -->
<div class="summary-box"> <div class="summary-box">
<div class="summary-title">📊 Movements Summary</div> <div class="summary-title">📊 Movements Summary</div>
<div style="display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: center;">
<div class="summary-grid"> <div class="summary-grid">
<!-- PPR Section --> <!-- PPR Section -->
<div style="grid-column: 1/-1; padding-bottom: 1rem; border-bottom: 2px solid rgba(255,255,255,0.3);"> <div style="grid-column: 1/-1; padding-bottom: 0.8rem; border-bottom: 2px solid rgba(255,255,255,0.3);">
<div style="font-size: 0.95rem; font-weight: 600; margin-bottom: 0.6rem;">PPR Movements</div> <div style="font-size: 0.85rem; font-weight: 600; margin-bottom: 0.4rem;">PPR Movements</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;"> <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.8rem;">
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem;">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Arrivals (Landings)</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Arrivals</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="ppr-arrivals">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="ppr-arrivals">0</div>
</div> </div>
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem;">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Departures (Takeoffs)</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Departures</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="ppr-departures">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="ppr-departures">0</div>
</div> </div>
<div class="summary-item" style="border-left-color: #ffd700; background: rgba(255,215,0,0.1); padding: 0.6rem;"> <div class="summary-item" style="border-left-color: #ffd700; background: rgba(255,215,0,0.1); padding: 0.4rem;">
<div class="summary-item-label" style="font-weight: 600; font-size: 0.75rem; margin-bottom: 0.2rem;">PPR Total</div> <div class="summary-item-label" style="font-weight: 600; font-size: 0.7rem; margin-bottom: 0.1rem;">Total</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="ppr-total">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="ppr-total">0</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Non-PPR Section --> <!-- Non-PPR Section -->
<div style="grid-column: 1/-1; padding-bottom: 1rem; border-bottom: 2px solid rgba(255,255,255,0.3);"> <div style="grid-column: 1/-1; padding-top: 0.8rem;">
<div style="font-size: 0.95rem; font-weight: 600; margin-bottom: 0.6rem;">Non-PPR Movements</div> <div style="font-size: 0.85rem; font-weight: 600; margin-bottom: 0.4rem;">Non-PPR Movements</div>
<div style="display: grid; grid-template-columns: repeat(6, 1fr); gap: 1rem;"> <div style="display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.8rem;">
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem; cursor: pointer;" onclick="filterOtherFlights('LOCAL')">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Local Flights</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Local</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="local-flights-movements">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="local-flights-movements">0</div>
</div> </div>
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem; cursor: pointer;" onclick="filterOtherFlights('CIRCUIT')">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Circuits</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Circuits</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="circuits-movements">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="circuits-movements">0</div>
</div> </div>
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem; cursor: pointer;" onclick="filterOtherFlights('ARRIVAL')">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Arrivals</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Arrivals</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="non-ppr-arrivals">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="non-ppr-arrivals">0</div>
</div> </div>
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem; cursor: pointer;" onclick="filterOtherFlights('DEPARTURE')">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Departures</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Departures</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="non-ppr-departures">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="non-ppr-departures">0</div>
</div> </div>
<div class="summary-item" style="padding: 0.6rem;"> <div class="summary-item" style="padding: 0.4rem; cursor: pointer;" onclick="filterOtherFlights('OVERFLIGHT')">
<div class="summary-item-label" style="font-size: 0.75rem; margin-bottom: 0.2rem;">Overflights</div> <div class="summary-item-label" style="font-size: 0.7rem; margin-bottom: 0.1rem;">Overflights</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="overflights-count">0</div> <div class="summary-item-value" style="font-size: 1.1rem;" id="overflights-count">0</div>
</div>
<div class="summary-item" style="border-left-color: #ffd700; background: rgba(255,215,0,0.1); padding: 0.4rem;">
<div class="summary-item-label" style="font-weight: 600; font-size: 0.7rem; margin-bottom: 0.1rem;">Total</div>
<div class="summary-item-value" style="font-size: 1.1rem;" id="non-ppr-total">0</div>
</div> </div>
<div class="summary-item" style="border-left-color: #ffd700; background: rgba(255,215,0,0.1); padding: 0.6rem;">
<div class="summary-item-label" style="font-weight: 600; font-size: 0.75rem; margin-bottom: 0.2rem;">Non-PPR Total</div>
<div class="summary-item-value" style="font-size: 1.3rem;" id="non-ppr-total">0</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Grand Total --> <!-- Grand Total - positioned on the right -->
<div style="grid-column: 1/-1; text-align: center;"> <div style="text-align: center; padding: 1rem; background: rgba(255,215,0,0.05); border-radius: 8px; border-left: 4px solid #ffd700; min-width: 120px;">
<div style="font-size: 0.85rem; opacity: 0.9; margin-bottom: 0.3rem;">Grand Total Movements</div> <div style="font-size: 0.75rem; opacity: 0.85; margin-bottom: 0.2rem;">GRAND TOTAL</div>
<div style="font-size: 2rem; font-weight: 700;" id="grand-total-movements">0</div> <div style="font-size: 2.2rem; font-weight: 700;" id="grand-total-movements">0</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Reports Table --> <!-- Reports Table -->
<div class="reports-table"> <div class="reports-table" id="ppr-reports-section">
<div class="table-header"> <div class="table-header">
<div> <div>
<strong>PPR Records</strong> <strong>PPR Records</strong>
@@ -590,6 +604,7 @@
let accessToken = null; let accessToken = null;
let currentPPRs = []; // Store current results for export let currentPPRs = []; // Store current results for export
let currentOtherFlights = []; // Store other flights for export let currentOtherFlights = []; // Store other flights for export
let otherFlightsFilterType = null; // Track which non-PPR flight type is selected for filtering
// Load UI configuration from API // Load UI configuration from API
async function loadUIConfig() { async function loadUIConfig() {
@@ -639,14 +654,105 @@
await loadReports(); await loadReports();
} }
// Set default date range to current month // Set default date range to today
function setupDefaultDateRange() { function setupDefaultDateRange() {
setDateRangeToday();
}
// Toggle custom date range picker
function toggleCustomRange() {
const container = document.getElementById('custom-range-container');
const customBtn = document.getElementById('filter-custom');
const isVisible = container.style.display !== 'none';
container.style.display = isVisible ? 'none' : 'flex';
// Update button style
if (isVisible) {
customBtn.classList.remove('btn-primary');
customBtn.classList.add('btn-secondary');
} else {
customBtn.classList.remove('btn-secondary');
customBtn.classList.add('btn-primary');
// Focus on the first date input when opening
document.getElementById('date-from').focus();
}
}
// Set date range to today
function setDateRangeToday() {
const today = new Date().toISOString().split('T')[0];
document.getElementById('date-from').value = today;
document.getElementById('date-to').value = today;
// Hide custom range picker if it's open
document.getElementById('custom-range-container').style.display = 'none';
updateFilterButtonStyles('today');
loadReports();
}
// Set date range to this week (Monday to Sunday)
function setDateRangeThisWeek() {
const now = new Date();
const dayOfWeek = now.getDay();
const diff = now.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1); // Adjust when day is Sunday
const monday = new Date(now.setDate(diff));
const sunday = new Date(now.setDate(diff + 6));
document.getElementById('date-from').value = monday.toISOString().split('T')[0];
document.getElementById('date-to').value = sunday.toISOString().split('T')[0];
// Hide custom range picker if it's open
document.getElementById('custom-range-container').style.display = 'none';
updateFilterButtonStyles('week');
loadReports();
}
// Set date range to this month
function setDateRangeThisMonth() {
const now = new Date(); const now = new Date();
const firstDay = new Date(now.getFullYear(), now.getMonth(), 1); const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0); const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
document.getElementById('date-from').value = firstDay.toISOString().split('T')[0]; document.getElementById('date-from').value = firstDay.toISOString().split('T')[0];
document.getElementById('date-to').value = lastDay.toISOString().split('T')[0]; document.getElementById('date-to').value = lastDay.toISOString().split('T')[0];
// Hide custom range picker if it's open
document.getElementById('custom-range-container').style.display = 'none';
updateFilterButtonStyles('month');
loadReports();
}
// Update button styles to show which filter is active
function updateFilterButtonStyles(activeFilter) {
const todayBtn = document.getElementById('filter-today');
const weekBtn = document.getElementById('filter-week');
const monthBtn = document.getElementById('filter-month');
// Reset all buttons
[todayBtn, weekBtn, monthBtn].forEach(btn => {
btn.classList.remove('btn-primary');
btn.classList.add('btn-secondary');
});
// Highlight active button
switch(activeFilter) {
case 'today':
todayBtn.classList.remove('btn-secondary');
todayBtn.classList.add('btn-primary');
break;
case 'week':
weekBtn.classList.remove('btn-secondary');
weekBtn.classList.add('btn-primary');
break;
case 'month':
monthBtn.classList.remove('btn-secondary');
monthBtn.classList.add('btn-primary');
break;
}
} }
// Authentication management // Authentication management
@@ -985,20 +1091,103 @@
} }
} }
// Filter other flights by type
function filterOtherFlights(flightType) {
// Toggle filter if clicking the same type
if (otherFlightsFilterType === flightType) {
otherFlightsFilterType = null;
} else {
otherFlightsFilterType = flightType;
}
// Show/hide PPR section based on filter
const pprSection = document.getElementById('ppr-reports-section');
if (pprSection) {
pprSection.style.display = otherFlightsFilterType ? 'none' : 'block';
}
// Update visual indication of active filter
updateFilterIndicators();
// Re-display flights with new filter
displayOtherFlights(currentOtherFlights);
}
// Update visual indicators for active filter
function updateFilterIndicators() {
// Select all clickable non-PPR summary items (those with onclick attribute)
const summaryItems = document.querySelectorAll('.summary-item[onclick*="filterOtherFlights"]');
summaryItems.forEach(item => {
item.style.opacity = '1';
item.style.borderLeftColor = '';
item.style.borderLeftWidth = '0';
});
if (otherFlightsFilterType) {
// Get the ID of the selected filter's summary item
let selectedId = '';
switch(otherFlightsFilterType) {
case 'LOCAL':
selectedId = 'local-flights-movements';
break;
case 'CIRCUIT':
selectedId = 'circuits-movements';
break;
case 'ARRIVAL':
selectedId = 'non-ppr-arrivals';
break;
case 'DEPARTURE':
selectedId = 'non-ppr-departures';
break;
case 'OVERFLIGHT':
selectedId = 'overflights-count';
break;
}
// Find and highlight the selected item
if (selectedId) {
const selectedElement = document.getElementById(selectedId);
if (selectedElement) {
const summaryItem = selectedElement.closest('.summary-item');
if (summaryItem) {
summaryItem.style.borderLeftColor = '#4CAF50';
summaryItem.style.borderLeftWidth = '4px';
summaryItem.style.opacity = '1';
}
}
}
// Dim other items that are clickable (non-PPR items)
const allSummaryItems = document.querySelectorAll('.summary-item[onclick]');
allSummaryItems.forEach(item => {
if (item.querySelector('#' + selectedId) === null) {
item.style.opacity = '0.5';
}
});
}
}
// Display other flights in table // Display other flights in table
function displayOtherFlights(flights) { function displayOtherFlights(flights) {
const tbody = document.getElementById('other-flights-table-body'); const tbody = document.getElementById('other-flights-table-body');
const tableInfo = document.getElementById('other-flights-info'); const tableInfo = document.getElementById('other-flights-info');
tableInfo.textContent = `${flights.length} flights found`; // Apply filter if one is selected
let filteredFlights = flights;
if (otherFlightsFilterType) {
filteredFlights = flights.filter(flight => flight.flightType === otherFlightsFilterType);
}
if (flights.length === 0) { tableInfo.textContent = `${filteredFlights.length} flights found` + (otherFlightsFilterType ? ` (filtered by ${otherFlightsFilterType})` : '');
if (filteredFlights.length === 0) {
document.getElementById('other-flights-no-data').style.display = 'block'; document.getElementById('other-flights-no-data').style.display = 'block';
document.getElementById('other-flights-table-content').style.display = 'none';
return; return;
} }
// Sort by time field (ascending) // Sort by time field (ascending)
flights.sort((a, b) => { filteredFlights.sort((a, b) => {
const aTime = a.timeField; const aTime = a.timeField;
const bTime = b.timeField; const bTime = b.timeField;
if (!aTime) return 1; if (!aTime) return 1;
@@ -1008,8 +1197,9 @@
tbody.innerHTML = ''; tbody.innerHTML = '';
document.getElementById('other-flights-table-content').style.display = 'block'; document.getElementById('other-flights-table-content').style.display = 'block';
document.getElementById('other-flights-no-data').style.display = 'none';
for (const flight of flights) { for (const flight of filteredFlights) {
const row = document.createElement('tr'); const row = document.createElement('tr');
const typeLabel = flight.flightType; const typeLabel = flight.flightType;