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);