Journaling for all flights

This commit is contained in:
2025-12-18 07:34:19 -05:00
parent f3eb83665f
commit a2682314c9
16 changed files with 594 additions and 87 deletions

View File

@@ -528,6 +528,14 @@
<p style="color: #666; font-style: italic;">Loading circuits...</p>
</div>
</div>
<!-- Journal Section -->
<div id="local-flight-journal-section" style="margin-top: 2rem; border-top: 1px solid #ddd; padding-top: 1rem;">
<h3>📋 Activity Journal</h3>
<div id="local-flight-journal-entries" class="journal-entries">
Loading journal...
</div>
</div>
</div>
</div>
</div>
@@ -650,6 +658,14 @@
</button>
</div>
</form>
<!-- Journal Section -->
<div id="departure-journal-section" style="margin-top: 2rem; border-top: 1px solid #ddd; padding-top: 1rem;">
<h3>📋 Activity Journal</h3>
<div id="departure-journal-entries" class="journal-entries">
Loading journal...
</div>
</div>
</div>
</div>
</div>
@@ -710,6 +726,14 @@
</button>
</div>
</form>
<!-- Journal Section -->
<div id="arrival-journal-section" style="margin-top: 2rem; border-top: 1px solid #ddd; padding-top: 1rem;">
<h3>📋 Activity Journal</h3>
<div id="arrival-journal-entries" class="journal-entries">
Loading journal...
</div>
</div>
</div>
</div>
</div>
@@ -1604,15 +1628,27 @@
function displayDeparted(departed) {
const tbody = document.getElementById('departed-table-body');
document.getElementById('departed-count').textContent = departed.length;
if (departed.length === 0) {
// Deduplicate departed flights by ID to prevent duplicates from race conditions
const seenIds = new Set();
const uniqueDeparted = departed.filter(flight => {
if (seenIds.has(flight.id)) {
console.warn(`Duplicate departed flight detected and filtered: ID ${flight.id}`);
return false;
}
seenIds.add(flight.id);
return true;
});
document.getElementById('departed-count').textContent = uniqueDeparted.length;
if (uniqueDeparted.length === 0) {
document.getElementById('departed-no-data').style.display = 'block';
return;
}
// Sort by departed time
departed.sort((a, b) => {
uniqueDeparted.sort((a, b) => {
const aTime = a.departed_dt;
const bTime = b.departed_dt;
return new Date(aTime) - new Date(bTime);
@@ -1621,7 +1657,7 @@
tbody.innerHTML = '';
document.getElementById('departed-table-content').style.display = 'block';
for (const flight of departed) {
for (const flight of uniqueDeparted) {
const row = document.createElement('tr');
const isLocal = flight.isLocalFlight;
const isDeparture = flight.isDeparture;
@@ -1726,15 +1762,27 @@
function displayParked(parked) {
const tbody = document.getElementById('parked-table-body');
document.getElementById('parked-count').textContent = parked.length;
if (parked.length === 0) {
// Deduplicate parked flights by ID to prevent duplicates from race conditions
const seenIds = new Set();
const uniqueParked = parked.filter(flight => {
if (seenIds.has(flight.id)) {
console.warn(`Duplicate parked flight detected and filtered: ID ${flight.id}`);
return false;
}
seenIds.add(flight.id);
return true;
});
document.getElementById('parked-count').textContent = uniqueParked.length;
if (uniqueParked.length === 0) {
document.getElementById('parked-no-data').style.display = 'block';
return;
}
// Sort by landed time
parked.sort((a, b) => {
uniqueParked.sort((a, b) => {
if (!a.landed_dt) return 1;
if (!b.landed_dt) return -1;
return new Date(a.landed_dt) - new Date(b.landed_dt);
@@ -1743,7 +1791,7 @@
tbody.innerHTML = '';
document.getElementById('parked-table-content').style.display = 'block';
for (const ppr of parked) {
for (const ppr of uniqueParked) {
const row = document.createElement('tr');
const isBookedIn = ppr.isBookedIn;
@@ -1841,20 +1889,32 @@
function displayUpcoming(upcoming) {
const tbody = document.getElementById('upcoming-table-body');
document.getElementById('upcoming-count').textContent = upcoming.length;
if (upcoming.length === 0) {
// Deduplicate upcoming flights by ID to prevent duplicates from race conditions
const seenIds = new Set();
const uniqueUpcoming = upcoming.filter(ppr => {
if (seenIds.has(ppr.id)) {
console.warn(`Duplicate upcoming flight detected and filtered: ID ${ppr.id}`);
return false;
}
seenIds.add(ppr.id);
return true;
});
document.getElementById('upcoming-count').textContent = uniqueUpcoming.length;
if (uniqueUpcoming.length === 0) {
// Don't show anything if collapsed by default
return;
}
// Sort by ETA date and time
upcoming.sort((a, b) => new Date(a.eta) - new Date(b.eta));
uniqueUpcoming.sort((a, b) => new Date(a.eta) - new Date(b.eta));
tbody.innerHTML = '';
// Don't auto-expand, keep collapsed by default
for (const ppr of upcoming) {
for (const ppr of uniqueUpcoming) {
const row = document.createElement('tr');
row.onclick = () => openPPRModal(ppr.id);
row.style.cssText = 'font-size: 0.85rem !important;';
@@ -1932,14 +1992,26 @@
async function displayArrivals(arrivals) {
const tbody = document.getElementById('arrivals-table-body');
const recordCount = document.getElementById('arrivals-count');
recordCount.textContent = arrivals.length;
if (arrivals.length === 0) {
// Deduplicate arrivals by ID to prevent duplicates from race conditions
const seenIds = new Set();
const uniqueArrivals = arrivals.filter(flight => {
if (seenIds.has(flight.id)) {
console.warn(`Duplicate arrival detected and filtered: ID ${flight.id}`);
return false;
}
seenIds.add(flight.id);
return true;
});
recordCount.textContent = uniqueArrivals.length;
if (uniqueArrivals.length === 0) {
document.getElementById('arrivals-no-data').style.display = 'block';
return;
}
// Sort arrivals by ETA/departure time (ascending)
arrivals.sort((a, b) => {
uniqueArrivals.sort((a, b) => {
const aTime = a.eta || a.departure_dt;
const bTime = b.eta || b.departure_dt;
if (!aTime) return 1;
@@ -1948,7 +2020,7 @@
});
tbody.innerHTML = '';
document.getElementById('arrivals-table-content').style.display = 'block';
for (const flight of arrivals) {
for (const flight of uniqueArrivals) {
const row = document.createElement('tr');
const isLocal = flight.isLocalFlight;
const isBookedIn = flight.isBookedIn;
@@ -2091,14 +2163,26 @@
async function displayDepartures(departures) {
const tbody = document.getElementById('departures-table-body');
const recordCount = document.getElementById('departures-count');
recordCount.textContent = departures.length;
if (departures.length === 0) {
// Deduplicate departures by ID to prevent duplicates from race conditions
const seenIds = new Set();
const uniqueDepartures = departures.filter(flight => {
if (seenIds.has(flight.id)) {
console.warn(`Duplicate flight detected and filtered: ID ${flight.id}`);
return false;
}
seenIds.add(flight.id);
return true;
});
recordCount.textContent = uniqueDepartures.length;
if (uniqueDepartures.length === 0) {
document.getElementById('departures-no-data').style.display = 'block';
return;
}
// Sort departures by ETD (ascending), nulls last
departures.sort((a, b) => {
uniqueDepartures.sort((a, b) => {
const aTime = a.etd || a.created_dt;
const bTime = b.etd || b.created_dt;
if (!aTime) return 1;
@@ -2107,7 +2191,7 @@
});
tbody.innerHTML = '';
document.getElementById('departures-table-content').style.display = 'block';
for (const flight of departures) {
for (const flight of uniqueDepartures) {
const row = document.createElement('tr');
const isLocal = flight.isLocalFlight;
const isDeparture = flight.isDeparture;
@@ -2461,9 +2545,10 @@
});
}
async function loadJournal(pprId) {
// Generic function to load journal for any entity type
async function loadJournalForEntity(entityType, entityId, containerElementId) {
try {
const response = await fetch(`/api/v1/pprs/${pprId}/journal`, {
const response = await fetch(`/api/v1/journal/${entityType}/${entityId}`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
@@ -2473,16 +2558,40 @@
throw new Error('Failed to fetch journal');
}
const entries = await response.json();
displayJournal(entries);
const data = await response.json();
// The new API returns { entity_type, entity_id, entries, total_entries }
displayJournalForContainer(data.entries || [], containerElementId);
} catch (error) {
console.error('Error loading journal:', error);
document.getElementById('journal-entries').innerHTML = 'Error loading journal entries';
const container = document.getElementById(containerElementId);
if (container) container.innerHTML = 'Error loading journal entries';
}
}
function displayJournal(entries) {
const container = document.getElementById('journal-entries');
// PPR-specific journal loader (backward compatible)
async function loadJournal(pprId) {
await loadJournalForEntity('PPR', pprId, 'journal-entries');
}
// Local Flight specific journal loader
async function loadLocalFlightJournal(flightId) {
await loadJournalForEntity('LOCAL_FLIGHT', flightId, 'local-flight-journal-entries');
}
// Departure specific journal loader
async function loadDepartureJournal(departureId) {
await loadJournalForEntity('DEPARTURE', departureId, 'departure-journal-entries');
}
// Arrival specific journal loader
async function loadArrivalJournal(arrivalId) {
await loadJournalForEntity('ARRIVAL', arrivalId, 'arrival-journal-entries');
}
// Display journal in a specific container
function displayJournalForContainer(entries, containerElementId) {
const container = document.getElementById(containerElementId);
if (!container) return;
if (entries.length === 0) {
container.innerHTML = '<p>No journal entries yet.</p>';
@@ -2496,6 +2605,10 @@
</div>
`).join('');
}
}
function displayJournal(entries) {
displayJournalForContainer(entries, 'journal-entries');
// Always show journal section when displaying entries
document.getElementById('journal-section').style.display = 'block';
@@ -3390,6 +3503,9 @@
}
document.getElementById('localFlightEditModal').style.display = 'block';
// Load journal for this local flight
await loadLocalFlightJournal(flightId);
} catch (error) {
console.error('Error loading flight:', error);
showNotification('Error loading flight details', true);
@@ -3472,6 +3588,9 @@
document.getElementById('departure-edit-title').textContent = `${departure.registration} to ${departure.out_to}`;
document.getElementById('departureEditModal').style.display = 'block';
// Load journal for this departure
await loadDepartureJournal(departureId);
} catch (error) {
console.error('Error loading departure:', error);
showNotification('Error loading departure details', true);
@@ -3573,6 +3692,9 @@
// Show modal
document.getElementById('arrivalEditModal').style.display = 'block';
// Load journal for this arrival
await loadArrivalJournal(arrivalId);
} catch (error) {
console.error('Error loading arrival:', error);
showNotification('Error loading arrival details', true);