# web/ — Vue 3 (PWA)

Ce fichier complète le CLAUDE.md racine. Stack : Vue 3 (`<script setup>`),
Vite, Pinia, vue-router, PWA (vite-plugin-pwa).

## Commandes

```bash
npm install
npm run dev
npm run build
npm run test           # si configuré (vitest)
```

## Structure attendue

```
web/src/
├── api/                # clients HTTP générés/maintenus depuis le contrat
│   └── client.ts       # instance axios/fetch de base (baseURL, intercepteurs)
├── composables/         # un composable par ressource métier
│   ├── useDepartures.ts
│   ├── useBookings.ts
│   └── usePayments.ts
├── stores/              # Pinia (auth, panier de réservation en cours)
├── views/
├── components/
└── service-worker/      # cache du billet pour consultation hors-ligne
```

## Conventions de code

- **Jamais d'appel `fetch`/`axios` directement dans un composant.** Tout
  appel API passe par un composable dans `composables/`, qui lui-même
  passe par `api/client.ts`.
- Les composables s'alignent sur `../contracts/openapi.yaml` : mêmes noms
  de champs, mêmes shapes de réponse, mêmes codes d'erreur gérés.
- Conversion snake_case (API) → camelCase (front) centralisée dans
  `api/client.ts` (intercepteur de réponse), jamais répétée composable par
  composable.
- Gestion des montants : reçus en centimes FCFA depuis l'API, formatés en
  FCFA uniquement dans la couche d'affichage (un composable
  `useCurrency.ts` ou équivalent).

## PWA & offline

- Le **billet acheté (QR + code SMS)** doit rester consultable sans
  connexion : mise en cache explicite côté service worker (IndexedDB ou
  Cache API) dès que le paiement est confirmé, pas seulement un cache HTTP
  générique.
- Le reste de l'app (recherche, paiement) nécessite une connexion — ne pas
  sur-ingénierer l'offline au-delà du billet pour le MVP.

## Quand on te demande d'intégrer un nouvel endpoint

1. Vérifie le endpoint dans `../contracts/openapi.yaml`.
2. Crée/étends le composable correspondant dans `composables/`.
3. Branche-le dans la vue concernée.
4. Si le contrat n'existe pas encore pour cet endpoint, dis-le explicitement
   plutôt que d'inventer la forme de la réponse.
