Répercussions des modifications de l'api.

This commit is contained in:
Erwann PHILIPPE 2026-09-20 15:35:59 +02:00
parent 448cb25002
commit 5882b049b7
11 changed files with 630 additions and 46 deletions

View file

@ -76,10 +76,12 @@ voituresAPI-Front/
| Méthode | Route | Action correspondante | Accès |
|---|---|---|---|
| `GET` | `/` ou `/cars` | Liste tous les véhicules (`GET /cars`) | Public |
| `GET` | `/` | Page d'accueil & présentation du projet + bouton création | Public |
| `GET` | `/cars` | Parc automobile complet (`GET /cars`) ou mes véhicules (`GET /cars/mes-voitures`) selon rôle | Authentifié (Admin / User) |
| `GET` | `/cars?mine=1` | Liste mes véhicules (`GET /cars/mes-voitures`) | Authentifié |
| `GET` | `/cars?immatriculation=...` | Recherche par plaque (`GET /cars/immatriculation/{plaque}`) | Admin uniquement |
| `GET` | `/cars/{id}` | Fiche détaillée avec km et entretiens (`GET /cars/{id}`) | Public |
| `GET` | `/cars/create` | Formulaire d'ajout d'un véhicule | Authentifié |
| `GET` | `/cars/create` | Formulaire d'ajout d'un véhicule | Authentifié (Connexion requise) |
| `POST`| `/cars/create` | Envoi à l'API (`POST /cars`) | Authentifié |
| `GET` | `/cars/{id}/edit` | Formulaire d'édition | Propriétaire / Admin |
| `POST`| `/cars/{id}/edit` | Mise à jour dans l'API (`PUT /cars/{id}`) | Propriétaire / Admin |
@ -87,12 +89,21 @@ voituresAPI-Front/
| `POST`| `/cars/{id}/kilometrage` | Ajout relevé km (`POST /cars/{id}/kilometrage`) | Propriétaire / Admin |
| `POST`| `/cars/{id}/maintenance` | Ajout entretien (`POST /cars/{id}/maintenance`) | Propriétaire / Admin |
| `POST`| `/cars/{id}/notes` | Ajout note de suivi (`POST /cars/{id}/notes`) | Propriétaire / Admin |
| `GET` | `/login` / `POST /login` | Connexion (`POST /auth/login`) | Public |
| `GET` | `/login` / `POST /login` | Connexion (`POST /auth/login`) avec support de redirection (`?redirect=...`) | Public |
| `GET` | `/register` / `POST /register` | Inscription (`POST /auth/register`) | Public |
| `GET` | `/logout` | Déconnexion (`POST /auth/logout`) | Authentifié |
---
## 🔒 Sécurité & Privilèges (API REST v1.1.0)
Conformément à la version **1.1.0** de l'API :
- **Privatisation de `GET /cars`** : L'accès à la flotte complète est restreint au rôle `admin` (`401` si non authentifié, `403` si `user`). Les utilisateurs accèdent automatiquement à leurs véhicules déclarés (`GET /cars/mes-voitures`).
- **Privatisation de la recherche d'immatriculation** : `GET /cars/immatriculation/{plaque}` est réservé aux administrateurs.
- **Page d'accueil dédiée** : Route `/` présentant le projet et disposant d'un appel à l'action pour créer une voiture (orientant les visiteurs non connectés vers la page d'authentification).
---
## 💻 Démarrage du serveur
Assurez-vous que l'API tourne sur `http://localhost:8888`.

View file

@ -926,3 +926,300 @@ body {
border-radius: 4px;
color: #0f172a;
}
/* ==========================================================================
Homepage / Hero & Features Styles
========================================================================== */
.hero-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 3.5rem 2.5rem;
text-align: center;
box-shadow: var(--shadow-md);
margin-bottom: 3rem;
position: relative;
overflow: hidden;
}
.hero-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #2563eb, #3b82f6, #60a5fa);
}
.hero-badge-wrapper {
display: inline-flex;
margin-bottom: 1.5rem;
}
.hero-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.85rem;
font-weight: 700;
padding: 0.35rem 0.9rem;
border-radius: 9999px;
letter-spacing: 0.02em;
}
.badge-online {
background: #ecfdf5;
color: #065f46;
border: 1px solid #a7f3d0;
}
.badge-offline {
background: #fef2f2;
color: #991b1b;
border: 1px solid #fecaca;
}
.hero-title {
font-size: 2.5rem;
font-weight: 800;
line-height: 1.25;
color: var(--text-main);
margin-bottom: 1.25rem;
letter-spacing: -0.02em;
}
.hero-title-highlight {
background: linear-gradient(135deg, #2563eb, #1d4ed8);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.hero-lead {
font-size: 1.15rem;
color: var(--text-muted);
max-width: 820px;
margin: 0 auto 2.25rem auto;
line-height: 1.7;
}
.hero-cta-group {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
margin-bottom: 1.5rem;
}
.btn-cta {
padding: 0.85rem 1.75rem;
font-size: 1.05rem;
font-weight: 700;
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.btn-cta:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.hero-requirement-badge {
display: inline-flex;
align-items: center;
gap: 0.6rem;
background: #fffbeb;
border: 1px solid #fde68a;
color: #92400e;
padding: 0.6rem 1.25rem;
border-radius: 9999px;
font-size: 0.9rem;
margin-top: 0.5rem;
}
.hero-note {
font-size: 0.95rem;
color: var(--text-muted);
margin-top: 0.5rem;
}
/* Features Grid */
.section-title-wrap {
text-align: center;
margin-bottom: 2.5rem;
}
.section-title {
font-size: 1.85rem;
font-weight: 800;
color: var(--text-main);
margin-bottom: 0.5rem;
}
.section-subtitle {
font-size: 1rem;
color: var(--text-muted);
max-width: 650px;
margin: 0 auto;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 1.75rem;
margin-bottom: 3rem;
}
.feature-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 2rem 1.75rem;
transition: transform 0.2s ease, box-shadow 0.2s ease;
display: flex;
flex-direction: column;
}
.feature-card:hover {
transform: translateY(-3px);
box-shadow: var(--shadow-md);
}
.feature-icon-wrapper {
width: 52px;
height: 52px;
border-radius: var(--radius-md);
background: #eff6ff;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
margin-bottom: 1.25rem;
}
.feature-title {
font-size: 1.2rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 0.6rem;
}
.feature-desc {
font-size: 0.92rem;
color: var(--text-muted);
line-height: 1.6;
flex: 1;
}
/* Security & Changelog Section */
.security-card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 2.5rem;
margin-bottom: 3rem;
box-shadow: var(--shadow-sm);
}
.security-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.security-icon {
font-size: 2rem;
}
.security-rules-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.25rem;
}
.security-rule-box {
background: #f8fafc;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
padding: 1.25rem;
}
.security-rule-title {
font-size: 0.95rem;
font-weight: 700;
margin-bottom: 0.4rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.security-rule-desc {
font-size: 0.88rem;
color: var(--text-muted);
line-height: 1.5;
}
/* Demo accounts banner */
.demo-banner {
background: linear-gradient(135deg, #1e293b, #0f172a);
color: #ffffff;
border-radius: var(--radius-lg);
padding: 2.5rem;
margin-bottom: 3rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
flex-wrap: wrap;
}
.demo-banner-content h3 {
font-size: 1.4rem;
font-weight: 800;
margin-bottom: 0.5rem;
}
.demo-banner-content p {
color: #94a3b8;
font-size: 0.95rem;
max-width: 600px;
}
.demo-banner-buttons {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.btn-demo {
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.btn-demo:hover {
background: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
@media (max-width: 768px) {
.hero-card {
padding: 2.5rem 1.5rem;
}
.hero-title {
font-size: 1.9rem;
}
.hero-lead {
font-size: 1rem;
}
.features-grid {
grid-template-columns: 1fr;
}
}

View file

@ -30,11 +30,15 @@ require_once __DIR__ . '/../src/Core/Autoloader.php';
\App\Core\Autoloader::register();
use App\Core\Router;
use App\Controllers\HomeController;
use App\Controllers\CarController;
use App\Controllers\AuthController;
$router = new Router();
// --- Page d'accueil (Présentation du projet) ---
$router->get('/', [HomeController::class, 'index']);
// --- Routes d'authentification ---
$router->get('/login', [AuthController::class, 'login']);
$router->post('/login', [AuthController::class, 'authenticate']);
@ -43,7 +47,6 @@ $router->post('/register', [AuthController::class, 'storeUser']);
$router->get('/logout', [AuthController::class, 'logout']);
// --- Routes des véhicules ---
$router->get('/', [CarController::class, 'index']);
$router->get('/cars', [CarController::class, 'index']);
// Création d'un véhicule

View file

@ -21,13 +21,17 @@ class AuthController extends BaseController
*/
public function login(): void
{
$redirect = trim((string)($_GET['redirect'] ?? ''));
if (!empty($_SESSION['user'])) {
$this->redirect('/cars');
$target = (!empty($redirect) && str_starts_with($redirect, '/') && !str_starts_with($redirect, '//')) ? $redirect : '/cars';
$this->redirect($target);
return;
}
$this->render('auth/login', [
'title' => 'Connexion à l\'espace gestionnaire'
'title' => 'Connexion à l\'espace gestionnaire',
'redirect' => $redirect
]);
}
@ -38,10 +42,11 @@ class AuthController extends BaseController
{
$username = trim($_POST['username'] ?? '');
$password = (string)($_POST['password'] ?? '');
$redirect = trim((string)($_POST['redirect'] ?? ''));
if (empty($username) || empty($password)) {
$this->setFlash('danger', 'Veuillez saisir votre nom d\'utilisateur et mot de passe.');
$this->redirect('/login');
$this->redirect('/login' . (!empty($redirect) ? '?redirect=' . urlencode($redirect) : ''));
return;
}
@ -55,10 +60,15 @@ class AuthController extends BaseController
$_SESSION['user'] = $response['data']['user'];
$this->setFlash('success', "Bienvenue, {$response['data']['user']['nom']} ! Connecté en tant que " . ucfirst($response['data']['user']['role']));
$this->redirect('/cars');
if (!empty($redirect) && str_starts_with($redirect, '/') && !str_starts_with($redirect, '//')) {
$this->redirect($redirect);
} else {
$this->redirect('/cars');
}
} else {
$this->setFlash('danger', $response['error'] ?? 'Identifiants invalides');
$this->redirect('/login');
$this->redirect('/login' . (!empty($redirect) ? '?redirect=' . urlencode($redirect) : ''));
}
}
@ -116,6 +126,6 @@ class AuthController extends BaseController
unset($_SESSION['token'], $_SESSION['user']);
$this->setFlash('info', 'Vous avez été déconnecté avec succès.');
$this->redirect('/cars');
$this->redirect('/');
}
}

View file

@ -26,9 +26,9 @@ class BaseController
}
// Chargement du layout global
require_once $baseViewDir . 'layout/header.php';
require_once $viewFile;
require_once $baseViewDir . 'layout/footer.php';
require $baseViewDir . 'layout/header.php';
require $viewFile;
require $baseViewDir . 'layout/footer.php';
}
/**

View file

@ -18,24 +18,52 @@ class CarController extends BaseController
}
/**
* Liste des véhicules avec support des filtres (mes voitures, immatriculation)
* Liste des véhicules avec support des rôles (API v1.1.0)
*/
public function index(): void
{
$queryParams = [];
$isMineOnly = !empty($_GET['mine']);
$user = $_SESSION['user'] ?? null;
$token = $_SESSION['token'] ?? null;
if ($isMineOnly && !empty($_SESSION['token'])) {
$response = $this->apiService->get('/cars/mes-voitures');
} elseif (!empty($_GET['immatriculation'])) {
$plate = trim((string)$_GET['immatriculation']);
$response = $this->apiService->get("/cars/immatriculation/{$plate}");
} else {
$response = $this->apiService->get('/cars');
// Si l'utilisateur n'est pas connecté : redirection vers la connexion
if (empty($token) || empty($user)) {
$this->setFlash('warning', 'La consultation des véhicules requiert d\'être connecté. Connectez-vous avec votre compte utilisateur ou administrateur.');
$this->redirect('/login?redirect=/cars');
return;
}
$isAdmin = ($user['role'] ?? '') === 'admin';
$searchedPlate = trim((string)($_GET['immatriculation'] ?? ''));
$isMineRequested = !empty($_GET['mine']);
$cars = [];
$apiError = null;
$isMineOnly = false;
// Règle API v1.1.0 :
// - Les utilisateurs standards (role: user) doivent utiliser GET /cars/mes-voitures
// - Seuls les administrateurs ont accès à GET /cars et GET /cars/immatriculation/{plaque}
if (!$isAdmin) {
$isMineOnly = true;
if (!empty($searchedPlate)) {
$this->setFlash('warning', 'La recherche par plaque d\'immatriculation est réservée aux administrateurs (API v1.1.0).');
$this->redirect('/cars');
return;
}
$response = $this->apiService->get('/cars/mes-voitures');
} else {
// Administrateur
if ($isMineRequested) {
$isMineOnly = true;
$response = $this->apiService->get('/cars/mes-voitures');
} elseif (!empty($searchedPlate)) {
$response = $this->apiService->get("/cars/immatriculation/{$searchedPlate}");
} else {
$response = $this->apiService->get('/cars');
}
}
if ($response['success']) {
$data = $response['data'];
@ -52,11 +80,12 @@ class CarController extends BaseController
}
$this->render('cars/index', [
'title' => 'Parc Automobile - Véhicules',
'cars' => $cars,
'apiError' => $apiError,
'isMineOnly' => $isMineOnly,
'searchedPlate' => $_GET['immatriculation'] ?? ''
'title' => $isMineOnly ? 'Mes Véhicules' : 'Parc Automobile (Administration)',
'cars' => $cars,
'apiError' => $apiError,
'isMineOnly' => $isMineOnly,
'isAdmin' => $isAdmin,
'searchedPlate' => $searchedPlate
]);
}
@ -69,7 +98,7 @@ class CarController extends BaseController
if (!$carResponse['success'] || empty($carResponse['data'])) {
$this->setFlash('danger', $carResponse['error'] ?? "Véhicule #{$id} introuvable.");
$this->redirect('/cars');
$this->redirect(empty($_SESSION['token']) ? '/' : '/cars');
return;
}
@ -109,7 +138,7 @@ class CarController extends BaseController
{
if (empty($_SESSION['token'])) {
$this->setFlash('warning', 'Veuillez vous connecter pour ajouter un véhicule.');
$this->redirect('/login');
$this->redirect('/login?redirect=/cars/create');
return;
}
@ -128,7 +157,7 @@ class CarController extends BaseController
{
if (empty($_SESSION['token'])) {
$this->setFlash('danger', 'Session expirée ou non connecté.');
$this->redirect('/login');
$this->redirect('/login?redirect=/cars/create');
return;
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Controllers;
use App\Services\ApiService;
/**
* Contrôleur de la page d'accueil de présentation du projet
*/
class HomeController extends BaseController
{
private ApiService $apiService;
public function __construct()
{
$this->apiService = new ApiService();
}
/**
* Affiche la page d'accueil présentant le projet et ses fonctionnalités
*/
public function index(): void
{
// Interrogation de la racine de l'API pour récupérer les métadonnées et le statut
$apiResponse = $this->apiService->get('/');
$apiOnline = $apiResponse['success'] ?? false;
$apiData = $apiResponse['data'] ?? null;
$this->render('home/index', [
'title' => 'Accueil - Présentation du projet ' . APP_NAME,
'apiOnline' => $apiOnline,
'apiData' => $apiData,
'currentUser' => $_SESSION['user'] ?? null
]);
}
}

View file

@ -6,7 +6,14 @@
<p class="auth-subtitle">Accédez à la gestion de vos véhicules et carnets d'entretien</p>
</div>
<?php if (!empty($redirect)): ?>
<div class="alert alert-info">
🔒 Authentification requise : Veuillez vous connecter pour accéder à cette fonctionnalité<?= $redirect === '/cars/create' ? ' (créer un véhicule)' : '' ?>.
</div>
<?php endif; ?>
<form action="/login" method="POST" id="loginForm">
<input type="hidden" name="redirect" value="<?= htmlspecialchars($redirect ?? '') ?>">
<div class="form-group">
<label for="username" class="form-label">Nom d'utilisateur</label>
<input type="text" id="username" name="username" class="form-input" required

View file

@ -1,10 +1,16 @@
<div class="page-header">
<div>
<h1 class="page-title">
<?= $isMineOnly ? 'Mes Véhicules' : 'Parc Automobile' ?>
<?= $isMineOnly ? 'Mes Véhicules' : 'Parc Automobile (Administration)' ?>
</h1>
<p class="page-subtitle">
<?= $isMineOnly ? 'Véhicules dont vous êtes le propriétaire déclaré' : 'Consultation en direct des données depuis l\'API REST' ?>
<?php if (!$isAdmin): ?>
Gestion de vos véhicules déclarés (L'accès au parc global est réservé aux administrateurs selon l'API v1.1.0)
<?php elseif ($isMineOnly): ?>
Véhicules dont vous êtes le propriétaire déclaré
<?php else: ?>
Supervision et gestion de l'intégralité du parc automobile (Mode Administrateur)
<?php endif; ?>
</p>
</div>
<div class="page-actions">
@ -13,8 +19,8 @@
<span>+</span> Ajouter un véhicule
</a>
<?php else: ?>
<a href="/login" class="btn btn-outline">
🔐 Se connecter pour ajouter un véhicule
<a href="/login?redirect=/cars/create" class="btn btn-primary">
🔒 Se connecter pour ajouter un véhicule
</a>
<?php endif; ?>
</div>
@ -41,9 +47,14 @@
</div>
<div class="filter-tabs">
<a href="/cars" class="filter-tab <?= !$isMineOnly ? 'active' : '' ?>">Tous les véhicules</a>
<?php if (!empty($_SESSION['user'])): ?>
<?php if ($isAdmin): ?>
<a href="/cars" class="filter-tab <?= !$isMineOnly ? 'active' : '' ?>">Tous les véhicules (Admin)</a>
<a href="/cars?mine=1" class="filter-tab <?= $isMineOnly ? 'active' : '' ?>">Mes véhicules</a>
<?php else: ?>
<span class="filter-tab active">Mes véhicules</span>
<span class="badge badge-secondary" title="Accès au parc complet réservé aux administrateurs (API v1.1.0)" style="margin-left: 0.5rem; align-self: center;">
🔒 Parc global réservé aux admins
</span>
<?php endif; ?>
</div>

171
src/Views/home/index.php Normal file
View file

@ -0,0 +1,171 @@
<div class="hero-card">
<div class="hero-badge-wrapper">
<?php if ($apiOnline): ?>
<span class="hero-badge badge-online">
🟢 API REST v1.1.0 connectée & opérationnelle
</span>
<?php else: ?>
<span class="hero-badge badge-offline">
🔴 API REST non connectée sur <?= htmlspecialchars(API_BASE_URL) ?>
</span>
<?php endif; ?>
</div>
<h1 class="hero-title">
Gestion de Parc Automobile <br>
<span class="hero-title-highlight">& Carnet d'Entretien Numérique</span>
</h1>
<p class="hero-lead">
Une application web complète conçue en architecture <strong>MVC PHP</strong> et connectée à une <strong>API RESTful</strong> sécurisée.
Administrez vos véhicules, enregistrez les relevés kilométriques, suivez les interventions de maintenance et conservez un journal de bord technique en toute sécurité.
</p>
<!-- Bouton principal pour créer une voiture (nécessitant de se connecter) -->
<div class="hero-cta-group">
<?php if ($currentUser): ?>
<a href="/cars/create" class="btn btn-primary btn-cta">
🚗 Créer une voiture
</a>
<?php if (($currentUser['role'] ?? '') === 'admin'): ?>
<a href="/cars" class="btn btn-outline btn-cta">
🛡️ Explorer le parc complet (Admin)
</a>
<?php else: ?>
<a href="/cars" class="btn btn-outline btn-cta">
📁 Accéder à mes véhicules
</a>
<?php endif; ?>
<?php else: ?>
<a href="/login?redirect=/cars/create" class="btn btn-primary btn-cta" title="Connexion requise pour créer un véhicule">
🚗 Créer une voiture
</a>
<a href="/login" class="btn btn-outline btn-cta">
🔐 Se connecter
</a>
<a href="/register" class="btn btn-secondary btn-cta">
📝 S'inscrire
</a>
<?php endif; ?>
</div>
<?php if ($currentUser): ?>
<p class="hero-note">
Connecté en tant que <strong><?= htmlspecialchars($currentUser['nom'] ?? $currentUser['username']) ?></strong>
<span class="user-role-badge badge-<?= htmlspecialchars(strtolower($currentUser['role'] ?? 'user')) ?>">
<?= htmlspecialchars(ucfirst($currentUser['role'] ?? 'User')) ?>
</span>
</p>
<?php else: ?>
<div>
<span class="hero-requirement-badge">
🔒 <strong>Connexion requise :</strong> Cliquez sur <em>Créer une voiture</em> pour vous identifier et enregistrer votre premier véhicule.
</span>
</div>
<?php endif; ?>
</div>
<!-- Présentation du Projet : Piliers Fonctionnels -->
<div class="section-title-wrap">
<h2 class="section-title">Fonctionnalités & Modules de l'Application</h2>
<p class="section-subtitle">
Une solution pensée pour les propriétaires exigeants et les gestionnaires de flotte automobile.
</p>
</div>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon-wrapper">🚗</div>
<h3 class="feature-title">Fiches Véhicules Détaillées</h3>
<p class="feature-desc">
Enregistrement complet des caractéristiques clés : Marque, Modèle, Millésime, Date d'acquisition, Numéro de châssis VIN et Plaque d'immatriculation au format français standardisé.
</p>
</div>
<div class="feature-card">
<div class="feature-icon-wrapper">🛣️</div>
<h3 class="feature-title">Suivi Kilométrique Évolutif</h3>
<p class="feature-desc">
Historisation chronologique des relevés kilométriques, calcul automatique du dernier kilométrage connu et évaluation de la distance totale parcourue depuis l'acquisition.
</p>
</div>
<div class="feature-card">
<div class="feature-icon-wrapper">🔧</div>
<h3 class="feature-title">Carnet d'Entretien & Révisions</h3>
<p class="feature-desc">
Journal précis de chaque opération mécanique (vidange, courroie, freins, pneumatiques...), avec date, kilométrage lors de l'intervention, descriptif des travaux et coût financier en euros.
</p>
</div>
<div class="feature-card">
<div class="feature-icon-wrapper">📝</div>
<h3 class="feature-title">Notes Confidentielles</h3>
<p class="feature-desc">
Espace d'annotations privées et de suivi technique, accessible exclusivement au propriétaire légitime du véhicule et aux administrateurs pour une confidentialité totale.
</p>
</div>
</div>
<!-- Nouveautés Sécurité API v1.1.0 -->
<div class="security-card">
<div class="security-header">
<span class="security-icon">🛡️</span>
<div>
<h3 style="font-size: 1.3rem; font-weight: 800; color: var(--text-main);">
Sécurité & Droits d'Accès (Mise à jour API v1.1.0)
</h3>
<p style="color: var(--text-muted); font-size: 0.92rem;">
Le système intègre les dernières directives de sécurité et de séparation des privilèges (RBAC) :
</p>
</div>
</div>
<div class="security-rules-grid">
<div class="security-rule-box">
<div class="security-rule-title" style="color: #991b1b;">
<span>🔒</span> Privatisation du Parc Global
</div>
<p class="security-rule-desc">
L'endpoint <code>GET /cars</code> est désormais strictement réservé aux <strong>administrateurs</strong>. Toute tentative d'accès non autorisée est rejetée avec un statut <code>401</code> ou <code>403</code>.
</p>
</div>
<div class="security-rule-box">
<div class="security-rule-title" style="color: #1e40af;">
<span>👤</span> Espace Dédié Propriétaire
</div>
<p class="security-rule-desc">
Les utilisateurs standards (<code>role: user</code>) accèdent de manière hermétique à leurs seuls véhicules déclarés via le point d'accès <code>GET /cars/mes-voitures</code>.
</p>
</div>
<div class="security-rule-box">
<div class="security-rule-title" style="color: #92400e;">
<span>🔍</span> Recherche Plaque Sécurisée
</div>
<p class="security-rule-desc">
L'identification d'un véhicule par immatriculation <code>GET /cars/immatriculation/{plaque}</code> est réservée aux administrateurs pour prévenir toute fuite de données personnelles.
</p>
</div>
</div>
</div>
<!-- Bannière Comptes Démonstration -->
<div class="demo-banner">
<div class="demo-banner-content">
<h3>💡 Prise en main rapide avec les comptes de démonstration</h3>
<p>
Testez immédiatement les deux niveaux de privilèges préconfigurés dans l'API REST sans avoir à créer de compte manuel :
</p>
</div>
<div class="demo-banner-buttons">
<a href="/login" class="btn btn-demo">
👑 Compte Admin (<code>admin</code>)
</a>
<a href="/login" class="btn btn-demo">
🚗 Compte Utilisateur (<code>user</code>)
</a>
</div>
</div>

View file

@ -22,18 +22,26 @@ $currentUser = $_SESSION['user'] ?? null;
<span class="logo-text"><?= APP_NAME ?></span>
</a>
<!-- Formulaire de recherche par plaque rapide -->
<form action="/cars" method="GET" class="navbar-search">
<input type="text" name="immatriculation" placeholder="Plaque : AB-123-CD..."
value="<?= htmlspecialchars($_GET['immatriculation'] ?? '') ?>" class="navbar-search-input">
<button type="submit" class="navbar-search-btn">🔍</button>
</form>
<!-- Formulaire de recherche par plaque rapide (réservé aux administrateurs selon API v1.1.0) -->
<?php if ($currentUser && ($currentUser['role'] ?? '') === 'admin'): ?>
<form action="/cars" method="GET" class="navbar-search" title="Recherche par immatriculation (Réservé Admin)">
<input type="text" name="immatriculation" placeholder="Recherche plaque : AB-123-CD..."
value="<?= htmlspecialchars($_GET['immatriculation'] ?? '') ?>" class="navbar-search-input">
<button type="submit" class="navbar-search-btn">🔍</button>
</form>
<?php endif; ?>
<nav class="nav-links">
<a href="/cars" class="nav-link">Tous les véhicules</a>
<a href="/" class="nav-link">Accueil</a>
<?php if ($currentUser): ?>
<a href="/cars?mine=1" class="nav-link">Mes véhicules</a>
<?php if (($currentUser['role'] ?? '') === 'admin'): ?>
<a href="/cars" class="nav-link">Parc automobile</a>
<a href="/cars?mine=1" class="nav-link">Mes véhicules</a>
<?php else: ?>
<a href="/cars" class="nav-link">Mes véhicules</a>
<?php endif; ?>
<a href="/cars/create" class="btn btn-primary btn-sm">+ Nouveau véhicule</a>
<div class="user-pill">
@ -44,8 +52,9 @@ $currentUser = $_SESSION['user'] ?? null;
<a href="/logout" class="nav-link-logout" title="Déconnexion">🚪</a>
</div>
<?php else: ?>
<a href="/login?redirect=/cars/create" class="btn btn-primary btn-sm">🚗 Créer une voiture</a>
<a href="/login" class="btn btn-outline btn-sm">Se connecter</a>
<a href="/register" class="btn btn-primary btn-sm">S'inscrire</a>
<a href="/register" class="btn btn-secondary btn-sm">S'inscrire</a>
<?php endif; ?>
</nav>
</div>