Compare commits
2 Commits
0aeed2268a
...
f7467690e4
| Author | SHA1 | Date | |
|---|---|---|---|
| f7467690e4 | |||
| 1d1c504f91 |
@@ -3,20 +3,95 @@ from fastapi import APIRouter, Depends
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from app.api.deps import get_db
|
from app.api.deps import get_db
|
||||||
from app.crud.crud_ppr import ppr as crud_ppr
|
from app.crud.crud_ppr import ppr as crud_ppr
|
||||||
|
from app.crud.crud_local_flight import local_flight as crud_local_flight
|
||||||
from app.schemas.ppr import PPRPublic
|
from app.schemas.ppr import PPRPublic
|
||||||
|
from app.models.local_flight import LocalFlightStatus
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get("/arrivals", response_model=List[PPRPublic])
|
@router.get("/arrivals")
|
||||||
async def get_public_arrivals(db: Session = Depends(get_db)):
|
async def get_public_arrivals(db: Session = Depends(get_db)):
|
||||||
"""Get today's arrivals for public display"""
|
"""Get today's arrivals for public display (PPR and local flights)"""
|
||||||
arrivals = crud_ppr.get_arrivals_today(db)
|
arrivals = crud_ppr.get_arrivals_today(db)
|
||||||
return arrivals
|
|
||||||
|
# Convert PPR arrivals to dictionaries
|
||||||
|
arrivals_list = []
|
||||||
|
for arrival in arrivals:
|
||||||
|
arrivals_list.append({
|
||||||
|
'ac_call': arrival.ac_call,
|
||||||
|
'ac_reg': arrival.ac_reg,
|
||||||
|
'ac_type': arrival.ac_type,
|
||||||
|
'in_from': arrival.in_from,
|
||||||
|
'eta': arrival.eta,
|
||||||
|
'landed_dt': arrival.landed_dt,
|
||||||
|
'status': arrival.status.value,
|
||||||
|
'isLocalFlight': False
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add local flights with DEPARTED status
|
||||||
|
local_flights = crud_local_flight.get_multi(
|
||||||
|
db,
|
||||||
|
status=LocalFlightStatus.DEPARTED,
|
||||||
|
limit=1000
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert local flights to match the PPR format for display
|
||||||
|
for flight in local_flights:
|
||||||
|
arrivals_list.append({
|
||||||
|
'ac_call': flight.callsign or flight.registration,
|
||||||
|
'ac_reg': flight.registration,
|
||||||
|
'ac_type': flight.type,
|
||||||
|
'in_from': None,
|
||||||
|
'eta': flight.departure_dt,
|
||||||
|
'landed_dt': None,
|
||||||
|
'status': 'DEPARTED',
|
||||||
|
'isLocalFlight': True,
|
||||||
|
'flight_type': flight.flight_type.value
|
||||||
|
})
|
||||||
|
|
||||||
|
return arrivals_list
|
||||||
|
|
||||||
|
|
||||||
@router.get("/departures", response_model=List[PPRPublic])
|
@router.get("/departures")
|
||||||
async def get_public_departures(db: Session = Depends(get_db)):
|
async def get_public_departures(db: Session = Depends(get_db)):
|
||||||
"""Get today's departures for public display"""
|
"""Get today's departures for public display (PPR and local flights)"""
|
||||||
departures = crud_ppr.get_departures_today(db)
|
departures = crud_ppr.get_departures_today(db)
|
||||||
return departures
|
|
||||||
|
# Convert PPR departures to dictionaries
|
||||||
|
departures_list = []
|
||||||
|
for departure in departures:
|
||||||
|
departures_list.append({
|
||||||
|
'ac_call': departure.ac_call,
|
||||||
|
'ac_reg': departure.ac_reg,
|
||||||
|
'ac_type': departure.ac_type,
|
||||||
|
'out_to': departure.out_to,
|
||||||
|
'etd': departure.etd,
|
||||||
|
'departed_dt': departure.departed_dt,
|
||||||
|
'status': departure.status.value,
|
||||||
|
'isLocalFlight': False
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add local flights with BOOKED_OUT status
|
||||||
|
local_flights = crud_local_flight.get_multi(
|
||||||
|
db,
|
||||||
|
status=LocalFlightStatus.BOOKED_OUT,
|
||||||
|
limit=1000
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert local flights to match the PPR format for display
|
||||||
|
for flight in local_flights:
|
||||||
|
departures_list.append({
|
||||||
|
'ac_call': flight.callsign or flight.registration,
|
||||||
|
'ac_reg': flight.registration,
|
||||||
|
'ac_type': flight.type,
|
||||||
|
'out_to': None,
|
||||||
|
'etd': flight.booked_out_dt,
|
||||||
|
'departed_dt': None,
|
||||||
|
'status': 'BOOKED_OUT',
|
||||||
|
'isLocalFlight': True,
|
||||||
|
'flight_type': flight.flight_type.value
|
||||||
|
})
|
||||||
|
|
||||||
|
return departures_list
|
||||||
@@ -982,25 +982,24 @@
|
|||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_registration">Aircraft Registration *</label>
|
<label for="local_registration">Aircraft Registration *</label>
|
||||||
<input type="text" id="local_registration" name="registration" required oninput="handleLocalAircraftLookup(this.value)">
|
<input type="text" id="local_registration" name="registration" required oninput="handleLocalAircraftLookup(this.value)" tabindex="1">
|
||||||
<div id="local-aircraft-lookup-results"></div>
|
<div id="local-aircraft-lookup-results"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_type">Aircraft Type *</label>
|
<label for="local_type">Aircraft Type *</label>
|
||||||
<input type="text" id="local_type" name="type" required tabindex="-1">
|
<input type="text" id="local_type" name="type" required tabindex="-1" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_callsign">Callsign (optional)</label>
|
<label for="local_callsign">Callsign (optional)</label>
|
||||||
<input type="text" id="local_callsign" name="callsign" placeholder="If different from registration">
|
<input type="text" id="local_callsign" name="callsign" placeholder="If different from registration" tabindex="4">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_pob">Persons on Board *</label>
|
<label for="local_pob">Persons on Board *</label>
|
||||||
<input type="number" id="local_pob" name="pob" required min="1">
|
<input type="number" id="local_pob" name="pob" required min="1" tabindex="2">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="local_flight_type">Flight Type *</label>
|
<label for="local_flight_type">Flight Type *</label>
|
||||||
<select id="local_flight_type" name="flight_type" required>
|
<select id="local_flight_type" name="flight_type" required tabindex="3">
|
||||||
<option value="">Select Type</option>
|
|
||||||
<option value="LOCAL">Local Flight</option>
|
<option value="LOCAL">Local Flight</option>
|
||||||
<option value="CIRCUITS">Circuits</option>
|
<option value="CIRCUITS">Circuits</option>
|
||||||
<option value="DEPARTURE">Departure</option>
|
<option value="DEPARTURE">Departure</option>
|
||||||
@@ -1504,6 +1503,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Press 'Escape' to close Book Out modal if it's open (allow even when typing in inputs)
|
||||||
|
if (e.key === 'Escape' && document.getElementById('localFlightModal').style.display === 'block') {
|
||||||
|
e.preventDefault();
|
||||||
|
closeLocalFlightModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Only trigger other shortcuts when not typing in input fields
|
// Only trigger other shortcuts when not typing in input fields
|
||||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
|
||||||
return;
|
return;
|
||||||
@@ -1514,6 +1520,24 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
openNewPPRModal();
|
openNewPPRModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Press 'b' to book out local flight (LOCAL type)
|
||||||
|
if (e.key === 'b' || e.key === 'B') {
|
||||||
|
e.preventDefault();
|
||||||
|
openLocalFlightModal('LOCAL');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Press 'c' to book out circuits
|
||||||
|
if (e.key === 'c' || e.key === 'C') {
|
||||||
|
e.preventDefault();
|
||||||
|
openLocalFlightModal('CIRCUITS');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Press 'd' to book out departure
|
||||||
|
if (e.key === 'd' || e.key === 'D') {
|
||||||
|
e.preventDefault();
|
||||||
|
openLocalFlightModal('DEPARTURE');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2115,15 +2139,18 @@
|
|||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
// Local flight display
|
// Local flight display
|
||||||
const callsign = flight.callsign && flight.callsign.trim() ? flight.callsign : flight.registration;
|
if (flight.callsign && flight.callsign.trim()) {
|
||||||
aircraftDisplay = `<strong>${callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.registration}</span>`;
|
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;
|
acType = flight.type;
|
||||||
fromDisplay = '-';
|
fromDisplay = `<i>${flight.flight_type === 'CIRCUITS' ? 'Circuits' : flight.flight_type === 'LOCAL' ? 'Local Flight' : 'Departure'}</i>`;
|
||||||
eta = flight.departure_dt ? formatTimeOnly(flight.departure_dt) : '-';
|
eta = flight.departure_dt ? formatTimeOnly(flight.departure_dt) : '-';
|
||||||
pob = flight.pob || '-';
|
pob = flight.pob || '-';
|
||||||
fuel = '-';
|
fuel = '-';
|
||||||
actionButtons = `
|
actionButtons = `
|
||||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'LANDED')" title="Mark as Landed">
|
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, true)" title="Mark as Landed">
|
||||||
LAND
|
LAND
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||||
@@ -2132,8 +2159,11 @@
|
|||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
// PPR display
|
// PPR display
|
||||||
const callsign = flight.ac_call && flight.ac_call.trim() ? flight.ac_call : flight.ac_reg;
|
if (flight.ac_call && flight.ac_call.trim()) {
|
||||||
aircraftDisplay = `<strong>${callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.ac_reg}</span>`;
|
aircraftDisplay = `<strong>${flight.ac_call}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.ac_reg}</span>`;
|
||||||
|
} else {
|
||||||
|
aircraftDisplay = `<strong>${flight.ac_reg}</strong>`;
|
||||||
|
}
|
||||||
acType = flight.ac_type;
|
acType = flight.ac_type;
|
||||||
|
|
||||||
// Lookup airport name for in_from
|
// Lookup airport name for in_from
|
||||||
@@ -2214,9 +2244,12 @@
|
|||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
// Local flight display
|
// Local flight display
|
||||||
const callsign = flight.callsign && flight.callsign.trim() ? flight.callsign : flight.registration;
|
if (flight.callsign && flight.callsign.trim()) {
|
||||||
aircraftDisplay = `<strong>${callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.registration}</span>`;
|
aircraftDisplay = `<strong>${flight.callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.registration}</span>`;
|
||||||
toDisplay = '-';
|
} else {
|
||||||
|
aircraftDisplay = `<strong>${flight.registration}</strong>`;
|
||||||
|
}
|
||||||
|
toDisplay = `<i>${flight.flight_type === 'CIRCUITS' ? 'Circuits' : flight.flight_type === 'LOCAL' ? 'Local Flight' : 'Departure'}</i>`;
|
||||||
etd = flight.booked_out_dt ? formatTimeOnly(flight.booked_out_dt) : '-';
|
etd = flight.booked_out_dt ? formatTimeOnly(flight.booked_out_dt) : '-';
|
||||||
pob = flight.pob || '-';
|
pob = flight.pob || '-';
|
||||||
fuel = '-';
|
fuel = '-';
|
||||||
@@ -2225,7 +2258,7 @@
|
|||||||
// Action buttons for local flight
|
// Action buttons for local flight
|
||||||
if (flight.status === 'BOOKED_OUT') {
|
if (flight.status === 'BOOKED_OUT') {
|
||||||
actionButtons = `
|
actionButtons = `
|
||||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'DEPARTED')" title="Mark as Departed">
|
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('DEPARTED', ${flight.id}, true)" title="Mark as Departed">
|
||||||
TAKE OFF
|
TAKE OFF
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||||
@@ -2234,7 +2267,7 @@
|
|||||||
`;
|
`;
|
||||||
} else if (flight.status === 'DEPARTED') {
|
} else if (flight.status === 'DEPARTED') {
|
||||||
actionButtons = `
|
actionButtons = `
|
||||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'LANDED')" title="Mark as Landed">
|
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, true)" title="Mark as Landed">
|
||||||
LAND
|
LAND
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||||
@@ -2246,8 +2279,11 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// PPR display
|
// PPR display
|
||||||
const callsign = flight.ac_call && flight.ac_call.trim() ? flight.ac_call : flight.ac_reg;
|
if (flight.ac_call && flight.ac_call.trim()) {
|
||||||
aircraftDisplay = `<strong>${callsign}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.ac_reg}</span>`;
|
aircraftDisplay = `<strong>${flight.ac_call}</strong><br><span style="font-size: 0.8em; color: #666; font-style: italic;">${flight.ac_reg}</span>`;
|
||||||
|
} else {
|
||||||
|
aircraftDisplay = `<strong>${flight.ac_reg}</strong>`;
|
||||||
|
}
|
||||||
toDisplay = flight.out_to || '-';
|
toDisplay = flight.out_to || '-';
|
||||||
if (flight.out_to && flight.out_to.length === 4 && /^[A-Z]{4}$/.test(flight.out_to)) {
|
if (flight.out_to && flight.out_to.length === 4 && /^[A-Z]{4}$/.test(flight.out_to)) {
|
||||||
toDisplay = await getAirportDisplay(flight.out_to);
|
toDisplay = await getAirportDisplay(flight.out_to);
|
||||||
@@ -2539,11 +2575,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Timestamp modal functions
|
// Timestamp modal functions
|
||||||
function showTimestampModal(status, pprId = null) {
|
function showTimestampModal(status, pprId = null, isLocalFlight = false) {
|
||||||
const targetPprId = pprId || currentPPRId;
|
const targetId = pprId || (isLocalFlight ? currentLocalFlightId : currentPPRId);
|
||||||
if (!targetPprId) return;
|
if (!targetId) return;
|
||||||
|
|
||||||
pendingStatusUpdate = { status: status, pprId: targetPprId };
|
pendingStatusUpdate = { status: status, pprId: targetId, isLocalFlight: isLocalFlight };
|
||||||
|
|
||||||
const modalTitle = document.getElementById('timestamp-modal-title');
|
const modalTitle = document.getElementById('timestamp-modal-title');
|
||||||
const submitBtn = document.getElementById('timestamp-submit-btn');
|
const submitBtn = document.getElementById('timestamp-submit-btn');
|
||||||
@@ -2589,7 +2625,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/v1/pprs/${pendingStatusUpdate.pprId}/status`, {
|
// Determine the correct API endpoint based on flight type
|
||||||
|
const isLocal = pendingStatusUpdate.isLocalFlight;
|
||||||
|
const endpoint = isLocal ?
|
||||||
|
`/api/v1/local-flights/${pendingStatusUpdate.pprId}/status` :
|
||||||
|
`/api/v1/pprs/${pendingStatusUpdate.pprId}/status`;
|
||||||
|
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -2608,9 +2650,11 @@
|
|||||||
|
|
||||||
const updatedStatus = pendingStatusUpdate.status;
|
const updatedStatus = pendingStatusUpdate.status;
|
||||||
closeTimestampModal();
|
closeTimestampModal();
|
||||||
loadPPRs(); // Refresh both tables
|
loadPPRs(); // Refresh all tables
|
||||||
showNotification(`Status updated to ${updatedStatus}`);
|
showNotification(`Status updated to ${updatedStatus}`);
|
||||||
closePPRModal(); // Close PPR modal after successful status update
|
if (!isLocal) {
|
||||||
|
closePPRModal(); // Close PPR modal after successful status update
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating status:', error);
|
console.error('Error updating status:', error);
|
||||||
showNotification(`Error updating status: ${error.message}`, true);
|
showNotification(`Error updating status: ${error.message}`, true);
|
||||||
@@ -3407,10 +3451,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Local Flight (Book Out) Modal Functions
|
// Local Flight (Book Out) Modal Functions
|
||||||
function openLocalFlightModal() {
|
function openLocalFlightModal(flightType = 'LOCAL') {
|
||||||
document.getElementById('local-flight-form').reset();
|
document.getElementById('local-flight-form').reset();
|
||||||
document.getElementById('local-flight-id').value = '';
|
document.getElementById('local-flight-id').value = '';
|
||||||
document.getElementById('local-flight-modal-title').textContent = 'Book Out';
|
document.getElementById('local-flight-modal-title').textContent = 'Book Out';
|
||||||
|
document.getElementById('local_flight_type').value = flightType;
|
||||||
document.getElementById('localFlightModal').style.display = 'block';
|
document.getElementById('localFlightModal').style.display = 'block';
|
||||||
|
|
||||||
// Clear aircraft lookup results
|
// Clear aircraft lookup results
|
||||||
|
|||||||
120
web/index.html
120
web/index.html
@@ -304,27 +304,46 @@
|
|||||||
|
|
||||||
// Build rows asynchronously to lookup airport names
|
// Build rows asynchronously to lookup airport names
|
||||||
const rows = await Promise.all(arrivals.map(async (arrival) => {
|
const rows = await Promise.all(arrivals.map(async (arrival) => {
|
||||||
// Show callsign if available, otherwise registration
|
const isLocal = arrival.isLocalFlight;
|
||||||
const aircraftId = arrival.ac_call || arrival.ac_reg || '';
|
|
||||||
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.ac_type || '')})</span>`;
|
|
||||||
const fromDisplay = await getAirportName(arrival.in_from || '');
|
|
||||||
|
|
||||||
// Show landed time if available, otherwise ETA
|
if (isLocal) {
|
||||||
let timeDisplay;
|
// Local flight
|
||||||
if ((arrival.status === 'LANDED' || arrival.status === 'DEPARTED') && arrival.landed_dt) {
|
const aircraftId = arrival.ac_call || arrival.ac_reg || '';
|
||||||
const time = convertToLocalTime(arrival.landed_dt);
|
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.ac_type || '')})</span>`;
|
||||||
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>`;
|
const fromDisplay = `<i>${getFlightTypeDisplay(arrival.flight_type)}</i>`;
|
||||||
|
const time = convertToLocalTime(arrival.eta);
|
||||||
|
const 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;">IN AIR</span></div>`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${aircraftDisplay}</td>
|
||||||
|
<td>${fromDisplay}</td>
|
||||||
|
<td>${timeDisplay}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
} else {
|
} else {
|
||||||
timeDisplay = convertToLocalTime(arrival.eta);
|
// PPR
|
||||||
|
const aircraftId = arrival.ac_call || arrival.ac_reg || '';
|
||||||
|
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(arrival.ac_type || '')})</span>`;
|
||||||
|
const fromDisplay = await getAirportName(arrival.in_from || '');
|
||||||
|
|
||||||
|
// Show landed time if available, otherwise ETA
|
||||||
|
let timeDisplay;
|
||||||
|
if ((arrival.status === 'LANDED' || arrival.status === 'DEPARTED') && arrival.landed_dt) {
|
||||||
|
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 {
|
||||||
|
timeDisplay = convertToLocalTime(arrival.eta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${aircraftDisplay}</td>
|
||||||
|
<td>${escapeHtml(fromDisplay)}</td>
|
||||||
|
<td>${timeDisplay}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
|
||||||
<tr>
|
|
||||||
<td>${aircraftDisplay}</td>
|
|
||||||
<td>${escapeHtml(fromDisplay)}</td>
|
|
||||||
<td>${timeDisplay}</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tbody.innerHTML = rows.join('');
|
tbody.innerHTML = rows.join('');
|
||||||
@@ -355,27 +374,46 @@
|
|||||||
|
|
||||||
// Build rows asynchronously to lookup airport names
|
// Build rows asynchronously to lookup airport names
|
||||||
const rows = await Promise.all(departures.map(async (departure) => {
|
const rows = await Promise.all(departures.map(async (departure) => {
|
||||||
// Show callsign if available, otherwise registration
|
const isLocal = departure.isLocalFlight;
|
||||||
const aircraftId = departure.ac_call || departure.ac_reg || '';
|
|
||||||
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(departure.ac_type || '')})</span>`;
|
|
||||||
const toDisplay = await getAirportName(departure.out_to || '');
|
|
||||||
|
|
||||||
// Show departed time if available, otherwise ETD
|
if (isLocal) {
|
||||||
let timeDisplay;
|
// Local flight
|
||||||
if (departure.status === 'DEPARTED' && departure.departed_dt) {
|
const aircraftId = departure.ac_call || departure.ac_reg || '';
|
||||||
const time = convertToLocalTime(departure.departed_dt);
|
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(departure.ac_type || '')})</span>`;
|
||||||
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;">DEPARTED</span></div>`;
|
const toDisplay = `<i>${getFlightTypeDisplay(departure.flight_type)}</i>`;
|
||||||
|
const time = convertToLocalTime(departure.etd);
|
||||||
|
const timeDisplay = `<div>${escapeHtml(time)}</div>`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${aircraftDisplay}</td>
|
||||||
|
<td>${toDisplay}</td>
|
||||||
|
<td>${timeDisplay}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
} else {
|
} else {
|
||||||
timeDisplay = convertToLocalTime(departure.etd);
|
// PPR
|
||||||
|
const aircraftId = departure.ac_call || departure.ac_reg || '';
|
||||||
|
const aircraftDisplay = `${escapeHtml(aircraftId)} <span style="font-size: 0.8em; color: #666;">(${escapeHtml(departure.ac_type || '')})</span>`;
|
||||||
|
const toDisplay = await getAirportName(departure.out_to || '');
|
||||||
|
|
||||||
|
// Show departed time if available, otherwise ETD
|
||||||
|
let timeDisplay;
|
||||||
|
if (departure.status === 'DEPARTED' && departure.departed_dt) {
|
||||||
|
const time = convertToLocalTime(departure.departed_dt);
|
||||||
|
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;">DEPARTED</span></div>`;
|
||||||
|
} else {
|
||||||
|
timeDisplay = convertToLocalTime(departure.etd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr>
|
||||||
|
<td>${aircraftDisplay}</td>
|
||||||
|
<td>${escapeHtml(toDisplay)}</td>
|
||||||
|
<td>${timeDisplay}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
|
||||||
<tr>
|
|
||||||
<td>${aircraftDisplay}</td>
|
|
||||||
<td>${escapeHtml(toDisplay)}</td>
|
|
||||||
<td>${timeDisplay}</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
tbody.innerHTML = rows.join('');
|
tbody.innerHTML = rows.join('');
|
||||||
@@ -393,6 +431,16 @@
|
|||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map flight type enum to friendly name
|
||||||
|
function getFlightTypeDisplay(flightType) {
|
||||||
|
const typeMap = {
|
||||||
|
'CIRCUITS': 'Circuit Traffic',
|
||||||
|
'LOCAL': 'Local Area',
|
||||||
|
'DEPARTURE': 'Departure'
|
||||||
|
};
|
||||||
|
return typeMap[flightType] || flightType;
|
||||||
|
}
|
||||||
|
|
||||||
// Load data on page load
|
// Load data on page load
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function() {
|
||||||
loadArrivals();
|
loadArrivals();
|
||||||
|
|||||||
Reference in New Issue
Block a user