const PRICING = { 30: 2, 60: 3, 120: 5, 240: 8, 480: 15 }; const TIME_SLOTS = [ '06:00', '06:30', '07:00', '07:30', '08:00', '08:30', '09:00', '09:30', '10:00', '10:30', '11:00', '11:30', '12:00', '12:30', '13:00', '13:30', '14:00', '14:30', '15:00', '15:30', '16:00', '16:30', '17:00', '17:30', '18:00', '18:30', '19:00', '19:30', '20:00', '20:30', '21:00', '21:30', '22:00' ]; let currentReservation = null; document.addEventListener('DOMContentLoaded', () => { cleanupExpiredReservations(); initReservationForm(); initDatePicker(); initTimeSlots(); initPricePreview(); initConfirmationModal(); setInterval(cleanupExpiredReservations, 60000); }); function cleanupExpiredReservations() { let reservations = JSON.parse(localStorage.getItem('smart_parking_reservations') || '[]'); const now = new Date(); let cleaned = 0; reservations.forEach(r => { if (r.status !== 'active' && r.status !== 'pending') return; const endDateTime = new Date(r.date + 'T' + r.endTime); if (endDateTime < now) { r.status = 'completed'; cleaned++; } }); if (cleaned > 0) { localStorage.setItem('smart_parking_reservations', JSON.stringify(reservations)); } } function checkLocalConflict(spotId, date, startTime, endTime) { cleanupExpiredReservations(); const reservations = JSON.parse(localStorage.getItem('smart_parking_reservations') || '[]'); return reservations.some(r => { if (r.spotId !== spotId) return false; if (r.date !== date) return false; if (r.status !== 'active' && r.status !== 'pending') return false; return r.startTime < endTime && r.endTime > startTime; }); } function initReservationForm() { const form = document.getElementById('reservationForm'); if (!form) return; form.addEventListener('submit', handleReservationSubmit); } function initDatePicker() { const dateInput = document.getElementById('resDate'); if (!dateInput) return; const today = new Date().toISOString().split('T')[0]; dateInput.min = today; dateInput.value = today; } function initTimeSlots() { const select = document.getElementById('resStartTime'); if (!select) return; TIME_SLOTS.forEach(time => { const option = document.createElement('option'); option.value = time; option.textContent = time; select.appendChild(option); }); const now = new Date(); const currentHour = now.getHours(); const currentMins = now.getMinutes(); const nextSlot = TIME_SLOTS.find(t => { const [h, m] = t.split(':').map(Number); return h > currentHour || (h === currentHour && m > currentMins); }); if (nextSlot) select.value = nextSlot; } function initPricePreview() { const durationSelect = document.getElementById('resDuration'); if (!durationSelect) return; durationSelect.addEventListener('change', updatePricePreview); updatePricePreview(); } function updatePricePreview() { const duration = parseInt(document.getElementById('resDuration').value); const price = PRICING[duration] || 0; document.getElementById('previewPrice').textContent = price + '€'; } async function handleReservationSubmit(e) { e.preventDefault(); const user = JSON.parse(localStorage.getItem('smart_parking_user') || 'null'); if (!user) { Dashboard.showToast('Veuillez vous connecter', 'error'); return; } const spotId = parseInt(document.getElementById('resSpot').value); const date = document.getElementById('resDate').value; const startTime = document.getElementById('resStartTime').value; const duration = parseInt(document.getElementById('resDuration').value); const vehicle = document.getElementById('resVehicle').value; if (!spotId || !date || !startTime || !vehicle) { Dashboard.showToast('Veuillez remplir tous les champs', 'error'); return; } const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); const spot = spots.find(s => s.id === spotId); if (spot && spot.status === 'occupied') { Dashboard.showToast('Une voiture est deja sur cette place', 'error'); if (window.ParkingMap) window.ParkingMap.refresh(); return; } const endDate = new Date(date + 'T' + startTime); endDate.setMinutes(endDate.getMinutes() + duration); const endTime = endDate.toTimeString().slice(0, 5); if (checkLocalConflict(spotId, date, startTime, endTime)) { Dashboard.showToast( 'Cette place est deja reservee a cet horaire. Essayez un autre creneau ou une autre date.', 'error' ); return; } const token = localStorage.getItem('smart_parking_token'); let apiSuccess = false; if (token) { try { const response = await fetch('/api/reservations', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, body: JSON.stringify({ spotId, date, startTime, endTime, duration, vehicle: vehicle.toUpperCase(), price: PRICING[duration] }) }); const data = await response.json(); if (!data.success) { Dashboard.showToast(data.message, 'error'); return; } currentReservation = { id: data.data.id, userId: user.id, userName: user.name, spotId: spotId, spotNumber: spot ? spot.number : spotId, date, startTime, endTime, duration, vehicle: vehicle.toUpperCase(), price: PRICING[duration], status: 'active', createdAt: new Date().toISOString() }; apiSuccess = true; } catch (_err) { apiSuccess = false; } } if (!apiSuccess) { currentReservation = { id: Date.now(), userId: user.id, userName: user.name, spotId: spotId, spotNumber: spot ? spot.number : spotId, date, startTime, endTime, duration, vehicle: vehicle.toUpperCase(), price: PRICING[duration], status: 'active', createdAt: new Date().toISOString() }; } let reservations = JSON.parse(localStorage.getItem('smart_parking_reservations') || '[]'); reservations.push(currentReservation); localStorage.setItem('smart_parking_reservations', JSON.stringify(reservations)); const today = new Date().toISOString().split('T')[0]; const now = new Date(); const resStart = new Date(date + 'T' + startTime); const diffMin = (resStart - now) / 60000; if (date === today && diffMin <= 30 && window.ParkingMap) { window.ParkingMap.setSpotStatus(spotId, 'reserved'); } addToHistory( 'Reservation', 'Place ' + currentReservation.spotNumber + ' reservee le ' + date + ' de ' + startTime + ' a ' + endTime + ' — ' + PRICING[duration] + '€' ); document.getElementById('reservationForm').reset(); initDatePicker(); updatePricePreview(); showConfirmationModal(); } function initConfirmationModal() { document.getElementById('closeConfirmationModal')?.addEventListener('click', hideConfirmationModal); document.getElementById('closeConfirmationBtn')?.addEventListener('click', hideConfirmationModal); } function showConfirmationModal() { if (!currentReservation) return; const modal = document.getElementById('confirmationModal'); document.getElementById('paySpot').textContent = 'Place ' + currentReservation.spotNumber; document.getElementById('payDate').textContent = formatDate(currentReservation.date); document.getElementById('payTime').textContent = currentReservation.startTime + ' - ' + currentReservation.endTime; document.getElementById('payDuration').textContent = formatDuration(currentReservation.duration); document.getElementById('payTotal').textContent = currentReservation.price + '€'; modal.classList.remove('hidden'); } function hideConfirmationModal() { document.getElementById('confirmationModal').classList.add('hidden'); if (window.Dashboard) { Dashboard.navigateToPage('my-reservations'); document.querySelector('[data-page="my-reservations"]')?.classList.add('active'); document.querySelector('[data-page="reservation"]')?.classList.remove('active'); } if (window.Dashboard) Dashboard.refreshStats(); } function addToHistory(action, details) { let history = JSON.parse(localStorage.getItem('smart_parking_history') || '[]'); history.unshift({ id: Date.now(), action, details, timestamp: new Date().toISOString() }); if (history.length > 100) history = history.slice(0, 100); localStorage.setItem('smart_parking_history', JSON.stringify(history)); } function formatDate(dateString) { return new Date(dateString).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric' }); } function formatDuration(minutes) { if (minutes >= 480) return 'Journee (8h)'; if (minutes >= 60) { const h = Math.floor(minutes / 60); const m = minutes % 60; return m > 0 ? h + 'h ' + m + 'min' : h + 'h'; } return minutes + ' min'; } window.Reservation = { PRICING, TIME_SLOTS, formatDuration, addToHistory };