Many more states WIP
This commit is contained in:
+60
-34
@@ -1837,8 +1837,8 @@
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const bookedInToday = bookedInArrivals
|
||||
.filter(arrival => {
|
||||
// Only include arrivals booked in today (created_dt) with BOOKED_IN status
|
||||
if (!arrival.created_dt || arrival.status !== 'BOOKED_IN') return false;
|
||||
// Only include arrivals booked in today (created_dt) with INBOUND, LOCAL, or CIRCUIT status
|
||||
if (!arrival.created_dt || !['INBOUND', 'LOCAL', 'CIRCUIT'].includes(arrival.status)) return false;
|
||||
const bookedDate = arrival.created_dt.split('T')[0];
|
||||
return bookedDate === today;
|
||||
})
|
||||
@@ -1860,7 +1860,7 @@
|
||||
document.getElementById('arrivals-loading').style.display = 'none';
|
||||
}
|
||||
|
||||
// Load departures (LANDED status for PPR, BOOKED_OUT only for local flights)
|
||||
// Load departures (LANDED status for PPR, GROUND/LOCAL for local flights)
|
||||
async function loadDepartures() {
|
||||
document.getElementById('departures-loading').style.display = 'block';
|
||||
document.getElementById('departures-table-content').style.display = 'none';
|
||||
@@ -1868,7 +1868,7 @@
|
||||
|
||||
try {
|
||||
// Load PPR departures, local flight departures, and airport departures simultaneously
|
||||
const [pprResponse, localBookedOutResponse, localOutGroundResponse, localLocalResponse, localCircuitResponse, depBookedOutResponse, depOutGroundResponse, depLocalResponse, arrLocalResponse, arrCircuitResponse] = await Promise.all([
|
||||
const [pprResponse, localBookedOutResponse, localOutGroundResponse, localLocalResponse, localCircuitResponse, depBookedOutResponse, depOutGroundResponse, depLocalResponse] = await Promise.all([
|
||||
authenticatedFetch('/api/v1/pprs/?limit=1000'),
|
||||
authenticatedFetch('/api/v1/local-flights/?status=BOOKED_OUT&limit=1000'),
|
||||
authenticatedFetch('/api/v1/local-flights/?status=GROUND&limit=1000'),
|
||||
@@ -1876,9 +1876,7 @@
|
||||
authenticatedFetch('/api/v1/local-flights/?status=CIRCUIT&limit=1000'),
|
||||
authenticatedFetch('/api/v1/departures/?status=BOOKED_OUT&limit=1000'),
|
||||
authenticatedFetch('/api/v1/departures/?status=GROUND&limit=1000'),
|
||||
authenticatedFetch('/api/v1/departures/?status=LOCAL&limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?status=LOCAL&limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?status=CIRCUIT&limit=1000')
|
||||
authenticatedFetch('/api/v1/departures/?status=LOCAL&limit=1000')
|
||||
]);
|
||||
|
||||
if (!pprResponse.ok) {
|
||||
@@ -1893,8 +1891,6 @@
|
||||
const depBookedOut = depBookedOutResponse.ok ? await depBookedOutResponse.json() : [];
|
||||
const depOutGround = depOutGroundResponse.ok ? await depOutGroundResponse.json() : [];
|
||||
const depLocal = depLocalResponse.ok ? await depLocalResponse.json() : [];
|
||||
const arrLocal = arrLocalResponse.ok ? await arrLocalResponse.json() : [];
|
||||
const arrCircuit = arrCircuitResponse.ok ? await arrCircuitResponse.json() : [];
|
||||
|
||||
// Combine local flights
|
||||
const allLocalFlights = [...localBookedOut, ...localOutGround, ...localLocal, ...localCircuit];
|
||||
@@ -1912,7 +1908,7 @@
|
||||
return etdDate === today;
|
||||
});
|
||||
|
||||
// Add local flights (BOOKED_OUT, GROUND, and LOCAL status - ready to go) - only those booked out today
|
||||
// Add local flights (GROUND and LOCAL status - ready to go) - only those booked out today
|
||||
const localDepartures = allLocalFlights
|
||||
.filter(flight => {
|
||||
// Only include flights booked out today (created_dt)
|
||||
@@ -1933,20 +1929,6 @@
|
||||
}));
|
||||
departures.push(...depDepartures);
|
||||
|
||||
// Add arrivals in LOCAL status
|
||||
const arrDepartures = arrLocal.map(flight => ({
|
||||
...flight,
|
||||
isArrival: true // Flag to distinguish from PPR
|
||||
}));
|
||||
departures.push(...arrDepartures);
|
||||
|
||||
// Add arrivals in CIRCUIT status
|
||||
const arrCircuitDepartures = arrCircuit.map(flight => ({
|
||||
...flight,
|
||||
isArrival: true // Flag to distinguish from PPR
|
||||
}));
|
||||
departures.push(...arrCircuitDepartures);
|
||||
|
||||
displayDepartures(departures);
|
||||
} catch (error) {
|
||||
console.error('Error loading departures:', error);
|
||||
@@ -2500,14 +2482,58 @@
|
||||
eta = flight.eta ? formatTimeOnly(flight.eta) : (flight.landed_dt ? formatTimeOnly(flight.landed_dt) : '-');
|
||||
pob = flight.pob || '-';
|
||||
fuel = '-';
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
|
||||
// Different action buttons based on status
|
||||
if (flight.status === 'INBOUND') {
|
||||
actionButtons = `
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'LOCAL')" title="Mark as Local">
|
||||
LOCAL
|
||||
</button>
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
} else if (flight.status === 'LOCAL') {
|
||||
// Arrival in local area - show circuit and land buttons
|
||||
let circuitButton = `<button class="btn btn-info btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showCircuitModal()" title="Record Touch & Go">
|
||||
T&G
|
||||
</button>`;
|
||||
actionButtons = `
|
||||
<button class="btn btn-warning btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CIRCUIT')" title="Join Circuit">
|
||||
CIRCUIT
|
||||
</button>
|
||||
${circuitButton}
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
`;
|
||||
} else if (flight.status === 'CIRCUIT') {
|
||||
// Arrival in circuit - show local, T&G and land buttons
|
||||
let circuitButton = `<button class="btn btn-info btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showCircuitModal()" title="Record Touch & Go">
|
||||
T&G
|
||||
</button>`;
|
||||
actionButtons = `
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'LOCAL')" title="Move to Local Area">
|
||||
LOCAL
|
||||
</button>
|
||||
${circuitButton}
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
`;
|
||||
} else {
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
// PPR display
|
||||
if (flight.ac_call && flight.ac_call.trim()) {
|
||||
@@ -4581,8 +4607,8 @@
|
||||
// Show/hide quick action buttons based on status
|
||||
const landedBtn = document.getElementById('arrival-btn-landed');
|
||||
const cancelBtn = document.getElementById('arrival-btn-cancel');
|
||||
landedBtn.style.display = arrival.status === 'BOOKED_IN' ? 'block' : 'none';
|
||||
cancelBtn.style.display = arrival.status === 'BOOKED_IN' ? 'block' : 'none';
|
||||
landedBtn.style.display = arrival.status === 'INBOUND' ? 'block' : 'none';
|
||||
cancelBtn.style.display = arrival.status === 'INBOUND' ? 'block' : 'none';
|
||||
|
||||
// Show modal
|
||||
document.getElementById('arrivalEditModal').style.display = 'block';
|
||||
|
||||
+214
-70
@@ -70,6 +70,23 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.aircraft-item.local-flight,
|
||||
.aircraft-item.circuit {
|
||||
background-color: #ffcccc; /* light red */
|
||||
}
|
||||
|
||||
.aircraft-item.departure {
|
||||
background-color: #ffffcc; /* light yellow */
|
||||
}
|
||||
|
||||
.aircraft-item.inbound {
|
||||
background-color: #ccccff; /* light blue */
|
||||
}
|
||||
|
||||
.aircraft-item.overflight {
|
||||
background-color: #ccffcc; /* light green */
|
||||
}
|
||||
|
||||
.aircraft-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -158,8 +175,22 @@
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.status-btn:active {
|
||||
transform: scale(0.95);
|
||||
.status-btn.small-btn {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.status-btn.active-position {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.status-btn.active-position:hover {
|
||||
background-color: #218838;
|
||||
border-color: #1e7e34;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@@ -213,9 +244,9 @@
|
||||
</div>
|
||||
|
||||
<div class="atc-container">
|
||||
<!-- Row 1: Awaiting Departure -->
|
||||
<!-- Row 1: Apron -->
|
||||
<div class="atc-section">
|
||||
<h2>🛫 Awaiting Departure <span class="count" id="departing-count">0</span></h2>
|
||||
<h2>🛫 Apron <span class="count" id="departing-count">0</span></h2>
|
||||
<div class="aircraft-list" id="departing-list">
|
||||
<div class="no-aircraft">No departing aircraft</div>
|
||||
</div>
|
||||
@@ -247,15 +278,15 @@
|
||||
|
||||
<!-- Row 2: Circuit Traffic -->
|
||||
<div class="atc-section">
|
||||
<h2>🔄 Circuits <span class="count" id="circuit-count">0</span></h2>
|
||||
<h2>🔄 Circuit Traffic <span class="count" id="circuit-count">0</span></h2>
|
||||
<div class="aircraft-list" id="circuit-list">
|
||||
<div class="no-aircraft">No aircraft in circuit</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Row 2: Pending PPRs -->
|
||||
<!-- Row 2: Today's PPRs -->
|
||||
<div class="atc-section">
|
||||
<h2>📝 Pending PPRs <span class="count" id="pending-ppr-count">0</span></h2>
|
||||
<h2>📝 Today's PPRs <span class="count" id="pending-ppr-count">0</span></h2>
|
||||
<div class="aircraft-list" id="pending-ppr-list">
|
||||
<div class="no-aircraft">No pending PPRs</div>
|
||||
</div>
|
||||
@@ -1827,8 +1858,8 @@
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const bookedInToday = bookedInArrivals
|
||||
.filter(arrival => {
|
||||
// Only include arrivals booked in today (created_dt) with BOOKED_IN status
|
||||
if (!arrival.created_dt || arrival.status !== 'BOOKED_IN') return false;
|
||||
// Only include arrivals booked in today (created_dt) with INBOUND, LOCAL, or CIRCUIT status
|
||||
if (!arrival.created_dt || !['INBOUND', 'LOCAL', 'CIRCUIT'].includes(arrival.status)) return false;
|
||||
const bookedDate = arrival.created_dt.split('T')[0];
|
||||
return bookedDate === today;
|
||||
})
|
||||
@@ -1850,7 +1881,7 @@
|
||||
document.getElementById('arrivals-loading').style.display = 'none';
|
||||
}
|
||||
|
||||
// Load departures (LANDED status for PPR, BOOKED_OUT only for local flights)
|
||||
// Load departures (LANDED status for PPR, GROUND/LOCAL for local flights)
|
||||
async function loadDepartures() {
|
||||
document.getElementById('departures-loading').style.display = 'block';
|
||||
document.getElementById('departures-table-content').style.display = 'none';
|
||||
@@ -1896,7 +1927,7 @@
|
||||
return etdDate === today;
|
||||
});
|
||||
|
||||
// Add local flights (BOOKED_OUT, GROUND, and LOCAL status - ready to go) - only those booked out today
|
||||
// Add local flights (GROUND and LOCAL status - ready to go) - only those booked out today
|
||||
const localDepartures = allLocalFlights
|
||||
.filter(flight => {
|
||||
// Only include flights booked out today (created_dt)
|
||||
@@ -1971,6 +2002,7 @@
|
||||
for (const flight of overflights) {
|
||||
const row = document.createElement('tr');
|
||||
row.style.cursor = 'pointer';
|
||||
row.style.backgroundColor = '#ccffcc';
|
||||
row.onclick = () => {
|
||||
openOverflightEditModal(flight.id);
|
||||
};
|
||||
@@ -2131,7 +2163,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Load booked out aircraft (BOOKED_OUT status for departures and local flights)
|
||||
// Load booked out aircraft (BOOKED_OUT status for departures only)
|
||||
async function loadParked() {
|
||||
document.getElementById('parked-loading').style.display = 'block';
|
||||
document.getElementById('parked-table-content').style.display = 'none';
|
||||
@@ -2470,14 +2502,58 @@
|
||||
eta = flight.eta ? formatTimeOnly(flight.eta) : (flight.landed_dt ? formatTimeOnly(flight.landed_dt) : '-');
|
||||
pob = flight.pob || '-';
|
||||
fuel = '-';
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
|
||||
// Different action buttons based on status
|
||||
if (flight.status === 'INBOUND') {
|
||||
actionButtons = `
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'LOCAL')" title="Mark as Local">
|
||||
LOCAL
|
||||
</button>
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
} else if (flight.status === 'LOCAL') {
|
||||
// Arrival in local area - show circuit and land buttons
|
||||
let circuitButton = `<button class="btn btn-info btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showCircuitModal()" title="Record Touch & Go">
|
||||
T&G
|
||||
</button>`;
|
||||
actionButtons = `
|
||||
<button class="btn btn-warning btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CIRCUIT')" title="Join Circuit">
|
||||
CIRCUIT
|
||||
</button>
|
||||
${circuitButton}
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
`;
|
||||
} else if (flight.status === 'CIRCUIT') {
|
||||
// Arrival in circuit - show local, T&G and land buttons
|
||||
let circuitButton = `<button class="btn btn-info btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showCircuitModal()" title="Record Touch & Go">
|
||||
T&G
|
||||
</button>`;
|
||||
actionButtons = `
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'LOCAL')" title="Move to Local Area">
|
||||
LOCAL
|
||||
</button>
|
||||
${circuitButton}
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
`;
|
||||
} else {
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentBookedInArrivalId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, false, false, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateArrivalStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Arrival">
|
||||
CANCEL
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
// PPR display
|
||||
if (flight.ac_call && flight.ac_call.trim()) {
|
||||
@@ -3076,6 +3152,14 @@
|
||||
|
||||
const circuit = await response.json();
|
||||
showNotification(`✈️ Circuit recorded at ${formatTimeOnly(circuit.circuit_timestamp)}`);
|
||||
|
||||
// Update flight status to CIRCUIT after successful T&G recording
|
||||
if (currentLocalFlightId) {
|
||||
await updateLocalFlightStatusFromTable(currentLocalFlightId, 'CIRCUIT');
|
||||
} else if (currentArrivalId) {
|
||||
await updateArrivalStatusFromTable(currentArrivalId, 'CIRCUIT');
|
||||
}
|
||||
|
||||
closeCircuitModal();
|
||||
|
||||
// Refresh ATC display
|
||||
@@ -4488,8 +4572,8 @@
|
||||
// Show/hide quick action buttons based on status
|
||||
const landedBtn = document.getElementById('arrival-btn-landed');
|
||||
const cancelBtn = document.getElementById('arrival-btn-cancel');
|
||||
landedBtn.style.display = arrival.status === 'BOOKED_IN' ? 'block' : 'none';
|
||||
cancelBtn.style.display = arrival.status === 'BOOKED_IN' ? 'block' : 'none';
|
||||
landedBtn.style.display = arrival.status === 'INBOUND' ? 'block' : 'none';
|
||||
cancelBtn.style.display = arrival.status === 'INBOUND' ? 'block' : 'none';
|
||||
|
||||
// Show modal
|
||||
document.getElementById('arrivalEditModal').style.display = 'block';
|
||||
@@ -5192,13 +5276,13 @@
|
||||
buttonTitle = 'Mark as Local';
|
||||
clickType = 'departure';
|
||||
}
|
||||
const itemClass = isLocal ? 'local-flight' : 'departure';
|
||||
|
||||
return `
|
||||
<div class="aircraft-item" onclick="handleATCClick('${ac.id}', '${clickType}')">
|
||||
<div class="aircraft-item ${itemClass}" onclick="handleATCClick('${ac.id}', '${clickType}')">
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${reg}</div>
|
||||
<div class="aircraft-details">${type}${dest ? ` → ${dest}` : ` (Local)`}</div>
|
||||
<div class="aircraft-time">${ac.etd ? formatTimeOnly(ac.etd) : ''}</div>
|
||||
<div class="aircraft-details">${type}${dest ? ` → ${dest}` : ` Local Flight`}</div>
|
||||
</div>
|
||||
<button class="status-btn" onclick="${takeoffOnclick}" title="${buttonTitle}">${buttonText}</button>
|
||||
</div>
|
||||
@@ -5212,13 +5296,15 @@
|
||||
const response = await Promise.all([
|
||||
authenticatedFetch('/api/v1/local-flights/?status=LOCAL&limit=1000'),
|
||||
authenticatedFetch('/api/v1/departures/?status=LOCAL&limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?status=LOCAL&limit=1000')
|
||||
authenticatedFetch('/api/v1/arrivals/?status=LOCAL&limit=1000'),
|
||||
authenticatedFetch('/api/v1/overflights/?status=ACTIVE&limit=1000')
|
||||
]);
|
||||
|
||||
let locals = [];
|
||||
if (response[0].ok) locals = (await response[0].json()).map(l => ({ ...l, isLocalFlight: true }));
|
||||
if (response[1].ok) locals = locals.concat((await response[1].json()).map(d => ({ ...d, isDeparture: true })));
|
||||
if (response[2].ok) locals = locals.concat((await response[2].json()).map(a => ({ ...a, isArrival: true })));
|
||||
if (response[3].ok) locals = locals.concat((await response[3].json()).map(o => ({ ...o, isOverflight: true })));
|
||||
|
||||
displayLocalAircraft(locals);
|
||||
} catch (error) {
|
||||
@@ -5238,26 +5324,34 @@
|
||||
}
|
||||
|
||||
container.innerHTML = aircraft.map(ac => {
|
||||
const reg = ac.registration || ac.ac_reg;
|
||||
const type = ac.type || ac.ac_type;
|
||||
const reg = ac.registration || ac.ac_reg || ac.callsign || '-';
|
||||
const type = ac.type || ac.ac_type || ac.aircraft_type || '';
|
||||
const dest = ac.out_to;
|
||||
const isDeparture = ac.isDeparture;
|
||||
|
||||
let buttons;
|
||||
if (isDeparture) {
|
||||
// Departure in LOCAL status - show QSY button
|
||||
buttons = `<button class="status-btn" onclick="event.stopPropagation(); currentDepartureId = '${ac.id}'; showTimestampModal('DEPARTED', ${ac.id}, false, true)">QSY</button>`;
|
||||
// Departure in LOCAL status - show QSY and REJOIN buttons
|
||||
buttons = `
|
||||
<button class="status-btn" onclick="event.stopPropagation(); currentDepartureId = '${ac.id}'; showTimestampModal('DEPARTED', ${ac.id}, false, true)">QSY</button>
|
||||
<button class="status-btn" onclick="event.stopPropagation(); updateDepartureStatusFromTable('${ac.id}', 'CIRCUIT')">REJOIN</button>
|
||||
`;
|
||||
} else if (ac.isLocalFlight || ac.isArrival) {
|
||||
// Local flight or arrival in LOCAL status - show REJOIN button
|
||||
buttons = `<button class="status-btn" onclick="event.stopPropagation(); ${ac.isArrival ? `updateArrivalStatusFromTable('${ac.id}', 'CIRCUIT')` : `updateLocalFlightStatusFromTable('${ac.id}', 'CIRCUIT')`}">REJOIN</button>`;
|
||||
} else if (ac.isOverflight) {
|
||||
// Overflight in ACTIVE status - show QSY button
|
||||
buttons = `<button class="status-btn" onclick="event.stopPropagation(); currentOverflightId = '${ac.id}'; showOverflightQSYModal()">QSY</button>`;
|
||||
}
|
||||
const itemClass = isDeparture ? 'departure' : (ac.isArrival ? 'inbound' : (ac.isOverflight ? 'overflight' : 'local-flight'));
|
||||
const detailsText = isDeparture ? `${type}${dest ? ` → ${dest}` : ` (Local)`}` : (ac.isOverflight ? `${ac.departure_airfield || '?'} → ${ac.destination_airfield || '?'}` : (ac.isArrival ? `${type} from ${ac.in_from || '?'}` : `${type}${dest ? ` → ${dest}` : ` Local Flight`}`));
|
||||
const entityType = isDeparture ? 'departure' : (ac.isArrival ? 'arrival' : (ac.isOverflight ? 'overflight' : 'local'));
|
||||
|
||||
return `
|
||||
<div class="aircraft-item" onclick="handleATCClick('${ac.id}', '${isDeparture ? 'departure' : 'local'}')">
|
||||
<div class="aircraft-item ${itemClass}" onclick="handleATCClick('${ac.id}', '${entityType}')">
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${reg}</div>
|
||||
<div class="aircraft-details">${type}${dest ? ` → ${dest}` : ` (Local)`}</div>
|
||||
<div class="aircraft-time">${formatTimeOnly(ac.created_dt)}</div>
|
||||
<div class="aircraft-details">${detailsText}</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
${buttons}
|
||||
@@ -5272,7 +5366,7 @@
|
||||
try {
|
||||
const response = await Promise.all([
|
||||
authenticatedFetch('/api/v1/pprs/?limit=1000'),
|
||||
authenticatedFetch('/api/v1/arrivals/?status=BOOKED_IN&limit=1000')
|
||||
authenticatedFetch('/api/v1/arrivals/?status=INBOUND&limit=1000')
|
||||
]);
|
||||
|
||||
const pprs = response[0].ok ? await response[0].json() : [];
|
||||
@@ -5307,16 +5401,15 @@
|
||||
|
||||
let buttons = '';
|
||||
if (ac.isArrival) {
|
||||
// Arrival in BOOKED_IN status - show CONTACT button
|
||||
buttons = `<button class="status-btn" onclick="event.stopPropagation(); updateArrivalStatusFromTable('${ac.id}', 'LOCAL')">CONTACT</button>`;
|
||||
// Arrival in INBOUND status - show LOCAL button
|
||||
buttons = `<button class="status-btn" onclick="event.stopPropagation(); updateArrivalStatusFromTable('${ac.id}', 'LOCAL')">→ LOCAL</button>`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="aircraft-item" onclick="handleATCClick('${ac.id}', '${ac.isArrival ? 'arrival' : 'ppr'}')">
|
||||
<div class="aircraft-item inbound" onclick="handleATCClick('${ac.id}', '${ac.isArrival ? 'arrival' : 'ppr'}')">
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${reg}</div>
|
||||
<div class="aircraft-details">${type} from ${from || '?'}</div>
|
||||
<div class="aircraft-time">${eta ? formatTimeOnly(eta) : ''}</div>
|
||||
<div class="aircraft-details">${type} from ${from || 'Local Flight'}</div>
|
||||
</div>
|
||||
${buttons ? `<div style="display: flex; gap: 0.5rem;">${buttons}</div>` : '<div class="aircraft-status status-inbound">IB</div>'}
|
||||
</div>
|
||||
@@ -5334,9 +5427,9 @@
|
||||
]);
|
||||
|
||||
let circuits = [];
|
||||
if (response[0].ok) circuits = await response[0].json();
|
||||
if (response[1].ok) circuits = circuits.concat(await response[1].json());
|
||||
if (response[2].ok) circuits = circuits.concat((await response[2].json()).map(a => ({ ...a, isArrival: true })));
|
||||
if (response[0].ok) circuits = circuits.concat(await response[0].json());
|
||||
if (response[1].ok) circuits = circuits.concat((await response[1].json()).map(l => ({ ...l, circuitStatus: getCircuitStatus(l.status) })));
|
||||
if (response[2].ok) circuits = circuits.concat((await response[2].json()).map(a => ({ ...a, isArrival: true, circuitStatus: getCircuitStatus(a.status) })));
|
||||
|
||||
displayCircuitAircraft(circuits);
|
||||
} catch (error) {
|
||||
@@ -5344,10 +5437,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to determine circuit status from API response
|
||||
function getCircuitStatus(status) {
|
||||
if (status === 'CIRCUIT_DOWNWIND') return 'DOWNWIND';
|
||||
if (status === 'CIRCUIT_BASE') return 'BASE';
|
||||
if (status === 'CIRCUIT_FINAL') return 'FINAL';
|
||||
return 'CIRCUIT';
|
||||
}
|
||||
|
||||
function displayCircuitAircraft(aircraft) {
|
||||
const container = document.getElementById('circuit-list');
|
||||
const countEl = document.getElementById('circuit-count');
|
||||
|
||||
// Define status order for sorting
|
||||
const statusOrder = {
|
||||
'CIRCUIT': 1,
|
||||
'DOWNWIND': 2,
|
||||
'BASE': 3,
|
||||
'FINAL': 4
|
||||
};
|
||||
|
||||
// Sort aircraft by circuit status
|
||||
aircraft.sort((a, b) => {
|
||||
const aStatus = a.circuitStatus || 'CIRCUIT';
|
||||
const bStatus = b.circuitStatus || 'CIRCUIT';
|
||||
return statusOrder[aStatus] - statusOrder[bStatus];
|
||||
});
|
||||
|
||||
countEl.textContent = aircraft.length;
|
||||
|
||||
if (aircraft.length === 0) {
|
||||
@@ -5360,12 +5476,22 @@
|
||||
const entityType = isArrival ? 'arrival' : 'local';
|
||||
const updateFunction = isArrival ? 'updateArrivalStatusFromTable' : 'updateLocalFlightStatusFromTable';
|
||||
const landFunction = isArrival ? `${updateFunction}('${ac.id}', 'LANDED')` : `showTimestampModal('LANDED', ${ac.id}, true)`;
|
||||
const circuitStatus = ac.circuitStatus || 'CIRCUIT';
|
||||
|
||||
let buttons = `
|
||||
<button class="status-btn" onclick="event.stopPropagation(); ${updateFunction}('${ac.id}', 'LOCAL')">LOCAL</button>
|
||||
`;
|
||||
|
||||
// Show T&G for both local flights and arrivals
|
||||
// Circuit position buttons - show all, highlight current
|
||||
const downwindClass = circuitStatus === 'DOWNWIND' ? 'active-position' : '';
|
||||
const baseClass = circuitStatus === 'BASE' ? 'active-position' : '';
|
||||
const finalClass = circuitStatus === 'FINAL' ? 'active-position' : '';
|
||||
|
||||
buttons += `<button class="status-btn small-btn ${downwindClass}" onclick="event.stopPropagation(); ${updateFunction}('${ac.id}', 'CIRCUIT_DOWNWIND')" title="Downwind">D</button>`;
|
||||
buttons += `<button class="status-btn small-btn ${baseClass}" onclick="event.stopPropagation(); ${updateFunction}('${ac.id}', 'CIRCUIT_BASE')" title="Base">B</button>`;
|
||||
buttons += `<button class="status-btn small-btn ${finalClass}" onclick="event.stopPropagation(); ${updateFunction}('${ac.id}', 'CIRCUIT_FINAL')" title="Final">F</button>`;
|
||||
|
||||
// Show T&G for both local flights and arrivals - resets to CIRCUIT
|
||||
const tgFunction = isArrival
|
||||
? `currentArrivalId = '${ac.id}'; showCircuitModal(null, '${ac.id}')`
|
||||
: `currentLocalFlightId = '${ac.id}'; showCircuitModal('${ac.id}')`;
|
||||
@@ -5373,14 +5499,18 @@
|
||||
|
||||
buttons += `<button class="status-btn" onclick="event.stopPropagation(); ${landFunction}">LAND</button>`;
|
||||
|
||||
const itemClass = isArrival ? 'inbound' : 'circuit';
|
||||
|
||||
// Display text: for arrivals show origin, for local flights show type
|
||||
const displayText = isArrival ? `${ac.type} from ${ac.in_from || '?'}` : `${ac.type}`;
|
||||
|
||||
return `
|
||||
<div class="aircraft-item" onclick="handleATCClick('${ac.id}', '${entityType}')">
|
||||
<div class="aircraft-item ${itemClass}" onclick="handleATCClick('${ac.id}', '${entityType}')">
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${ac.registration}</div>
|
||||
<div class="aircraft-details">${ac.type}</div>
|
||||
<div class="aircraft-time">${formatTimeOnly(ac.created_dt)}</div>
|
||||
<div class="aircraft-details">${displayText}</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
<div style="display: flex; gap: 0.25rem;">
|
||||
${buttons}
|
||||
</div>
|
||||
</div>
|
||||
@@ -5417,7 +5547,6 @@
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${ppr.ac_reg}</div>
|
||||
<div class="aircraft-details">${ppr.ac_type}</div>
|
||||
<div class="aircraft-time">${ppr.eta ? formatTimeOnly(ppr.eta) : ''}</div>
|
||||
</div>
|
||||
<div class="aircraft-status status-pending">PPR</div>
|
||||
</div>
|
||||
@@ -5427,26 +5556,32 @@
|
||||
// Load parked visitors
|
||||
async function loadParkedVisitors() {
|
||||
try {
|
||||
const [depBookedOutResponse, localBookedOutResponse] = await Promise.all([
|
||||
authenticatedFetch('/api/v1/departures/?status=BOOKED_OUT&limit=1000'),
|
||||
authenticatedFetch('/api/v1/local-flights/?status=BOOKED_OUT&limit=1000')
|
||||
const [localBookedOutResponse, depBookedOutResponse] = await Promise.all([
|
||||
authenticatedFetch('/api/v1/local-flights/?status=BOOKED_OUT&limit=1000'),
|
||||
authenticatedFetch('/api/v1/departures/?status=BOOKED_OUT&limit=1000')
|
||||
]);
|
||||
|
||||
const depBookedOut = depBookedOutResponse.ok ? await depBookedOutResponse.json() : [];
|
||||
const localBookedOut = localBookedOutResponse.ok ? await localBookedOutResponse.json() : [];
|
||||
const depBookedOut = depBookedOutResponse.ok ? await depBookedOutResponse.json() : [];
|
||||
|
||||
// Combine and filter for today's bookings
|
||||
// Filter for today's bookings
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const bookedOutAircraft = [...depBookedOut, ...localBookedOut]
|
||||
.filter(flight => {
|
||||
const bookedOutAircraft = [
|
||||
...localBookedOut.filter(flight => {
|
||||
const createdDate = flight.created_dt.split('T')[0];
|
||||
return createdDate === today;
|
||||
})
|
||||
.map(flight => ({
|
||||
}).map(flight => ({
|
||||
...flight,
|
||||
isDeparture: depBookedOut.includes(flight),
|
||||
isLocal: localBookedOut.includes(flight)
|
||||
}));
|
||||
isLocalFlight: true
|
||||
})),
|
||||
...depBookedOut.filter(flight => {
|
||||
const createdDate = flight.created_dt.split('T')[0];
|
||||
return createdDate === today;
|
||||
}).map(flight => ({
|
||||
...flight,
|
||||
isDeparture: true
|
||||
}))
|
||||
];
|
||||
|
||||
displayParkedVisitors(bookedOutAircraft);
|
||||
} catch (error) {
|
||||
@@ -5471,21 +5606,27 @@
|
||||
const dest = flight.out_to;
|
||||
const createdTime = flight.created_dt ? formatTimeOnly(flight.created_dt) : '';
|
||||
|
||||
let typeIcon = '';
|
||||
if (flight.isDeparture) {
|
||||
typeIcon = '<span style="color: #ff6b35; font-weight: bold;" title="Departure to Other Airport">D</span>';
|
||||
} else if (flight.isLocal) {
|
||||
typeIcon = '<span style="color: #4CAF50; font-weight: bold;" title="Local Flight">L</span>';
|
||||
// Determine the entity type and display details
|
||||
let entityType, displayDetails, clickHandler, cssClass;
|
||||
if (flight.isLocalFlight) {
|
||||
entityType = 'local';
|
||||
displayDetails = `${type} - ${flight.flight_type || 'Local Flight'}`;
|
||||
clickHandler = `handleATCClick('${flight.id}', 'local')`;
|
||||
cssClass = 'local-flight';
|
||||
} else {
|
||||
entityType = 'departure';
|
||||
displayDetails = `${type} → ${dest || 'Other Airport'}`;
|
||||
clickHandler = `handleATCClick('${flight.id}', 'departure')`;
|
||||
cssClass = 'departure';
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="aircraft-item" onclick="handleATCClick('${flight.id}', '${flight.isDeparture ? 'departure' : 'local'}')">
|
||||
<div class="aircraft-item ${cssClass}" onclick="${clickHandler}">
|
||||
<div class="aircraft-info">
|
||||
<div class="aircraft-reg">${reg} ${typeIcon}</div>
|
||||
<div class="aircraft-details">${type} → ${dest || '?'}</div>
|
||||
<div class="aircraft-time">${createdTime}</div>
|
||||
<div class="aircraft-reg">${reg}</div>
|
||||
<div class="aircraft-details">${displayDetails}</div>
|
||||
</div>
|
||||
<button class="status-btn" onclick="event.stopPropagation(); ${flight.isDeparture ? `currentDepartureId = ${flight.id}; showTimestampModal('GROUND', ${flight.id}, false, true)` : `currentLocalFlightId = ${flight.id}; showTimestampModal('GROUND', ${flight.id}, true)`}" title="Contact Pilot">CONTACT</button>
|
||||
<button class="status-btn" onclick="event.stopPropagation(); ${flight.isLocalFlight ? `currentLocalFlightId = '${flight.id}'; showTimestampModal('GROUND', '${flight.id}', true, false)` : `currentDepartureId = ${flight.id}; showTimestampModal('GROUND', ${flight.id}, false, true)`}" title="Contact Pilot">CONTACT</button>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
@@ -5506,6 +5647,9 @@
|
||||
case 'arrival':
|
||||
openArrivalEditModal(entityId);
|
||||
break;
|
||||
case 'overflight':
|
||||
openOverflightEditModal(entityId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -699,7 +699,7 @@
|
||||
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
|
||||
// Show ETA if INBOUND
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user