diff --git a/.env b/.env index 627e939..f9b15bc 100644 --- a/.env +++ b/.env @@ -1,25 +1,10 @@ -# ============================================================ -# 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 - -# ── JWT ────────────────────────────────────────────────────── -# ⚠️ Changez cette valeur ! Mettez une chaîne longue et aléatoire. -JWT_SECRET=une_chaine_tres_longue_et_secrete_changez_moi_absolument - -# ── Serveur ────────────────────────────────────────────────── +JWT_SECRET=une_chaine_tres_longue_et_secrete PORT=3000 NODE_ENV=production - -# ── MQTT (Mosquitto) ───────────────────────────────────────── -# Si Mosquitto tourne dans Docker (service "mqtt") → mqtt -# Si Mosquitto est installé directement sur le Pi → localhost -MQTT_HOST=mqtt +MQTT_HOST=172.18.0.1 MQTT_PORT=1883 \ No newline at end of file diff --git a/README.md b/README.md index 2a2bf24..2bae275 100644 --- a/README.md +++ b/README.md @@ -1,208 +1,239 @@ -# 🅿️ Smart Parking +# Smart Parking -> Système complet de gestion de parking intelligent avec authentification, réservation et paiement QR code +Systeme de gestion et de surveillance de parking automatise avec detection en temps reel par capteurs infrarouges, reservation en ligne et administration complete. -## 📋 Fonctionnalités +Projet BTS CIEL IR — APON BARUA — Groupe scolaire La Salle Saint-Denis — 2025/2026 -### 🔐 Authentification -- Inscription avec nom, email, téléphone et mot de passe -- Connexion sécurisée -- Gestion de profil -- Deux rôles : Client et Administrateur +## Fonctionnalites -### 🗺️ Carte du Parking -- **10 places** visuelles (modifiable par l'admin) -- 3 états : Libre ✅, Occupée 🚗, Réservée 📅 -- Mise à jour en temps réel -- Détails de chaque place au clic +### Authentification +- Inscription et connexion securisees +- Mots de passe haches avec BCrypt +- Authentification par token JWT (expire apres 24h) +- Deux roles : Client et Administrateur -### 📅 Système de Réservation -- Sélection de la place -- Choix de la date et heure -- Durée : 30min, 1h, 2h, 4h, Journée +### Carte du Parking +- Affichage en temps reel de l'etat des places +- 3 etats : Libre, Occupee, Reservee +- Mise a jour automatique toutes les 3 secondes +- Details de chaque place au clic + +### Detection automatique +- Capteurs infrarouges IR LM393 sur chaque place +- ESP32 connecte en WiFi au Raspberry Pi +- Communication MQTT vers le serveur +- Changement d'etat instantane sur le site + +### Systeme de Reservation +- Selection de la place, date, heure et duree +- Verification des conflits d'horaire - Saisie de la plaque d'immatriculation +- Expiration automatique des reservations -### 💳 Paiement QR Code -- Génération de QR code unique -- Code de paiement affiché -- Confirmation du paiement +### Panel Administrateur +- Statistiques globales du parking +- Gestion de l'etat de chaque place (1 a 20 places) +- Liste des utilisateurs avec suppression +- Gestion des reservations (terminer, annuler) +- Historique complet des actions -### 👤 Espace Client -- Consulter la carte des places -- Voir les tarifs -- Faire une réservation -- Voir l'historique des réservations -- Gérer son profil +## Tarifs -### ⚙️ Panel Admin -- Voir toutes les statistiques -- Modifier le nombre de places -- Gérer l'état de chaque place -- Voir tous les utilisateurs -- Voir toutes les réservations -- Annuler/terminer des réservations -- Voir l'historique complet - -## 💰 Tarifs - -| Durée | Prix | +| Duree | Prix | |-------|------| | 30 minutes | 2€ | | 1 heure | 3€ | | 2 heures | 5€ | | 4 heures | 8€ | -| Journée (8h) | 15€ | +| Journee (8h) | 15€ | -## 🚀 Installation - -### Prérequis -- Node.js 18+ -- npm - -### Étape 1 : Installer les dépendances - -```bash -cd server -npm install -``` - -### Étape 2 : Démarrer le serveur - -```bash -npm start -``` - -Pour le développement (avec redémarrage automatique) : -```bash -npm run dev -``` - -### Étape 3 : Accéder au site - -Ouvrir un navigateur et aller sur : -``` -http://localhost:3000 -``` - -## 🔑 Compte par défaut - -**Administrateur :** -- Email : `admin@smartparking.fr` -- Mot de passe : `admin123` - -## 📁 Structure du projet +## Architecture technique ``` -smart-parking/ -├── index.html # Page de connexion/inscription -├── css/ -│ ├── style.css # Styles globaux -│ ├── auth.css # Styles authentification -│ └── dashboard.css # Styles dashboard -├── js/ -│ ├── auth.js # Gestion authentification -│ ├── dashboard.js # Gestion dashboard -│ ├── map.js # Carte des places -│ ├── reservation.js # Système de réservation -│ └── admin.js # Panel admin -├── pages/ -│ └── dashboard.html # Dashboard principal -├── server/ -│ ├── package.json # Dépendances Node.js -│ ├── server.js # Serveur principal -│ ├── db/ -│ │ └── database.js # Gestion SQLite -│ ├── middleware/ -│ │ └── auth.js # Middleware JWT -│ └── routes/ -│ └── api.js # Routes API -└── README.md # Ce fichier +[Voiture] → [Capteur IR LM393] → [ESP32 WiFi] → [MQTT] → [Mosquitto] + → [Node.js] → [MariaDB] → [API REST] → [Site web] ``` -## 🔌 API REST +### Materiel +| Composant | Reference | Quantite | +|-----------|-----------|----------| +| Microcontroleur WiFi | ESP32 Freenove WROVER | 1 | +| Capteur infrarouge | Module IR LM393 | 3 | +| Serveur central | Raspberry Pi 4 | 1 | +| Cables | Dupont F-F et F-M | Plusieurs | -### Authentification -| Méthode | Endpoint | Description | -|---------|----------|-------------| -| POST | `/api/register` | Inscription | -| POST | `/api/login` | Connexion | +### Configuration reseau +| Interface | Adresse IP | Utilisation | +|-----------|-----------|-------------| +| Ethernet | 172.16.60.40 | Reseau ecole | +| WiFi (hotspot) | 172.20.10.2 | Communication ESP32 | +| Docker bridge | 172.18.0.1 | Reseau interne Docker | -### Utilisateurs -| Méthode | Endpoint | Description | -|---------|----------|-------------| -| GET | `/api/users` | Liste des utilisateurs (admin) | -| DELETE | `/api/users/:id` | Supprimer un utilisateur (admin) | +### Ports +| Service | Port | +|---------|------| +| Node.js (HTTP) | 3000 | +| MariaDB | 3306 | +| Mosquitto (MQTT) | 1883 | -### Places -| Méthode | Endpoint | Description | -|---------|----------|-------------| -| GET | `/api/spots` | Liste des places | -| PUT | `/api/spots/:id/status` | Modifier le statut | -| POST | `/api/spots/init` | Réinitialiser les places (admin) | +## Technologies utilisees -### Réservations -| Méthode | Endpoint | Description | -|---------|----------|-------------| -| GET | `/api/reservations` | Mes réservations | -| GET | `/api/reservations/all` | Toutes les réservations (admin) | -| POST | `/api/reservations` | Créer une réservation | -| PUT | `/api/reservations/:id/cancel` | Annuler une réservation | - -### Statistiques -| Méthode | Endpoint | Description | -|---------|----------|-------------| -| GET | `/api/stats` | Statistiques du parking | -| GET | `/api/history` | Historique (admin) | - -## 🛠️ Technologies utilisées - -### Frontend -- HTML5 -- CSS3 (responsive) -- JavaScript vanilla -- Chart.js (graphiques) -- QRCode.js (génération QR) +### Hardware +- ESP32 Freenove WROVER (WiFi + GPIO) +- Capteurs IR LM393 (GPIO 15, 4, 12) +- Raspberry Pi 4 (serveur central) ### Backend -- Node.js -- Express.js -- SQLite3 +- Node.js + Express.js +- MariaDB (base de donnees relationnelle) +- Mosquitto (broker MQTT) +- Docker + Docker Compose (conteneurisation) - JWT (authentification) -- bcryptjs (hashage mots de passe) +- BCrypt (hachage mots de passe) -## 📱 Fonctionnement +### Frontend +- HTML5 / CSS3 / JavaScript (sans framework) +- Design responsive (PC et mobile) -### Pour les clients : -1. Créer un compte ou se connecter -2. Consulter la carte des places disponibles -3. Choisir une place libre -4. Sélectionner date, heure et durée -5. Scanner le QR code pour payer -6. La place est réservée ! +### Outils +- Arduino IDE (programmation ESP32) +- Gitea + GitHub (versioning) +- Draw.io (schemas) -### Pour l'administrateur : -1. Se connecter avec le compte admin -2. Accéder au panel Admin -3. Voir toutes les statistiques -4. Gérer les places (cliquer pour changer l'état) -5. Modifier le nombre total de places -6. Gérer les utilisateurs et réservations +## Structure du projet -## 🔒 Sécurité +``` +Parking/ +├── index.html +├── css/ +│ ├── style.css +│ ├── auth.css +│ └── dashboard.css +├── js/ +│ ├── auth.js +│ ├── dashboard.js +│ ├── map.js +│ ├── reservation.js +│ └── admin.js +├── pages/ +│ └── dashboard.html +├── server/ +│ ├── server.js +│ ├── package.json +│ ├── db/ +│ │ └── database.js +│ ├── middleware/ +│ │ └── auth.js +│ └── routes/ +│ └── api.js +├── init.sql +├── Dockerfile +├── docker-compose.yml +├── mosquitto.conf +└── README.md +``` -- Mots de passe hashés avec bcrypt -- Authentification JWT -- Protection des routes sensibles -- Validation des données +## API REST -## 📝 Notes +### Authentification +| Methode | Endpoint | Description | +|---------|----------|-------------| +| POST | /api/register | Creer un compte | +| POST | /api/login | Se connecter | -- Les données sont stockées dans SQLite (`server/db/smart-parking.db`) -- Le système fonctionne aussi en mode offline (stockage local) -- La simulation automatique change l'état des places toutes les 5 secondes +### Utilisateurs +| Methode | Endpoint | Description | +|---------|----------|-------------| +| GET | /api/users | Liste des utilisateurs (admin) | +| PUT | /api/users/profile | Modifier mon profil | +| DELETE | /api/users/:id | Supprimer un utilisateur (admin) | + +### Places +| Methode | Endpoint | Description | +|---------|----------|-------------| +| GET | /api/spots | Liste des places | +| PUT | /api/spots/:id/status | Modifier le statut | +| POST | /api/spots/init | Reinitialiser les places (admin) | + +### Reservations +| Methode | Endpoint | Description | +|---------|----------|-------------| +| GET | /api/reservations | Mes reservations | +| GET | /api/reservations/all | Toutes les reservations (admin) | +| POST | /api/reservations | Creer une reservation | +| PUT | /api/reservations/:id/cancel | Annuler une reservation | +| PUT | /api/reservations/:id/complete | Terminer une reservation (admin) | + +### Statistiques +| Methode | Endpoint | Description | +|---------|----------|-------------| +| GET | /api/stats | Statistiques du parking | +| GET | /api/history | Historique (admin) | + +## Base de donnees (6 tables) + +| Table | Description | +|-------|-------------| +| users | Comptes utilisateurs (id, name, email, password, role) | +| spots | Places de parking (id, number, status, sensor_id) | +| reservations | Reservations (user_id, spot_id, date, start_time, end_time, vehicle, price) | +| history | Historique des actions (action, details, user_id, timestamp) | +| stats | Statistiques periodiques (occupation, taux) | +| mqtt_events | Messages MQTT recus (topic, message, received_at) | + +## Installation et demarrage + +### 1. Demarrer le hotspot WiFi sur le telephone + +### 2. Sur le Raspberry Pi +```bash +sudo systemctl start mosquitto +sudo iptables -I INPUT -s 172.18.0.0/16 -p tcp --dport 1883 -j ACCEPT +sudo iptables -I INPUT -s 172.17.0.0/16 -p tcp --dport 1883 -j ACCEPT +cd /home/aponlucas/Parking +docker compose up -d +``` + +### 3. Televerser le code ESP32 +Ouvrir Arduino IDE, selectionner ESP32 Wrover Module, televerser le code. + +### 4. Acceder au site +``` +http://172.20.10.2:3000 +``` + +## Compte administrateur par defaut + +- Email : admin@smartparking.fr +- Mot de passe : admin123 + +## Securite + +- Mots de passe haches avec BCrypt (10 rounds) +- Authentification JWT avec expiration 24h +- Middleware de verification sur toutes les routes protegees +- Fichier .env non versionne (secrets exclus de GitHub) +- Regles iptables pour le pare-feu Linux + +## Chiffres cles + +| Metrique | Valeur | +|----------|--------| +| Lignes de code | ~3 900 | +| Fichiers | 17 | +| Langages | JavaScript, HTML, CSS, C++, SQL | +| Endpoints API | 12 | +| Tables MariaDB | 6 | +| Delai de mise a jour | Moins de 3 secondes | + +## Equipe + +| Membre | Role | +|--------|------| +| APON BARUA | IoT, Web, Backend, BDD, Docker, Securite | +| Lucas | Electronique, Arduino, Barrieres, Schemas | +| Mahmoud | Camera de surveillance | --- -
- 🅿️ Smart Parking - BTS CIEL IR 2025 🅿️ -
+Smart Parking — BTS CIEL IR — APON BARUA — 2025/2026 \ No newline at end of file diff --git a/css/auth.css b/css/auth.css index 8b48643..1a928ce 100644 --- a/css/auth.css +++ b/css/auth.css @@ -1,7 +1,3 @@ -/* ============================================ - AUTHENTIFICATION - STYLES - ============================================ */ - .auth-page { min-height: 100vh; display: flex; @@ -149,7 +145,6 @@ color: var(--success); } -/* Responsive */ @media (max-width: 480px) { .auth-box { padding: 24px; diff --git a/css/dashboard.css b/css/dashboard.css index 06d4367..aa2530f 100644 --- a/css/dashboard.css +++ b/css/dashboard.css @@ -1,8 +1,3 @@ -/* ============================================ - DASHBOARD - STYLES SPÉCIFIQUES - ============================================ */ - -/* Reservation form */ .reservation-form-container { max-width: 600px; margin: 0 auto; @@ -35,14 +30,14 @@ color: var(--primary-light); } -/* Chart container */ + .chart-container { position: relative; height: 300px; width: 100%; } -/* Admin table actions */ + .table-actions { display: flex; gap: 8px; @@ -53,7 +48,7 @@ font-size: 1rem; } -/* Status badges */ + .badge { display: inline-block; padding: 4px 12px; @@ -73,7 +68,7 @@ color: var(--info); } -/* Animation pour les mises à jour */ + @keyframes pulse-update { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } @@ -83,7 +78,6 @@ animation: pulse-update 0.3s ease; } -/* Loading state */ .loading { position: relative; pointer-events: none; diff --git a/css/style.css b/css/style.css index 0130158..7fd16cc 100644 --- a/css/style.css +++ b/css/style.css @@ -1,45 +1,40 @@ -/* ============================================ - SMART PARKING - STYLES GLOBAUX - ============================================ */ - :root { - /* Couleurs principales */ + --primary: #6366f1; --primary-dark: #4f46e5; --primary-light: #818cf8; - /* Couleurs états */ + --success: #10b981; --danger: #ef4444; --warning: #f59e0b; --info: #06b6d4; - /* Couleurs places */ --spot-free: #10b981; --spot-occupied: #ef4444; --spot-reserved: #3b82f6; - /* Fonds */ + --bg-dark: #0f172a; --bg-darker: #020617; --bg-card: #1e293b; --bg-hover: #334155; - /* Texte */ + --text-primary: #f8fafc; --text-secondary: #cbd5e1; --text-muted: #94a3b8; - /* Bordures */ + --border: #334155; --border-radius: 12px; --border-radius-sm: 8px; - /* Ombres */ + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); --shadow-lg: 0 10px 25px -5px rgba(0, 0, 0, 0.4); - /* Transitions */ + --transition: all 0.3s ease; } @@ -67,9 +62,7 @@ body { padding: 0 20px; } -/* ============================================ - HEADER - ============================================ */ + .header { background: var(--bg-card); border-bottom: 1px solid var(--border); @@ -110,7 +103,7 @@ body { -webkit-text-fill-color: transparent; } -/* Navigation */ + .nav { display: flex; gap: 8px; @@ -143,7 +136,7 @@ body { font-size: 1.1rem; } -/* Header right */ + .header-right { display: flex; align-items: center; @@ -166,9 +159,7 @@ body { color: var(--text-muted); } -/* ============================================ - MAIN & PAGES - ============================================ */ + .main { padding: 30px 0; min-height: calc(100vh - 80px); @@ -201,9 +192,7 @@ body { font-size: 1.5rem; } -/* ============================================ - BOUTONS - ============================================ */ + .btn { display: inline-flex; align-items: center; @@ -262,9 +251,7 @@ body { cursor: not-allowed; } -/* ============================================ - FORMULAIRES - ============================================ */ + .form-group { margin-bottom: 16px; } @@ -317,9 +304,6 @@ select.form-control { flex: 1; } -/* ============================================ - CARTES - ============================================ */ .card { background: var(--bg-card); border-radius: var(--border-radius); @@ -328,9 +312,7 @@ select.form-control { box-shadow: var(--shadow); } -/* ============================================ - STATS GRID - ============================================ */ + .stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); @@ -392,9 +374,7 @@ select.form-control { margin-top: 4px; } -/* ============================================ - PARKING MAP - ============================================ */ + .parking-section { display: grid; grid-template-columns: 1fr 350px; @@ -446,7 +426,7 @@ select.form-control { .parking-spot .spot-number { font-size: 1.1rem; } .parking-spot .spot-icon { font-size: 1.3rem; } -/* Legend */ + .legend { display: flex; justify-content: center; @@ -471,7 +451,7 @@ select.form-control { .legend-color.occupied { background: var(--spot-occupied); } .legend-color.reserved { background: var(--spot-reserved); } -/* Spot details */ + .spot-details { min-height: 200px; } .no-selection { @@ -500,9 +480,7 @@ select.form-control { .spot-status-occupied { color: var(--spot-occupied); } .spot-status-reserved { color: var(--spot-reserved); } -/* ============================================ - PRICING - ============================================ */ + .pricing-section { background: var(--bg-card); border-radius: var(--border-radius); @@ -547,9 +525,7 @@ select.form-control { color: var(--primary-light); } -/* ============================================ - MODAL - ============================================ */ + .modal { position: fixed; top: 0; @@ -597,7 +573,7 @@ select.form-control { .modal-body { padding: 24px; } -/* Récapitulatif réservation */ + .payment-summary { background: var(--bg-dark); border-radius: var(--border-radius-sm); @@ -625,9 +601,7 @@ select.form-control { font-size: 1.1rem; } -/* ============================================ - MESSAGE DE CONFIRMATION (remplace QR code) - ============================================ */ + .confirmation-message { text-align: center; padding: 24px 16px; @@ -658,9 +632,7 @@ select.form-control { color: var(--text-primary); } -/* ============================================ - RESERVATIONS LIST - ============================================ */ + .reservations-list { display: flex; flex-direction: column; @@ -739,9 +711,7 @@ select.form-control { margin-bottom: 20px; } -/* ============================================ - PROFILE - ============================================ */ + .profile-container { display: grid; grid-template-columns: 1fr 1fr; @@ -813,9 +783,7 @@ select.form-control { color: var(--text-muted); } -/* ============================================ - ADMIN - ============================================ */ + .admin-page .admin-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); @@ -882,7 +850,7 @@ select.form-control { .admin-place-item.occupied { background: rgba(239, 68, 68, 0.2); color: var(--spot-occupied); } .admin-place-item.reserved { background: rgba(59, 130, 246, 0.2); color: var(--spot-reserved); } -/* Tables */ + .table-container { overflow-x: auto; } .data-table { @@ -908,7 +876,7 @@ select.form-control { .data-table tr:hover td { background: var(--bg-hover); } -/* Log container */ + .log-container { max-height: 300px; overflow-y: auto; @@ -933,9 +901,7 @@ select.form-control { font-size: 0.75rem; } -/* ============================================ - TOAST NOTIFICATIONS - ============================================ */ + .toast-container { position: fixed; bottom: 20px; @@ -965,17 +931,13 @@ select.form-control { to { transform: translateX(0); opacity: 1; } } -/* ============================================ - UTILITAIRES - ============================================ */ + .hidden { display: none !important; } .admin-only { display: none; } .admin-only.visible { display: flex; } -/* ============================================ - RESPONSIVE - ============================================ */ + @media (max-width: 1024px) { .stats-grid { grid-template-columns: repeat(2, 1fr); } .parking-section { grid-template-columns: 1fr; } @@ -1004,7 +966,6 @@ select.form-control { .reservation-actions { align-items: flex-start; } } -/* Scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: var(--bg-dark); border-radius: 4px; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } diff --git a/docker-compose.yml b/docker-compose.yml index 4544c92..7df63d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,13 @@ -version: '3.8' - -# ============================================================ -# Smart Parking v2.0 — Docker Compose -# Services : MariaDB + App Node.js + Mosquitto MQTT -# ============================================================ - services: - - # ── Base de données MariaDB ──────────────────────────────── db: image: mariadb:10.11 container_name: smartparking-db restart: always environment: - MARIADB_ROOT_PASSWORD: rootpassword # ⚠️ À changer en production + MARIADB_ROOT_PASSWORD: rootpassword MARIADB_DATABASE: smartparking MARIADB_USER: smartparking_user - MARIADB_PASSWORD: smartparking_pass # ⚠️ À changer en production + MARIADB_PASSWORD: smartparking_pass volumes: - db_data:/var/lib/mysql - ./init.sql:/docker-entrypoint-initdb.d/init.sql @@ -27,25 +18,6 @@ services: timeout: 10s retries: 5 - # ── Broker MQTT Mosquitto ────────────────────────────────── - # Si vous avez déjà Mosquitto installé directement sur le Pi - # (pas dans Docker), commentez ce bloc et mettez - # MQTT_HOST=localhost dans la section "app" ci-dessous. - mqtt: - image: eclipse-mosquitto:2 - container_name: smartparking-mqtt - restart: always - ports: - - "1883:1883" # Port MQTT (Arduino se connecte ici) - - "9001:9001" # Port WebSocket (optionnel) - volumes: - - ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf - - mosquitto_data:/mosquitto/data - - mosquitto_log:/mosquitto/log - networks: - - smartparking-network - - # ── Application Node.js ──────────────────────────────────── app: build: . container_name: smartparking-app @@ -55,31 +27,23 @@ services: depends_on: db: condition: service_healthy - mqtt: - condition: service_started environment: - # Base de données - DB_HOST: db - DB_PORT: 3306 - DB_USER: smartparking_user + DB_HOST: db + DB_PORT: 3306 + DB_USER: smartparking_user DB_PASSWORD: smartparking_pass - DB_NAME: smartparking - # JWT - JWT_SECRET: ${JWT_SECRET:-une_chaine_tres_longue_et_secrete_changez_moi} - # MQTT — utiliser "mqtt" si Mosquitto est dans Docker - # utiliser "localhost" si Mosquitto est installé directement sur le Pi - MQTT_HOST: mqtt - MQTT_PORT: 1883 - # Environnement + DB_NAME: smartparking + JWT_SECRET: une_chaine_tres_longue_et_secrete NODE_ENV: production - PORT: 3000 + MQTT_HOST: 172.18.0.1 + MQTT_PORT: 1883 + extra_hosts: + - "host.docker.internal:host-gateway" networks: - smartparking-network volumes: db_data: - mosquitto_data: - mosquitto_log: networks: smartparking-network: \ No newline at end of file diff --git a/index.html b/index.html index c172a07..24a8afe 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@Gestion intelligente de parking
- +