Ajout documentation
This commit is contained in:
@@ -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 : Lun–Ven / Sam–Dim / 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 % : 1–90%
|
||||
- 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)
|
||||
Reference in New Issue
Block a user