Live ac search

This commit is contained in:
James Pattinson
2025-10-21 21:06:38 +00:00
parent 4f952a5a1b
commit c1accd82c5
4 changed files with 232 additions and 70 deletions

View File

@@ -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>