392 lines
16 KiB
Markdown
392 lines
16 KiB
Markdown
# 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 : 1–7 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
|
||
```
|