Files
ppr-ng/web/book.html

1158 lines
44 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flight Booking - Pilot Portal</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: #f5f5f5;
padding: 10px;
color: #333;
}
.container {
max-width: 600px;
margin: 0 auto;
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
color: white;
padding: 20px;
margin: -20px -20px 20px -20px;
border-radius: 8px 8px 0 0;
text-align: center;
}
.header h1 {
font-size: 24px;
margin-bottom: 5px;
}
.header p {
font-size: 14px;
opacity: 0.9;
}
.tabs {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab-btn {
flex: 1;
padding: 12px;
border: 2px solid #ddd;
background: white;
color: #333;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
min-width: 120px;
}
.tab-btn.active {
background: #3498db;
color: white;
border-color: #3498db;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 600;
font-size: 14px;
color: #2c3e50;
}
input, select, textarea {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
font-family: inherit;
}
input[type="time"],
input[type="number"] {
padding: 12px 8px;
text-align: center;
min-width: 80px;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
textarea {
resize: vertical;
min-height: 80px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.form-row-3col {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
.btn-submit {
width: 100%;
padding: 14px;
background: #27ae60;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-submit:hover {
background: #229954;
}
.btn-submit:active {
transform: scale(0.98);
}
.btn-submit:disabled {
background: #bdc3c7;
cursor: not-allowed;
}
.success-message {
display: none;
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 6px;
margin-bottom: 15px;
border-left: 4px solid #28a745;
}
.error-message {
display: none;
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 6px;
margin-bottom: 15px;
border-left: 4px solid #f5c6cb;
}
.info-box {
background: #e3f2fd;
border-left: 4px solid #2196f3;
padding: 12px;
border-radius: 4px;
font-size: 13px;
margin-bottom: 15px;
color: #1565c0;
}
.required {
color: #e74c3c;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.footer {
text-align: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
font-size: 13px;
color: #666;
}
.aircraft-lookup-results {
margin-top: 5px;
padding: 5px;
background-color: #f8f9fa;
border-radius: 4px;
font-size: 13px;
min-height: 20px;
border: 1px solid #e9ecef;
}
.airport-lookup-results {
margin-top: 5px;
padding: 5px;
background-color: #f8f9fa;
border-radius: 4px;
font-size: 13px;
min-height: 20px;
border: 1px solid #e9ecef;
}
.lookup-match {
padding: 3px;
background-color: #e8f5e8;
border: 1px solid #c3e6c3;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-weight: bold;
}
.lookup-no-match {
color: #6c757d;
font-style: italic;
}
.lookup-searching {
color: #007bff;
}
.aircraft-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #dee2e6;
border-radius: 4px;
background-color: white;
}
.aircraft-option {
padding: 5px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.2s ease;
display: flex;
justify-content: space-between;
align-items: center;
}
.aircraft-option:hover {
background-color: #f8f9fa;
}
.aircraft-option:last-child {
border-bottom: none;
}
.aircraft-code {
font-family: 'Courier New', monospace;
font-weight: bold;
color: #495057;
}
.aircraft-details {
color: #6c757d;
font-size: 12px;
}
.recent-regs-section {
background: #f9f9f9;
border: 1px solid #e0e0e0;
border-radius: 6px;
padding: 10px;
margin-bottom: 15px;
}
.recent-regs-title {
font-weight: 600;
font-size: 12px;
color: #666;
margin-bottom: 6px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.recent-regs-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.recent-reg-btn {
background: white;
border: 1px solid #3498db;
color: #3498db;
padding: 6px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
}
.recent-reg-btn:hover {
background: #3498db;
color: white;
}
.recent-regs-section.empty {
display: none;
}
@media (max-width: 480px) {
.container {
padding: 15px;
}
.header {
margin: -15px -15px 15px -15px;
padding: 15px;
}
.tabs {
gap: 5px;
}
.tab-btn {
font-size: 12px;
padding: 10px;
min-width: 100px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Swansea - Book Out</h1>
</div>
<div id="successMessage" class="success-message"></div>
<div id="errorMessage" class="error-message"></div>
<div class="info-box">
Booking out really helps our volunteers in the tower, thank you!
</div>
<div class="tabs">
<button class="tab-btn active" onclick="switchTab(this, 'local')">Local</button>
<button class="tab-btn" onclick="switchTab(this, 'circuits')">Circuits</button>
<button class="tab-btn" onclick="switchTab(this, 'departure')">Departure</button>
</div>
<!-- Local Flight Form -->
<div id="local" class="tab-content active">
<div id="localRecentSection" class="recent-regs-section">
<div class="recent-regs-title">⏱️ Recently Used</div>
<div id="localRecent" class="recent-regs-container"></div>
</div>
<form id="localFlightForm" onsubmit="handleSubmit(event, 'local')">
<div class="form-group">
<label for="localReg">Aircraft Registration <span class="required">*</span></label>
<input type="text" id="localReg" name="registration" placeholder="e.g gbamc" required oninput="handleAircraftLookup(this.value, 'local')">
<div id="localReg-lookup-results" class="aircraft-lookup-results"></div>
</div>
<div class="form-row">
<div class="form-group">
<label for="localType">Aircraft Type</label>
<input type="text" id="localType" name="type" placeholder="e.g., Cessna 172">
</div>
<div class="form-group">
<label for="localCallsign">Callsign</label>
<input type="text" id="localCallsign" name="callsign" placeholder="Optional">
</div>
</div>
<div class="form-row-3col">
<div class="form-group">
<label for="localPOB">POB <span class="required">*</span></label>
<input type="number" id="localPOB" name="pob" value="1" min="1" required>
</div>
<div class="form-group">
<label for="localDuration">Duration</label>
<input type="number" id="localDuration" name="duration" value="45" min="5">
</div>
<div class="form-group">
<label for="localETD">ETD</label>
<input type="time" id="localETD" name="etd">
</div>
</div>
<div class="form-group">
<label for="localNotes">Notes</label>
<textarea id="localNotes" name="notes" placeholder="Any additional information..."></textarea>
</div>
<button type="submit" class="btn-submit">📋 Book Local Flight</button>
</form>
</div>
<!-- Circuit Form -->
<div id="circuits" class="tab-content">
<div id="circuitsRecentSection" class="recent-regs-section">
<div class="recent-regs-title">⏱️ Recently Used</div>
<div id="circuitsRecent" class="recent-regs-container"></div>
</div>
<form id="circuitForm" onsubmit="handleSubmit(event, 'circuits')">
<div class="form-group">
<label for="circuitReg">Aircraft Registration <span class="required">*</span></label>
<input type="text" id="circuitReg" name="registration" placeholder="e.g., G-BAMC" required oninput="handleAircraftLookup(this.value, 'circuits')">
<div id="circuitReg-lookup-results" class="aircraft-lookup-results"></div>
</div>
<div class="form-row">
<div class="form-group">
<label for="circuitType">Aircraft Type</label>
<input type="text" id="circuitType" name="type" placeholder="e.g., Cessna 172">
</div>
<div class="form-group">
<label for="circuitCallsign">Callsign</label>
<input type="text" id="circuitCallsign" name="callsign" placeholder="Optional">
</div>
</div>
<div class="form-row-3col">
<div class="form-group">
<label for="circuitPOB">Persons on Board <span class="required">*</span></label>
<input type="number" id="circuitPOB" name="pob" value="1" min="1" required>
</div>
<div class="form-group">
<label for="circuitDuration">Est. Duration (minutes)</label>
<input type="number" id="circuitDuration" name="duration" value="30" min="5">
</div>
<div class="form-group">
<label for="circuitETD">Est. Takeoff Time</label>
<input type="time" id="circuitETD" name="etd">
</div>
</div>
<div class="form-group">
<label for="circuitNotes">Notes</label>
<textarea id="circuitNotes" name="notes" placeholder="Any additional information..."></textarea>
</div>
<button type="submit" class="btn-submit">✈️ Book Circuits</button>
</form>
</div>
<!-- Departure Form -->
<div id="departure" class="tab-content">
<div id="departureRecentSection" class="recent-regs-section">
<div class="recent-regs-title">⏱️ Recently Used</div>
<div id="departureRecent" class="recent-regs-container"></div>
</div>
<form id="departureForm" onsubmit="handleSubmit(event, 'departure')">
<div class="form-group">
<label for="depReg">Aircraft Registration <span class="required">*</span></label>
<input type="text" id="depReg" name="registration" placeholder="e.g., N12345" required oninput="handleAircraftLookup(this.value, 'departure')">
<div id="depReg-lookup-results" class="aircraft-lookup-results"></div>
</div>
<div class="form-row">
<div class="form-group">
<label for="depType">Aircraft Type</label>
<input type="text" id="depType" name="type" placeholder="e.g., Cessna 172">
</div>
<div class="form-group">
<label for="depCallsign">Callsign</label>
<input type="text" id="depCallsign" name="callsign" placeholder="Optional">
</div>
</div>
<div class="form-row-3col">
<div class="form-group">
<label for="depPOB">Persons on Board <span class="required">*</span></label>
<input type="number" id="depPOB" name="pob" value="1" min="1" required>
</div>
<div class="form-group">
<label for="depTo">Destination (ICAO) <span class="required">*</span></label>
<input type="text" id="depTo" name="out_to" placeholder="e.g., KJFK" required oninput="handleAirportLookup(this.value, 'depTo')">
</div>
<div class="form-group">
<label for="depETD">Takeoff Time</label>
<input type="time" id="depETD" name="etd">
</div>
</div>
<div id="depTo-lookup-results" class="airport-lookup-results"></div>
<div class="form-group">
<label for="depNotes">Notes</label>
<textarea id="depNotes" name="notes" placeholder="Any additional information..."></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="depEmail">Pilot Email</label>
<input type="email" id="depEmail" name="pilot_email">
</div>
<div class="form-group">
<label for="depName">Pilot Name</label>
<input type="text" id="depName" name="pilot_name" placeholder="Optional">
</div>
</div>
<button type="submit" class="btn-submit">✈️ Book Departure</button>
</form>
</div>
<!-- Arrival Form -->
<div id="arrival" class="tab-content">
<div id="arrivalRecentSection" class="recent-regs-section">
<div class="recent-regs-title">⏱️ Recently Used</div>
<div id="arrivalRecent" class="recent-regs-container"></div>
</div>
<form id="arrivalForm" onsubmit="handleSubmit(event, 'arrival')">
<div class="form-group">
<label for="arrReg">Aircraft Registration <span class="required">*</span></label>
<input type="text" id="arrReg" name="registration" placeholder="e.g., N12345" required oninput="handleAircraftLookup(this.value, 'arrival')">
<div id="arrReg-lookup-results" class="aircraft-lookup-results"></div>
</div>
<div class="form-row">
<div class="form-group">
<label for="arrType">Aircraft Type</label>
<input type="text" id="arrType" name="type" placeholder="e.g., Cessna 172">
</div>
<div class="form-group">
<label for="arrCallsign">Callsign</label>
<input type="text" id="arrCallsign" name="callsign" placeholder="Optional">
</div>
</div>
<div class="form-row-3col">
<div class="form-group">
<label for="arrPOB">Persons on Board <span class="required">*</span></label>
<input type="number" id="arrPOB" name="pob" value="1" min="1" required>
</div>
<div class="form-group">
<label for="arrFrom">Origin (ICAO) <span class="required">*</span></label>
<input type="text" id="arrFrom" name="in_from" placeholder="e.g., KJFK" required oninput="handleAirportLookup(this.value, 'arrFrom')">
</div>
<div class="form-group">
<label for="arrETA">Arrival Time</label>
<input type="time" id="arrETA" name="eta">
</div>
</div>
<div id="arrFrom-lookup-results" class="airport-lookup-results"></div>
<div class="form-group">
<label for="arrNotes">Notes</label>
<textarea id="arrNotes" name="notes" placeholder="Any additional information..."></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="arrEmail">Pilot Email</label>
<input type="email" id="arrEmail" name="pilot_email">
</div>
<div class="form-group">
<label for="arrName">Pilot Name</label>
<input type="text" id="arrName" name="pilot_name" placeholder="Optional">
</div>
</div>
<button type="submit" class="btn-submit">✈️ Book Arrival</button>
</form>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p style="margin-top: 15px; color: #666;">Processing...</p>
</div>
<div class="footer">
<p>📞 Questions? Tower 01792 687042</p>
<p style="margin-top: 10px; font-size: 12px; color: #999;">Version 1.0</p>
</div>
</div>
<script>
const API_BASE = window.location.origin + '/api/v1';
const STORAGE_RECENT_REGS_KEY = 'bookingPage_recentRegs';
const STORAGE_AIRCRAFT_TYPES_KEY = 'bookingPage_aircraftTypes';
const MAX_RECENT = 5;
// ==================== localStorage Management ====================
function getRecentRegistrations() {
try {
const stored = localStorage.getItem(STORAGE_RECENT_REGS_KEY);
return stored ? JSON.parse(stored) : [];
} catch (e) {
console.error('Error getting recent registrations:', e);
return [];
}
}
function saveRecentRegistration(registration) {
try {
if (!registration || registration.trim() === '') {
console.log('⚠️ saveRecentRegistration: empty registration');
return;
}
const reg = registration.toUpperCase().trim();
let recentRegs = getRecentRegistrations();
console.log('📝 Saving recent reg:', reg);
// Remove if already exists
recentRegs = recentRegs.filter(r => r !== reg);
// Add to front
recentRegs.unshift(reg);
// Keep only last MAX_RECENT
recentRegs = recentRegs.slice(0, MAX_RECENT);
localStorage.setItem(STORAGE_RECENT_REGS_KEY, JSON.stringify(recentRegs));
console.log('✅ Recent regs updated:', recentRegs);
updateAllRecentRegsUI();
} catch (e) {
console.error('Error saving recent registration:', e);
}
}
function cacheAircraftType(registration, typeCode) {
try {
if (!registration || !typeCode) {
console.log('⚠️ cache skipped - registration:', registration, 'typeCode:', typeCode);
return;
}
const reg = registration.toUpperCase().trim();
const stored = localStorage.getItem(STORAGE_AIRCRAFT_TYPES_KEY);
let aircraftTypes = stored ? JSON.parse(stored) : {};
aircraftTypes[reg] = typeCode;
localStorage.setItem(STORAGE_AIRCRAFT_TYPES_KEY, JSON.stringify(aircraftTypes));
console.log('✅ Cached aircraft type:', reg, '=', typeCode);
console.log('All cached types:', aircraftTypes);
} catch (e) {
console.error('Error caching aircraft type:', e);
}
}
function getCachedAircraftType(registration) {
try {
const reg = registration.toUpperCase().trim();
const stored = localStorage.getItem(STORAGE_AIRCRAFT_TYPES_KEY);
if (!stored) {
console.log('⚠️ No cached types found for:', reg);
return null;
}
const aircraftTypes = JSON.parse(stored);
const typeCode = aircraftTypes[reg] || null;
console.log('🔍 Retrieved cached type for', reg, '=', typeCode);
return typeCode;
} catch (e) {
console.error('Error getting cached aircraft type:', e);
return null;
}
}
function applyRecentReg(registration, formType) {
const registerIdMap = {
'local': 'localReg',
'circuits': 'circuitReg',
'departure': 'depReg',
'arrival': 'arrReg'
};
const typeIdMap = {
'local': 'localType',
'circuits': 'circuitType',
'departure': 'depType',
'arrival': 'arrType'
};
const regId = registerIdMap[formType];
const typeId = typeIdMap[formType];
console.log('📍 applyRecentReg called - reg:', registration, 'form:', formType);
document.getElementById(regId).value = registration;
// Restore cached type if available (always overwrite)
const cachedType = getCachedAircraftType(registration);
if (cachedType) {
console.log('✔️ Applying cached type:', cachedType);
document.getElementById(typeId).value = cachedType;
} else {
console.log(' No cached type found');
}
document.getElementById(regId).focus();
// Trigger the lookup
handleAircraftLookup(registration, formType);
}
function updateRecentRegsUI(formType) {
const sectionIdMap = {
'local': 'localRecentSection',
'circuits': 'circuitsRecentSection',
'departure': 'departureRecentSection',
'arrival': 'arrivalRecentSection'
};
const containerIdMap = {
'local': 'localRecent',
'circuits': 'circuitsRecent',
'departure': 'departureRecent',
'arrival': 'arrivalRecent'
};
const recentRegs = getRecentRegistrations();
const section = document.getElementById(sectionIdMap[formType]);
const container = document.getElementById(containerIdMap[formType]);
if (recentRegs.length === 0) {
section.classList.add('empty');
return;
}
section.classList.remove('empty');
container.innerHTML = recentRegs.map(reg => `
<button type="button" class="recent-reg-btn" onclick="applyRecentReg('${reg}', '${formType}')" title="Use ${reg}">
${reg}
</button>
`).join('');
}
function updateAllRecentRegsUI() {
['local', 'circuits', 'departure', 'arrival'].forEach(formType => {
updateRecentRegsUI(formType);
});
}
// ==================== End localStorage Management ====================
function switchTab(button, tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab and activate button
document.getElementById(tabName).classList.add('active');
button.classList.add('active');
}
function showMessage(message, isError = false) {
const successEl = document.getElementById('successMessage');
const errorEl = document.getElementById('errorMessage');
if (isError) {
errorEl.textContent = message;
errorEl.style.display = 'block';
successEl.style.display = 'none';
} else {
successEl.textContent = message;
successEl.style.display = 'block';
errorEl.style.display = 'none';
}
// Auto-hide after 5 seconds
setTimeout(() => {
successEl.style.display = 'none';
errorEl.style.display = 'none';
}, 5000);
}
async function handleSubmit(event, formType) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// Save the registration to localStorage
if (data.registration) {
saveRecentRegistration(data.registration);
// Also cache the aircraft type if provided
if (data.type) {
console.log('💾 Caching type on submit - reg:', data.registration, 'type:', data.type);
cacheAircraftType(data.registration, data.type);
}
}
// Set flight_type based on form type
if (formType === 'local' && !data.flight_type) {
data.flight_type = 'LOCAL';
} else if (formType === 'circuits' && !data.flight_type) {
data.flight_type = 'CIRCUITS';
}
// Convert time inputs to ISO datetime (combine with today's date)
const today = new Date().toISOString().split('T')[0];
const timeFields = ['etd', 'circuit_timestamp', 'eta'];
timeFields.forEach(field => {
if (data[field]) {
// data[field] is in HH:MM format (time input)
const datetime = new Date(`${today}T${data[field]}:00`);
data[field] = datetime.toISOString();
}
});
let endpoint = '';
switch(formType) {
case 'local':
endpoint = '/public-book/local-flights';
break;
case 'circuits':
endpoint = '/public-book/local-flights';
break;
case 'departure':
endpoint = '/public-book/departures';
break;
case 'arrival':
endpoint = '/public-book/arrivals';
break;
}
document.getElementById('loading').style.display = 'block';
try {
const response = await fetch(`${API_BASE}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || `Error: ${response.status}`);
}
const result = await response.json();
showMessage(`✅ Success! Your booking has been submitted. Booking ID: ${result.id}`);
form.reset();
} catch (error) {
console.error('Error:', error);
showMessage(`❌ Error: ${error.message}`, true);
} finally {
document.getElementById('loading').style.display = 'none';
}
}
// Set current time as default for time inputs
function setDefaultTimes() {
const now = new Date();
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const timeValue = `${hours}:${minutes}`;
// ETD fields should default to 15 minutes from now
const futureTime = new Date(now.getTime() + 15 * 60000);
const futureHours = String(futureTime.getHours()).padStart(2, '0');
const futureMinutes = String(futureTime.getMinutes()).padStart(2, '0');
const futureTimeValue = `${futureHours}:${futureMinutes}`;
const etdFieldIds = ['localETD', 'circuitETD', 'depETD'];
document.querySelectorAll('input[type="time"]').forEach(input => {
if (!input.value) {
if (etdFieldIds.includes(input.id)) {
input.value = futureTimeValue;
} else {
input.value = timeValue;
}
}
});
}
// Aircraft lookup functionality
let aircraftLookupTimeouts = {};
function formatAircraftRegistration(input) {
const reg = input.trim().toUpperCase();
// If 5 alpha characters, add hyphen: GIVYY -> G-IVYY
if (/^[A-Z]{5}$/.test(reg)) {
return reg[0] + '-' + reg.slice(1);
}
// Otherwise just capitalize
return reg;
}
async function handleAircraftLookup(registration, formType) {
if (aircraftLookupTimeouts[formType]) {
clearTimeout(aircraftLookupTimeouts[formType]);
}
const registerIdMap = {
'local': 'localReg',
'circuits': 'circuitReg',
'departure': 'depReg',
'arrival': 'arrReg'
};
const typeIdMap = {
'local': 'localType',
'circuits': 'circuitType',
'departure': 'depType',
'arrival': 'arrType'
};
const regId = registerIdMap[formType];
const typeId = typeIdMap[formType];
const resultsDiv = document.getElementById(`${regId}-lookup-results`);
if (!registration || registration.length < 4) {
resultsDiv.innerHTML = '';
return;
}
console.log('🔎 Aircraft lookup:', registration, 'formType:', formType);
resultsDiv.innerHTML = '<span class="lookup-searching">Searching...</span>';
aircraftLookupTimeouts[formType] = setTimeout(async () => {
try {
const response = await fetch(`${API_BASE}/aircraft/public/lookup/${registration.toUpperCase()}`);
if (response.ok) {
const data = await response.json();
console.log('📡 API Response:', data, 'Length:', data ? data.length : 'null');
if (data && data.length > 0) {
if (data.length === 1) {
const aircraft = data[0];
const regValue = aircraft.registration || registration.toUpperCase();
console.log('✨ Single match found:', regValue);
console.log('📋 Full aircraft object:', aircraft);
console.log('🔤 aircraft.type_code:', aircraft.type_code);
document.getElementById(regId).value = regValue;
// Cache the aircraft type
if (aircraft.type_code) {
console.log('💾 About to cache - reg:', regValue, 'type:', aircraft.type_code);
cacheAircraftType(regValue, aircraft.type_code);
} else {
console.log('⚠️ type_code is empty or null, not caching');
}
resultsDiv.innerHTML = `
<div class="lookup-match">
${regValue} - ${aircraft.type_code || 'Unknown'} ${aircraft.model ? `(${aircraft.model})` : ''}
</div>
`;
if (!document.getElementById(typeId).value) {
document.getElementById(typeId).value = aircraft.type_code || '';
}
} else if (data.length <= 10) {
console.log('🎲 Multiple matches found:', data.length);
resultsDiv.innerHTML = `
<div class="aircraft-list">
${data.map(aircraft => `
<div class="aircraft-option" onclick="selectAircraft('${formType}', '${aircraft.registration || registration.toUpperCase()}', '${aircraft.type_code || ''}')">
<div class="aircraft-code">${aircraft.registration || registration.toUpperCase()}</div>
<div class="aircraft-details">${aircraft.type_code || 'Unknown'} ${aircraft.model ? `(${aircraft.model})` : ''}</div>
</div>
`).join('')}
</div>
`;
} else {
resultsDiv.innerHTML = '<span class="lookup-no-match">Too many matches, please be more specific</span>';
}
} else {
// No aircraft found - auto-format and apply the registration
const formattedReg = formatAircraftRegistration(registration);
document.getElementById(regId).value = formattedReg;
console.log('❌ No match - formatted:', registration, '→', formattedReg);
resultsDiv.innerHTML = `<span class="lookup-no-match">No match found - formatted as ${formattedReg}</span>`;
}
} else {
console.log('⚠️ Lookup response not ok:', response.status);
resultsDiv.innerHTML = '';
}
} catch (error) {
console.error('Aircraft lookup error:', error);
resultsDiv.innerHTML = '';
}
}, 500);
}
function selectAircraft(formType, registration, typeCode) {
const registerIdMap = {
'local': 'localReg',
'circuits': 'circuitReg',
'departure': 'depReg',
'arrival': 'arrReg'
};
const typeIdMap = {
'local': 'localType',
'circuits': 'circuitType',
'departure': 'depType',
'arrival': 'arrType'
};
const regId = registerIdMap[formType];
const typeId = typeIdMap[formType];
console.log('🎯 selectAircraft called:', registration, typeCode);
document.getElementById(regId).value = registration;
document.getElementById(typeId).value = typeCode;
// Cache the aircraft type
if (typeCode) {
cacheAircraftType(registration, typeCode);
}
document.getElementById(`${regId}-lookup-results`).innerHTML = '';
document.getElementById(regId).blur();
}
// Airport lookup functionality
let airportLookupTimeouts = {};
async function handleAirportLookup(query, fieldType) {
if (airportLookupTimeouts[fieldType]) {
clearTimeout(airportLookupTimeouts[fieldType]);
}
const fieldIdMap = {
'depTo': 'depTo',
'arrFrom': 'arrFrom'
};
const fieldId = fieldIdMap[fieldType];
const resultsDiv = document.getElementById(`${fieldId}-lookup-results`);
if (!query || query.length < 2) {
resultsDiv.innerHTML = '';
return;
}
resultsDiv.innerHTML = '<span class="lookup-searching">Searching...</span>';
airportLookupTimeouts[fieldType] = setTimeout(async () => {
try {
const response = await fetch(`${API_BASE}/airport/public/lookup/${query.toUpperCase()}`);
if (response.ok) {
const data = await response.json();
if (data && data.length > 0) {
if (data.length === 1) {
const airport = data[0];
resultsDiv.innerHTML = `
<div class="aircraft-list">
<div class="aircraft-option" onclick="selectAirport('${fieldId}', '${airport.icao}')">
<div class="aircraft-code">${airport.icao}/${airport.iata || ''}</div>
<div class="aircraft-details">${airport.name}, ${airport.country}</div>
</div>
</div>
`;
} else if (data.length <= 10) {
resultsDiv.innerHTML = `
<div class="aircraft-list">
${data.map(airport => `
<div class="aircraft-option" onclick="selectAirport('${fieldId}', '${airport.icao}')">
<div class="aircraft-code">${airport.icao}/${airport.iata || ''}</div>
<div class="aircraft-details">${airport.name}, ${airport.country}</div>
</div>
`).join('')}
</div>
`;
} else {
resultsDiv.innerHTML = '<span class="lookup-no-match">Too many matches, please be more specific</span>';
}
} else {
resultsDiv.innerHTML = '<span class="lookup-no-match">No airport found</span>';
}
} else {
resultsDiv.innerHTML = '';
}
} catch (error) {
console.error('Airport lookup error:', error);
resultsDiv.innerHTML = '';
}
}, 500);
}
function selectAirport(fieldId, icaoCode) {
document.getElementById(fieldId).value = icaoCode;
document.getElementById(`${fieldId}-lookup-results`).innerHTML = '';
document.getElementById(fieldId).blur();
}
// Clear lookup results on blur
document.addEventListener('DOMContentLoaded', function() {
console.log('🚀 Page loaded - initializing...');
['localReg', 'depReg', 'arrReg', 'depTo', 'arrFrom'].forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field) {
field.addEventListener('blur', function() {
setTimeout(() => {
document.getElementById(`${fieldId}-lookup-results`).innerHTML = '';
}, 150);
});
}
});
// Initialize recent registrations UI
updateAllRecentRegsUI();
console.log('✅ Recent regs UI initialized');
console.log('🗄️ Cached aircraft types:', localStorage.getItem(STORAGE_AIRCRAFT_TYPES_KEY));
// Set default times
setDefaultTimes();
});
// Initialize on page load
document.addEventListener('DOMContentLoaded', setDefaultTimes);
</script>
</body>
</html>