Compare commits
2 Commits
f65c54109e
...
f3eb83665f
| Author | SHA1 | Date | |
|---|---|---|---|
| f3eb83665f | |||
| f572fb75f5 |
@@ -32,6 +32,7 @@ def upgrade() -> None:
|
||||
sa.Column('pob', sa.Integer(), nullable=False),
|
||||
sa.Column('flight_type', sa.Enum('LOCAL', 'CIRCUITS', 'DEPARTURE', name='localflighttype'), nullable=False),
|
||||
sa.Column('status', sa.Enum('BOOKED_OUT', 'DEPARTED', 'LANDED', 'CANCELLED', name='localflightstatus'), nullable=False, server_default='BOOKED_OUT'),
|
||||
sa.Column('duration', sa.Integer(), nullable=True, comment='Duration in minutes'),
|
||||
sa.Column('notes', sa.Text(), nullable=True),
|
||||
sa.Column('created_dt', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False),
|
||||
sa.Column('etd', sa.DateTime(), nullable=True),
|
||||
|
||||
@@ -51,12 +51,18 @@ async def get_public_arrivals(db: Session = Depends(get_db)):
|
||||
# Only include flights booked out today
|
||||
if not (today_start <= flight.created_dt < today_end):
|
||||
continue
|
||||
|
||||
# Calculate ETA from departed_dt + duration (if both are available)
|
||||
eta = flight.departed_dt
|
||||
if flight.departed_dt and flight.duration:
|
||||
eta = flight.departed_dt + timedelta(minutes=flight.duration)
|
||||
|
||||
arrivals_list.append({
|
||||
'ac_call': flight.callsign or flight.registration,
|
||||
'ac_reg': flight.registration,
|
||||
'ac_type': flight.type,
|
||||
'in_from': None,
|
||||
'eta': flight.departed_dt,
|
||||
'eta': eta,
|
||||
'landed_dt': None,
|
||||
'status': 'DEPARTED',
|
||||
'isLocalFlight': True,
|
||||
|
||||
@@ -27,6 +27,7 @@ class LocalFlight(Base):
|
||||
pob = Column(Integer, nullable=False) # Persons on board
|
||||
flight_type = Column(SQLEnum(LocalFlightType), nullable=False, index=True)
|
||||
status = Column(SQLEnum(LocalFlightStatus), nullable=False, default=LocalFlightStatus.BOOKED_OUT, index=True)
|
||||
duration = Column(Integer, nullable=True) # Duration in minutes
|
||||
notes = Column(Text, nullable=True)
|
||||
created_dt = Column(DateTime, nullable=False, server_default=func.current_timestamp(), index=True)
|
||||
etd = Column(DateTime, nullable=True, index=True) # Estimated Time of Departure
|
||||
|
||||
@@ -42,6 +42,7 @@ class ArrivalCreate(ArrivalBase):
|
||||
|
||||
|
||||
class ArrivalUpdate(BaseModel):
|
||||
registration: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
callsign: Optional[str] = None
|
||||
pob: Optional[int] = None
|
||||
|
||||
@@ -43,6 +43,7 @@ class DepartureCreate(DepartureBase):
|
||||
|
||||
|
||||
class DepartureUpdate(BaseModel):
|
||||
registration: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
callsign: Optional[str] = None
|
||||
pob: Optional[int] = None
|
||||
|
||||
@@ -23,6 +23,7 @@ class LocalFlightBase(BaseModel):
|
||||
callsign: Optional[str] = None
|
||||
pob: int
|
||||
flight_type: LocalFlightType
|
||||
duration: Optional[int] = 45 # Duration in minutes, default 45
|
||||
etd: Optional[datetime] = None # Estimated Time of Departure
|
||||
notes: Optional[str] = None
|
||||
|
||||
@@ -57,6 +58,7 @@ class LocalFlightUpdate(BaseModel):
|
||||
callsign: Optional[str] = None
|
||||
pob: Optional[int] = None
|
||||
flight_type: Optional[LocalFlightType] = None
|
||||
duration: Optional[int] = None
|
||||
status: Optional[LocalFlightStatus] = None
|
||||
etd: Optional[datetime] = None
|
||||
departed_dt: Optional[datetime] = None
|
||||
|
||||
347
web/admin.html
347
web/admin.html
@@ -353,6 +353,9 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-info" onclick="closePPRModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" id="btn-cancel" onclick="updateStatus('CANCELED')">
|
||||
❌ Cancel PPR
|
||||
</button>
|
||||
@@ -409,6 +412,10 @@
|
||||
<option value="DEPARTURE">Departure to Other Airport</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_duration">Duration (minutes)</label>
|
||||
<input type="number" id="local_duration" name="duration" min="5" max="480" value="45" placeholder="Duration in minutes" tabindex="7">
|
||||
</div>
|
||||
<div class="form-group" id="departure-destination-group" style="display: none;">
|
||||
<label for="local_out_to" id="departure-destination-label">Destination Airport</label>
|
||||
<input type="text" id="local_out_to" name="out_to" placeholder="ICAO Code or Airport Name" oninput="handleLocalOutToAirportLookup(this.value)" tabindex="3">
|
||||
@@ -427,8 +434,8 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeLocalFlightModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeLocalFlightModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
🛫 Book Out
|
||||
@@ -455,7 +462,7 @@
|
||||
🛬 Land
|
||||
</button>
|
||||
<button id="local-btn-cancel" class="btn btn-danger btn-sm" onclick="updateLocalFlightStatus('CANCELLED')" style="display: none;">
|
||||
❌ Cancel
|
||||
❌ Cancel Flight
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -487,6 +494,10 @@
|
||||
<option value="DEPARTURE">Departure</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_edit_duration">Duration (minutes)</label>
|
||||
<input type="number" id="local_edit_duration" name="duration" min="5" max="480">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="local_edit_departure_dt">Departure Time</label>
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
@@ -501,8 +512,8 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeLocalFlightEditModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeLocalFlightEditModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
💾 Save Changes
|
||||
@@ -568,8 +579,8 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeBookInModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeBookInModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
🛬 Book In
|
||||
@@ -593,7 +604,7 @@
|
||||
🛫 Mark Departed
|
||||
</button>
|
||||
<button id="departure-btn-cancel" class="btn btn-danger btn-sm" onclick="updateDepartureStatus('CANCELLED')" style="display: none;">
|
||||
❌ Cancel
|
||||
❌ Cancel Departure
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -603,37 +614,100 @@
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label for="departure_edit_registration">Aircraft Registration</label>
|
||||
<input type="text" id="departure_edit_registration" name="registration" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="text" id="departure_edit_registration" name="registration">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="departure_edit_type">Aircraft Type</label>
|
||||
<input type="text" id="departure_edit_type" name="type" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="text" id="departure_edit_type" name="type">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="departure_edit_callsign">Callsign</label>
|
||||
<input type="text" id="departure_edit_callsign" name="callsign" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="text" id="departure_edit_callsign" name="callsign">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="departure_edit_out_to">Destination</label>
|
||||
<input type="text" id="departure_edit_out_to" name="out_to" readonly style="background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="text" id="departure_edit_out_to" name="out_to">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="departure_edit_etd">ETD (Estimated Time of Departure)</label>
|
||||
<div style="display: flex; gap: 0.5rem;">
|
||||
<input type="date" id="departure_edit_etd_date" name="etd_date" readonly style="flex: 1; background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="time" id="departure_edit_etd_time" name="etd_time" readonly style="flex: 1; background-color: #f5f5f5; cursor: not-allowed;">
|
||||
<input type="date" id="departure_edit_etd_date" name="etd_date" style="flex: 1;">
|
||||
<input type="time" id="departure_edit_etd_time" name="etd_time" style="flex: 1;">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group full-width">
|
||||
<label for="departure_edit_notes">Notes</label>
|
||||
<textarea id="departure_edit_notes" name="notes" rows="3" readonly style="background-color: #f5f5f5; cursor: not-allowed;"></textarea>
|
||||
<textarea id="departure_edit_notes" name="notes" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeDepartureEditModal()">
|
||||
<button type="button" class="btn btn-info" onclick="closeDepartureEditModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Arrival Edit Modal -->
|
||||
<div id="arrivalEditModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="arrival-edit-title">Arrival Details</h2>
|
||||
<button class="close" onclick="closeArrivalEditModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="quick-actions">
|
||||
<button id="arrival-btn-landed" class="btn btn-primary btn-sm" onclick="updateArrivalStatus('LANDED')" style="display: none;">
|
||||
🛬 Mark Landed
|
||||
</button>
|
||||
<button id="arrival-btn-cancel" class="btn btn-danger btn-sm" onclick="updateArrivalStatus('CANCELLED')" style="display: none;">
|
||||
❌ Cancel Arrival
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="arrival-edit-form">
|
||||
<input type="hidden" id="arrival-edit-id" name="id">
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label for="arrival_edit_registration">Aircraft Registration</label>
|
||||
<input type="text" id="arrival_edit_registration" name="registration">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="arrival_edit_type">Aircraft Type</label>
|
||||
<input type="text" id="arrival_edit_type" name="type">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="arrival_edit_callsign">Callsign</label>
|
||||
<input type="text" id="arrival_edit_callsign" name="callsign">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="arrival_edit_in_from">Origin Airport</label>
|
||||
<input type="text" id="arrival_edit_in_from" name="in_from">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="arrival_edit_pob">POB (Persons on Board)</label>
|
||||
<input type="number" id="arrival_edit_pob" name="pob" min="1">
|
||||
</div>
|
||||
<div class="form-group full-width">
|
||||
<label for="arrival_edit_notes">Notes</label>
|
||||
<textarea id="arrival_edit_notes" name="notes" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-info" onclick="closeArrivalEditModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -651,7 +725,7 @@
|
||||
<!-- Content will be populated by JavaScript -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" onclick="closeTableHelp()">Close</button>
|
||||
<button class="btn btn-info" onclick="closeTableHelp()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -730,8 +804,8 @@
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeUserModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeUserModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
💾 Save User
|
||||
@@ -764,8 +838,8 @@
|
||||
<input type="password" id="change-password-confirm" name="confirm_password" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeChangePasswordModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeChangePasswordModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-warning">
|
||||
🔐 Change Password
|
||||
@@ -793,8 +867,8 @@
|
||||
<input type="datetime-local" id="event-timestamp" name="timestamp" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeTimestampModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeTimestampModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success" id="timestamp-submit-btn">
|
||||
Confirm
|
||||
@@ -819,8 +893,8 @@
|
||||
<input type="datetime-local" id="circuit-timestamp" name="timestamp" required>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-primary" onclick="closeCircuitModal()">
|
||||
Cancel
|
||||
<button type="button" class="btn btn-info" onclick="closeCircuitModal()">
|
||||
Close
|
||||
</button>
|
||||
<button type="submit" class="btn btn-success">
|
||||
Record Circuit
|
||||
@@ -1129,6 +1203,27 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Press 'Escape' to close local flight edit modal if it's open (allow even when typing in inputs)
|
||||
if (e.key === 'Escape' && document.getElementById('localFlightEditModal').style.display === 'block') {
|
||||
e.preventDefault();
|
||||
closeLocalFlightEditModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Press 'Escape' to close departure edit modal if it's open (allow even when typing in inputs)
|
||||
if (e.key === 'Escape' && document.getElementById('departureEditModal').style.display === 'block') {
|
||||
e.preventDefault();
|
||||
closeDepartureEditModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Press 'Escape' to close arrival edit modal if it's open (allow even when typing in inputs)
|
||||
if (e.key === 'Escape' && document.getElementById('arrivalEditModal').style.display === 'block') {
|
||||
e.preventDefault();
|
||||
closeArrivalEditModal();
|
||||
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;
|
||||
@@ -1863,9 +1958,7 @@
|
||||
if (isLocal) {
|
||||
openLocalFlightEditModal(flight.id);
|
||||
} else if (isBookedIn) {
|
||||
// For booked-in flights, we might add a view modal later
|
||||
// For now, just show a message
|
||||
showNotification(`Booked-in flight: ${flight.registration}`, false);
|
||||
openArrivalEditModal(flight.id);
|
||||
} else {
|
||||
openPPRModal(flight.id);
|
||||
}
|
||||
@@ -1890,7 +1983,16 @@
|
||||
acType = flight.type;
|
||||
typeIcon = '';
|
||||
fromDisplay = `<i>${flight.flight_type === 'CIRCUITS' ? 'Circuits' : flight.flight_type === 'LOCAL' ? 'Local Flight' : 'Departure'}</i>`;
|
||||
eta = flight.departure_dt ? formatTimeOnly(flight.departure_dt) : '-';
|
||||
|
||||
// Calculate ETA: use departed_dt (actual departure) if available, otherwise etd (planned departure)
|
||||
// Then add duration to get ETA
|
||||
let departureTime = flight.departed_dt || flight.etd;
|
||||
let etaTime = departureTime;
|
||||
if (departureTime && flight.duration) {
|
||||
const departTime = new Date(departureTime);
|
||||
etaTime = new Date(departTime.getTime() + flight.duration * 60000).toISOString(); // duration is in minutes
|
||||
}
|
||||
eta = etaTime ? formatTimeOnly(etaTime) : '-';
|
||||
pob = flight.pob || '-';
|
||||
fuel = '-';
|
||||
|
||||
@@ -3255,11 +3357,14 @@
|
||||
document.getElementById('local_edit_callsign').value = flight.callsign || '';
|
||||
document.getElementById('local_edit_pob').value = flight.pob;
|
||||
document.getElementById('local_edit_flight_type').value = flight.flight_type;
|
||||
document.getElementById('local_edit_duration').value = flight.duration || 45;
|
||||
document.getElementById('local_edit_notes').value = flight.notes || '';
|
||||
|
||||
// Parse and populate departure time if exists
|
||||
if (flight.departure_dt) {
|
||||
const dept = new Date(flight.departure_dt);
|
||||
// Use departed_dt (actual departure) if available, otherwise etd (planned departure)
|
||||
const departureTime = flight.departed_dt || flight.etd;
|
||||
if (departureTime) {
|
||||
const dept = new Date(departureTime);
|
||||
document.getElementById('local_edit_departure_date').value = dept.toISOString().slice(0, 10);
|
||||
document.getElementById('local_edit_departure_time').value = dept.toISOString().slice(11, 16);
|
||||
}
|
||||
@@ -3378,6 +3483,182 @@
|
||||
currentDepartureId = null;
|
||||
}
|
||||
|
||||
// Departure edit form submission
|
||||
document.getElementById('departure-edit-form').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!currentDepartureId || !accessToken) return;
|
||||
|
||||
const formData = new FormData(this);
|
||||
const updateData = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
if (key === 'id') return;
|
||||
|
||||
// Handle date/time combination for ETD
|
||||
if (key === 'etd_date' || key === 'etd_time') {
|
||||
if (!updateData.etd && formData.get('etd_date') && formData.get('etd_time')) {
|
||||
const dateStr = formData.get('etd_date');
|
||||
const timeStr = formData.get('etd_time');
|
||||
updateData.etd = new Date(`${dateStr}T${timeStr}`).toISOString();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Only include non-empty values
|
||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||
if (value.trim) {
|
||||
updateData[key] = value.trim();
|
||||
} else {
|
||||
updateData[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/departures/${currentDepartureId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update departure');
|
||||
|
||||
closeDepartureEditModal();
|
||||
loadPPRs(); // Refresh departures display
|
||||
showNotification('Departure updated successfully');
|
||||
} catch (error) {
|
||||
console.error('Error updating departure:', error);
|
||||
showNotification('Error updating departure', true);
|
||||
}
|
||||
});
|
||||
|
||||
// Arrival Edit Modal Functions
|
||||
let currentArrivalId = null;
|
||||
|
||||
async function openArrivalEditModal(arrivalId) {
|
||||
if (!accessToken) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/arrivals/${arrivalId}`, {
|
||||
headers: { 'Authorization': `Bearer ${accessToken}` }
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to load arrival');
|
||||
|
||||
const arrival = await response.json();
|
||||
currentArrivalId = arrival.id;
|
||||
|
||||
// Populate form
|
||||
document.getElementById('arrival-edit-id').value = arrival.id;
|
||||
document.getElementById('arrival_edit_registration').value = arrival.registration || '';
|
||||
document.getElementById('arrival_edit_type').value = arrival.type || '';
|
||||
document.getElementById('arrival_edit_callsign').value = arrival.callsign || '';
|
||||
document.getElementById('arrival_edit_in_from').value = arrival.in_from || '';
|
||||
document.getElementById('arrival_edit_pob').value = arrival.pob || '';
|
||||
document.getElementById('arrival_edit_notes').value = arrival.notes || '';
|
||||
|
||||
// Update title
|
||||
const regOrCallsign = arrival.callsign || arrival.registration;
|
||||
document.getElementById('arrival-edit-title').textContent = `Arrival: ${regOrCallsign}`;
|
||||
|
||||
// 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';
|
||||
|
||||
// Show modal
|
||||
document.getElementById('arrivalEditModal').style.display = 'block';
|
||||
} catch (error) {
|
||||
console.error('Error loading arrival:', error);
|
||||
showNotification('Error loading arrival details', true);
|
||||
}
|
||||
}
|
||||
|
||||
function closeArrivalEditModal() {
|
||||
document.getElementById('arrivalEditModal').style.display = 'none';
|
||||
currentArrivalId = null;
|
||||
}
|
||||
|
||||
// Arrival edit form submission
|
||||
document.getElementById('arrival-edit-form').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!currentArrivalId || !accessToken) return;
|
||||
|
||||
const formData = new FormData(this);
|
||||
const updateData = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
if (key === 'id') return;
|
||||
|
||||
// Only include non-empty values
|
||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||
if (key === 'pob') {
|
||||
updateData[key] = parseInt(value);
|
||||
} else if (value.trim) {
|
||||
updateData[key] = value.trim();
|
||||
} else {
|
||||
updateData[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/arrivals/${currentArrivalId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error('Failed to update arrival');
|
||||
|
||||
closeArrivalEditModal();
|
||||
loadPPRs(); // Refresh arrivals display
|
||||
showNotification('Arrival updated successfully');
|
||||
} catch (error) {
|
||||
console.error('Error updating arrival:', error);
|
||||
showNotification('Error updating arrival', true);
|
||||
}
|
||||
});
|
||||
|
||||
async function updateArrivalStatus(status) {
|
||||
if (!currentArrivalId || !accessToken) return;
|
||||
|
||||
if (status === 'CANCELLED') {
|
||||
if (!confirm('Are you sure you want to cancel this arrival?')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/v1/arrivals/${currentArrivalId}/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 arrival status');
|
||||
|
||||
closeArrivalEditModal();
|
||||
loadPPRs(); // Refresh arrivals display
|
||||
showNotification(`Arrival marked as ${status.toLowerCase()}`);
|
||||
} catch (error) {
|
||||
console.error('Error updating arrival status:', error);
|
||||
showNotification('Error updating arrival status', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Update status from table buttons (with flight ID passed)
|
||||
async function updateLocalFlightStatusFromTable(flightId, status) {
|
||||
if (!accessToken) return;
|
||||
@@ -3588,7 +3869,7 @@
|
||||
|
||||
// Only include non-empty values
|
||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||
if (key === 'pob') {
|
||||
if (key === 'pob' || key === 'duration') {
|
||||
updateData[key] = parseInt(value);
|
||||
} else if (value.trim) {
|
||||
updateData[key] = value.trim();
|
||||
@@ -3649,7 +3930,7 @@
|
||||
|
||||
// Only include non-empty values
|
||||
if (typeof value === 'number' || (typeof value === 'string' && value.trim() !== '')) {
|
||||
if (key === 'pob') {
|
||||
if (key === 'pob' || key === 'duration') {
|
||||
flightData[key] = parseInt(value);
|
||||
} else if (value.trim) {
|
||||
flightData[key] = value.trim();
|
||||
|
||||
@@ -317,7 +317,7 @@
|
||||
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>`;
|
||||
const 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>`;
|
||||
|
||||
html = `<tr><td>${aircraftDisplay}</td><td>${fromDisplay}</td><td>${timeDisplay}</td></tr>`;
|
||||
sortKey = `0-${arrival.eta}`; // Live flights, sort by ETA
|
||||
|
||||
Reference in New Issue
Block a user