Booking form fixes

This commit is contained in:
2026-03-25 17:25:26 -04:00
parent 9867156334
commit 2dce14507b
3 changed files with 237 additions and 86 deletions
+2 -2
View File
@@ -76,11 +76,11 @@
}
.aircraft-item.departure {
background-color: #ffffcc; /* light yellow */
background-color: #ccccff; /* light blue */
}
.aircraft-item.inbound {
background-color: #ccccff; /* light blue */
background-color: #ffffcc; /* light yellow */
}
.aircraft-item.overflight {
+224 -84
View File
@@ -102,11 +102,8 @@
font-family: inherit;
}
input[type="time"],
input[type="number"] {
padding: 12px 8px;
text-align: center;
min-width: 80px;
}
input:focus, select:focus, textarea:focus {
@@ -198,6 +195,65 @@
padding: 20px;
}
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-overlay.active {
display: flex;
}
.modal {
background: white;
border-radius: 8px;
padding: 30px;
max-width: 400px;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.modal-icon {
font-size: 48px;
margin-bottom: 20px;
}
.modal h2 {
color: #27ae60;
margin-bottom: 15px;
font-size: 20px;
}
.modal p {
color: #333;
margin-bottom: 20px;
line-height: 1.5;
}
.modal-button {
background: #27ae60;
color: white;
border: none;
padding: 12px 30px;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.3s ease;
}
.modal-button:hover {
background: #229954;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
@@ -372,6 +428,15 @@
<div id="successMessage" class="success-message"></div>
<div id="errorMessage" class="error-message"></div>
<div id="successModal" class="modal-overlay">
<div class="modal">
<div class="modal-icon"></div>
<h2>Booking Submitted!</h2>
<p id="modalMessage"></p>
<button class="modal-button" onclick="closeSuccessModal()">Done</button>
</div>
</div>
<div class="info-box">
Booking out really helps our volunteers in the tower, thank you!
</div>
@@ -390,16 +455,22 @@
</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 class="form-row">
<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-group">
<label for="localType">Aircraft Type</label>
<input type="text" id="localType" name="type" placeholder="e.g., Cessna 172">
</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">
<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="localCallsign">Callsign</label>
@@ -407,18 +478,14 @@
</div>
</div>
<div class="form-row-3col">
<div class="form-row">
<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>
<label for="localETD">ETD</label>
<input type="text" id="localETD" name="etd" placeholder="HH:MM" pattern="([0-9]{2}:[0-9]{2}|[0-9]{4})" inputmode="decimal" oninput="formatTimeInput(this)">
</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">
<input type="number" id="localDuration" name="duration" value="45" min="5" inputmode="numeric">
</div>
</div>
@@ -439,16 +506,22 @@
</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 class="form-row">
<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-group">
<label for="circuitType">Aircraft Type</label>
<input type="text" id="circuitType" name="type" placeholder="e.g., Cessna 172">
</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">
<label for="circuitPOB">POB <span class="required">*</span></label>
<input type="number" id="circuitPOB" name="pob" value="1" min="1" required>
</div>
<div class="form-group">
<label for="circuitCallsign">Callsign</label>
@@ -456,18 +529,14 @@
</div>
</div>
<div class="form-row-3col">
<div class="form-row">
<div class="form-group">
<label for="circuitPOB">POB <span class="required">*</span></label>
<input type="number" id="circuitPOB" name="pob" value="1" min="1" required>
<label for="circuitETD">ETD</label>
<input type="text" id="circuitETD" name="etd" placeholder="HH:MM" pattern="([0-9]{2}:[0-9]{2}|[0-9]{4})" inputmode="decimal" oninput="formatTimeInput(this)">
</div>
<div class="form-group">
<label for="circuitDuration">Duration</label>
<input type="number" id="circuitDuration" name="duration" value="30" min="5">
</div>
<div class="form-group">
<label for="circuitETD">ETD</label>
<input type="time" id="circuitETD" name="etd">
<input type="number" id="circuitDuration" name="duration" value="30" min="5" inputmode="numeric">
</div>
</div>
@@ -488,16 +557,22 @@
</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 class="form-row">
<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-group">
<label for="depType">Aircraft Type</label>
<input type="text" id="depType" name="type" placeholder="e.g., Cessna 172">
</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">
<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="depCallsign">Callsign</label>
@@ -505,18 +580,14 @@
</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-row">
<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">
<input type="text" id="depETD" name="etd" placeholder="HH:MM" pattern="([0-9]{2}:[0-9]{2}|[0-9]{4})" inputmode="decimal" oninput="formatTimeInput(this)">
</div>
</div>
<div id="depTo-lookup-results" class="airport-lookup-results"></div>
@@ -540,16 +611,22 @@
</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 class="form-row">
<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-group">
<label for="arrType">Aircraft Type</label>
<input type="text" id="arrType" name="type" placeholder="e.g., Cessna 172">
</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">
<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="arrCallsign">Callsign</label>
@@ -557,18 +634,14 @@
</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-row">
<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">
<input type="text" id="arrETA" name="eta" placeholder="HH:MM" pattern="([0-9]{2}:[0-9]{2}|[0-9]{4})" inputmode="decimal" oninput="formatTimeInput(this)">
</div>
</div>
<div id="arrFrom-lookup-results" class="airport-lookup-results"></div>
@@ -608,6 +681,7 @@
const API_BASE = window.location.origin + '/api/v1';
const STORAGE_RECENT_REGS_KEY = 'bookingPage_recentRegs';
const STORAGE_AIRCRAFT_TYPES_KEY = 'bookingPage_aircraftTypes';
const STORAGE_CALLSIGNS_KEY = 'bookingPage_callsigns';
const MAX_RECENT = 5;
// ==================== localStorage Management ====================
@@ -690,6 +764,43 @@
}
}
function cacheCallsign(registration, callsign) {
try {
if (!registration || !callsign || callsign.trim() === '') {
return;
}
const reg = registration.toUpperCase().trim();
const cs = callsign.toUpperCase().trim();
const stored = localStorage.getItem(STORAGE_CALLSIGNS_KEY);
let callsigns = stored ? JSON.parse(stored) : {};
callsigns[reg] = cs;
localStorage.setItem(STORAGE_CALLSIGNS_KEY, JSON.stringify(callsigns));
console.log('✅ Cached callsign:', reg, '=', cs);
} catch (e) {
console.error('Error caching callsign:', e);
}
}
function getCachedCallsign(registration) {
try {
const reg = registration.toUpperCase().trim();
const stored = localStorage.getItem(STORAGE_CALLSIGNS_KEY);
if (!stored) {
return null;
}
const callsigns = JSON.parse(stored);
const callsign = callsigns[reg] || null;
console.log('🔍 Retrieved cached callsign for', reg, '=', callsign);
return callsign;
} catch (e) {
console.error('Error retrieving cached callsign:', e);
return null;
}
}
function applyRecentReg(registration, formType) {
const registerIdMap = {
'local': 'localReg',
@@ -705,8 +816,16 @@
'arrival': 'arrType'
};
const callsignIdMap = {
'local': 'localCallsign',
'circuits': 'circuitCallsign',
'departure': 'depCallsign',
'arrival': 'arrCallsign'
};
const regId = registerIdMap[formType];
const typeId = typeIdMap[formType];
const callsignId = callsignIdMap[formType];
console.log('📍 applyRecentReg called - reg:', registration, 'form:', formType);
document.getElementById(regId).value = registration;
@@ -720,6 +839,13 @@
console.log('️ No cached type found');
}
// Restore cached callsign if available
const cachedCallsign = getCachedCallsign(registration);
if (cachedCallsign) {
console.log('✔️ Applying cached callsign:', cachedCallsign);
document.getElementById(callsignId).value = cachedCallsign;
}
document.getElementById(regId).focus();
// Trigger the lookup
handleAircraftLookup(registration, formType);
@@ -780,24 +906,24 @@
}
function showMessage(message, isError = false) {
const successEl = document.getElementById('successMessage');
const errorEl = document.getElementById('errorMessage');
if (isError) {
const errorEl = document.getElementById('errorMessage');
errorEl.textContent = message;
errorEl.style.display = 'block';
successEl.style.display = 'none';
// Auto-hide error after 5 seconds
setTimeout(() => {
errorEl.style.display = 'none';
}, 5000);
} else {
successEl.textContent = message;
successEl.style.display = 'block';
errorEl.style.display = 'none';
// Show success modal instead
const modalMessage = document.getElementById('modalMessage');
modalMessage.textContent = message;
document.getElementById('successModal').classList.add('active');
}
}
// Auto-hide after 5 seconds
setTimeout(() => {
successEl.style.display = 'none';
errorEl.style.display = 'none';
}, 5000);
function closeSuccessModal() {
document.getElementById('successModal').classList.remove('active');
}
async function handleSubmit(event, formType) {
@@ -816,6 +942,11 @@
console.log('💾 Caching type on submit - reg:', data.registration, 'type:', data.type);
cacheAircraftType(data.registration, data.type);
}
// Cache the callsign if provided
if (data.callsign) {
cacheCallsign(data.registration, data.callsign);
}
}
// Set flight_type based on form type
@@ -830,8 +961,12 @@
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`);
let timeValue = data[field];
// Normalize 4-digit format (HHMM) to HH:MM
if (/^[0-9]{4}$/.test(timeValue)) {
timeValue = timeValue.slice(0, 2) + ':' + timeValue.slice(2);
}
const datetime = new Date(`${today}T${timeValue}:00`);
data[field] = datetime.toISOString();
}
});
@@ -869,7 +1004,7 @@
}
const result = await response.json();
showMessage(`✅ Success! Your booking has been submitted. Booking ID: ${result.id}`);
showMessage(`Thanks - Your booking has been submitted. Have a great flight!`);
form.reset();
} catch (error) {
console.error('Error:', error);
@@ -880,27 +1015,32 @@
}
// Set current time as default for time inputs
function formatTimeInput(input) {
let value = input.value.replace(/[^0-9]/g, '');
// Auto-format: if user types 1430, it becomes 14:30
if (value.length === 4) {
input.value = value.slice(0, 2) + ':' + value.slice(2);
} else if (value.length <= 2) {
input.value = value;
} else if (value.length === 3) {
input.value = value.slice(0, 2) + ':' + value.slice(2);
}
}
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 futureTime = new Date(now.getTime() + 10 * 60000); // 10 minutes from now
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'];
const etdFieldIds = ['localETD', 'circuitETD', 'depETD', 'arrETA'];
document.querySelectorAll('input[type="time"]').forEach(input => {
if (!input.value) {
if (etdFieldIds.includes(input.id)) {
input.value = futureTimeValue;
} else {
input.value = timeValue;
}
etdFieldIds.forEach(id => {
const input = document.getElementById(id);
if (input && !input.value) {
input.value = futureTimeValue;
}
});
}