Feature enhancement
This commit is contained in:
317
web/lookups.js
Normal file
317
web/lookups.js
Normal file
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* Lookup Utilities - Reusable functions for aircraft and airport lookups
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a reusable lookup handler
|
||||
* @param {string} fieldId - ID of the input field
|
||||
* @param {string} resultsId - ID of the results container
|
||||
* @param {function} selectCallback - Function to call when item is selected
|
||||
* @param {object} options - Additional options (minLength, debounceMs, etc.)
|
||||
*/
|
||||
function createLookup(fieldId, resultsId, selectCallback, options = {}) {
|
||||
const defaults = {
|
||||
minLength: 2,
|
||||
debounceMs: 300,
|
||||
isAirport: false,
|
||||
isAircraft: false,
|
||||
maxResults: 10
|
||||
};
|
||||
const config = { ...defaults, ...options };
|
||||
let debounceTimeout;
|
||||
|
||||
const lookup = {
|
||||
// Main handler called by oninput
|
||||
handle: (value) => {
|
||||
clearTimeout(debounceTimeout);
|
||||
|
||||
if (!value || value.trim().length < config.minLength) {
|
||||
lookup.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
lookup.showSearching();
|
||||
debounceTimeout = setTimeout(() => {
|
||||
lookup.perform(value);
|
||||
}, config.debounceMs);
|
||||
},
|
||||
|
||||
// Perform the lookup
|
||||
perform: async (searchTerm) => {
|
||||
try {
|
||||
const cleanInput = searchTerm.trim();
|
||||
let endpoint;
|
||||
|
||||
if (config.isAircraft) {
|
||||
const cleaned = cleanInput.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
|
||||
if (cleaned.length < config.minLength) {
|
||||
lookup.clear();
|
||||
return;
|
||||
}
|
||||
endpoint = `/api/v1/aircraft/lookup/${cleaned}`;
|
||||
} else if (config.isAirport) {
|
||||
endpoint = `/api/v1/airport/lookup/${encodeURIComponent(cleanInput)}`;
|
||||
}
|
||||
|
||||
if (!endpoint) throw new Error('Invalid lookup type');
|
||||
|
||||
const response = await authenticatedFetch(endpoint);
|
||||
if (!response.ok) throw new Error('Lookup failed');
|
||||
|
||||
const results = await response.json();
|
||||
lookup.display(results, cleanInput);
|
||||
} catch (error) {
|
||||
console.error('Lookup error:', error);
|
||||
lookup.showError();
|
||||
}
|
||||
},
|
||||
|
||||
// Display results
|
||||
display: (results, searchTerm) => {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
|
||||
if (config.isAircraft) {
|
||||
// Aircraft lookup: auto-populate on single match, show message on multiple
|
||||
if (!results || results.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="aircraft-no-match">No matches found</div>';
|
||||
} else if (results.length === 1) {
|
||||
// Single match - auto-populate
|
||||
const aircraft = results[0];
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="aircraft-match">
|
||||
✓ ${aircraft.manufacturer_name || ''} ${aircraft.model || aircraft.type_code || ''}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Auto-populate the form fields
|
||||
const field = document.getElementById(fieldId);
|
||||
if (field) field.value = aircraft.registration;
|
||||
|
||||
// Also populate type field
|
||||
let typeFieldId;
|
||||
if (fieldId === 'ac_reg') {
|
||||
typeFieldId = 'ac_type';
|
||||
} else if (fieldId === 'local_registration') {
|
||||
typeFieldId = 'local_type';
|
||||
}
|
||||
|
||||
if (typeFieldId) {
|
||||
const typeField = document.getElementById(typeFieldId);
|
||||
if (typeField) typeField.value = aircraft.type_code || '';
|
||||
}
|
||||
} else {
|
||||
// Multiple matches
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="aircraft-no-match">
|
||||
Multiple matches found (${results.length}) - please be more specific
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
// Airport lookup: show list of options
|
||||
if (!results || results.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="lookup-no-match">No matches found - will use as entered</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
const itemsToShow = results.slice(0, config.maxResults);
|
||||
const matchText = itemsToShow.length === 1 ? 'Match found - click to select:' : 'Multiple matches found - select one:';
|
||||
|
||||
let html = `<div class="lookup-no-match" style="margin-bottom: 0.5rem;">${matchText}</div><div class="lookup-list">`;
|
||||
|
||||
itemsToShow.forEach(item => {
|
||||
html += `
|
||||
<div class="lookup-option" onclick="lookupManager.selectItem('${resultsId}', '${fieldId}', '${item.icao}')">
|
||||
<div class="lookup-code">${item.icao}</div>
|
||||
<div class="lookup-name">${item.name || '-'}</div>
|
||||
${item.city ? `<div class="lookup-location">${item.city}, ${item.country}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
resultsDiv.innerHTML = html;
|
||||
}
|
||||
},
|
||||
|
||||
// Show searching state
|
||||
showSearching: () => {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
if (resultsDiv) {
|
||||
resultsDiv.innerHTML = '<div class="lookup-searching">Searching...</div>';
|
||||
}
|
||||
},
|
||||
|
||||
// Show error state
|
||||
showError: () => {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
if (resultsDiv) {
|
||||
resultsDiv.innerHTML = '<div class="lookup-no-match">Lookup failed - will use as entered</div>';
|
||||
}
|
||||
},
|
||||
|
||||
// Clear results
|
||||
clear: () => {
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
if (resultsDiv) {
|
||||
resultsDiv.innerHTML = '';
|
||||
}
|
||||
},
|
||||
|
||||
// Set the selected value
|
||||
setValue: (value) => {
|
||||
const field = document.getElementById(fieldId);
|
||||
if (field) {
|
||||
field.value = value;
|
||||
}
|
||||
lookup.clear();
|
||||
if (selectCallback) selectCallback(value);
|
||||
}
|
||||
};
|
||||
|
||||
return lookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global lookup manager for all lookups on the page
|
||||
*/
|
||||
const lookupManager = {
|
||||
lookups: {},
|
||||
|
||||
// Register a lookup instance
|
||||
register: (name, lookup) => {
|
||||
lookupManager.lookups[name] = lookup;
|
||||
},
|
||||
|
||||
// Generic item selection handler
|
||||
selectItem: (resultsId, fieldId, itemCode) => {
|
||||
const field = document.getElementById(fieldId);
|
||||
if (field) {
|
||||
field.value = itemCode;
|
||||
}
|
||||
const resultsDiv = document.getElementById(resultsId);
|
||||
if (resultsDiv) {
|
||||
resultsDiv.innerHTML = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize all lookups when page loads
|
||||
function initializeLookups() {
|
||||
// Create reusable lookup instances
|
||||
const arrivalAirportLookup = createLookup(
|
||||
'in_from',
|
||||
'arrival-airport-lookup-results',
|
||||
null,
|
||||
{ isAirport: true, minLength: 2 }
|
||||
);
|
||||
lookupManager.register('arrival-airport', arrivalAirportLookup);
|
||||
|
||||
const departureAirportLookup = createLookup(
|
||||
'out_to',
|
||||
'departure-airport-lookup-results',
|
||||
null,
|
||||
{ isAirport: true, minLength: 2 }
|
||||
);
|
||||
lookupManager.register('departure-airport', departureAirportLookup);
|
||||
|
||||
const localOutToLookup = createLookup(
|
||||
'local_out_to',
|
||||
'local-out-to-lookup-results',
|
||||
null,
|
||||
{ isAirport: true, minLength: 2 }
|
||||
);
|
||||
lookupManager.register('local-out-to', localOutToLookup);
|
||||
|
||||
const aircraftLookup = createLookup(
|
||||
'ac_reg',
|
||||
'aircraft-lookup-results',
|
||||
null,
|
||||
{ isAircraft: true, minLength: 4, debounceMs: 300 }
|
||||
);
|
||||
lookupManager.register('aircraft', aircraftLookup);
|
||||
|
||||
const localAircraftLookup = createLookup(
|
||||
'local_registration',
|
||||
'local-aircraft-lookup-results',
|
||||
null,
|
||||
{ isAircraft: true, minLength: 4, debounceMs: 300 }
|
||||
);
|
||||
lookupManager.register('local-aircraft', localAircraftLookup);
|
||||
}
|
||||
|
||||
// Initialize on DOM ready or immediately if already loaded
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initializeLookups);
|
||||
} else {
|
||||
initializeLookups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience wrapper functions for backward compatibility
|
||||
*/
|
||||
function handleArrivalAirportLookup(value) {
|
||||
const lookup = lookupManager.lookups['arrival-airport'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function handleDepartureAirportLookup(value) {
|
||||
const lookup = lookupManager.lookups['departure-airport'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function handleLocalOutToAirportLookup(value) {
|
||||
const lookup = lookupManager.lookups['local-out-to'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function handleAircraftLookup(value) {
|
||||
const lookup = lookupManager.lookups['aircraft'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function handleLocalAircraftLookup(value) {
|
||||
const lookup = lookupManager.lookups['local-aircraft'];
|
||||
if (lookup) lookup.handle(value);
|
||||
}
|
||||
|
||||
function clearArrivalAirportLookup() {
|
||||
const lookup = lookupManager.lookups['arrival-airport'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function clearDepartureAirportLookup() {
|
||||
const lookup = lookupManager.lookups['departure-airport'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function clearLocalOutToAirportLookup() {
|
||||
const lookup = lookupManager.lookups['local-out-to'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function clearAircraftLookup() {
|
||||
const lookup = lookupManager.lookups['aircraft'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function clearLocalAircraftLookup() {
|
||||
const lookup = lookupManager.lookups['local-aircraft'];
|
||||
if (lookup) lookup.clear();
|
||||
}
|
||||
|
||||
function selectArrivalAirport(icaoCode) {
|
||||
lookupManager.selectItem('arrival-airport-lookup-results', 'in_from', icaoCode);
|
||||
}
|
||||
|
||||
function selectDepartureAirport(icaoCode) {
|
||||
lookupManager.selectItem('departure-airport-lookup-results', 'out_to', icaoCode);
|
||||
}
|
||||
|
||||
function selectLocalOutToAirport(icaoCode) {
|
||||
lookupManager.selectItem('local-out-to-lookup-results', 'local_out_to', icaoCode);
|
||||
}
|
||||
|
||||
function selectLocalAircraft(registration) {
|
||||
lookupManager.selectItem('local-aircraft-lookup-results', 'local_registration', registration);
|
||||
}
|
||||
Reference in New Issue
Block a user