Répercussions des modifications de l'api.
This commit is contained in:
parent
448cb25002
commit
5882b049b7
11 changed files with 630 additions and 46 deletions
17
README.md
17
README.md
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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('/');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
36
src/Controllers/HomeController.php
Normal file
36
src/Controllers/HomeController.php
Normal 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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
171
src/Views/home/index.php
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue