/**
* js/main.js
* Core application logic, event handling, and orchestrator.
*/
import api from './api.js';
import ui from './ui.js';
import {calculateRentalPrice, formatDate} from './utils.js';
document.addEventListener('DOMContentLoaded', async () => {
// Accessibility: make skip link focus the main content
const skipLink = document.querySelector('.skip-link');
const mainContent = document.getElementById('maincontent');
if (skipLink && mainContent) {
skipLink.addEventListener('click', (e) => {
e.preventDefault();
mainContent.setAttribute('tabindex', '-1');
mainContent.focus();
});
}
// --- Initial setup ---
// Set default dates for the search form (today to +3 days)
const today = new Date();
const threeDaysLater = new Date(today);
threeDaysLater.setDate(today.getDate() + 3);
const pickupDateInput = document.getElementById('pickup-date');
const returnDateInput = document.getElementById('return-date');
if (pickupDateInput && returnDateInput) {
pickupDateInput.value = today.toISOString().split('T')[0];
returnDateInput.value = threeDaysLater.toISOString().split('T')[0];
// Ensure return date is not before pickup date
pickupDateInput.addEventListener('change', () => {
if (returnDateInput.value < pickupDateInput.value) {
returnDateInput.value = pickupDateInput.value;
}
});
returnDateInput.addEventListener('change', () => {
if (returnDateInput.value < pickupDateInput.value) {
pickupDateInput.value = returnDateInput.value;
}
});
}
// Load initial list of cars on page load
await loadCars();
// Update active nav link based on URL hash on load
ui.updateActiveNavLink(window.location.hash || '#home');
// --- Event Listeners ---
// Mobile menu toggle
if (ui.mobileMenuButton) {
ui.mobileMenuButton.addEventListener('click', () => ui.toggleMobileMenu());
}
// Close mobile menu when a nav link is clicked
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', (e) => {
ui.closeMobileMenu();
// Update active nav link
ui.updateActiveNavLink(e.target.getAttribute('href'));
});
});
// Handle hash change for navigation (for simple SPA-like behavior)
window.addEventListener('hashchange', () => {
ui.updateActiveNavLink(window.location.hash || '#home');
// If you had different "pages" (sections) that you wanted to show/hide
// based on hash, you'd add that logic here.
// For now, it just scrolls to the section.
});
// Hero section search form submission
const heroSearchForm = document.getElementById('hero-search-form');
if (heroSearchForm) {
heroSearchForm.addEventListener('submit', async (e) => {
e.preventDefault(); // Prevent default form submission
const pickupLocation = document.getElementById('pickup-location').value;
const pickupDate = document.getElementById('pickup-date').value;
const returnDate = document.getElementById('return-date').value;
if (!pickupLocation || !pickupDate || !returnDate) {
ui.showNotification('กรุณากรอกข้อมูลการค้นหาให้ครบถ้วน', 'warning');
return;
}
ui.showNotification(`กำลังค้นหารถยนต์จาก ${pickupLocation} ระหว่างวันที่ ${formatDate(pickupDate)} ถึง ${formatDate(returnDate)}`, 'info');
// In a real scenario, these filters would be sent to the API
await loadCars({
pickupLocation,
pickupDate,
returnDate
});
// Scroll to the cars section after search
ui.carsSection.scrollIntoView({behavior: 'smooth'});
});
}
// Event delegation for "View Details" buttons in the car list
if (ui.carListContainer) {
ui.carListContainer.addEventListener('click', async (e) => {
if (e.target.classList.contains('btn-primary') && e.target.dataset.carId) {
const carId = e.target.dataset.carId;
await fetchCarDetails(carId);
}
});
}
// --- Core Functions ---
/**
* Fetches and renders the list of cars.
* @param {object} filters - Optional filters for the car list.
*/
async function loadCars(filters = {}) {
ui.showLoading(true);
const result = await api.get('/cars', filters); // Call the mockup API or real API
if (result.success) {
ui.renderCarList(result.data);
if (result.data.length > 0) {
ui.showNotification('โหลดรายการรถยนต์สำเร็จ!', 'success');
} else if (Object.keys(filters).length > 0) {
ui.showNotification('ไม่พบรถยนต์ที่ตรงกับเงื่อนไขการค้นหา', 'info');
}
} else {
ui.showNotification(`เกิดข้อผิดพลาดในการโหลดรถยนต์: ${result.message}`, 'error');
ui.renderCarList([]); // Render empty list or error state
}
ui.showLoading(false);
}
/**
* Fetches details for a specific car and displays them (e.g., in a modal/popup).
* For this simplified example, it just shows a notification.
* In a real application, you'd render a dedicated detail view.
* @param {string} carId - The ID of the car to fetch.
*/
async function fetchCarDetails(carId) {
ui.showLoading(true);
const result = await api.get(`/cars/${carId}`);
if (result.success && result.data) {
const car = result.data;
const rentalPrice = calculateRentalPrice(car.pricePerDay, pickupDateInput.value, returnDateInput.value);
// --- Simulate showing car details (e.g., in a modal/popup) ---
// For now, we'll just log and show a notification
console.log("รายละเอียดรถ:", car);
ui.showNotification(`รถรุ่น ${car.brand} ${car.model}: ${car.description} ราคาเช่าประมาณ ฿${rentalPrice ? rentalPrice.toLocaleString() : car.pricePerDay.toLocaleString()} (สำหรับ ${rentalPrice ? 'วันที่เลือก' : '1 วัน'})`, 'info');
// Here you would typically open a modal with more details, a booking button, etc.
// Example of how to add a modal dynamically (conceptual):
/*
const modalHtml = `
<div class="car-detail-modal-overlay">
<div class="car-detail-modal">
<span class="close-btn">×</span>
<img src="${car.imageUrl}" alt="${car.brand} ${car.model}">
<h3>${car.brand} ${car.model} (${car.year})</h3>
<p>${car.description}</p>
<p><strong>ประเภท:</strong> ${car.type}</p>
<p><strong>จำนวนที่นั่ง:</strong> ${car.seats}</p>
<p><strong>ราคาต่อวัน:</strong> ฿${car.pricePerDay.toLocaleString()}</p>
${rentalPrice ? `<p><strong>ราคารวม (ประมาณ):</strong> ฿${rentalPrice.toLocaleString()} (สำหรับ ${formatDate(pickupDateInput.value)} - ${formatDate(returnDateInput.value)})</p>` : ''}
<button class="btn btn-primary btn-book-now" data-car-id="${car.id}">จองเลย</button>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);
document.querySelector('.car-detail-modal-overlay').addEventListener('click', (e) => {
if (e.target.classList.contains('car-detail-modal-overlay') || e.target.classList.contains('close-btn')) {
document.querySelector('.car-detail-modal-overlay').remove();
}
});
document.querySelector('.btn-book-now').addEventListener('click', () => handleBooking(car.id, pickupDateInput.value, returnDateInput.value));
*/
} else {
ui.showNotification(`ไม่พบข้อมูลรถยนต์ ID: ${carId}`, 'error');
}
ui.showLoading(false);
}
/**
* Simulates a booking process.
* @param {string} carId
* @param {string} pickupDate
* @param {string} returnDate
*/
async function handleBooking(carId, pickupDate, returnDate) {
ui.showLoading(true);
const bookingData = {
carId,
pickupDate,
returnDate,
userId: 'guest_user_123', // In a real app, this would come from authenticated user
bookingDate: new Date().toISOString().split('T')[0]
};
const result = await api.post('/bookings', bookingData);
if (result.success) {
ui.showNotification(`จองรถสำเร็จ! Booking ID: ${result.data.bookingId}`, 'success');
console.log("Booking Confirmation:", result.data);
// Optionally, redirect to a confirmation page or show confirmation modal
} else {
ui.showNotification(`การจองล้มเหลว: ${result.message}`, 'error');
}
ui.showLoading(false);
}
// Initial scroll to top if hash is #home or empty
if (window.location.hash === '#home' || !window.location.hash) {
window.scrollTo(0, 0);
}
});