Live ac search
This commit is contained in:
229
web/admin.html
229
web/admin.html
@@ -41,16 +41,25 @@
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
.top-menu {
|
||||
background: #2c3e50;
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.menu-left {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.menu-right {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@@ -421,14 +430,48 @@
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Aircraft Lookup Styles */
|
||||
#aircraft-lookup-results {
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
min-height: 20px;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.aircraft-match {
|
||||
padding: 0.3rem;
|
||||
background-color: #e8f5e8;
|
||||
border: 1px solid #c3e6c3;
|
||||
border-radius: 4px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.aircraft-no-match {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.aircraft-searching {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.controls {
|
||||
.top-menu {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 1rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.menu-left, .menu-right {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
@@ -452,28 +495,20 @@
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="controls">
|
||||
<div class="top-menu">
|
||||
<div class="menu-left">
|
||||
<button class="btn btn-success" onclick="openNewPPRModal()">
|
||||
➕ New PPR Entry
|
||||
</button>
|
||||
|
||||
<div class="filter-group">
|
||||
<label for="viewDate">Date:</label>
|
||||
<input type="date" id="viewDate" onchange="loadPPRs()">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="menu-right">
|
||||
<button class="btn btn-primary" onclick="loadPPRs()">
|
||||
🔄 Refresh
|
||||
</button>
|
||||
|
||||
<div class="filter-group" style="margin-left: auto;">
|
||||
<label>
|
||||
<input type="checkbox" id="showAllStatuses" onchange="loadPPRs()">
|
||||
Show all statuses (including departed/canceled)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<!-- Arrivals Table -->
|
||||
<div class="ppr-table">
|
||||
@@ -604,7 +639,8 @@
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label for="ac_reg">Aircraft Registration *</label>
|
||||
<input type="text" id="ac_reg" name="ac_reg" required>
|
||||
<input type="text" id="ac_reg" name="ac_reg" required oninput="handleAircraftLookup(this.value)">
|
||||
<div id="aircraft-lookup-results"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ac_type">Aircraft Type *</label>
|
||||
@@ -715,7 +751,6 @@
|
||||
currentUser = cachedUser;
|
||||
document.getElementById('current-user').textContent = cachedUser;
|
||||
loadPPRs();
|
||||
setDefaultDates();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -778,7 +813,6 @@
|
||||
|
||||
hideLogin();
|
||||
loadPPRs();
|
||||
setDefaultDates();
|
||||
} else {
|
||||
throw new Error(data.detail || 'Authentication failed');
|
||||
}
|
||||
@@ -814,11 +848,6 @@
|
||||
showLogin();
|
||||
}
|
||||
|
||||
function setDefaultDates() {
|
||||
const today = new Date();
|
||||
document.getElementById('viewDate').value = today.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
// Enhanced fetch wrapper with token expiry handling
|
||||
async function authenticatedFetch(url, options = {}) {
|
||||
if (!accessToken) {
|
||||
@@ -861,13 +890,9 @@
|
||||
document.getElementById('arrivals-no-data').style.display = 'none';
|
||||
|
||||
try {
|
||||
const viewDate = document.getElementById('viewDate').value;
|
||||
const showAll = document.getElementById('showAllStatuses').checked;
|
||||
|
||||
let url = '/api/v1/pprs/?limit=1000';
|
||||
if (viewDate) {
|
||||
url += `&date_from=${viewDate}&date_to=${viewDate}`;
|
||||
}
|
||||
// Always load today's date
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
let url = `/api/v1/pprs/?limit=1000&date_from=${today}&date_to=${today}`;
|
||||
|
||||
const response = await authenticatedFetch(url);
|
||||
|
||||
@@ -877,17 +902,10 @@
|
||||
|
||||
const allPPRs = await response.json();
|
||||
|
||||
// Filter for arrivals (NEW, CONFIRMED, or all if showAll is checked)
|
||||
let arrivals;
|
||||
if (showAll) {
|
||||
// Show all PPRs with ETA for the selected date
|
||||
arrivals = allPPRs.filter(ppr => ppr.eta);
|
||||
} else {
|
||||
// Show only NEW and CONFIRMED with ETA
|
||||
arrivals = allPPRs.filter(ppr =>
|
||||
(ppr.status === 'NEW' || ppr.status === 'CONFIRMED') && ppr.eta
|
||||
);
|
||||
}
|
||||
// Filter for arrivals (NEW and CONFIRMED with ETA only)
|
||||
const arrivals = allPPRs.filter(ppr =>
|
||||
(ppr.status === 'NEW' || ppr.status === 'CONFIRMED') && ppr.eta
|
||||
);
|
||||
|
||||
displayArrivals(arrivals);
|
||||
} catch (error) {
|
||||
@@ -907,13 +925,9 @@
|
||||
document.getElementById('departures-no-data').style.display = 'none';
|
||||
|
||||
try {
|
||||
const viewDate = document.getElementById('viewDate').value;
|
||||
const showAll = document.getElementById('showAllStatuses').checked;
|
||||
|
||||
let url = '/api/v1/pprs/?limit=1000';
|
||||
if (viewDate) {
|
||||
url += `&date_from=${viewDate}&date_to=${viewDate}`;
|
||||
}
|
||||
// Always load today's date
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
let url = `/api/v1/pprs/?limit=1000&date_from=${today}&date_to=${today}`;
|
||||
|
||||
const response = await authenticatedFetch(url);
|
||||
|
||||
@@ -923,15 +937,8 @@
|
||||
|
||||
const allPPRs = await response.json();
|
||||
|
||||
// Filter for departures (LANDED, or include DEPARTED if showAll is checked)
|
||||
let departures;
|
||||
if (showAll) {
|
||||
departures = allPPRs.filter(ppr =>
|
||||
ppr.status === 'LANDED' || ppr.status === 'DEPARTED'
|
||||
);
|
||||
} else {
|
||||
departures = allPPRs.filter(ppr => ppr.status === 'LANDED');
|
||||
}
|
||||
// Filter for departures (LANDED status only)
|
||||
const departures = allPPRs.filter(ppr => ppr.status === 'LANDED');
|
||||
|
||||
displayDepartures(departures);
|
||||
} catch (error) {
|
||||
@@ -1063,7 +1070,15 @@
|
||||
document.getElementById('ppr-form').reset();
|
||||
document.getElementById('ppr-id').value = '';
|
||||
|
||||
// Clear aircraft lookup results
|
||||
clearAircraftLookup();
|
||||
|
||||
document.getElementById('pprModal').style.display = 'block';
|
||||
|
||||
// Auto-focus on aircraft registration field
|
||||
setTimeout(() => {
|
||||
document.getElementById('ac_reg').focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async function openPPRModal(pprId) {
|
||||
@@ -1270,6 +1285,90 @@
|
||||
closePPRModal();
|
||||
}
|
||||
}
|
||||
|
||||
// Aircraft Lookup Functions
|
||||
let aircraftLookupTimeout;
|
||||
|
||||
function handleAircraftLookup(registration) {
|
||||
// Clear previous timeout
|
||||
if (aircraftLookupTimeout) {
|
||||
clearTimeout(aircraftLookupTimeout);
|
||||
}
|
||||
|
||||
// Clear results if input is too short
|
||||
if (registration.length < 4) {
|
||||
clearAircraftLookup();
|
||||
return;
|
||||
}
|
||||
|
||||
// Show searching indicator
|
||||
document.getElementById('aircraft-lookup-results').innerHTML =
|
||||
'<div class="aircraft-searching">Searching...</div>';
|
||||
|
||||
// Debounce the search - wait 300ms after user stops typing
|
||||
aircraftLookupTimeout = setTimeout(() => {
|
||||
performAircraftLookup(registration);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
async function performAircraftLookup(registration) {
|
||||
try {
|
||||
// Clean the input (remove non-alphanumeric characters and make uppercase)
|
||||
const cleanInput = registration.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
|
||||
|
||||
if (cleanInput.length < 4) {
|
||||
clearAircraftLookup();
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the real API
|
||||
const response = await authenticatedFetch(`/api/v1/aircraft/lookup/${cleanInput}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch aircraft data');
|
||||
}
|
||||
|
||||
const matches = await response.json();
|
||||
displayAircraftLookupResults(matches, cleanInput);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Aircraft lookup error:', error);
|
||||
document.getElementById('aircraft-lookup-results').innerHTML =
|
||||
'<div class="aircraft-no-match">Lookup failed - please enter manually</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function displayAircraftLookupResults(matches, searchTerm) {
|
||||
const resultsDiv = document.getElementById('aircraft-lookup-results');
|
||||
|
||||
if (matches.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="aircraft-no-match">No matches found</div>';
|
||||
} else if (matches.length === 1) {
|
||||
// Unique match found - auto-populate
|
||||
const aircraft = matches[0];
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="aircraft-match">
|
||||
✓ ${aircraft.manufacturer_name} ${aircraft.model} (${aircraft.type_code})
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Auto-populate the form fields
|
||||
document.getElementById('ac_reg').value = aircraft.registration;
|
||||
document.getElementById('ac_type').value = aircraft.type_code;
|
||||
|
||||
} else {
|
||||
// Multiple matches - show list but don't auto-populate
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="aircraft-no-match">
|
||||
Multiple matches found (${matches.length}) - please be more specific
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function clearAircraftLookup() {
|
||||
document.getElementById('aircraft-lookup-results').innerHTML = '';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user