diff --git a/backend/app/api/endpoints/public.py b/backend/app/api/endpoints/public.py
index 4cf9ece..1b77640 100644
--- a/backend/app/api/endpoints/public.py
+++ b/backend/app/api/endpoints/public.py
@@ -10,7 +10,7 @@ from app.schemas.ppr import PPRPublic
from app.models.local_flight import LocalFlightStatus
from app.models.departure import DepartureStatus
from app.models.arrival import ArrivalStatus
-from datetime import date
+from datetime import date, datetime, timedelta
router = APIRouter()
@@ -34,15 +34,23 @@ async def get_public_arrivals(db: Session = Depends(get_db)):
'isLocalFlight': False
})
- # Add local flights with DEPARTED status
+ # Add local flights with DEPARTED status that were booked out today
local_flights = crud_local_flight.get_multi(
db,
status=LocalFlightStatus.DEPARTED,
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 local flights to match the PPR format for display
for flight in local_flights:
+ # Only include flights booked out today
+ if not (today_start <= flight.created_dt < today_end):
+ continue
arrivals_list.append({
'ac_call': flight.callsign or flight.registration,
'ac_reg': flight.registration,
@@ -78,15 +86,23 @@ async def get_public_departures(db: Session = Depends(get_db)):
'isDeparture': False
})
- # Add local flights with BOOKED_OUT status
+ # Add local flights with BOOKED_OUT status that were booked out today
local_flights = crud_local_flight.get_multi(
db,
status=LocalFlightStatus.BOOKED_OUT,
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 local flights to match the PPR format for display
for flight in local_flights:
+ # Only include flights booked out today
+ if not (today_start <= flight.created_dt < today_end):
+ continue
departures_list.append({
'ac_call': flight.callsign or flight.registration,
'ac_reg': flight.registration,
diff --git a/web/admin.html b/web/admin.html
index f131aa7..647ba63 100644
--- a/web/admin.html
+++ b/web/admin.html
@@ -493,6 +493,66 @@
+
+
@@ -1112,13 +1172,21 @@
return etaDate === today;
});
- // Add local flights in DEPARTED status (in the air, heading back)
+ // Add local flights in DEPARTED status (in the air, heading back) - only those booked out today
if (localResponse.ok) {
const localFlights = await localResponse.json();
- const localInAir = localFlights.map(flight => ({
- ...flight,
- isLocalFlight: true // Flag to distinguish from PPR
- }));
+ const today = new Date().toISOString().split('T')[0];
+ const localInAir = localFlights
+ .filter(flight => {
+ // Only include flights booked out today (created_dt)
+ if (!flight.created_dt) return false;
+ const createdDate = flight.created_dt.split('T')[0];
+ return createdDate === today;
+ })
+ .map(flight => ({
+ ...flight,
+ isLocalFlight: true // Flag to distinguish from PPR
+ }));
arrivals.push(...localInAir);
}
@@ -1164,13 +1232,21 @@
return etdDate === today;
});
- // Add local flights (BOOKED_OUT status - ready to go)
+ // Add local flights (BOOKED_OUT status - ready to go) - only those booked out today
if (localResponse.ok) {
const localFlights = await localResponse.json();
- const localDepartures = localFlights.map(flight => ({
- ...flight,
- isLocalFlight: true // Flag to distinguish from PPR
- }));
+ const today = new Date().toISOString().split('T')[0];
+ const localDepartures = localFlights
+ .filter(flight => {
+ // Only include flights booked out today (created_dt)
+ if (!flight.created_dt) return false;
+ const createdDate = flight.created_dt.split('T')[0];
+ return createdDate === today;
+ })
+ .map(flight => ({
+ ...flight,
+ isLocalFlight: true // Flag to distinguish from PPR
+ }));
departures.push(...localDepartures);
}
@@ -1202,9 +1278,10 @@
document.getElementById('departed-no-data').style.display = 'none';
try {
- const [pprResponse, localResponse] = await Promise.all([
+ const [pprResponse, localResponse, depResponse] = 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/departures/?status=DEPARTED&limit=1000')
]);
if (!pprResponse.ok) {
@@ -1227,8 +1304,8 @@
if (localResponse.ok) {
const localFlights = await localResponse.json();
const localDeparted = localFlights.filter(flight => {
- if (!flight.departure_dt) return false;
- const departedDate = flight.departure_dt.split('T')[0];
+ if (!flight.departed_dt) return false;
+ const departedDate = flight.departed_dt.split('T')[0];
return departedDate === today;
}).map(flight => ({
...flight,
@@ -1237,6 +1314,20 @@
departed.push(...localDeparted);
}
+ // Add departures to other airports that departed today
+ if (depResponse.ok) {
+ const depFlights = await depResponse.json();
+ const depDeparted = depFlights.filter(flight => {
+ if (!flight.departed_dt) return false;
+ const departedDate = flight.departed_dt.split('T')[0];
+ return departedDate === today;
+ }).map(flight => ({
+ ...flight,
+ isDeparture: true
+ }));
+ departed.push(...depDeparted);
+ }
+
displayDeparted(departed);
} catch (error) {
console.error('Error loading departed aircraft:', error);
@@ -1259,8 +1350,8 @@
// Sort by departed time
departed.sort((a, b) => {
- const aTime = a.departed_dt || a.departure_dt;
- const bTime = b.departed_dt || b.departure_dt;
+ const aTime = a.departed_dt;
+ const bTime = b.departed_dt;
return new Date(aTime) - new Date(bTime);
});
@@ -1270,10 +1361,13 @@
for (const flight of departed) {
const row = document.createElement('tr');
const isLocal = flight.isLocalFlight;
+ const isDeparture = flight.isDeparture;
row.onclick = () => {
if (isLocal) {
openLocalFlightEditModal(flight.id);
+ } else if (isDeparture) {
+ openDepartureEditModal(flight.id);
} else {
openPPRModal(flight.id);
}
@@ -1285,7 +1379,14 @@
${flight.registration || '-'} |
${flight.callsign || '-'} |
- |
- ${formatTimeOnly(flight.departure_dt)} |
+ ${formatTimeOnly(flight.departed_dt)} |
+ `;
+ } else if (isDeparture) {
+ row.innerHTML = `
+ ${flight.registration || '-'} |
+ ${flight.callsign || '-'} |
+ ${flight.out_to || '-'} |
+ ${formatTimeOnly(flight.departed_dt)} |
`;
} else {
row.innerHTML = `
@@ -1657,7 +1758,7 @@
if (isLocal) {
openLocalFlightEditModal(flight.id);
} else if (isDeparture) {
- // TODO: Open departure edit modal
+ openDepartureEditModal(flight.id);
} else {
openPPRModal(flight.id);
}
@@ -2789,6 +2890,55 @@
currentLocalFlightId = null;
}
+ // Open departure edit modal
+ async function openDepartureEditModal(departureId) {
+ if (!accessToken) return;
+
+ try {
+ const response = await fetch(`/api/v1/departures/${departureId}`, {
+ headers: { 'Authorization': `Bearer ${accessToken}` }
+ });
+
+ if (!response.ok) throw new Error('Failed to load departure');
+
+ const departure = await response.json();
+ currentDepartureId = departure.id;
+
+ // Populate form
+ document.getElementById('departure-edit-id').value = departure.id;
+ document.getElementById('departure_edit_registration').value = departure.registration;
+ document.getElementById('departure_edit_type').value = departure.type;
+ document.getElementById('departure_edit_callsign').value = departure.callsign || '';
+ document.getElementById('departure_edit_out_to').value = departure.out_to;
+ document.getElementById('departure_edit_notes').value = departure.notes || '';
+
+ // Parse and populate ETD if exists
+ if (departure.etd) {
+ const etd = new Date(departure.etd);
+ document.getElementById('departure_edit_etd_date').value = etd.toISOString().slice(0, 10);
+ document.getElementById('departure_edit_etd_time').value = etd.toISOString().slice(11, 16);
+ }
+
+ // Show/hide action buttons based on status
+ const deptBtn = document.getElementById('departure-btn-departed');
+ const cancelBtn = document.getElementById('departure-btn-cancel');
+
+ if (deptBtn) deptBtn.style.display = departure.status === 'BOOKED_OUT' ? 'inline-block' : 'none';
+ if (cancelBtn) cancelBtn.style.display = departure.status === 'BOOKED_OUT' ? 'inline-block' : 'none';
+
+ document.getElementById('departure-edit-title').textContent = `${departure.registration} to ${departure.out_to}`;
+ document.getElementById('departureEditModal').style.display = 'block';
+ } catch (error) {
+ console.error('Error loading departure:', error);
+ showNotification('Error loading departure details', true);
+ }
+ }
+
+ function closeDepartureEditModal() {
+ document.getElementById('departureEditModal').style.display = 'none';
+ currentDepartureId = null;
+ }
+
// Update status from table buttons (with flight ID passed)
async function updateLocalFlightStatusFromTable(flightId, status) {
if (!accessToken) return;
@@ -2876,6 +3026,37 @@
}
}
+ async function updateDepartureStatus(status) {
+ if (!currentDepartureId || !accessToken) return;
+
+ // Show confirmation for cancel actions
+ if (status === 'CANCELLED') {
+ if (!confirm('Are you sure you want to cancel this departure? This action cannot be easily undone.')) {
+ return;
+ }
+ }
+
+ try {
+ const response = await fetch(`/api/v1/departures/${currentDepartureId}/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');
+
+ closeDepartureEditModal();
+ loadPPRs(); // Refresh display
+ showNotification(`Departure marked as ${status.toLowerCase()}`);
+ } catch (error) {
+ console.error('Error updating status:', error);
+ showNotification('Error updating departure status', true);
+ }
+ }
+
// Local flight edit form submission
document.getElementById('local-flight-edit-form').addEventListener('submit', async function(e) {
e.preventDefault();