Files
backend/documentation/CDC_BackOffice.md
T
2026-07-04 22:47:55 +02:00

15 KiB
Raw Blame History

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)

// 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 :

// 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 :

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

// 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

// 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

# 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)