diff --git a/.env b/.env index 5472096..627e939 100644 --- a/.env +++ b/.env @@ -1,15 +1,25 @@ -# Configuration de la base de données +# ============================================================ +# Smart Parking v2.0 — Variables d'environnement +# Copiez ce fichier en .env à la racine du projet +# ============================================================ + +# ── Base de données ────────────────────────────────────────── DB_HOST=db DB_PORT=3306 DB_USER=smartparking_user DB_PASSWORD=smartparking_pass DB_NAME=smartparking -# Configuration JWT -JWT_SECRET=une_chaine_tres_longue_et_secrete_à_changer +# ── JWT ────────────────────────────────────────────────────── +# ⚠️ Changez cette valeur ! Mettez une chaîne longue et aléatoire. +JWT_SECRET=une_chaine_tres_longue_et_secrete_changez_moi_absolument -# Port du serveur +# ── Serveur ────────────────────────────────────────────────── PORT=3000 +NODE_ENV=production -# Mode environnement -NODE_ENV=production \ No newline at end of file +# ── MQTT (Mosquitto) ───────────────────────────────────────── +# Si Mosquitto tourne dans Docker (service "mqtt") → mqtt +# Si Mosquitto est installé directement sur le Pi → localhost +MQTT_HOST=mqtt +MQTT_PORT=1883 \ No newline at end of file diff --git a/js/admin.js b/js/admin.js index 30744ac..8e1f93d 100644 --- a/js/admin.js +++ b/js/admin.js @@ -1,175 +1,141 @@ /** * ============================================ * ADMIN.JS - Panel d'administration - * Smart Parking - BTS CIEL IR + * Smart Parking v3.0 + * CORRIGÉ : + * - Suppression du graphique d'occupation + * - Historique affiche la date complète + * (jour + mois + année + heure + minute) * ============================================ */ -// Initialisation document.addEventListener('DOMContentLoaded', () => { console.log('⚙️ Initialisation du panel admin...'); - - // Vérifier si l'utilisateur est admin if (!isAdmin()) return; - initAdminPanel(); }); -/** - * Vérifie si l'utilisateur est admin - */ function isAdmin() { const user = JSON.parse(localStorage.getItem('smart_parking_user') || 'null'); return user && user.role === 'admin'; } -/** - * Initialise le panel admin - */ function initAdminPanel() { loadAdminStats(); initPlacesControl(); loadUsersTable(); loadReservationsTable(); - initOccupancyChart(); loadHistoryLog(); - - // Rafraîchissement périodique + + // Rafraîchissement périodique toutes les 10 secondes setInterval(() => { loadAdminStats(); loadReservationsTable(); + loadHistoryLog(); }, 10000); } -/** - * Charge les statistiques admin - */ +// ============================================ +// STATISTIQUES ADMIN +// ============================================ + function loadAdminStats() { - const users = JSON.parse(localStorage.getItem('smart_parking_users') || '[]'); + const users = JSON.parse(localStorage.getItem('smart_parking_users') || '[]'); const reservations = JSON.parse(localStorage.getItem('smart_parking_reservations') || '[]'); - const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); - - // Calculer les stats - const totalUsers = users.length + 1; // +1 pour l'admin par défaut + const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); + + const totalUsers = users.length + 1; const totalReservations = reservations.length; - const totalRevenue = reservations + const totalRevenue = reservations .filter(r => r.status === 'active' || r.status === 'completed') .reduce((sum, r) => sum + (r.price || 0), 0); - - const occupied = spots.filter(s => s.status === 'occupied').length; - const reserved = spots.filter(s => s.status === 'reserved').length; - const occupancyRate = spots.length > 0 - ? Math.round(((occupied + reserved) / spots.length) * 100) + + const occupied = spots.filter(s => s.status === 'occupied').length; + const reserved = spots.filter(s => s.status === 'reserved').length; + const occupancyRate = spots.length > 0 + ? Math.round(((occupied + reserved) / spots.length) * 100) : 0; - - // Mettre à jour l'affichage - document.getElementById('adminTotalUsers').textContent = totalUsers; + + document.getElementById('adminTotalUsers').textContent = totalUsers; document.getElementById('adminTotalReservations').textContent = totalReservations; - document.getElementById('adminTotalRevenue').textContent = totalRevenue + '€'; - document.getElementById('adminOccupancyRate').textContent = occupancyRate + '%'; + document.getElementById('adminTotalRevenue').textContent = totalRevenue + '€'; + document.getElementById('adminOccupancyRate').textContent = occupancyRate + '%'; } -/** - * Initialise le contrôle des places - */ +// ============================================ +// GESTION DES PLACES +// ============================================ + function initPlacesControl() { - const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); - - // Mettre à jour le champ du nombre de places + const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); const spotsInput = document.getElementById('adminTotalSpots'); - if (spotsInput) { - spotsInput.value = spots.length || 10; - } - - // Bouton de mise à jour + if (spotsInput) spotsInput.value = spots.length || 10; + document.getElementById('updateSpotsBtn')?.addEventListener('click', () => { const newCount = parseInt(document.getElementById('adminTotalSpots').value); if (newCount < 5 || newCount > 50) { Dashboard.showToast('Le nombre de places doit être entre 5 et 50', 'error'); return; } - if (window.ParkingMap) { window.ParkingMap.setTotalSpots(newCount); renderAdminPlacesList(); Dashboard.showToast('Nombre de places mis à jour', 'success'); } }); - - // Rendre la liste des places + renderAdminPlacesList(); } -/** - * Rend la liste des places dans l'admin - */ function renderAdminPlacesList() { const container = document.getElementById('adminPlacesList'); if (!container) return; - + const spots = JSON.parse(localStorage.getItem('smart_parking_spots') || '[]'); - + container.innerHTML = spots.map(spot => ` -
Aucun historique
'; + container.innerHTML = 'Aucun historique
'; return; } - - container.innerHTML = history.slice(0, 20).map(item => ` + + container.innerHTML = history.slice(0, 50).map(item => `