// Main Application Script // Handles UI interactions and form submission document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); function initializeApp() { // Populate city datalist populateCityList(); // Set up form event listeners setupFormListeners(); // Set minimum date to today const today = new Date().toISOString().split('T')[0]; document.getElementById('departureDate').setAttribute('min', today); document.getElementById('returnDate').setAttribute('min', today); console.log('Government Travel Cost Estimator initialized'); console.log(`Loaded ${CITIES.length} cities`); } function populateCityList() { const datalist = document.getElementById('cityList'); CITIES.forEach(city => { const option = document.createElement('option'); option.value = city.name; option.textContent = `${city.name}, ${city.province}`; datalist.appendChild(option); }); } function setupFormListeners() { const form = document.getElementById('travelForm'); const privateAccomCheckbox = document.getElementById('privateAccommodation'); const departureCityInput = document.getElementById('departureCity'); const destinationCityInput = document.getElementById('destinationCity'); const departureDateInput = document.getElementById('departureDate'); const returnDateInput = document.getElementById('returnDate'); // Form submission form.addEventListener('submit', handleFormSubmit); // Private accommodation toggle privateAccomCheckbox.addEventListener('change', function() { const details = document.getElementById('privateAccommodationDetails'); details.style.display = this.checked ? 'block' : 'none'; }); // City validation departureCityInput.addEventListener('blur', function() { validateCityInput(this, 'departureCityError'); }); destinationCityInput.addEventListener('blur', function() { validateCityInput(this, 'destinationCityError'); }); // Date validation departureDateInput.addEventListener('change', function() { if (returnDateInput.value) { validateDateRange(); } }); returnDateInput.addEventListener('change', function() { if (departureDateInput.value) { validateDateRange(); } }); } function validateCityInput(input, errorId) { const errorSpan = document.getElementById(errorId); const cityName = input.value.trim(); if (!cityName) { errorSpan.textContent = ''; return true; } if (!validateCity(cityName)) { errorSpan.textContent = `"${cityName}" is not in our database. Please select a valid Canadian city.`; input.setCustomValidity('Invalid city'); return false; } errorSpan.textContent = ''; input.setCustomValidity(''); return true; } function validateDateRange() { const departureDateInput = document.getElementById('departureDate'); const returnDateInput = document.getElementById('returnDate'); const validation = validateDates(departureDateInput.value, returnDateInput.value); if (!validation.valid) { returnDateInput.setCustomValidity(validation.error); alert(validation.error); return false; } returnDateInput.setCustomValidity(''); return true; } async function handleFormSubmit(event) { event.preventDefault(); // Clear previous results and errors document.getElementById('results').style.display = 'none'; document.getElementById('error').style.display = 'none'; // Get form values const departureCity = document.getElementById('departureCity').value.trim(); const destinationCity = document.getElementById('destinationCity').value.trim(); const departureDate = document.getElementById('departureDate').value; const returnDate = document.getElementById('returnDate').value; const currency = document.getElementById('currency').value; const privateAccommodation = document.getElementById('privateAccommodation').checked; const privateAccommodationRate = parseFloat(document.getElementById('privateAccommodationRate').value) || null; const searchFlightsOption = document.getElementById('searchFlights').checked; // Validate cities if (!validateCityInput(document.getElementById('departureCity'), 'departureCityError') || !validateCityInput(document.getElementById('destinationCity'), 'destinationCityError')) { return; } // Validate dates if (!validateDateRange()) { return; } try { // Show loading state showLoading(); // Calculate costs const results = await calculateTravelCosts({ departureCity, destinationCity, departureDate, returnDate, currency, privateAccommodation, privateAccommodationRate, searchFlights: searchFlightsOption }); // Display results displayResults(results); // Store results for export window.lastCalculation = results; } catch (error) { console.error('Calculation error:', error); showError(error.message); } finally { hideLoading(); } } function showLoading() { const submitBtn = document.querySelector('button[type="submit"]'); submitBtn.disabled = true; submitBtn.textContent = 'Calculating...'; } function hideLoading() { const submitBtn = document.querySelector('button[type="submit"]'); submitBtn.disabled = false; submitBtn.textContent = 'Calculate Travel Costs'; } function displayResults(results) { const resultsDiv = document.getElementById('results'); const { tripDetails, costs, totals } = results; // Accommodation section const accommodationDetails = document.getElementById('accommodationDetails'); accommodationDetails.innerHTML = `
Type: ${costs.accommodation.type} Accommodation
Nightly Rate: ${formatCurrency(costs.accommodation.nightlyRate, 'CAD')}
Number of Nights: ${costs.accommodation.nights}
Total Accommodation: ${formatCurrency(costs.accommodation.total, 'CAD')}
`; // Per Diem section const perDiemDetails = document.getElementById('perDiemDetails'); perDiemDetails.innerHTML = `
Daily Per Diem Rate: ${formatCurrency(costs.perDiem.dailyRate, 'CAD')}
Number of Days: ${tripDetails.duration.days}
Daily Breakdown:
Breakfast: ${formatCurrency(costs.perDiem.breakdown.breakfast, 'CAD')}
Lunch: ${formatCurrency(costs.perDiem.breakdown.lunch, 'CAD')}
Dinner: ${formatCurrency(costs.perDiem.breakdown.dinner, 'CAD')}
Incidentals: ${formatCurrency(costs.perDiem.breakdown.incidentals, 'CAD')}
Total Per Diem: ${formatCurrency(costs.perDiem.total, 'CAD')}
`; // Flight information if (costs.flights) { const flightInfo = document.getElementById('flightInfo'); const flightDetails = document.getElementById('flightDetails'); flightInfo.style.display = 'block'; const flight = costs.flights.details; flightDetails.innerHTML = ` ${flight.businessClassEligible ? '
✓ Business Class Eligible
' + flight.message + '
' : '
' + flight.message + '
'}

Outbound Flight

Route: ${flight.outbound.departure.city} (${flight.outbound.departure.airport}) → ${flight.outbound.arrival.city} (${flight.outbound.arrival.airport})
Departure: ${flight.outbound.departure.date} at ${flight.outbound.departure.time}
Duration: ${flight.outbound.durationFormatted} ${flight.outbound.stops > 0 ? `(${flight.outbound.stops} stop)` : '(direct)'}

Return Flight

Route: ${flight.return.departure.city} (${flight.return.departure.airport}) → ${flight.return.arrival.city} (${flight.return.arrival.airport})
Departure: ${flight.return.departure.date} at ${flight.return.departure.time}
Duration: ${flight.return.durationFormatted} ${flight.return.stops > 0 ? `(${flight.return.stops} stop)` : '(direct)'}

Pricing

Economy Class: ${formatCurrency(flight.pricing.economy, 'CAD')}
Business Class: ${formatCurrency(flight.pricing.business, 'CAD')}
Selected (${flight.businessClassEligible ? 'Business' : 'Economy'}): ${formatCurrency(costs.flights.total, 'CAD')}

${flight.note}

`; } else { document.getElementById('flightInfo').style.display = 'none'; } // Currency conversion info if (totals.conversion) { const currencyInfo = document.getElementById('currencyInfo'); const currencyDetails = document.getElementById('currencyDetails'); currencyInfo.style.display = 'block'; currencyDetails.innerHTML = `
Exchange Rate: ${totals.conversion.formatted.rate}
Original (CAD): ${totals.conversion.formatted.original}
Converted (${totals.selectedCurrency}): ${totals.conversion.formatted.converted}
`; } else { document.getElementById('currencyInfo').style.display = 'none'; } // Total cost const totalCostDiv = document.getElementById('totalCost'); totalCostDiv.innerHTML = `
${formatCurrency(totals.selectedCurrencyTotal, totals.selectedCurrency)}
${totals.selectedCurrency !== 'CAD' ? `
(${formatCurrency(totals.cadTotal, 'CAD')} CAD)
` : ''} `; // Show results resultsDiv.style.display = 'block'; resultsDiv.scrollIntoView({ behavior: 'smooth' }); } function showError(message) { const errorDiv = document.getElementById('error'); errorDiv.textContent = `Error: ${message}`; errorDiv.style.display = 'block'; errorDiv.scrollIntoView({ behavior: 'smooth' }); } // Export results as JSON function exportToJSON() { if (!window.lastCalculation) { alert('No calculation results to export'); return; } const dataStr = JSON.stringify(window.lastCalculation, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `travel-estimate-${new Date().toISOString().split('T')[0]}.json`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } // Make exportToJSON available globally window.exportToJSON = exportToJSON;