main.js

9.00 KB
12/09/2025 08:18
JS
main.js
/**
 * 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">&times;</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);
  }
});