Book in functions
This commit is contained in:
357
web/admin.html
357
web/admin.html
@@ -19,6 +19,9 @@
|
||||
<button class="btn btn-info" onclick="openLocalFlightModal()">
|
||||
🛫 Book Out
|
||||
</button>
|
||||
<button class="btn btn-info" onclick="openBookInModal()">
|
||||
🛬 Book In
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="window.open('reports.html', '_blank')">
|
||||
📊 Reports
|
||||
</button>
|
||||
@@ -505,6 +508,65 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Book In Modal -->
|
||||
<div id="bookInModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2>Book In</h2>
|
||||
<button class="close" onclick="closeBookInModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="book-in-form">
|
||||
<input type="hidden" id="book-in-id" name="id">
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label for="book_in_registration">Aircraft Registration *</label>
|
||||
<input type="text" id="book_in_registration" name="registration" required oninput="handleBookInAircraftLookup(this.value)" tabindex="1">
|
||||
<div id="book-in-aircraft-lookup-results"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="book_in_type">Aircraft Type</label>
|
||||
<input type="text" id="book_in_type" name="type" tabindex="4" placeholder="e.g., C172, PA34, AA5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="book_in_callsign">Callsign (optional)</label>
|
||||
<input type="text" id="book_in_callsign" name="callsign" placeholder="If different from registration" tabindex="5">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="book_in_pob">Persons on Board *</label>
|
||||
<input type="number" id="book_in_pob" name="pob" required min="1" tabindex="2">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="book_in_from">Coming From (Airport) *</label>
|
||||
<input type="text" id="book_in_from" name="in_from" placeholder="ICAO Code or Airport Name" required oninput="handleBookInArrivalAirportLookup(this.value)" tabindex="3">
|
||||
<div id="book-in-arrival-airport-lookup-results"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="book_in_eta_time">ETA (Estimated Time of Arrival) *</label>
|
||||
<select id="book_in_eta_time" name="eta_time" required>
|
||||
<option value="">Select Time</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group full-width">
|
||||
<label for="book_in_notes">Notes</label>
|
||||
<textarea id="book_in_notes" name="notes" rows="3" placeholder="e.g., any special requirements"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeBookInModal()">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
🛬 Book In
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Departure Edit Modal -->
|
||||
<div id="departureEditModal" class="modal">
|
||||
<div class="modal-content">
|
||||
@@ -1014,6 +1076,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Press 'Escape' to close Book In modal if it's open (allow even when typing in inputs)
|
||||
if (e.key === 'Escape' && document.getElementById('bookInModal').style.display === 'block') {
|
||||
e.preventDefault();
|
||||
closeBookInModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only trigger other shortcuts when not typing in input fields
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
|
||||
return;
|
||||
@@ -1042,6 +1111,12 @@
|
||||
e.preventDefault();
|
||||
openLocalFlightModal('DEPARTURE');
|
||||
}
|
||||
|
||||
// Press 'i' to book in arrival
|
||||
if (e.key === 'i' || e.key === 'I') {
|
||||
e.preventDefault();
|
||||
openBookInModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1177,10 +1252,11 @@
|
||||
document.getElementById('arrivals-no-data').style.display = 'none';
|
||||
|
||||
try {
|
||||
// Load PPRs and local flights that are in the air
|
||||
const [pprResponse, localResponse] = await Promise.all([
|
||||
// Load PPRs, local flights, and booked-in arrivals
|
||||
const [pprResponse, localResponse, bookInResponse] = await Promise.all([
|
||||
authenticatedFetch('/api/v1/pprs/?limit=1000'),
|
||||
authenticatedFetch('/api/v1/local-flights/?status=DEPARTED&limit=1000')
|
||||
authenticatedFetch('/api/v1/local-flights/?status=DEPARTED&limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?limit=1000')
|
||||
]);
|
||||
|
||||
if (!pprResponse.ok) {
|
||||
@@ -1218,6 +1294,24 @@
|
||||
arrivals.push(...localInAir);
|
||||
}
|
||||
|
||||
// Add booked-in arrivals from the arrivals table
|
||||
if (bookInResponse.ok) {
|
||||
const bookedInArrivals = await bookInResponse.json();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const bookedInToday = bookedInArrivals
|
||||
.filter(arrival => {
|
||||
// Only include arrivals booked in today (created_dt) with BOOKED_IN status
|
||||
if (!arrival.created_dt || arrival.status !== 'BOOKED_IN') return false;
|
||||
const bookedDate = arrival.created_dt.split('T')[0];
|
||||
return bookedDate === today;
|
||||
})
|
||||
.map(arrival => ({
|
||||
...arrival,
|
||||
isBookedIn: true // Flag to distinguish from PPR and local
|
||||
}));
|
||||
arrivals.push(...bookedInToday);
|
||||
}
|
||||
|
||||
displayArrivals(arrivals);
|
||||
} catch (error) {
|
||||
console.error('Error loading arrivals:', error);
|
||||
@@ -1435,13 +1529,17 @@
|
||||
document.getElementById('parked-no-data').style.display = 'none';
|
||||
|
||||
try {
|
||||
const response = await authenticatedFetch('/api/v1/pprs/?limit=1000');
|
||||
// Load both PPRs and booked-in arrivals
|
||||
const [pprResponse, bookedInResponse] = await Promise.all([
|
||||
authenticatedFetch('/api/v1/pprs/?limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?limit=1000')
|
||||
]);
|
||||
|
||||
if (!response.ok) {
|
||||
if (!pprResponse.ok) {
|
||||
throw new Error('Failed to fetch parked visitors');
|
||||
}
|
||||
|
||||
const allPPRs = await response.json();
|
||||
const allPPRs = await pprResponse.json();
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
// Filter for parked visitors: LANDED status and (no ETD or ETD not today)
|
||||
@@ -1459,6 +1557,18 @@
|
||||
return etdDate !== today;
|
||||
});
|
||||
|
||||
// Add booked-in arrivals with LANDED status
|
||||
if (bookedInResponse.ok) {
|
||||
const bookedInArrivals = await bookedInResponse.json();
|
||||
const bookedInParked = bookedInArrivals
|
||||
.filter(arrival => arrival.status === 'LANDED')
|
||||
.map(arrival => ({
|
||||
...arrival,
|
||||
isBookedIn: true // Flag to distinguish from PPR
|
||||
}));
|
||||
parked.push(...bookedInParked);
|
||||
}
|
||||
|
||||
displayParked(parked);
|
||||
} catch (error) {
|
||||
console.error('Error loading parked visitors:', error);
|
||||
@@ -1491,9 +1601,25 @@
|
||||
|
||||
for (const ppr of parked) {
|
||||
const row = document.createElement('tr');
|
||||
row.onclick = () => openPPRModal(ppr.id);
|
||||
const isBookedIn = ppr.isBookedIn;
|
||||
|
||||
// Click handler that routes to correct modal/display
|
||||
if (isBookedIn) {
|
||||
row.style.cursor = 'default'; // Booked-in arrivals don't have a modal yet
|
||||
} else {
|
||||
row.onclick = () => openPPRModal(ppr.id);
|
||||
}
|
||||
row.style.cssText = 'font-size: 0.85rem !important; font-style: italic;';
|
||||
|
||||
// Get registration based on type (PPR vs booked-in)
|
||||
const registration = ppr.ac_reg || ppr.registration || '-';
|
||||
|
||||
// Get aircraft type based on type (PPR vs booked-in)
|
||||
const acType = ppr.ac_type || ppr.type || '-';
|
||||
|
||||
// Get from airport
|
||||
const fromAirport = ppr.in_from || '-';
|
||||
|
||||
// Format arrival: time if today, date if not
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
let arrivedDisplay = '-';
|
||||
@@ -1517,9 +1643,9 @@
|
||||
}
|
||||
|
||||
row.innerHTML = `
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${ppr.ac_reg || '-'}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${ppr.ac_type || '-'}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${ppr.in_from || '-'}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${registration}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${acType}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${fromAirport}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${arrivedDisplay}</td>
|
||||
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${etdDisplay}</td>
|
||||
`;
|
||||
@@ -1674,11 +1800,16 @@
|
||||
for (const flight of arrivals) {
|
||||
const row = document.createElement('tr');
|
||||
const isLocal = flight.isLocalFlight;
|
||||
const isBookedIn = flight.isBookedIn;
|
||||
|
||||
// Click handler that routes to correct modal
|
||||
row.onclick = () => {
|
||||
if (isLocal) {
|
||||
openLocalFlightEditModal(flight.id);
|
||||
} else if (isBookedIn) {
|
||||
// For booked-in flights, we might add a view modal later
|
||||
// For now, just show a message
|
||||
showNotification(`Booked-in flight: ${flight.registration}`, false);
|
||||
} else {
|
||||
openPPRModal(flight.id);
|
||||
}
|
||||
@@ -1713,6 +1844,34 @@
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
} else if (isBookedIn) {
|
||||
// Booked-in arrival display
|
||||
if (flight.callsign && flight.callsign.trim()) {
|
||||
aircraftDisplay = `<strong>${flight.callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.registration}</span>`;
|
||||
} else {
|
||||
aircraftDisplay = `<strong>${flight.registration}</strong>`;
|
||||
}
|
||||
acType = flight.type;
|
||||
|
||||
// Lookup airport name for in_from
|
||||
let fromDisplay_temp = flight.in_from;
|
||||
if (flight.in_from && flight.in_from.length === 4 && /^[A-Z]{4}$/.test(flight.in_from)) {
|
||||
fromDisplay_temp = await getAirportDisplay(flight.in_from);
|
||||
}
|
||||
fromDisplay = fromDisplay_temp;
|
||||
|
||||
// Show ETA if available, otherwise show landed_dt
|
||||
eta = flight.eta ? formatTimeOnly(flight.eta) : (flight.landed_dt ? formatTimeOnly(flight.landed_dt) : '-');
|
||||
pob = flight.pob || '-';
|
||||
fuel = '-';
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
} else {
|
||||
// PPR display
|
||||
if (flight.ac_call && flight.ac_call.trim()) {
|
||||
@@ -2165,11 +2324,11 @@
|
||||
}
|
||||
|
||||
// Timestamp modal functions
|
||||
function showTimestampModal(status, pprId = null, isLocalFlight = false, isDeparture = false) {
|
||||
const targetId = pprId || (isLocalFlight ? currentLocalFlightId : currentPPRId);
|
||||
function showTimestampModal(status, pprId = null, isLocalFlight = false, isDeparture = false, isBookedIn = false) {
|
||||
const targetId = pprId || (isLocalFlight ? currentLocalFlightId : (isBookedIn ? currentBookedInArrivalId : currentPPRId));
|
||||
if (!targetId) return;
|
||||
|
||||
pendingStatusUpdate = { status: status, pprId: targetId, isLocalFlight: isLocalFlight, isDeparture: isDeparture };
|
||||
pendingStatusUpdate = { status: status, pprId: targetId, isLocalFlight: isLocalFlight, isDeparture: isDeparture, isBookedIn: isBookedIn };
|
||||
|
||||
const modalTitle = document.getElementById('timestamp-modal-title');
|
||||
const submitBtn = document.getElementById('timestamp-submit-btn');
|
||||
@@ -2218,12 +2377,15 @@
|
||||
// Determine the correct API endpoint based on flight type
|
||||
const isLocal = pendingStatusUpdate.isLocalFlight;
|
||||
const isDeparture = pendingStatusUpdate.isDeparture;
|
||||
const isBookedIn = pendingStatusUpdate.isBookedIn;
|
||||
let endpoint;
|
||||
|
||||
if (isLocal) {
|
||||
endpoint = `/api/v1/local-flights/${pendingStatusUpdate.pprId}/status`;
|
||||
} else if (isDeparture) {
|
||||
endpoint = `/api/v1/departures/${pendingStatusUpdate.pprId}/status`;
|
||||
} else if (isBookedIn) {
|
||||
endpoint = `/api/v1/arrivals/${pendingStatusUpdate.pprId}/status`;
|
||||
} else {
|
||||
endpoint = `/api/v1/pprs/${pendingStatusUpdate.pprId}/status`;
|
||||
}
|
||||
@@ -2247,9 +2409,14 @@
|
||||
|
||||
const updatedStatus = pendingStatusUpdate.status;
|
||||
closeTimestampModal();
|
||||
loadPPRs(); // Refresh all tables
|
||||
// Refresh appropriate table based on flight type
|
||||
if (isBookedIn) {
|
||||
loadArrivals(); // Refresh arrivals table
|
||||
} else {
|
||||
loadPPRs(); // Refresh all tables (PPR, local, departures)
|
||||
}
|
||||
showNotification(`Status updated to ${updatedStatus}`);
|
||||
if (!isLocal) {
|
||||
if (!isLocal && !isBookedIn) {
|
||||
closePPRModal(); // Close PPR modal after successful status update
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2730,6 +2897,7 @@
|
||||
const userManagementModal = document.getElementById('userManagementModal');
|
||||
const userModal = document.getElementById('userModal');
|
||||
const tableHelpModal = document.getElementById('tableHelpModal');
|
||||
const bookInModal = document.getElementById('bookInModal');
|
||||
|
||||
if (event.target === pprModal) {
|
||||
closePPRModal();
|
||||
@@ -2746,6 +2914,9 @@
|
||||
if (event.target === tableHelpModal) {
|
||||
closeTableHelp();
|
||||
}
|
||||
if (event.target === bookInModal) {
|
||||
closeBookInModal();
|
||||
}
|
||||
}
|
||||
|
||||
function clearArrivalAirportLookup() {
|
||||
@@ -2787,6 +2958,50 @@
|
||||
document.getElementById('localFlightModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function openBookInModal() {
|
||||
document.getElementById('book-in-form').reset();
|
||||
document.getElementById('book-in-id').value = '';
|
||||
document.getElementById('bookInModal').style.display = 'block';
|
||||
|
||||
// Clear aircraft lookup results
|
||||
clearBookInAircraftLookup();
|
||||
clearBookInArrivalAirportLookup();
|
||||
|
||||
// Populate ETA time slots
|
||||
populateETATimeSlots();
|
||||
|
||||
// Auto-focus on registration field
|
||||
setTimeout(() => {
|
||||
document.getElementById('book_in_registration').focus();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function closeBookInModal() {
|
||||
document.getElementById('bookInModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function populateETATimeSlots() {
|
||||
const select = document.getElementById('book_in_eta_time');
|
||||
const next15MinSlot = getNext10MinuteSlot();
|
||||
|
||||
select.innerHTML = '';
|
||||
|
||||
for (let i = 0; i < 14; i++) {
|
||||
const time = new Date(next15MinSlot.getTime() + i * 10 * 60 * 1000);
|
||||
const hours = time.getHours().toString().padStart(2, '0');
|
||||
const minutes = time.getMinutes().toString().padStart(2, '0');
|
||||
const timeStr = `${hours}:${minutes}`;
|
||||
|
||||
const option = document.createElement('option');
|
||||
option.value = timeStr;
|
||||
option.textContent = timeStr;
|
||||
if (i === 0) {
|
||||
option.selected = true;
|
||||
}
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle flight type change to show/hide destination field
|
||||
function handleFlightTypeChange(flightType) {
|
||||
const destGroup = document.getElementById('departure-destination-group');
|
||||
@@ -2870,6 +3085,7 @@
|
||||
|
||||
// Local Flight Edit Modal Functions
|
||||
let currentLocalFlightId = null;
|
||||
let currentBookedInArrivalId = null;
|
||||
|
||||
async function openLocalFlightEditModal(flightId) {
|
||||
if (!accessToken) return;
|
||||
@@ -3026,6 +3242,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Update status from table for booked-in arrivals
|
||||
async function updateArrivalStatusFromTable(arrivalId, status) {
|
||||
if (!accessToken) return;
|
||||
|
||||
// Show confirmation for cancel actions
|
||||
if (status === 'CANCELLED') {
|
||||
if (!confirm('Are you sure you want to cancel this arrival? This action cannot be easily undone.')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/arrivals/${arrivalId}/status`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
body: JSON.stringify({ status: status })
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update status');
|
||||
|
||||
loadArrivals(); // Refresh arrivals table
|
||||
showNotification(`Arrival marked as ${status.toLowerCase()}`);
|
||||
} catch (error) {
|
||||
console.error('Error updating status:', error);
|
||||
showNotification('Error updating arrival status', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Update status from modal (uses currentLocalFlightId)
|
||||
async function updateLocalFlightStatus(status) {
|
||||
if (!currentLocalFlightId || !accessToken) return;
|
||||
@@ -3267,6 +3514,86 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('book-in-form').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!accessToken) return;
|
||||
|
||||
const formData = new FormData(this);
|
||||
const arrivalData = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
// Skip the hidden id field and empty values
|
||||
if (key === 'id') return;
|
||||
|
||||
// Handle time-only ETA (always today)
|
||||
if (key === 'eta_time') {
|
||||
if (value.trim()) {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
const dateStr = `${year}-${month}-${day}`;
|
||||
// Store ETA in the eta field
|
||||
arrivalData.eta = new Date(`${dateStr}T${value}`).toISOString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only include non-empty values
|
||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||
if (key === 'pob') {
|
||||
arrivalData[key] = parseInt(value);
|
||||
} else if (value.trim) {
|
||||
arrivalData[key] = value.trim();
|
||||
} else {
|
||||
arrivalData[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Book In uses LANDED status (they're arriving now)
|
||||
arrivalData.status = 'LANDED';
|
||||
|
||||
console.log('Submitting arrivals data:', arrivalData);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/v1/arrivals/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
body: JSON.stringify(arrivalData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
let errorMessage = 'Failed to book in arrival';
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
if (errorData.detail) {
|
||||
errorMessage = typeof errorData.detail === 'string' ? errorData.detail : JSON.stringify(errorData.detail);
|
||||
} else if (errorData.errors) {
|
||||
errorMessage = errorData.errors.map(e => e.msg).join(', ');
|
||||
}
|
||||
} catch (e) {
|
||||
const text = await response.text();
|
||||
console.error('Server response:', text);
|
||||
errorMessage = `Server error (${response.status})`;
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
closeBookInModal();
|
||||
loadPPRs(); // Refresh tables
|
||||
showNotification(`Aircraft ${result.registration} booked in successfully!`);
|
||||
} catch (error) {
|
||||
console.error('Error booking in arrival:', error);
|
||||
showNotification(`Error: ${error.message}`, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Add hover listeners to all notes tooltips
|
||||
function setupTooltips() {
|
||||
document.querySelectorAll('.notes-tooltip').forEach(tooltip => {
|
||||
|
||||
@@ -234,8 +234,8 @@
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('WebSocket message received:', data);
|
||||
|
||||
// Refresh display when any PPR-related, local flight, or departure event occurs
|
||||
if (data.type && (data.type.includes('ppr_') || data.type === 'status_update' || data.type.includes('local_flight_') || data.type.includes('departure_'))) {
|
||||
// Refresh display when any PPR-related, local flight, departure, or arrival event occurs
|
||||
if (data.type && (data.type.includes('ppr_') || data.type === 'status_update' || data.type.includes('local_flight_') || data.type.includes('departure_') || data.type.includes('arrival_'))) {
|
||||
console.log('Flight update detected, refreshing display...');
|
||||
loadArrivals();
|
||||
loadDepartures();
|
||||
@@ -305,6 +305,7 @@
|
||||
// Build rows asynchronously to lookup airport names
|
||||
const rows = await Promise.all(arrivals.map(async (arrival) => {
|
||||
const isLocal = arrival.isLocalFlight;
|
||||
const isBookedIn = arrival.isBookedIn;
|
||||
|
||||
if (isLocal) {
|
||||
// Local flight
|
||||
@@ -321,6 +322,30 @@
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
} else if (isBookedIn) {
|
||||
// Booked-in arrival
|
||||
const aircraftId = arrival.callsign || arrival.registration || '';
|
||||
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.type || '')})</span>`;
|
||||
const fromDisplay = await getAirportName(arrival.in_from || '');
|
||||
|
||||
let timeDisplay;
|
||||
if (arrival.status === 'LANDED' && arrival.landed_dt) {
|
||||
// Show landed time if LANDED
|
||||
const time = convertToLocalTime(arrival.landed_dt);
|
||||
timeDisplay = `<div style="display: flex; align-items: center; gap: 8px;"><span style="color: #27ae60; font-weight: bold;">${time}</span><span style="font-size: 0.7em; background: #27ae60; color: white; padding: 2px 4px; border-radius: 3px; white-space: nowrap;">LANDED</span></div>`;
|
||||
} else {
|
||||
// Show ETA if BOOKED_IN
|
||||
const time = convertToLocalTime(arrival.eta);
|
||||
timeDisplay = `<div style="display: flex; align-items: center; gap: 8px;"><span style="color: #3498db; font-weight: bold;">${time}</span><span style="font-size: 0.7em; background: #3498db; color: white; padding: 2px 4px; border-radius: 3px; white-space: nowrap;">IN AIR</span></div>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${escapeHtml(fromDisplay)}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
} else {
|
||||
// PPR
|
||||
const aircraftId = arrival.ac_call || arrival.ac_reg || '';
|
||||
|
||||
@@ -191,6 +191,8 @@ function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
typeFieldId = 'ac_type';
|
||||
} else if (fieldId === 'local_registration') {
|
||||
typeFieldId = 'local_type';
|
||||
} else if (fieldId === 'book_in_registration') {
|
||||
typeFieldId = 'book_in_type';
|
||||
}
|
||||
|
||||
if (typeFieldId) {
|
||||
@@ -342,12 +344,29 @@ function initializeLookups() {
|
||||
{ isAircraft: true, minLength: 4, debounceMs: 300 }
|
||||
);
|
||||
lookupManager.register('local-aircraft', localAircraftLookup);
|
||||
|
||||
const bookInAircraftLookup = createLookup(
|
||||
'book_in_registration',
|
||||
'book-in-aircraft-lookup-results',
|
||||
null,
|
||||
{ isAircraft: true, minLength: 4, debounceMs: 300 }
|
||||
);
|
||||
lookupManager.register('book-in-aircraft', bookInAircraftLookup);
|
||||
|
||||
const bookInArrivalAirportLookup = createLookup(
|
||||
'book_in_from',
|
||||
'book-in-arrival-airport-lookup-results',
|
||||
null,
|
||||
{ isAirport: true, minLength: 2 }
|
||||
);
|
||||
lookupManager.register('book-in-arrival-airport', bookInArrivalAirportLookup);
|
||||
|
||||
// Attach keyboard handlers to airport input fields
|
||||
setTimeout(() => {
|
||||
if (arrivalAirportLookup.attachKeyboardHandler) arrivalAirportLookup.attachKeyboardHandler();
|
||||
if (departureAirportLookup.attachKeyboardHandler) departureAirportLookup.attachKeyboardHandler();
|
||||
if (localOutToLookup.attachKeyboardHandler) localOutToLookup.attachKeyboardHandler();
|
||||
if (bookInArrivalAirportLookup.attachKeyboardHandler) bookInArrivalAirportLookup.attachKeyboardHandler();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@@ -426,3 +445,31 @@ function selectLocalOutToAirport(icaoCode) {
|
||||
function selectLocalAircraft(registration) {
|
||||
lookupManager.selectItem('local-aircraft-lookup-results', 'local_registration', registration);
|
||||
}
|
||||
|
||||
function handleBookInAircraftLookup(value) {
|
||||
const lookup = lookupManager.lookups['book-in-aircraft'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function handleBookInArrivalAirportLookup(value) {
|
||||
const lookup = lookupManager.lookups['book-in-arrival-airport'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function clearBookInAircraftLookup() {
|
||||
const lookup = lookupManager.lookups['book-in-aircraft'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function clearBookInArrivalAirportLookup() {
|
||||
const lookup = lookupManager.lookups['book-in-arrival-airport'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function selectBookInAircraft(registration) {
|
||||
lookupManager.selectItem('book-in-aircraft-lookup-results', 'book_in_registration', registration);
|
||||
}
|
||||
|
||||
function selectBookInArrivalAirport(icaoCode) {
|
||||
lookupManager.selectItem('book-in-arrival-airport-lookup-results', 'book_in_from', icaoCode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user