DOCS/section-event-map-setup.md
Thomas Fransolet c69cb15ba2 D1 corrigé : le bug offline nº1 était 283 lignes de code mort
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 16:57:23 +02:00

392 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Guide : SectionEvent, SectionMap & SectionGame — Configuration et affichage
> Cas d'usage typiques : **carnaval**, **parc à thème**, **festival**, **musée en plein air**, **escape game**
---
## Vue d'ensemble
La plateforme permet de proposer à un visiteur une **vue carte** ou une **vue liste** pour naviguer dans un événement géolocalisé, ou de lancer un **escape game** (géolocalisé ou non). Les types de section principaux :
| Type | Usage | Contient |
|---|---|---|
| `SectionMap` | Carte interactive ou liste de POI | GeoPoints, catégories, parcours guidés |
| `SectionEvent` | Événement avec dates + programme | Dates, carte de base, programme horaire, annotations carte, parcours guidés |
| `SectionGame` | Jeu interactif | Puzzle, puzzle glissant, **escape game** (géolocalisé ou non) |
---
## Modèles de données (manager-service)
### Section (base commune)
`ManagerService/Data/Section.cs`
Tous les types héritent de cette classe :
- `Id`, `Label`, `Title`, `Description`, `Order`, `Type`
- `ConfigurationId`, `InstanceId`, `IsActive`
- `IsSubSection`, `ParentId` — pour sections imbriquées
- `Latitude`, `Longitude`, `BeaconId`, `MeterZoneGPS` — déclenchement géoloc
### SectionMap
`ManagerService/Data/SubSection/SectionMap.cs`
```
SectionMap
├── MapMapProvider → Google ou MapBox
├── MapMapType → Normal / Satellite / Terrain / Hybrid (Google)
├── MapTypeMapbox → Standard / Streets / Outdoors / Light / Dark / Satellite
├── MapZoom → Niveau de zoom par défaut (défaut: 18)
├── MapCenterLatitude/Longitude
├── IsListViewEnabled → ⭐ toggle carte / liste dans la visitapp
├── MapCategories[] → Catégories de filtrage des points
└── MapPoints[] → GeoPoints (POI sur la carte)
```
**GeoPoint** (POI) :
- `Title`, `Description`, `Contents[]`
- `Geometry` (Point / Polyline / Circle / Polygon)
- `CategorieId` — pour le filtrage
- `SectionMapId` **ou** `SectionEventId` (multi-parent)
- `Schedules`, `Prices`, `Phone`, `Email`, `Site`
### SectionEvent
`ManagerService/Data/SubSection/SectionEvent.cs`
```
SectionEvent
├── StartDate / EndDate
├── BaseSectionMapId → ⭐ lien vers une SectionMap (carte de fond)
├── BaseMap → navigation property vers la SectionMap
├── GlobalMapAnnotations[]→ annotations au niveau de l'événement entier
├── Programme[] → blocs horaires
└── ParcoursIds[] → ⚠️ INUTILISÉ (voir GuidedPath.SectionEventId)
```
**ProgrammeBlock** (bloc horaire) :
- `StartTime`, `EndTime`
- `Title`, `Description` (traduits)
- `MapAnnotations[]` — annotations spécifiques à ce créneau
**MapAnnotation** :
- `GeometryType` : Point / Polyline / Circle / Polygon
- `Geometry` (PostGIS)
- `Type` (traduit) : "first_aid", "parking", "scène", etc.
- `PolyColor`, `Icon`, `IconResourceId`
- `SectionEventId` (null = annotation de bloc)
### SectionGame
`ManagerService/Data/SubSection/SectionGame.cs`
```
SectionGame
├── GameType → Puzzle | SlidingPuzzle | Escape ⭐
├── GameMessageDebut[] → message d'intro (traduit + media)
├── GameMessageFin[] → message de fin (traduit + media)
├── GamePuzzleImageId → image source du puzzle
├── GamePuzzleRows → nombre de lignes (défaut: 3)
└── GamePuzzleCols → nombre de colonnes (défaut: 3)
```
Pour le mode `Escape`, les parcours guidés sont portés par `GuidedPath.SectionGameId` (pas de champ direct sur SectionGame).
### GuidedPath (Parcours guidé)
`ManagerService/Data/SubSection/GuidedPath.cs`
Un parcours peut être attaché à une carte **ou** un événement **ou** un jeu :
```
GuidedPath
├── SectionMapId → lié à une SectionMap (nullable)
├── SectionEventId → lié à un SectionEvent (nullable)
├── SectionGameId → lié à un SectionGame (nullable) ⭐ escape game
├── IsLinear → ordre obligatoire
├── RequireSuccessToAdvance → résoudre une énigme pour avancer
├── HideNextStepsUntilComplete
├── Order
└── Steps[] → GuidedStep
```
**GuidedStep** (étape du parcours) :
- `Geometry`, `ZoneRadiusMeters` — zone de déclenchement géoloc
- `TriggerGeoPointId` — peut référencer un GeoPoint
- `IsHiddenInitially`, `IsStepLocked`
- `IsStepTimer`, `TimerSeconds`, `TimerExpiredMessage`
- `QuizQuestions[]` — énigmes/questions à résoudre pour valider l'étape
---
## Endpoints API (manager-service)
### SectionEvent → `/api/sectionevent/{id}/...`
| Méthode | Route | Description |
|---|---|---|
| GET | `/{id}/programmes` | Tous les blocs horaires |
| POST | `/{id}/programmes` | Créer un bloc |
| PUT | `/programmes` | Modifier un bloc |
| DELETE | `/programmes/{blockId}` | Supprimer un bloc |
| GET | `/{id}/global-map-annotations` | Annotations globales |
| POST | `/{id}/global-map-annotations` | Ajouter annotation globale |
| DELETE | `/map-annotations/{annotationId}` | Supprimer annotation |
### SectionMap → `/api/sectionmap/{id}/...`
| Méthode | Route | Description |
|---|---|---|
| GET | `/{id}/points` | Tous les GeoPoints |
| POST | `/{id}/points` | Créer un GeoPoint |
| PUT | `/points` | Modifier un GeoPoint |
| DELETE | `/points/{pointId}` | Supprimer un GeoPoint |
| GET | `/{id}/guided-path` | Tous les parcours |
| POST | `/{id}/guided-path` | Créer un parcours |
| PUT | `/guided-path` | Modifier un parcours |
| DELETE | `/guided-path/{pathId}` | Supprimer un parcours |
| GET | `/guided-path/{pathId}/guided-step` | Étapes d'un parcours |
| POST | `/guided-path/{pathId}/guided-step` | Créer une étape |
---
## Configuration dans le manager-app
### EventConfig
`manager-app/lib/Screens/Configurations/Section/SubSection/Event/event_config.dart`
Interface de configuration d'un SectionEvent :
1. **Dates** (début / fin avec date+heure picker)
2. **Carte de base** — dropdown des SectionMap disponibles dans la configuration → sette `baseSectionMapId`
3. **Annotations globales** — marqueurs/zones visibles sur toute la durée de l'événement
4. **Programme** — blocs horaires avec titre, description, heures
5. **Parcours guidés** — via `ParcoursConfig(isEvent: true, parentId: eventDTO.id!)`
### MapConfig
`manager-app/lib/Screens/Configurations/Section/SubSection/Map/map_config.dart`
Deux onglets :
- **"Points d'intérêt"** — CRUD sur les GeoPoints avec filtrage par catégorie
- **"Parcours"** — via `ParcoursConfig(isEvent: false, parentId: mapId)`
Paramètre clé : `isListViewEnabled` → détermine si la visitapp affiche la carte ou une liste.
### GameConfig
`manager-app/lib/Screens/Configurations/Section/SubSection/Game/game_config.dart`
Interface à **3 onglets** selon le `GameType` :
1. **Puzzle** — configuration lignes/colonnes + image source
2. **Puzzle Glissant** — idem
3. **Escape Game** — intègre `ParcoursConfig(isEscapeMode: true, parentId: gameId)`
### ParcoursConfig
`manager-app/lib/Screens/Configurations/Section/SubSection/Parcours/parcours_config.dart`
Composant réutilisé pour maps, events **et escape games** via 3 flags :
| Appel | Contexte | FK créée sur GuidedPath |
|---|---|---|
| `isEscapeMode: true` | SectionGame | `sectionGameId` |
| `isEvent: true` | SectionEvent | `sectionEventId` |
| les deux à false | SectionMap | `sectionMapId` |
- Liste réordrable (drag & drop) des parcours
- Création/édition via `showGuidedPathEditor` (`Parcours/guided_path_editor.dart`, refonte DB4 du 2026-08-11 — remplace les trois popups empilées `showNewOrUpdate…`)
- Suppression avec confirmation
`showGuidedPathEditor` ouvre une **fenêtre unique** avec un rail d'étapes à gauche et le panneau de détail à droite. On y configure :
- Titre / description (multilingue)
- Options : linéaire, réussite requise, cacher les étapes suivantes
- Les étapes, avec géolocalisation et quiz optionnel — la question se déplie dans le panneau de l'étape
Tout est **enregistré à la saisie** (débounce ~700 ms) : pas de bouton « Sauvegarder ». Le parcours est créé en base à la première modification, et les questions partent avec leur étape (elles n'ont pas d'endpoint propre).
---
## Affichage dans la visitapp
### Routing des sections
`mymuseum-visitapp/lib/Screens/section_page.dart`
```dart
switch(sectionDTO.type) {
case SectionType.Map: MapPage(mapDTO)
case SectionType.Agenda: AgendaPage(agendaDTO)
case SectionType.Game: GamePage(gameDTO)
// SectionType.Event n'est pas encore géré nativement
}
```
> ⚠️ **Limitation actuelle** : `SectionType.Event` n'a pas de page dédiée dans la visitapp. Les événements passent actuellement par `SectionType.Agenda`.
### MapPage
`mymuseum-visitapp/lib/Screens/Sections/Map/map_page.dart`
- `mapDTO.isListViewEnabled` → bascule carte / liste
- Provider : Google (`GoogleMapView`) ou MapBox (`MapBoxView` / `FlutterMapView`)
- Filtrage par catégorie via `GeoPointFilter`
- Tap sur point → détail du GeoPoint
### AgendaPage (événements)
`mymuseum-visitapp/lib/Screens/Sections/Agenda/agenda_page.dart`
- Grille 2 colonnes d'`EventAgenda` items
- Filtrage par mois
- Tap → `EventPopup` avec détails
### GamePage (jeux)
`mymuseum-visitapp/lib/Screens/Sections/Game/game_page.dart`
- Reçoit `GameDTO` avec `gameType`
- Affiche le message d'intro (`GameMessageDebut`) au lancement
- **Modes Puzzle / SlidingPuzzle** : découpe l'image en pièces, gameplay drag & drop, message de fin à la complétion
- **Mode Escape** : ⚠️ pas encore implémenté nativement dans la visitapp — `gameType` n'est pas encore dispatché vers une UI dédiée
---
## Exemple concret : Carnaval / Parc à thème
### Setup manager-app
#### Étape 1 — Créer la SectionMap "Plan du site"
- Provider : MapBox (Outdoors pour un parc)
- `isListViewEnabled: false` (vue carte par défaut)
- Ajouter les GeoPoints :
- Manèges / attractions → catégorie "Attractions"
- Restauration → catégorie "Nourriture"
- Premiers secours → catégorie "Services"
- Parkings → catégorie "Accès"
#### Étape 2 — Créer la SectionEvent "Carnaval 2025"
- Dates : 17 mars 2025
- **Carte de base** : sélectionner "Plan du site" (créé à l'étape 1)
- **Annotations globales** :
- Zones de parade (Polyline)
- Scène principale (Polygon)
- Points de premiers secours (Point + icône)
- **Programme** :
- "Parade d'ouverture" — 1 mars 14h-16h (annotation : tracé de la parade)
- "Spectacle de feu" — chaque soir 21h-22h (annotation : scène)
- "Feux d'artifice" — 7 mars 22h (annotation : point de lancement)
- **Parcours guidés** :
- "Circuit famille" (linéaire) — 5 étapes vers les attractions family-friendly
- "Tour photo" (non-linéaire) — spots photos librement visitables
- "Chasse au trésor" (linéaire, réussite requise) — énigmes géolocalisées
### Ce que voit le visiteur
**Vue carte** (baseSectionMapId défini) :
- Carte du site avec tous les GeoPoints
- Filtrage par catégorie (attractions, food, services...)
- Annotations globales superposées (zones, tracés)
- Selon l'heure : annotations du bloc programme actif surlignées
- Onglet / bouton "Parcours" pour lancer un itinéraire guidé
**Vue liste** (`isListViewEnabled: true` sur la SectionMap) :
- Liste des attractions avec détails
- Horaires par catégorie
---
## Escape Game — fonctionnement détaillé
### Deux variantes
#### Escape game non-géolocalisé
- Le visiteur progresse en résolvant des énigmes/quiz à chaque étape, **sans se déplacer physiquement**
- Les `GuidedStep` n'ont pas de `Geometry` ni de `ZoneRadiusMeters`
- Typiquement utilisé en intérieur (salle d'exposition, tablette fixe, kiosque)
- Navigation : l'app passe à l'étape suivante dès que la condition est remplie (`RequireSuccessToAdvance`)
#### Escape game géolocalisé
- Le visiteur doit **se rendre physiquement à un endroit** pour débloquer une étape
- Les `GuidedStep` ont une `Geometry` (Point avec `ZoneRadiusMeters`, ou Polygon)
- Le déclenchement peut aussi passer par un `TriggerGeoPointId` (un POI de la carte)
- Typiquement utilisé en extérieur (parc, ville, musée multi-salles)
- L'app détecte l'entrée dans la zone → débloque l'étape
### Configuration d'un escape game dans le manager-app
1. Créer une `SectionGame` avec `GameType: Escape`
2. Dans l'onglet **Escape Game** → ajouter un ou plusieurs **Parcours** via `ParcoursConfig(isEscapeMode: true)`
3. Pour chaque parcours, configurer :
- **isLinear** : oui = ordre imposé, non = étapes libres
- **requireSuccessToAdvance** : oui = résoudre l'énigme avant d'aller au suivant
- **hideNextStepsUntilComplete** : oui = étapes masquées tant que non débloquées
4. Pour chaque **étape** du parcours :
- Titre + description (multilingue)
- (Optionnel) Géométrie + rayon → pour version géolocalisée
- (Optionnel) `TriggerGeoPointId` → lier à un POI existant
- `IsStepTimer` + `TimerSeconds` → compte à rebours
- `QuizQuestions[]` → énigmes/questions à résoudre
### Structure de données
```
SectionGame (GameType: Escape)
└── GuidedPath[] (via SectionGameId)
├── IsLinear, RequireSuccessToAdvance, HideNextStepsUntilComplete
└── GuidedStep[]
├── Title, Description
├── Geometry + ZoneRadiusMeters ← géoloc (optionnel)
├── TriggerGeoPointId ← lien POI (optionnel)
├── IsStepTimer, TimerSeconds ← countdown (optionnel)
└── QuizQuestions[] ← énigmes à résoudre
```
### Exemple : Escape game géolocalisé dans un parc à thème
**Scénario** : "La Quête du Trésor" — 5 énigmes réparties dans le parc, à résoudre dans l'ordre.
**Config** :
- `SectionGame``GameType: Escape`
- 1 `GuidedPath` : `isLinear: true`, `requireSuccessToAdvance: true`, `hideNextStepsUntilComplete: true`
- 5 `GuidedStep` :
1. Zone fontaine centrale (polygon 10m) → quiz "Que représente cette statue ?"
2. Zone entrée château (point, rayon 15m) → quiz + timer 60s
3. Zone jardin secret (polygon) → quiz "Comptez les rosiers rouges"
4. Zone boutique (point, rayon 5m) → quiz avec image indice
5. Zone scène finale (polygon) → quiz final → débloque le message de fin
**Ce que voit le visiteur** *(à implémenter dans la visitapp)* :
- Carte avec marqueur de la prochaine étape
- En approchant → notification de déclenchement
- Énigme s'affiche → réponse validée → étape suivante révélée
- Toutes étapes complétées → `GameMessageFin` affiché
---
## Relation `ParcoursIds` — clarification
`SectionEvent.ParcoursIds` (liste de strings en JSONB) est **inutilisé** :
- La relation réelle est portée par `GuidedPath.SectionEventId` (FK)
- L'app charge les parcours via `sectionMapGetAllGuidedPathFromSection(eventId)`
- `ParcoursIds` est une relique de conception initiale, peut être supprimé
---
## Bugs / limitations connus
| Problème | Détail |
|---|---|
| `SectionType.Event` non géré dans visitapp | Passe par `SectionType.Agenda`, pas de page dédiée |
| Escape game non affiché dans visitapp | `GamePage` ne dispatche pas sur `gameType == Escape` |
| `GuidedPath.SectionGameId` non mis à jour | `UpdateGuidedPath()` dans le controller ne copie pas `sectionGameId` depuis le DTO |
| `SectionEvent.ParcoursIds` inutilisé | Relique, remplacé par `GuidedPath.SectionEventId` |
---
## Hiérarchie de types Section
```
Section (base)
├── SectionEvent ← événement avec carte + programme
├── SectionMap ← carte / liste de POI
├── SectionAgenda ← agenda d'événements externe (JSON)
├── SectionArticle
├── SectionGame ← puzzle | puzzle glissant | escape game (géoloc ou non)
├── SectionQuiz
├── SectionVideo
├── SectionWeb
├── SectionPdf
├── SectionMenu
├── SectionSlider
└── SectionWeather
```