Ajout documentation

This commit is contained in:
bastien
2026-07-04 22:47:55 +02:00
parent d95bfa6c58
commit 293f49adfa
6 changed files with 2627 additions and 0 deletions
+460
View File
@@ -0,0 +1,460 @@
# Cahier des Charges — Back-Office Superviseur
## Projet : Laverie Connectée — Interface d'Administration
**Version :** 1.0
**Date :** 2026-06-27
**Stack principale :** Laravel 11 (API déjà existante) + Vue.js 3 + Inertia.js
---
## 1. Contexte & Objectifs
### 1.1 Contexte
Interface web d'administration destinée aux **superviseurs** (exploitants de laveries). Elle s'appuie sur les endpoints `/api/v1/supervisor/*` de l'API Laravel et s'authentifie avec le guard `supervisor`.
Le back-office est une **SPA (Single Page Application)** rendue côté serveur via Inertia.js pour simplifier le déploiement (pas d'API séparée pour le back-office, utilisation directe de Laravel comme backend Inertia).
### 1.2 Utilisateurs cibles
| Rôle | Droits |
|------|--------|
| `admin` | Accès complet multi-établissements, gestion des superviseurs |
| `manager` | Accès complet à son établissement |
| `viewer` | Lecture seule (statistiques, tableau de bord) |
### 1.3 Objectifs fonctionnels
- Visualiser l'occupation en temps réel du parc de machines
- Consulter les statistiques d'utilisation et le chiffre d'affaires
- Gérer la tarification et les promotions
- Recevoir et gérer les alertes machines (pannes, hors ligne)
- (v2) Envoyer des campagnes de notifications aux utilisateurs
---
## 2. Stack Technique
| Composant | Technologie | Version | Justification |
|-----------|-------------|---------|---------------|
| Backend | Laravel 11 | (partagé avec l'API) | Inertia server-side rendering |
| Intégration SPA | Inertia.js | 2.x | SSR Laravel → Vue.js sans API REST dédiée |
| Framework UI | Vue.js | 3.x (Composition API) | Réactivité, écosystème riche |
| Build tool | Vite | 5.x | HMR rapide, bundling optimisé |
| UI Components | PrimeVue | 4.x | DataTable, Chart, Calendar, etc. |
| CSS | Tailwind CSS | 3.x | Utility-first, cohérence design |
| Graphiques | Chart.js via PrimeVue | 4.x | Courbes CA, camembert machines |
| State management | Pinia | 2.x | Store réactif Vue 3 |
| Temps réel | Laravel Echo + Pusher (ou Soketi self-hosted) | — | Mise à jour statut machines |
| Validation forms | VeeValidate + Zod | — | Validation côté client |
| Tables | PrimeVue DataTable | — | Tri, filtre, pagination, export |
| Auth | Laravel Sanctum (session cookie) | — | SPA same-domain |
| Tests back | Pest | — | Tests controllers superviseur |
| Tests front | Vitest + Vue Test Utils | — | Composants Vue |
| E2E | Playwright | — | Parcours critiques superviseur |
---
## 3. Architecture
### 3.1 Structure des fichiers
```
resources/
├── js/
│ ├── app.js # Point d'entrée Inertia
│ ├── bootstrap.js # Echo, Axios
│ ├── Components/
│ │ ├── Layout/
│ │ │ ├── AppLayout.vue # Layout principal (sidebar + topbar)
│ │ │ ├── Sidebar.vue
│ │ │ └── Topbar.vue
│ │ ├── Charts/
│ │ │ ├── RevenueChart.vue
│ │ │ ├── OccupancyChart.vue
│ │ │ └── MachineStatusChart.vue
│ │ ├── Machines/
│ │ │ ├── MachineCard.vue # Carte statut en temps réel
│ │ │ └── MachineGrid.vue
│ │ └── UI/
│ │ ├── StatCard.vue
│ │ ├── AlertBadge.vue
│ │ └── ConfirmDialog.vue
│ │
│ └── Pages/
│ ├── Auth/
│ │ └── Login.vue
│ ├── Dashboard/
│ │ └── Index.vue
│ ├── Machines/
│ │ ├── Index.vue
│ │ └── Show.vue
│ ├── Pricing/
│ │ ├── Index.vue
│ │ └── Edit.vue
│ ├── Promotions/
│ │ ├── Index.vue
│ │ └── Create.vue
│ ├── Stats/
│ │ └── Index.vue
│ └── Settings/
│ └── Index.vue
app/
├── Http/Controllers/Supervisor/ # (déjà définis dans CDC API)
│ ├── DashboardController.php
│ ├── MachineController.php
│ ├── PricingController.php
│ ├── PromotionController.php
│ └── StatsController.php
└── Events/
└── MachineStatusUpdated.php # Broadcast temps réel
```
### 3.2 Routage Laravel (Inertia)
```php
// routes/supervisor.php (middleware: auth:supervisor, inertia)
Route::prefix('supervisor')->name('supervisor.')->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
Route::resource('/machines', MachineController::class)->only(['index','show','update']);
Route::resource('/pricing', PricingController::class);
Route::resource('/promotions', PromotionController::class);
Route::get('/stats', StatsController::class)->name('stats');
Route::get('/settings', SettingsController::class)->name('settings');
});
```
---
## 4. Écrans & Fonctionnalités
### 4.1 Connexion (`/supervisor/login`)
**Formulaire :**
- Email + Mot de passe
- Bouton "Se connecter"
- Lien "Mot de passe oublié"
**Comportement :**
- Session cookie Sanctum (SPA same-domain)
- Redirection automatique vers `/supervisor/dashboard` si déjà connecté
- Rate limiting : 5 tentatives / 10 min
**Données Inertia passées :**
```php
// Aucune — page statique
```
---
### 4.2 Tableau de Bord (`/supervisor/dashboard`)
C'est l'écran central. Il se rafraîchit partiellement en temps réel via Laravel Echo.
#### 4.2.1 Bandeau de KPIs (haut de page)
| KPI | Description | Période |
|-----|-------------|---------|
| CA du jour | Somme des `washes.cost` du jour | Aujourd'hui |
| Lavages du jour | Nombre de `washes` terminés | Aujourd'hui |
| Taux d'occupation | % machines actives / total | Temps réel |
| Solde moyen rechargé | Moyenne des top-ups | Cette semaine |
Chaque KPI affiche la variation vs. la veille (flèche + couleur).
#### 4.2.2 Grille des Machines (temps réel)
- Grille de cartes, une carte par machine
- Mise à jour via **WebSocket** (Laravel Echo, channel `establishment.{id}`, event `MachineStatusUpdated`)
- Chaque carte affiche :
- Nom et type de machine (icône)
- Statut (badge coloré)
- Temps restant si en cours (countdown)
- Utilisateur en cours (avatar anonymisé : initiales)
- Dernière activité
**Couleurs statut :**
| Statut | Couleur |
|--------|---------|
| `available` | 🟢 Vert |
| `running` | 🔵 Bleu + animation pulse |
| `reserved` | 🟡 Jaune |
| `maintenance` | 🟠 Orange |
| `offline` | 🔴 Rouge |
**Actions rapides (rôle manager/admin) :**
- Mettre en maintenance
- Forcer disponible
- Voir le détail
#### 4.2.3 Alertes Actives
Panel latéral ou section dédié listant :
- Machines hors ligne (dernier heartbeat > 5 min)
- Machines en erreur signalée
- Réservations no-show récentes
Chaque alerte : icône, machine concernée, heure, bouton "Traité".
#### 4.2.4 Graphique d'occupation journalière
Courbe Chart.js (via PrimeVue) :
- Axe X : heures (00h → 23h)
- Axe Y : % d'occupation
- Deux courbes : Aujourd'hui vs Moyenne 30 jours
**Données Inertia passées :**
```php
Inertia::render('Dashboard/Index', [
'machines' => MachineResource::collection($machines),
'kpis' => $dashboardService->getTodayKpis($establishment),
'alerts' => AlertResource::collection($activeAlerts),
'occupancy_chart' => $statsService->getHourlyOccupancy($establishment, today()),
]);
```
---
### 4.3 Statistiques (`/supervisor/stats`)
Écran dédié à l'analyse de données avec filtres temporels.
#### 4.3.1 Filtres
- Période : Aujourd'hui / 7 jours / 30 jours / 3 mois / Personnalisée (date picker)
- Machine : Toutes / Par type / Par machine spécifique
#### 4.3.2 Blocs de statistiques
**Chiffre d'affaires**
- Graphique en barres : CA par jour sur la période
- Ligne de tendance (moyenne mobile 7 jours)
- Total période, meilleur jour, pire jour
**Utilisation des machines**
- Graphique camembert : Répartition par type de machine
- Nombre de cycles par machine (tableau trié)
- Taux d'utilisation horaire moyen
**Utilisateurs**
- Nouveaux inscrits sur la période
- Utilisateurs actifs (au moins 1 lavage)
- Top 10 utilisateurs (anonymisés : "Utilisateur #XXX")
**Réservations**
- Nombre de réservations vs. lavages directs
- Taux de no-show
- Taux d'annulation
**Rechargements**
- CA par source de paiement (Stripe, etc.)
- Montant moyen rechargé
- Fréquence de rechargement
#### 4.3.3 Export
- Export CSV des données brutes de la période sélectionnée
- Export PDF du rapport (via impression navigateur, layout print-optimized)
---
### 4.4 Gestion de la Tarification (`/supervisor/pricing`)
#### 4.4.1 Vue Liste
Tableau (PrimeVue DataTable) listant toutes les règles tarifaires :
| Machine | Jours | Horaire | Prix | Exclusive App | Actions |
|---------|-------|---------|------|---------------|---------|
| Lave-linge 7kg | Semaine | 08h-12h | 3.50€ | Non | ✏️ 🗑 |
| Lave-linge 7kg | Semaine | 18h-22h | 4.50€ | Oui | ✏️ 🗑 |
- Filtrable par machine, type de jour
- Tri par colonne
- Badge "Actif" / "Inactif" si les horaires sont hors plage
#### 4.4.2 Formulaire Création / Édition
**Champs :**
- Machine (select, avec recherche)
- Jours applicables (checkboxes : LunVen / SamDim / Jours fériés / Tous)
- Heure de début / fin (time pickers, validation : début < fin)
- Prix en euros (input numérique, 2 décimales, min 0.50€)
- Label (ex: "Heure creuse", "Heure pleine")
- Exclusive app ✓ (si coché, ce tarif n'est visible que via l'application)
**Validation :**
- Vérification de non-chevauchement avec les règles existantes sur la même machine
- Avertissement si la plage couvre toute la journée sans tarif de base restant
**Règle de priorité affichée :**
> "Une règle spécifique à une machine a priorité sur une règle par type de machine."
---
### 4.5 Gestion des Promotions (`/supervisor/promotions`)
#### 4.5.1 Vue Liste
Tableau avec onglets : **Actives** / **À venir** / **Terminées**
Colonnes : Type machine, Réduction, Début, Fin, Description, Statut, Actions.
#### 4.5.2 Formulaire Création
**Champs :**
- Type de machine concernée (Lave-linge petit / grand / Sèche-linge / Tous)
- Type de réduction :
- Pourcentage (ex: -20%)
- Montant fixe (ex: -0.50€)
- Valeur de la réduction
- Date/heure de début (datetime picker)
- Date/heure de fin (datetime picker)
- Description interne (texte libre, visible uniquement dans le back-office)
**Validation :**
- Date de fin > Date de début
- Réduction % : 190%
- Réduction fixe : ne peut pas dépasser le prix minimum (0.50€ minimum après remise)
- Avertissement si chevauchement avec une promotion existante du même type
**Impact affiché en temps réel :**
Aperçu du nouveau prix après promotion pour chaque règle tarifaire concernée.
---
### 4.6 Détail Machine (`/supervisor/machines/{uuid}`)
- Informations générales (nom, type, QR code, ID LLDP)
- Statut actuel avec boutons d'action (Maintenance / Disponible)
- Historique des 30 derniers cycles (tableau paginé)
- Grille tarifaire appliquée
- Uptime sur 30 jours (%)
- Historique des alertes / pannes (30 derniers jours)
---
## 5. Temps Réel — Laravel Echo
### 5.1 Configuration
```javascript
// bootstrap.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
wsHost: import.meta.env.VITE_PUSHER_HOST, // Soketi en self-hosted
wsPort: 6001,
forceTLS: false,
enabledTransports: ['ws', 'wss'],
});
```
### 5.2 Channels & Events
| Channel | Event | Payload | Déclencheur |
|---------|-------|---------|-------------|
| `private-establishment.{id}` | `MachineStatusUpdated` | `{ machine_uuid, status, current_user_initials, cycle_ends_at }` | Heartbeat LLDP + fin de cycle |
| `private-establishment.{id}` | `NewAlert` | `{ type, machine_uuid, message, created_at }` | Offline détecté, erreur machine |
| `private-establishment.{id}` | `KpiUpdated` | `{ ca_today, washes_today, occupancy_rate }` | Fin de chaque lavage |
### 5.3 Implémentation Vue
```javascript
// DashboardController.vue (Composition API)
onMounted(() => {
window.Echo
.private(`establishment.${props.establishment.id}`)
.listen('MachineStatusUpdated', (event) => {
machineStore.updateMachineStatus(event.machine_uuid, event);
})
.listen('NewAlert', (event) => {
alertStore.addAlert(event);
});
});
onUnmounted(() => {
window.Echo.leave(`establishment.${props.establishment.id}`);
});
```
---
## 6. Sécurité
### 6.1 Authentification
- Session cookie Sanctum (SameSite=Lax, Secure en prod)
- CSRF token sur toutes les mutations
- Timeout de session : 8 heures d'inactivité
### 6.2 Autorisation (Policies Laravel)
```
MachinePolicy::update() → role manager ou admin ET même établissement
PricingPolicy::create() → role manager ou admin
PromotionPolicy::delete() → role admin seulement
StatsPolicy::view() → tous les rôles
```
### 6.3 Données
- Les données utilisateurs affichées sont **anonymisées** (initiales, jamais nom complet ou email)
- Logs d'audit sur toutes les mutations (qui a modifié quoi et quand)
---
## 7. Responsive & Accessibilité
- Layout responsive : sidebar collapsible sur tablette, menu bottom sur mobile
- Taille minimale cible : tablette 768px (utilisation en mobilité sur le terrain)
- Contraste WCAG AA sur tous les textes
- Navigation clavier complète (focus visible)
- Attributs ARIA sur les graphiques (descriptions alternatives)
---
## 8. Tests
| Type | Outil | Portée |
|------|-------|--------|
| Tests unitaires | Pest | Services stats, services tarification |
| Tests controllers | Pest | Tous les controllers superviseur (mocks auth) |
| Tests composants | Vitest + VTU | StatCard, MachineCard, PricingForm |
| Tests E2E | Playwright | Login, modifier un tarif, créer une promo, consulter stats |
**Scénarios Playwright critiques :**
1. Login échoué (mauvais mot de passe) → message d'erreur
2. Login réussi → redirection dashboard
3. Créer une règle tarifaire → vérification dans la liste
4. Créer une promotion avec chevauchement → message d'avertissement
5. Mise en maintenance d'une machine → badge mis à jour en temps réel
---
## 9. Variables d'environnement spécifiques
```env
# Pusher / Soketi (temps réel)
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
```
---
## 10. Livrables attendus
- [ ] Code source Vue.js (dans le même dépôt Laravel ou dépôt séparé)
- [ ] Migrations et seeders pour les superviseurs de démo
- [ ] Documentation des rôles et permissions
- [ ] Tests Pest controllers (couverture > 80%)
- [ ] Tests Playwright (5 parcours critiques)
- [ ] Guide d'utilisation superviseur (PDF ou page dans le back-office)
- [ ] `README.md` avec instructions de déploiement front (Vite build + assets)