Files
2026-07-04 22:47:55 +02:00

461 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)