const MAP_CONFIG = { totalSpots: 10, updateInterval: 3000 }; let spotsState = { spots: [], selectedSpot: null }; const SPOT_STATUS = { FREE: 'free', OCCUPIED: 'occupied', RESERVED: 'reserved' }; document.addEventListener('DOMContentLoaded', () => { console.log('🗺️ Initialisation de la carte...'); initParkingMap(); }); async function initParkingMap() { const loaded = await loadSpotsFromAPI(); if (!loaded) loadSpotsFromStorage(); renderMap(); updateStats(); updateReservationForm(); startAPIPolling(); } async function loadSpotsFromAPI() { try { const token = localStorage.getItem('smart_parking_token'); if (!token) return false; const response = await fetch('/api/spots', { headers: { 'Authorization': 'Bearer ' + token } }); if (!response.ok) return false; const data = await response.json(); if (!data.success || !data.data.length) return false; spotsState.spots = data.data.map(s => ({ id: s.id, number: s.number, status: s.status, lastUpdate: s.last_update, sensorId: s.sensor_id })); saveSpots(); return true; } catch (_err) { return false; } } function loadSpotsFromStorage() { const stored = localStorage.getItem('smart_parking_spots'); if (stored) { spotsState.spots = JSON.parse(stored); } else { createDefaultSpots(); } } function createDefaultSpots() { spotsState.spots = []; for (let i = 1; i <= MAP_CONFIG.totalSpots; i++) { spotsState.spots.push({ id: i, number: i, status: SPOT_STATUS.FREE, lastUpdate: new Date().toISOString(), sensorId: `SENSOR_${String(i).padStart(3, '0')}` }); } saveSpots(); } function saveSpots() { localStorage.setItem('smart_parking_spots', JSON.stringify(spotsState.spots)); } function startAPIPolling() { setInterval(async () => { const loaded = await loadSpotsFromAPI(); if (loaded) { renderMap(); updateStats(); const resSpot = document.getElementById('resSpot'); const hasSelection = resSpot && resSpot.value !== ''; if (!hasSelection) { updateReservationForm(); } if (spotsState.selectedSpot) { const updated = spotsState.spots.find(s => s.id === spotsState.selectedSpot.id); if (updated) { spotsState.selectedSpot = updated; showSpotDetails(updated); } } } }, MAP_CONFIG.updateInterval); } function renderMap() { const mapContainer = document.getElementById('parkingMap'); if (!mapContainer) return; mapContainer.innerHTML = spotsState.spots.map(spot => `
${spot.number} ${getStatusIcon(spot.status)}
`).join(''); } function handleSpotClick(spotId) { const spot = spotsState.spots.find(s => s.id === spotId); if (!spot) return; spotsState.selectedSpot = spot; showSpotDetails(spot); } function showSpotDetails(spot) { const container = document.getElementById('spotDetails'); if (!container) return; const reservation = spot.status === SPOT_STATUS.RESERVED ? findReservationForSpot(spot.id) : null; container.innerHTML = `
Numéro Place ${spot.number}
État ${getStatusLabel(spot.status)}
Capteur ${spot.sensorId || 'N/A'}
Dernière mise à jour ${formatDate(spot.lastUpdate)}
${reservation ? `
Réservé jusqu'à ${reservation.endTime}
` : ''} ${spot.status !== SPOT_STATUS.OCCUPIED ? ` ` : `

đźš— Une voiture est physiquement sur cette place

`}
`; } function findReservationForSpot(spotId) { const reservations = JSON.parse(localStorage.getItem('smart_parking_reservations') || '[]'); return reservations.find(r => r.spotId === spotId && r.status === 'active'); } function updateStats() { const free = spotsState.spots.filter(s => s.status === SPOT_STATUS.FREE).length; const occupied = spotsState.spots.filter(s => s.status === SPOT_STATUS.OCCUPIED).length; const reserved = spotsState.spots.filter(s => s.status === SPOT_STATUS.RESERVED).length; document.getElementById('freeCount').textContent = free; document.getElementById('occupiedCount').textContent = occupied; document.getElementById('reservedCount').textContent = reserved; document.getElementById('totalCount').textContent = spotsState.spots.length; } function updateReservationForm() { const select = document.getElementById('resSpot'); if (!select) return; const currentValue = select.value; const firstOption = select.options[0]; select.innerHTML = ''; select.appendChild(firstOption); const availableSpots = spotsState.spots.filter(s => s.status !== SPOT_STATUS.OCCUPIED); availableSpots.forEach(spot => { const option = document.createElement('option'); option.value = spot.id; option.textContent = spot.status === SPOT_STATUS.RESERVED ? `Place ${spot.number} (réservée en ce moment)` : `Place ${spot.number}`; select.appendChild(option); }); if (currentValue) { select.value = currentValue; } } function selectSpotForReservation(spotId) { const select = document.getElementById('resSpot'); if (select) select.value = spotId; } async function setSpotStatus(spotId, status) { const spot = spotsState.spots.find(s => s.id === spotId || s.number === spotId); if (!spot) return; spot.status = status; spot.lastUpdate = new Date().toISOString(); saveSpots(); renderMap(); updateStats(); updateReservationForm(); try { const token = localStorage.getItem('smart_parking_token'); if (token) { await fetch(`/api/spots/${spot.id}/status`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, body: JSON.stringify({ status }) }); } } catch (_err) { /* mode offline */ } } function setTotalSpots(count) { MAP_CONFIG.totalSpots = count; if (count > spotsState.spots.length) { for (let i = spotsState.spots.length + 1; i <= count; i++) { spotsState.spots.push({ id: i, number: i, status: SPOT_STATUS.FREE, lastUpdate: new Date().toISOString(), sensorId: `SENSOR_${String(i).padStart(3, '0')}` }); } } else { spotsState.spots = spotsState.spots.slice(0, count); } saveSpots(); renderMap(); updateStats(); updateReservationForm(); } function isAdmin() { const user = JSON.parse(localStorage.getItem('smart_parking_user') || 'null'); return user && user.role === 'admin'; } function getStatusLabel(status) { return { free: 'Libre', occupied: 'Occupée', reserved: 'Réservée' }[status] || 'Inconnu'; } function getStatusIcon(status) { return { free: '✓', occupied: '🚗', reserved: '📅' }[status] || '?'; } function formatDate(dateString) { if (!dateString) return 'N/A'; return new Date(dateString).toLocaleString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } window.ParkingMap = { refresh: async () => { const loaded = await loadSpotsFromAPI(); if (!loaded) loadSpotsFromStorage(); renderMap(); updateStats(); updateReservationForm(); }, setSpotStatus, setTotalSpots, getSpots: () => spotsState.spots, getFreeSpots: () => spotsState.spots.filter(s => s.status === SPOT_STATUS.FREE), getStats: () => ({ total: spotsState.spots.length, free: spotsState.spots.filter(s => s.status === SPOT_STATUS.FREE).length, occupied: spotsState.spots.filter(s => s.status === SPOT_STATUS.OCCUPIED).length, reserved: spotsState.spots.filter(s => s.status === SPOT_STATUS.RESERVED).length }) };