Frontend e-commerce¶
Le frontend e-commerce est l'application principale exposée aux visiteurs et aux étudiants. C'est une Single Page Application React 18 servie par un conteneur Nginx, qui consomme exclusivement les APIs REST du backend Spring Boot. Elle joue trois rôles superposés : vitrine marchande réaliste, cible des chaos backend et frontend, et surface de jeu pour le parcours pédagogique.
Sources
frontend/src/App.jsx, frontend/src/main.jsx, frontend/src/services/api.js, frontend/src/pages/*.jsx, frontend/src/i18n/I18nContext.jsx
Stack¶
| Composant | Version / choix | Rôle |
|---|---|---|
| React | 18 (createRoot, Concurrent Mode) |
Rendu |
| React Router | v6 (BrowserRouter, Routes) |
Navigation côté client |
| Vite | 5 | Bundling, dev server, HMR |
| I18nContext maison | zéro dépendance | Traduction FR/EN par import statique |
fetch natif |
— | Appels API avec credentials: 'include' |
| Nginx (runtime) | 1.27 Alpine | Serveur statique + env-inject.sh |
Le point d'entrée main.jsx monte <App> à l'intérieur d'un <I18nProvider> et d'un <React.StrictMode>. App.jsx déclare le <Router> et un AppShell qui gère la navbar, le footer, et monte <PedagogiqueOrchestrator> en dehors des <Routes> afin que l'overlay pédagogique survive à toutes les navigations.
Routes¶
Les routes sont déclarées dans App.jsx. Toutes les routes sauf /s/:token sont rendues à l'intérieur de l'AppShell standard (navbar + footer).
| Chemin | Composant | Protection |
|---|---|---|
/ |
Redirection vers /products |
— |
/products |
Products |
Public |
/products/:id |
ProductDetail |
Public |
/login |
Login |
Redirige vers /products si déjà connecté |
/register |
Register |
Public |
/cart |
Cart |
Public (panier anonyme supporté) |
/checkout |
Checkout |
Exige un utilisateur connecté, sinon redirection vers /login |
/order-confirmation |
OrderConfirmation |
Public (arrivée après création de commande) |
/orders |
MyOrders |
Exige un utilisateur connecté |
/profile |
Profile |
Exige un utilisateur connecté |
/admin |
AdminPortal |
Public intentionnellement — portail de sécurité S10-S12 |
/s/:token |
PedagogiqueSucces |
Page standalone — sans navbar, sans footer, sans orchestrateur |
Les routes dont le chemin commence par /s/ sont identifiées par la constante STANDALONE_PREFIXES et rendues sans le chrome applicatif. Le token dans l'URL sert d'authentification opaque vers la page de succès : il est non-devinable et unique par session.
État global¶
App.jsx gère trois états locaux qui font office d'état global :
user— objet{ id }ounull, rempli au démarrage pargetAuthStatus()cart— tableau des items ajoutés localement, synchronisé avec le backendloading— booléen de démarrage, le temps de résoudre la session HTTP
Quatre callbacks sont passés en props à toutes les pages qui touchent au panier : addToCart, removeFromCart, updateCartQuantity, clearCart. L'ajout au panier appelle d'abord apiAddToCart() côté API puis met à jour l'état local — une approche « optimiste » qui masque la latence réseau.
Service API (services/api.js)¶
Le fichier services/api.js concentre tous les appels réseau du frontend. Aucune page React ne fait de fetch direct : elles passent systématiquement par les fonctions exportées de ce module.
Base URL¶
VITE_API_URL est injectée au démarrage du conteneur Nginx par le script env-inject.sh (même mécanisme que VITE_LANG) : cela permet de construire l'image une seule fois et de la déployer sur plusieurs cibles (dev, prod NAS, VPS) sans rebuild.
Tokens Chaos Scripting¶
Le module maintient un store interne _tokens qui stocke les cinq headers HTTP nécessaires aux niveaux Chaos Scripting (X-Session-Token, X-Action-Token, X-CSRF-Token, X-Step-Token, X-Signature). Le cycle de vie est centralisé :
| Fonction | Rôle |
|---|---|
_extractTokens(response) |
Lit les headers de réponse et met à jour _tokens |
_buildCheckoutHeaders() |
Construit les headers à envoyer sur chaque étape du checkout |
_clearTokens() |
Vide le store au logout |
_throwScriptingError(status, err) |
Propage les erreurs préfixées [Chaos Scripting CODE] |
Seul le tunnel checkout (submitAddress → submitShipping → submitPayment → createOrder) injecte ces headers. Les endpoints panier et catalogue restent libres — c'est une exigence fonctionnelle du Chaos Scripting : « le panier est toujours libre, seul le checkout est protégé ». Voir Chaos Scripting pour le détail.
Familles de fonctions exposées¶
| Famille | Fonctions principales |
|---|---|
| Authentification | login, logout, getAuthStatus |
| Catalogue | getProducts, getProduct, getCategories, searchProducts |
| Panier | addToCart, getCart, updateCartItem, removeFromCart, clearCart |
| Checkout | submitAddress, submitShipping, submitPayment, createOrder |
| Profil / pays | getProfile, updateProfile, getCountries |
| Commandes | verifyCheckoutAccess, getUserOrders, cancelOrder |
Toutes les fonctions retournent des promesses et lancent une Error dont le message est soit une clé i18n (ex : 'api.error.loadProducts') soit une chaîne brute si l'origine est serveur. La résolution de ces clés est déléguée au hook useT().tErr() décrit plus bas.
Propagation du token pédagogique¶
createOrder lit localStorage.getItem('ped_student_token') et, s'il existe, l'envoie dans le header X-Student-Token. Le backend reconnaît alors la commande comme venant d'un parcours pédagogique et répond avec l'agentCode qui sera affiché sur la page de confirmation.
Pages applicatives¶
Les pages sont toutes des composants fonctionnels qui consomment useT() pour l'i18n et les fonctions de services/api.js pour les appels réseau.
| Page | Rôle principal |
|---|---|
Home.jsx |
Landing marchande, redirigée vers /products par défaut |
Products.jsx |
Liste paginée du catalogue, filtre catégorie, recherche texte |
ProductDetail.jsx |
Fiche produit, bouton « Ajouter au panier » |
SearchBar.jsx |
Composant de recherche, utilisé dans l'en-tête catalogue |
CategorySidebar.jsx |
Panneau de filtrage par catégorie |
Cart.jsx |
Panier, modification des quantités, passage au checkout |
Checkout.jsx |
Tunnel multi-étapes : adresse, livraison, paiement, validation |
OrderConfirmation.jsx |
Récapitulatif post-commande, affichage de l'agentCode si parcours actif |
MyOrders.jsx |
Historique des commandes du compte connecté |
Profile.jsx |
Édition du profil utilisateur (civilité, date de naissance, adresse) |
Login.jsx |
Formulaire de connexion |
Register.jsx |
Création de compte |
AdminPortal.jsx |
Portail admin pédagogique S10-S12 — voir Portail admin |
Pages du parcours pédagogique¶
Trois composants et un hook forment le sous-système pédagogique :
usePedagogiqueState.js— hook qui poll/api/chaos/student/statustoutes les 15 secondes et renvoie{ state, refresh }. Il accepte unskipPollingRefpour suspendre le polling pendant les animations de note culturelle.PedagogiqueOrchestrator.jsx— point d'entrée monté une seule fois dansAppShell. Ne rend rien si le parcours est inactif. Navigue automatiquement vers/s/:tokenquand le parcours est complété, via unuseEffectdépendant destate.completed. Réinitialise ses flags locaux (localTimerExpired,navigatedRef) quandstate.levelchange — via unuseEffectpour éviter toute mutation de ref dans le corps du composant (anti-pattern React 18 Concurrent Mode).PedagogiqueTimer.jsx— compteur visuel persistant, reçoittimerRemainingettimerTotaldu hook et notifie l'orchestrateur viaonExpired.PedagogiqueOverlay.jsx— fenêtre modale non-bloquante qui affiche l'énigme courante, son énoncé, son champ de réponse, les indices et les étoiles. AcceptehintsEnabledavec rétrocompatibilité!== false(les anciens payloads serveur sans ce champ restent valides).pedagogique/succes/PedagogiqueSucces.jsx— page standalone de succès, chargée via la route/s/:token.
Voir Concept et architecture du parcours pédagogique pour le détail du fonctionnement.
Internationalisation¶
Le provider I18nContext.jsx charge statiquement les fichiers fr.json et en.json au build, et résout la langue active à partir de la variable Vite VITE_LANG (défaut fr). La langue est fixée au démarrage et ne change pas pendant la session — ce choix est aligné sur la stratégie globale PerfShop (voir Internationalisation).
Deux fonctions sont exposées par le hook useT() :
t(key, replacements)— traduit une clé ; le fallback est le dictionnaire français, puis la clé brutetErr(msg)— résout un message d'erreur qui peut être soit une clé i18n (comme celles levées parservices/api.js), soit une chaîne serveur brute. Cette fonction évite d'afficherapi.error.loadProductsà l'utilisateur final quand la traduction n'est pas trouvée.
Un useEffect met à jour document.documentElement.lang à chaque changement de lang, ce qui permet aux outils d'accessibilité et au référencement de connaître la langue du document.
Diagramme de navigation¶
flowchart TD
Root["/"] --> Products["/products"]
Products --> Detail["/products/:id"]
Detail --> Cart["/cart"]
Products --> Cart
Cart --> CheckoutGuard{Utilisateur<br/>connecté ?}
CheckoutGuard -- non --> Login["/login"]
Login -- après login --> Checkout["/checkout"]
CheckoutGuard -- oui --> Checkout
Checkout --> Confirm["/order-confirmation"]
Confirm --> MyOrders["/orders"]
Products --> Profile["/profile"]
Login --> Register["/register"]
Products --> AdminPortal["/admin<br/>portail S10-S12"]
Confirm -. "si parcours<br/>complété" .-> Success["/s/:token<br/>standalone"]
Chaos Agent frontend¶
App.jsx importe chaos-agent.js pour ses effets de bord — ce script s'exécute immédiatement côté navigateur et installe les handlers nécessaires au Chaos Frontend (CPU burn, memory leak, DOM flood, fetch flood). Il poll /api/chaos/frontend/state toutes les 5 secondes et active ou désactive les perturbations côté client en conséquence. Voir Chaos Frontend pour le détail des comportements injectés.
Build et déploiement¶
Le frontend est distribué sous forme d'image Docker (docker-compose.yml → service perfshop-frontend). Le Dockerfile effectue un build Vite puis copie les assets dans Nginx. Le script env-inject.sh exécuté à l'ENTRYPOINT remplace les marqueurs __VITE_API_URL__ et __VITE_LANG__ dans les fichiers JavaScript compilés — cela permet de publier une image générique et de la configurer à l'exécution.
Pour le build local, voir Build frontend.