# Voyage Tickets — monorepo

Plateforme de vente de tickets de car en ligne (Côte d'Ivoire). Une seule
réservation peut traverser les trois apps ci-dessous dans la même session.

## Structure du repo

```
voyage-tickets/
├── contracts/openapi.yaml   # ⭐ source de vérité de l'API — voir plus bas
├── api/                      # Laravel 12 — REST API
├── web/                      # Vue 3 + Vite — PWA voyageur / back-office
├── mobile/                   # Flutter — app voyageur + app agent
└── docs/                     # notes de design, ADRs
```

Chaque app a son propre `CLAUDE.md` (chargé automatiquement quand tu lis un
fichier dans ce dossier). Celui-ci ne contient que ce qui est universel.

## Règle n°1 : le contrat d'API est la source de vérité

`contracts/openapi.yaml` décrit tous les endpoints. Le flux pour toute
nouvelle fonctionnalité d'API est TOUJOURS :

1. Implémenter/modifier l'endpoint dans `api/` (route, contrôleur, Form
   Request, API Resource).
2. Mettre à jour `contracts/openapi.yaml` pour qu'il reflète l'endpoint réel
   (à la main pour l'instant ; génération automatique à brancher plus tard).
3. Si la fonctionnalité doit être consommée côté `web/` et/ou `mobile/`,
   créer/mettre à jour le client correspondant dans CES dossiers, en
   respectant le contrat — jamais d'URL ou de shape de réponse inventée.

Si une demande dit "intègre ça côté front" ou "ajoute-le aussi sur mobile"
sans préciser le endpoint, c'est implicitement : suis le contrat existant
dans `contracts/openapi.yaml`.

## Règles universelles

- Lance les commandes (composer, npm, flutter) depuis le dossier de l'app
  concernée, jamais depuis la racine.
- Conventional Commits (`feat:`, `fix:`, `chore:`...). Un commit peut couvrir
  plusieurs apps si c'est la même feature de bout en bout.
- Jamais de secret en dur (clés API, tokens) — toujours en variable
  d'environnement (`.env` côté api/web, fichiers de config ignorés côté
  mobile).
- Toute donnée monétaire (prix, montant payé) est un entier en centimes
  FCFA côté API pour éviter les soucis d'arrondi flottant ; converti à
  l'affichage seulement.
- Les noms de champs JSON sont en snake_case dans l'API (convention
  Laravel), camelCase côté Vue/Flutter — la conversion se fait dans la
  couche client, jamais en dur dans les composants/widgets.

## Glossaire métier (pour éviter les ambiguïtés)

- **Departure** (départ) : un trajet daté/horodaté d'une compagnie sur une
  ligne donnée (ex. Abidjan→Bouaké, 14h00, le 2026-07-01).
- **Booking** (réservation) : l'intention d'achat d'un voyageur sur un
  départ, avec un statut (`pending`, `paid`, `expired`, `cancelled`).
- **Ticket** : généré seulement quand un Booking passe à `paid`. Porte le
  QR code et le code SMS de secours.
- **Hold** : verrou temporaire sur un siège pendant le paiement, avec
  expiration (voir `api/CLAUDE.md`).

## Quand une tâche touche plusieurs apps

Découpe explicitement dans ta réponse : ce qui change dans `api/`, puis
`contracts/openapi.yaml`, puis `web/`, puis `mobile/` — dans cet ordre,
parce que les clients dépendent du contrat et le contrat dépend de l'API.
