Public board for local fligts
This commit is contained in:
@@ -982,25 +982,24 @@
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label for="local_registration">Aircraft Registration *</label>
|
||||
<input type="text" id="local_registration" name="registration" required oninput="handleLocalAircraftLookup(this.value)">
|
||||
<input type="text" id="local_registration" name="registration" required oninput="handleLocalAircraftLookup(this.value)" tabindex="1">
|
||||
<div id="local-aircraft-lookup-results"></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_type">Aircraft Type *</label>
|
||||
<input type="text" id="local_type" name="type" required tabindex="-1">
|
||||
<input type="text" id="local_type" name="type" required tabindex="-1" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_callsign">Callsign (optional)</label>
|
||||
<input type="text" id="local_callsign" name="callsign" placeholder="If different from registration">
|
||||
<input type="text" id="local_callsign" name="callsign" placeholder="If different from registration" tabindex="4">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_pob">Persons on Board *</label>
|
||||
<input type="number" id="local_pob" name="pob" required min="1">
|
||||
<input type="number" id="local_pob" name="pob" required min="1" tabindex="2">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_flight_type">Flight Type *</label>
|
||||
<select id="local_flight_type" name="flight_type" required>
|
||||
<option value="">Select Type</option>
|
||||
<select id="local_flight_type" name="flight_type" required tabindex="3">
|
||||
<option value="LOCAL">Local Flight</option>
|
||||
<option value="CIRCUITS">Circuits</option>
|
||||
<option value="DEPARTURE">Departure</option>
|
||||
@@ -1504,6 +1503,13 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Press 'Escape' to close Book Out modal if it's open (allow even when typing in inputs)
|
||||
if (e.key === 'Escape' && document.getElementById('localFlightModal').style.display === 'block') {
|
||||
e.preventDefault();
|
||||
closeLocalFlightModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only trigger other shortcuts when not typing in input fields
|
||||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.tagName === 'SELECT') {
|
||||
return;
|
||||
@@ -1514,6 +1520,24 @@
|
||||
e.preventDefault();
|
||||
openNewPPRModal();
|
||||
}
|
||||
|
||||
// Press 'b' to book out local flight (LOCAL type)
|
||||
if (e.key === 'b' || e.key === 'B') {
|
||||
e.preventDefault();
|
||||
openLocalFlightModal('LOCAL');
|
||||
}
|
||||
|
||||
// Press 'c' to book out circuits
|
||||
if (e.key === 'c' || e.key === 'C') {
|
||||
e.preventDefault();
|
||||
openLocalFlightModal('CIRCUITS');
|
||||
}
|
||||
|
||||
// Press 'd' to book out departure
|
||||
if (e.key === 'd' || e.key === 'D') {
|
||||
e.preventDefault();
|
||||
openLocalFlightModal('DEPARTURE');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2126,7 +2150,7 @@
|
||||
pob = flight.pob || '-';
|
||||
fuel = '-';
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'LANDED')" title="Mark as Landed">
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||
@@ -2234,7 +2258,7 @@
|
||||
// Action buttons for local flight
|
||||
if (flight.status === 'BOOKED_OUT') {
|
||||
actionButtons = `
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'DEPARTED')" title="Mark as Departed">
|
||||
<button class="btn btn-primary btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('DEPARTED', ${flight.id}, true)" title="Mark as Departed">
|
||||
TAKE OFF
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||
@@ -2243,7 +2267,7 @@
|
||||
`;
|
||||
} else if (flight.status === 'DEPARTED') {
|
||||
actionButtons = `
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'LANDED')" title="Mark as Landed">
|
||||
<button class="btn btn-success btn-icon" onclick="event.stopPropagation(); currentLocalFlightId = ${flight.id}; showTimestampModal('LANDED', ${flight.id}, true)" title="Mark as Landed">
|
||||
LAND
|
||||
</button>
|
||||
<button class="btn btn-danger btn-icon" onclick="event.stopPropagation(); updateLocalFlightStatusFromTable(${flight.id}, 'CANCELLED')" title="Cancel Flight">
|
||||
@@ -2551,11 +2575,11 @@
|
||||
}
|
||||
|
||||
// Timestamp modal functions
|
||||
function showTimestampModal(status, pprId = null) {
|
||||
const targetPprId = pprId || currentPPRId;
|
||||
if (!targetPprId) return;
|
||||
function showTimestampModal(status, pprId = null, isLocalFlight = false) {
|
||||
const targetId = pprId || (isLocalFlight ? currentLocalFlightId : currentPPRId);
|
||||
if (!targetId) return;
|
||||
|
||||
pendingStatusUpdate = { status: status, pprId: targetPprId };
|
||||
pendingStatusUpdate = { status: status, pprId: targetId, isLocalFlight: isLocalFlight };
|
||||
|
||||
const modalTitle = document.getElementById('timestamp-modal-title');
|
||||
const submitBtn = document.getElementById('timestamp-submit-btn');
|
||||
@@ -2601,7 +2625,13 @@
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/pprs/${pendingStatusUpdate.pprId}/status`, {
|
||||
// Determine the correct API endpoint based on flight type
|
||||
const isLocal = pendingStatusUpdate.isLocalFlight;
|
||||
const endpoint = isLocal ?
|
||||
`/api/v1/local-flights/${pendingStatusUpdate.pprId}/status` :
|
||||
`/api/v1/pprs/${pendingStatusUpdate.pprId}/status`;
|
||||
|
||||
const response = await fetch(endpoint, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -2620,9 +2650,11 @@
|
||||
|
||||
const updatedStatus = pendingStatusUpdate.status;
|
||||
closeTimestampModal();
|
||||
loadPPRs(); // Refresh both tables
|
||||
loadPPRs(); // Refresh all tables
|
||||
showNotification(`Status updated to ${updatedStatus}`);
|
||||
closePPRModal(); // Close PPR modal after successful status update
|
||||
if (!isLocal) {
|
||||
closePPRModal(); // Close PPR modal after successful status update
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating status:', error);
|
||||
showNotification(`Error updating status: ${error.message}`, true);
|
||||
@@ -3419,10 +3451,11 @@
|
||||
}
|
||||
|
||||
// Local Flight (Book Out) Modal Functions
|
||||
function openLocalFlightModal() {
|
||||
function openLocalFlightModal(flightType = 'LOCAL') {
|
||||
document.getElementById('local-flight-form').reset();
|
||||
document.getElementById('local-flight-id').value = '';
|
||||
document.getElementById('local-flight-modal-title').textContent = 'Book Out';
|
||||
document.getElementById('local_flight_type').value = flightType;
|
||||
document.getElementById('localFlightModal').style.display = 'block';
|
||||
|
||||
// Clear aircraft lookup results
|
||||
|
||||
120
web/index.html
120
web/index.html
@@ -304,27 +304,46 @@
|
||||
|
||||
// Build rows asynchronously to lookup airport names
|
||||
const rows = await Promise.all(arrivals.map(async (arrival) => {
|
||||
// Show callsign if available, otherwise registration
|
||||
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 || '');
|
||||
const isLocal = arrival.isLocalFlight;
|
||||
|
||||
// Show landed time if available, otherwise ETA
|
||||
let timeDisplay;
|
||||
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>`;
|
||||
if (isLocal) {
|
||||
// Local flight
|
||||
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 = `<i>${getFlightTypeDisplay(arrival.flight_type)}</i>`;
|
||||
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>
|
||||
`;
|
||||
} else {
|
||||
timeDisplay = convertToLocalTime(arrival.eta);
|
||||
// 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;
|
||||
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>`;
|
||||
} else {
|
||||
timeDisplay = convertToLocalTime(arrival.eta);
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${escapeHtml(fromDisplay)}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${escapeHtml(fromDisplay)}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
}));
|
||||
|
||||
tbody.innerHTML = rows.join('');
|
||||
@@ -355,27 +374,46 @@
|
||||
|
||||
// Build rows asynchronously to lookup airport names
|
||||
const rows = await Promise.all(departures.map(async (departure) => {
|
||||
// Show callsign if available, otherwise registration
|
||||
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 isLocal = departure.isLocalFlight;
|
||||
|
||||
// Show departed time if available, otherwise ETD
|
||||
let timeDisplay;
|
||||
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>`;
|
||||
if (isLocal) {
|
||||
// Local flight
|
||||
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 = `<i>${getFlightTypeDisplay(departure.flight_type)}</i>`;
|
||||
const time = convertToLocalTime(departure.etd);
|
||||
const timeDisplay = `<div>${escapeHtml(time)}</div>`;
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${toDisplay}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
} else {
|
||||
timeDisplay = convertToLocalTime(departure.etd);
|
||||
// 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;
|
||||
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>`;
|
||||
} else {
|
||||
timeDisplay = convertToLocalTime(departure.etd);
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${escapeHtml(toDisplay)}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td>${aircraftDisplay}</td>
|
||||
<td>${escapeHtml(toDisplay)}</td>
|
||||
<td>${timeDisplay}</td>
|
||||
</tr>
|
||||
`;
|
||||
}));
|
||||
|
||||
tbody.innerHTML = rows.join('');
|
||||
@@ -393,6 +431,16 @@
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Map flight type enum to friendly name
|
||||
function getFlightTypeDisplay(flightType) {
|
||||
const typeMap = {
|
||||
'CIRCUITS': 'Circuit Traffic',
|
||||
'LOCAL': 'Local Area',
|
||||
'DEPARTURE': 'Departure'
|
||||
};
|
||||
return typeMap[flightType] || flightType;
|
||||
}
|
||||
|
||||
// Load data on page load
|
||||
window.addEventListener('load', function() {
|
||||
loadArrivals();
|
||||
|
||||
Reference in New Issue
Block a user