diff --git a/backend/app/api/endpoints/public.py b/backend/app/api/endpoints/public.py
index b40add2..3fa9ac4 100644
--- a/backend/app/api/endpoints/public.py
+++ b/backend/app/api/endpoints/public.py
@@ -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
\ No newline at end of file
diff --git a/web/admin.html b/web/admin.html
index 4c16c37..5ab6a40 100644
--- a/web/admin.html
+++ b/web/admin.html
@@ -1561,7 +1561,7 @@
} else {
row.innerHTML = `
${flight.ac_reg || '-'} |
- P |
+ P |
${flight.ac_call || '-'} |
${flight.out_to || '-'} |
${formatTimeOnly(flight.departed_dt)} |
@@ -1665,7 +1665,7 @@
let typeIconParked = '';
if (!isBookedIn) {
// Add P icon for PPR flights
- typeIconParked = 'P';
+ typeIconParked = 'P';
}
// Get aircraft type based on type (PPR vs booked-in)
@@ -1780,7 +1780,7 @@
row.innerHTML = `
${dateDisplay} |
${ppr.ac_reg || '-'} |
- P |
+ P |
${ppr.ac_type || '-'} |
${ppr.in_from || '-'} |
${formatTimeOnly(ppr.eta)} |
@@ -1948,7 +1948,7 @@
aircraftDisplay = `${flight.ac_reg}`;
}
acType = flight.ac_type;
- typeIcon = 'P';
+ typeIcon = 'P';
// Lookup airport name for in_from
let fromDisplay_temp = flight.in_from;
@@ -2113,7 +2113,7 @@
} else {
aircraftDisplay = `${flight.ac_reg}`;
}
- typeIcon = 'P';
+ typeIcon = 'P';
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);
diff --git a/web/index.html b/web/index.html
index 81d4a0d..7175952 100644
--- a/web/index.html
+++ b/web/index.html
@@ -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 = `${time}IN AIR
`;
- return `
-
- | ${aircraftDisplay} |
- ${fromDisplay} |
- ${timeDisplay} |
-
- `;
+ html = `| ${aircraftDisplay} | ${fromDisplay} | ${timeDisplay} |
`;
+ 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)} (${escapeHtml(arrival.type || '')})`;
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 = `${time}LANDED
`;
+ sortTime = arrival.landed_dt;
} else {
// Show ETA if BOOKED_IN
const time = convertToLocalTime(arrival.eta);
timeDisplay = `${time}IN AIR
`;
+ sortTime = arrival.eta;
}
- return `
-
- | ${aircraftDisplay} |
- ${escapeHtml(fromDisplay)} |
- ${timeDisplay} |
-
- `;
+ html = `| ${aircraftDisplay} | ${escapeHtml(fromDisplay)} | ${timeDisplay} |
`;
+ sortKey = isLanded ? `1-${sortTime}` : `0-${sortTime}`; // 0=live, 1=completed
} else {
// PPR
const aircraftId = arrival.ac_call || arrival.ac_reg || '';
const aircraftDisplay = `${escapeHtml(aircraftId)} (${escapeHtml(arrival.ac_type || '')})`;
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 = `${time}LANDED
`;
+ sortTime = arrival.landed_dt;
} else {
timeDisplay = convertToLocalTime(arrival.eta);
+ sortTime = arrival.eta;
}
- return `
-
- | ${aircraftDisplay} |
- ${escapeHtml(fromDisplay)} |
- ${timeDisplay} |
-
- `;
+ html = `| ${aircraftDisplay} | ${escapeHtml(fromDisplay)} | ${timeDisplay} |
`;
+ 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 = `${escapeHtml(time)}
`;
- return `
-
- | ${aircraftDisplay} |
- ${toDisplay} |
- ${timeDisplay} |
-
- `;
+ html = `| ${aircraftDisplay} | ${toDisplay} | ${timeDisplay} |
`;
+ 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)} (${escapeHtml(departure.ac_type || '')})`;
const toDisplay = await getAirportName(departure.out_to || '');
- const time = convertToLocalTime(departure.etd);
- const timeDisplay = `${escapeHtml(time)}
`;
- return `
-
- | ${aircraftDisplay} |
- ${toDisplay} |
- ${timeDisplay} |
-
- `;
+ let timeDisplay, sortTime;
+ if (departure.status === 'DEPARTED' && departure.departed_dt) {
+ const time = convertToLocalTime(departure.departed_dt);
+ timeDisplay = `${time}DEPARTED
`;
+ sortTime = departure.departed_dt;
+ } else {
+ const time = convertToLocalTime(departure.etd);
+ timeDisplay = `${escapeHtml(time)}
`;
+ sortTime = departure.etd;
+ }
+
+ html = `| ${aircraftDisplay} | ${toDisplay} | ${timeDisplay} |
`;
+ sortKey = isDeparted ? `1-${sortTime}` : `0-${sortTime}`; // 0=live, 1=completed
} else {
// PPR
const aircraftId = departure.ac_call || departure.ac_reg || '';
const aircraftDisplay = `${escapeHtml(aircraftId)} (${escapeHtml(departure.ac_type || '')})`;
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 = `${time}DEPARTED
`;
+ sortTime = departure.departed_dt;
} else {
timeDisplay = convertToLocalTime(departure.etd);
+ sortTime = departure.etd;
}
- return `
-
- | ${aircraftDisplay} |
- ${escapeHtml(toDisplay)} |
- ${timeDisplay} |
-
- `;
+ html = `| ${aircraftDisplay} | ${escapeHtml(toDisplay)} | ${timeDisplay} |
`;
+ 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);