Public board fixes

This commit is contained in:
2025-12-18 04:54:19 -05:00
parent d53ddff4be
commit f65c54109e
3 changed files with 129 additions and 61 deletions

View File

@@ -69,10 +69,14 @@ async def get_public_arrivals(db: Session = Depends(get_db)):
# Only include BOOKED_IN and LANDED arrivals
if arrival.status not in (ArrivalStatus.BOOKED_IN, ArrivalStatus.LANDED):
continue
# For BOOKED_IN, only include those from today; for LANDED, include all
# For BOOKED_IN, only include those created today
if arrival.status == ArrivalStatus.BOOKED_IN:
if not (today_start <= arrival.created_dt < today_end):
continue
# For LANDED, only include those landed today
elif arrival.status == ArrivalStatus.LANDED:
if not arrival.landed_dt or not (today_start <= arrival.landed_dt < today_end):
continue
arrivals_list.append({
'registration': arrival.registration,
@@ -145,8 +149,16 @@ async def get_public_departures(db: Session = Depends(get_db)):
limit=1000
)
# Get today's date boundaries
today = date.today()
today_start = datetime.combine(today, datetime.min.time())
today_end = datetime.combine(today + timedelta(days=1), datetime.min.time())
# Convert departures to match the format for display
for dep in departures_to_airports:
# Only include departures booked out today
if not (today_start <= dep.created_dt < today_end):
continue
departures_list.append({
'ac_call': dep.callsign or dep.registration,
'ac_reg': dep.registration,
@@ -159,4 +171,27 @@ async def get_public_departures(db: Session = Depends(get_db)):
'isDeparture': True
})
# Add departures to other airports with DEPARTED status (taken off today)
departed_to_airports = crud_departure.get_multi(
db,
status=DepartureStatus.DEPARTED,
limit=1000
)
for dep in departed_to_airports:
# Only include departures that departed today
if not dep.departed_dt or not (today_start <= dep.departed_dt < today_end):
continue
departures_list.append({
'ac_call': dep.callsign or dep.registration,
'ac_reg': dep.registration,
'ac_type': dep.type,
'out_to': dep.out_to,
'etd': dep.etd or dep.created_dt,
'departed_dt': dep.departed_dt,
'status': 'DEPARTED',
'isLocalFlight': False,
'isDeparture': True
})
return departures_list

View File

@@ -1561,7 +1561,7 @@
} else {
row.innerHTML = `
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${flight.ac_reg || '-'}</td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important; text-align: center; width: 30px;"><span style="color: #e74c3c; font-weight: bold;" title="From PPR">P</span></td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important; text-align: center; width: 30px;"><span style="color: #032cfc; font-weight: bold;" title="From PPR">P</span></td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${flight.ac_call || '-'}</td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${flight.out_to || '-'}</td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${formatTimeOnly(flight.departed_dt)}</td>
@@ -1665,7 +1665,7 @@
let typeIconParked = '';
if (!isBookedIn) {
// Add P icon for PPR flights
typeIconParked = '<span style="color: #e74c3c; font-weight: bold;" title="From PPR">P</span>';
typeIconParked = '<span style="color: #032cfc; font-weight: bold;" title="From PPR">P</span>';
}
// Get aircraft type based on type (PPR vs booked-in)
@@ -1780,7 +1780,7 @@
row.innerHTML = `
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important;">${dateDisplay}</td>
<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; text-align: center; width: 30px;"><span style="color: #e74c3c; font-weight: bold;" title="From PPR">P</span></td>
<td style="padding: 0.3rem 0.4rem !important; font-size: 0.85rem !important; text-align: center; width: 30px;"><span style="color: #032cfc; font-weight: bold;" title="From PPR">P</span></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;">${formatTimeOnly(ppr.eta)}</td>
@@ -1948,7 +1948,7 @@
aircraftDisplay = `<strong>${flight.ac_reg}</strong>`;
}
acType = flight.ac_type;
typeIcon = '<span style="color: #e74c3c; font-weight: bold; font-size: 0.9em;" title="From PPR">P</span>';
typeIcon = '<span style="color: #032cfc; font-weight: bold; font-size: 0.9em;" title="From PPR">P</span>';
// Lookup airport name for in_from
let fromDisplay_temp = flight.in_from;
@@ -2113,7 +2113,7 @@
} else {
aircraftDisplay = `<strong>${flight.ac_reg}</strong>`;
}
typeIcon = '<span style="color: #e74c3c; font-weight: bold; font-size: 0.9em;" title="From PPR">P</span>';
typeIcon = '<span style="color: #032cfc; font-weight: bold; font-size: 0.9em;" title="From PPR">P</span>';
toDisplay = 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);

View File

@@ -302,10 +302,14 @@
return;
}
// Build rows asynchronously to lookup airport names
const rows = await Promise.all(arrivals.map(async (arrival) => {
// Build rows with metadata for sorting
const rowsWithData = await Promise.all(arrivals.map(async (arrival) => {
const isLocal = arrival.isLocalFlight;
const isBookedIn = arrival.isBookedIn;
const isLanded = arrival.status === 'LANDED' || (arrival.status === 'DEPARTED' && arrival.landed_dt);
let html = '';
let sortKey = '';
if (isLocal) {
// Local flight
@@ -315,63 +319,72 @@
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>
`;
html = `<tr><td>${aircraftDisplay}</td><td>${fromDisplay}</td><td>${timeDisplay}</td></tr>`;
sortKey = `0-${arrival.eta}`; // Live flights, sort by ETA
} 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;
let timeDisplay, sortTime;
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>`;
sortTime = arrival.landed_dt;
} 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>`;
sortTime = arrival.eta;
}
return `
<tr>
<td>${aircraftDisplay}</td>
<td>${escapeHtml(fromDisplay)}</td>
<td>${timeDisplay}</td>
</tr>
`;
html = `<tr><td>${aircraftDisplay}</td><td>${escapeHtml(fromDisplay)}</td><td>${timeDisplay}</td></tr>`;
sortKey = isLanded ? `1-${sortTime}` : `0-${sortTime}`; // 0=live, 1=completed
} else {
// 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;
let timeDisplay, sortTime;
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>`;
sortTime = arrival.landed_dt;
} else {
timeDisplay = convertToLocalTime(arrival.eta);
sortTime = arrival.eta;
}
return `
<tr>
<td>${aircraftDisplay}</td>
<td>${escapeHtml(fromDisplay)}</td>
<td>${timeDisplay}</td>
</tr>
`;
html = `<tr><td>${aircraftDisplay}</td><td>${escapeHtml(fromDisplay)}</td><td>${timeDisplay}</td></tr>`;
sortKey = isLanded ? `1-${sortTime}` : `0-${sortTime}`;
}
return { html, sortKey, isLanded };
}));
tbody.innerHTML = rows.join('');
// Sort: live flights first (0) by ETA ascending, then completed flights (1) by time descending
rowsWithData.sort((a, b) => {
const aParts = a.sortKey.split('-');
const bParts = b.sortKey.split('-');
const aType = parseInt(aParts[0]);
const bType = parseInt(bParts[0]);
if (aType !== bType) return aType - bType; // Live before completed
// Sort by time
if (aType === 0) {
// Live flights: ascending (earliest first)
return aParts[1].localeCompare(bParts[1]);
} else {
// Completed flights: descending (most recent first)
return bParts[1].localeCompare(aParts[1]);
}
});
tbody.innerHTML = rowsWithData.map(r => r.html).join('');
} catch (error) {
console.error('Error loading arrivals:', error);
@@ -397,10 +410,14 @@
return;
}
// Build rows asynchronously to lookup airport names
const rows = await Promise.all(departures.map(async (departure) => {
// Build rows with metadata for sorting
const rowsWithData = await Promise.all(departures.map(async (departure) => {
const isLocal = departure.isLocalFlight;
const isDeparture = departure.isDeparture;
const isDeparted = departure.status === 'DEPARTED';
let html = '';
let sortKey = '';
if (isLocal) {
// Local flight
@@ -410,54 +427,70 @@
const time = convertToLocalTime(departure.etd);
const timeDisplay = `<div>${escapeHtml(time)}</div>`;
return `
<tr>
<td>${aircraftDisplay}</td>
<td>${toDisplay}</td>
<td>${timeDisplay}</td>
</tr>
`;
html = `<tr><td>${aircraftDisplay}</td><td>${toDisplay}</td><td>${timeDisplay}</td></tr>`;
sortKey = `0-${departure.etd}`; // Live flights, sort by ETD
} else if (isDeparture) {
// Departure to other airport
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 || '');
const time = convertToLocalTime(departure.etd);
const timeDisplay = `<div>${escapeHtml(time)}</div>`;
return `
<tr>
<td>${aircraftDisplay}</td>
<td>${toDisplay}</td>
<td>${timeDisplay}</td>
</tr>
`;
let timeDisplay, sortTime;
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>`;
sortTime = departure.departed_dt;
} else {
const time = convertToLocalTime(departure.etd);
timeDisplay = `<div>${escapeHtml(time)}</div>`;
sortTime = departure.etd;
}
html = `<tr><td>${aircraftDisplay}</td><td>${toDisplay}</td><td>${timeDisplay}</td></tr>`;
sortKey = isDeparted ? `1-${sortTime}` : `0-${sortTime}`; // 0=live, 1=completed
} else {
// 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;
let timeDisplay, sortTime;
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>`;
sortTime = departure.departed_dt;
} else {
timeDisplay = convertToLocalTime(departure.etd);
sortTime = departure.etd;
}
return `
<tr>
<td>${aircraftDisplay}</td>
<td>${escapeHtml(toDisplay)}</td>
<td>${timeDisplay}</td>
</tr>
`;
html = `<tr><td>${aircraftDisplay}</td><td>${escapeHtml(toDisplay)}</td><td>${timeDisplay}</td></tr>`;
sortKey = isDeparted ? `1-${sortTime}` : `0-${sortTime}`;
}
return { html, sortKey, isDeparted };
}));
tbody.innerHTML = rows.join('');
// Sort: live flights first (0) by ETD ascending, then completed flights (1) by time descending
rowsWithData.sort((a, b) => {
const aParts = a.sortKey.split('-');
const bParts = b.sortKey.split('-');
const aType = parseInt(aParts[0]);
const bType = parseInt(bParts[0]);
if (aType !== bType) return aType - bType; // Live before completed
// Sort by time
if (aType === 0) {
// Live flights: ascending (earliest first)
return aParts[1].localeCompare(bParts[1]);
} else {
// Completed flights: descending (most recent first)
return bParts[1].localeCompare(aParts[1]);
}
});
tbody.innerHTML = rowsWithData.map(r => r.html).join('');
} catch (error) {
console.error('Error loading departures:', error);