Import initial de la documentation : statut, roadmap, plans V1/V2, specs verticales (creche, sport), audits securite, plan de test, analyse concurrentielle et maquettes de design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
20 KiB
Parité manager-app → apps visiteur
Question à laquelle ce document répond : tout ce qu'un client peut configurer dans
manager-appest-il réellement visible dansmymuseum-visitapp(mobile) etvisitapp-web?Audit du 2026-08-05, fait sur le code réel (pas sur la doc). Méthode : extraction des champs de tous les
ToDTO()du backend (manager-service/ManagerService/Data/+Data/SubSection/), puis vérification de chaque champ dans les 3 bases de code, puis lecture manuelle de chaque écart pour éliminer les faux positifs (renommagessnake_case→camelCase, champs chargés via un endpoint dédié, champs dérivés typeimageResourceId→imageUrl).
tablet-appest hors périmètre de cet audit (kiosk, cycle de release séparé) mais apparaît en colonne indicative quand l'information est utile.
1. Couverture par type de section
Les 13 types de section du backend (DTOs/SectionType.cs) sont dispatchés dans les deux apps visiteur — aucun type n'ouvre un écran vide.
| SectionType | Config manager-app | mymuseum-visitapp | visitapp-web |
|---|---|---|---|
| Map | Map/map_config.dart |
Screens/Sections/Map/ |
MapSection.tsx |
| Slider | Slider/ |
Sections/Slider/ |
SliderSection.tsx |
| Video | Video/ |
Sections/Video/ |
VideoSection.tsx |
| Web | Web/ |
Sections/Web/ |
WebSection.tsx |
| Menu | Menu/ |
Sections/Menu/ |
MenuSection.tsx |
| Quiz | Quizz/quizz_config.dart |
Sections/Quiz/ |
QuizSection.tsx |
| Article | Article/ |
Sections/Article/ |
ArticleSection.tsx |
Pdf/ |
Sections/PDF/ |
PdfSection.tsx |
|
| Game | Game/game_config.dart |
Sections/Game/game_page.dart |
GameSection.tsx (+ game/PuzzleGame, game/SlidingPuzzle) |
| Agenda | Agenda/agenda_config.dart |
Sections/Agenda/ |
AgendaSection.tsx |
| Weather | Weather/ |
Sections/Weather/ |
WeatherSection.tsx |
| Event | Event/event_config.dart |
Sections/Event/ |
EventSection.tsx (+ event/EventMap) |
| Parcours | SectionParcours/section_parcours_config.dart |
Sections/Parcours/parcours_page.dart |
ParcoursSection.tsx |
Conclusion niveau 1 : la couverture par type de section est complète dans les deux apps. Les écarts sont tous au niveau champ ou sous-comportement, décrits ci-dessous.
2. Écarts confirmés — mymuseum-visitapp (mobile)
Champ configurable dans manager-app, sans effet dans l'app mobile.
| # | Champ / comportement | Configuré dans | Réalité mobile | Web | Gravité |
|---|---|---|---|---|---|
SectionMap.centerLatitude / centerLongitude ("Centrer sur") |
map_config.dart:612-622 (picker sur mini-carte) |
Ignoré. flutter_map_view.dart:105, map_box_view.dart:202, google_map_view.dart:127 centrent sur mapDTO.latitude/longitude — c'est-à-dire le point GPS de la section (champ beacon/geofence), pas le centre choisi |
✅ MapSection.tsx:62-63 |
Haute — le client règle le cadrage de sa carte et rien ne bouge sur mobile | |
GeoPoint.schedules (horaires d'un point d'intérêt) |
showNewOrUpdateGeoPoint.dart |
Jamais affiché. marker_view.dart affiche title, description, contents, imageUrl, prices, phone, email, site — pas schedules |
✅ MapSection.tsx:419 (InfoBlock "Horaires") |
Haute — donnée saisie et invisible | |
| M3 | Section.meterZoneGPS (rayon de déclenchement) |
section_detail_screen.dart:650 |
Ignoré. configuration_page.dart:43 utilise une constante en dur : int meterToBeacon = 100; |
n/a (pas de BLE en web) | Moyenne — le rayon configuré par section n'a aucun effet |
GuidedStep.isHiddenInitially |
showNewOrUpdateGuidedStep.dart:203 (toggle) |
Aucun consommateur, dans aucune des deux apps | ❌ idem | Moyenne — toggle mensonger dans l'UI client | |
| M5 | ApplicationInstance.mainImageUrl |
manager-app | Non utilisé : home_3.0.dart:117 affiche config.imageSource |
✅ web utilise mainImageUrl |
Moyenne — l'image d'accueil diffère entre les deux canaux pour la même config |
| backend onboarding | Non applicable | ✅ app/[slug]/layout.tsx:29 → TrialWatermark |
❌ Faux positif — écarté le 2026-08-07 : l'essai gratuit ne concerne que le plan Essentiel, qui est web + kiosk sans app native. Un essai mobile ne peut pas exister, donc rien à afficher | ||
Mode contenu d'un parcours (ShowMap = false) : isStepLocked et hideNextStepsUntilComplete |
showNewOrUpdateGuidedStep.dart / showNewOrUpdateGuidedPath.dart |
Non implémentés dans guided_path_content_progression_page.dart : pas de verrouillage d'étape, pas de masquage des étapes futures. guided_path_map_progression_page.dart les gère (l.139, 409, 257). ✅ En revanche isLinear (l.119), requireSuccessToAdvance (l.78) et le timer (l.73) sont bien gérés en mode contenu |
❌ même trou côté web, plus large (voir W6/W7) | Haute — dans un escape indoor, le visiteur atteint les énigmes non résolues | |
isLinear = false (navigation libre : « le visiteur choisit ses étapes dans l'ordre qu'il veut ») |
showNewOrUpdateGuidedPath.dart |
N'existe dans aucune app : isLinear=false ne fait qu'ajouter un bouton Précédent. Aucun moyen de sauter à l'étape de son choix — les pins de carte sélectionnent une étape sans y naviguer |
❌ idem | Moyenne — l'option est proposée mais ne produit pas le comportement attendu |
3. Écarts confirmés — visitapp-web
| # | Champ / comportement | Réalité web | Mobile | Gravité |
|---|---|---|---|---|
| W1 | SectionMap.mapProvider / mapType / mapTypeMapbox |
Ignorés — map/LeafletMap.tsx a un TileLayer OSM en dur |
✅ 3 vues séparées (Google / Mapbox / flutter_map) | Moyenne — un client qui choisit "Google Hybrid" voit de l'OSM sur le web |
Categorie.icon / Categorie.resourceDTO (icône de catégorie) |
Ignorés — les marqueurs sont des pastilles de couleur (colorOf(p.categorieId), MapSection.tsx:304) |
✅ icône affichée | Moyenne — perte d'identité visuelle de la carte | |
| W3 | AppConfigurationLink.roundedValue (rayon des coins, theming instance) |
Aucun usage | ✅ ~47 usages (popups agenda, audio player, cards…) | Basse — cosmétique, mais c'est du theming vendu |
| W4 | AppConfigurationLink.isSectionImageBackground |
Aucun usage | ✅ section_page.dart:133 (MenuPage) |
Basse |
| W5 | SectionArticle.isContentTop |
Ignoré — ArticleSection.tsx place toujours le carrousel avant le HTML |
✅ | Basse |
GuidedPath.isLinear et GuidedStep.isStepLocked |
Zéro occurrence dans ParcoursSection.tsx (2600+ lignes) — prevStep (l.86) autorise toujours le retour arrière, même quand l'ordre devrait être imposé |
✅ isLinear géré dans les deux modes ; isStepLocked en mode carte |
Moyenne | |
Mode contenu d'un parcours : hideNextStepsUntilComplete |
Géré uniquement dans ParcoursMapProgression (l.332). ProgressView (mode sans carte, l.1275) ne masque rien |
❌ idem (M7) | Haute | |
Assistant IA (Instance.isAssistant / ApplicationInstance.isAssistant) |
Implémenté le 2026-08-06 — components/assistant/AssistantBubble.tsx, bulle flottante montée par le layout de configuration quand les deux drapeaux sont vrais. Consomme POST /api/AI/chat en AppType.Web |
✅ AssistantChatSheet |
Haute — débloque l'add-on IA sur le plan Essentiel, qui est web-only | |
| W9 | SectionAgenda.agendaMapProvider |
Ignoré — agenda/EventMiniMap.tsx en Leaflet fixe |
✅ | Basse |
| W10 | SectionMap.iconResourceId / SectionEvent.iconResourceId |
Ignorés | ⚠️ 2 usages mobile seulement | Basse — vérifier d'abord si ce champ a encore un rôle (cf. §4) |
✅ Corrigé le 2026-08-06 (M4, M7, M8, W6, W7) — refonte de la progression des parcours :
IsStepLocked,IsHiddenInitiallyetFactContentsupprimés du modèle (migrationRemoveDeadGuidedStepFlags).IsStepLockedétait non seulement redondant avecRequireSuccessToAdvance, mais rendait l'étape définitivement infranchissable même après réussite du défi (_canAdvancerenvoyaitfalse).- Le verrouillage d'une étape est désormais dérivé de la progression réelle du visiteur, dans les 4 vues (mobile carte/contenu, web carte/contenu).
HideNextStepsUntilCompleteappliqué aussi en mode contenu, mobile et web.IsLinearappliqué côté web (il ne l'était pas du tout), et navigation libre réellement implémentée : pins de carte tapables et segments de progression cliquables quandIsLinear = false.- Le retour arrière est désormais toujours possible vers une étape déjà atteinte, dans tous les modes.
✅ Corrigé le 2026-08-06 (M1, M2, W2) —
Helpers/mapCenter.dartcentralise la résolution du centre de carte (« Centrer sur » d'abord, puis le point de section, puis un défaut) pour les trois vues mobile ; les horaires d'un point d'intérêt sont affichés dansmarker_view.dart; les icônes de catégorie web utilisentcategorie.resourceDTO.url— champ qui n'existait pas du tout dans le type web.
3bis. Écart de cohérence entre les deux apps — bascule carte / contenu d'un parcours
✅ Corrigé le 2026-08-06 — les deux apps basculent maintenant sur
ShowMapseul (parcours_page.dart_showMap,ParcoursSection.tsxuseMapView).BaseSectionMapIdest redevenu ce qu'il devait être : un fond de carte optionnel qui ne conditionne rien. Le constat d'origine est conservé ci-dessous.
X1 — la condition d'affichage du mode carte n'était pas la même dans les deux apps.
| App | Condition | Fichier |
|---|---|---|
| mymuseum-visitapp | showMap == true et baseSectionMapId renseigné (une SectionMap de référence doit exister et se charger) |
parcours_page.dart:419 (_showMap), navigation l.723-733 |
| visitapp-web | showMap === true et au moins une étape géolocalisée (stepsHaveGeo) — baseSectionMapId n'entre pas en compte |
ParcoursSection.tsx:100-101 |
Conséquence : une même configuration peut s'afficher en mode carte sur un canal et en mode contenu sur l'autre — donc avec géodéclenchement d'un côté et sans de l'autre (cf. M7/W7).
| Configuration | mymuseum-visitapp | visitapp-web |
|---|---|---|
showMap=true, étapes géolocalisées, sans baseSectionMapId |
mode contenu → aucune géoloc | mode carte → géoloc active |
showMap=true, baseSectionMapId défini, étapes sans géométrie |
mode carte (sans pins d'étape exploitables) | mode contenu |
showMap=true, baseSectionMapId + étapes géolocalisées |
carte ✅ | carte ✅ |
C'est le premier point à trancher avant même de décider du sort du mode contenu : tant que la règle diverge, un même parcours est incomparable entre les deux canaux et les tests ne sont pas reproductibles.
Piège associé côté manager-app : showNewOrUpdateGuidedStep() reçoit isEscapeMode = path.isGameMode,
pas showMap (showNewOrUpdateGuidedPath.dart:265-269, l.334-338). Le formulaire d'étape ne sait donc
pas si la carte sera affichée, et propose le toggle « Déclenchement géolocalisé » avec le texte d'aide
« L'étape se débloque automatiquement quand le visiteur entre dans la zone » — une promesse non tenue
dès que le rendu tombe en mode contenu.
Note :
AppConfigurationLink.gridColSpan/gridRowSpanne sont pas un écart — le web les mappe encolSpan/rowSpan(client.ts:171-172) et les rend en CSS Grid (ConfigurationGrid.tsx:38-48). IdemSectionQuiz.bad_level… →badLevel, etSectionParcours.guidedPaths(chargés par un endpoint dédié côté mobile).
4. Champs backend morts ou sans propriétaire
| Champ | État |
|---|---|
GuidedStep.FactContent |
Persisté (MyInfoMateDbContext.cs:361), jamais édité, jamais affiché — nulle part dans les 4 apps |
SectionEvent.iconResource / iconResourceId |
Zéro usage réel côté visiteur, rôle indéterminé |
SectionMap.MapResourceId / MapResource |
Déjà signalé "rôle flou" dans todo-features — l'audit confirme : aucun usage visiteur exploitable |
SectionEvent.ParcoursIds |
Déjà connu comme legacy (remplacé par GuidedPath.SectionEventId) |
GuidedStep.TriggerGeoPointId |
N'existe pas dans le modèle (GuidedStep.cs) — le géodéclenchement se fait via Geometry + IsGeoTriggered + ZoneRadiusMeters. Voir §5 |
5. Erreurs constatées dans todo-features.md (avant correction du 2026-08-05)
| Ce que disait la doc | Réalité du code |
|---|---|
"Les 4 repos compilent (dotnet build, flutter analyze, npm run build)" — section onboarding |
Faux. Voir §6 : npm run build échoue, le projet de tests backend ne compile pas, flutter analyze remonte 8 erreurs sur mymuseum-visitapp |
| "Gestion users par instance admin" ❌ | Scoping backend fait : UserController sous policy InstanceAdmin, filtrage par instance sur GET/GET{id}/PUT/DELETE, InstanceId forcé à l'instance de l'appelant au POST, protection contre l'escalade de rôle. Manque uniquement le plafond de 5 utilisateurs et le compteur "X / 5" dans l'UI |
"triggerGeoPointId exposé en saisie texte ✅" + tâche "TriggerGeoPointId — picker intuitif" + tooltip associé |
Le champ n'existe pas. Tâches fantômes à supprimer |
"showGeoZoneNotification() dans _checkGeoZone des deux pages GuidedPath" |
Uniquement dans guided_path_map_progression_page.dart |
"GuidedPathContentProgressionPage : mode contenu — même logique" |
Sous-ensemble : ni géo, ni lock, ni linéaire, ni masquage (voir M7) |
| Refacto §8 "Ajouter Parcours dans la liste des types de section" ⚠️ | Fait : SectionParcours/section_parcours_config.dart avec toggle showMap (l.63) et picker baseSectionMapId (l.81-90). Restent réellement à faire : les tooltips ℹ️ (1 seul Tooltip dans tout le dossier Parcours) |
Escape game : "game_page.dart check gameDTO.gameType == GameTypes.Escape" (et test-plan §5.1) |
Obsolète — GameTypes backend = { Puzzle, SlidingPuzzle }, aucune référence à Escape dans les apps. ⚠️ Le client généré manager_api_new/lib/model/game_types.dart:28 contient encore Escape = 2 (désynchronisé) |
isHiddenInitially "non requis côté serveur" |
Mais exposé comme toggle dans manager-app → incohérence à trancher (retirer l'UI ou l'implémenter) |
6. Blocages de déploiement mesurés le 2026-08-05
Au début de cet audit, aucune des 4 apps ne buildait.
État au 2026-08-06 : B0, B1 et B2 sont corrigés — les 3 apps Flutter buildent,
visitapp-webbuild en prod, etdotnet testpasse 124/124. Seul B3 (tablet-app, Gradle) reste ouvert. Voir STATUS.md §1bis.
| # | Constat | Commande | État |
|---|---|---|---|
| B0 | Les 3 apps Flutter ne compilaient plus — Error: The language version override has to be the same in the library and its part(s). Cause : manager_api_new/lib/api/onboarding_api.dart, ajouté à la main pour l'onboarding (fichier untracked, avec part 'api/onboarding_api.dart'; ajouté dans api.dart), ne portait pas l'annotation // @dart=2.18 que porte api.dart. Un seul fichier cassait manager-app et mymuseum-visitapp et tablet-app. ⚠️ flutter analyze ne le voit pas — c'est une erreur du compilateur kernel, pas de l'analyzer |
flutter build web / flutter build apk |
✅ corrigé (annotation ajoutée) + typage explicite dans guided_step_challenge.dart:83 (<QuizQuestion>[...?step.quizQuestions]). manager-app → √ Built build\web ; mymuseum-visitapp → 3 APK produits |
| B1 | visitapp-web ne buildait pas en production. 7 erreurs TypeScript sur l'indexation de geometry.coordinates (typé unknown dans lib/api/types.ts:191) : map/LeafletMap.tsx (5) et MapSection.tsx (2). Ces deux fichiers indexaient les coordonnées sans le Array.isArray() qui narrow le unknown — narrowing que AgendaSection.tsx:41-43 faisait déjà correctement |
npm run build → Failed to type check |
✅ corrigé : helper getGeoPointLatLng() ajouté dans src/lib/geo.ts (à côté de getStepGeometryCenter) et utilisé sur les 3 sites. Build prod OK. ⚠️ invisible en next dev (Turbopack ne type-checke pas) |
| B2 | La suite de tests backend ne compile plus. 3 × CS7036 : MyInfoMateDbContext attend IHttpContextAccessor, UserController attend IEmailService, AuthenticationController attend ProfileLogic — les fakes de ManagerService.Tests/ n'ont pas suivi |
dotnet test |
✅ corrigé le 2026-08-06 — 124/124. La remise en route a révélé un vrai bug backend : BuildAuditEntries() modifiait le ChangeTracker pendant son énumération → InvalidOperationException sur toute écriture d'entité auditée. Invisible tant que les tests ne compilaient pas |
| B3 | tablet-app : échec Gradle sur flutter_tools/gradle/build.gradle.kts:7 (pas lié au client API). Le seul APK présent date d'avril |
flutter build apk |
❌ ouvert, à investiguer |
| B4 | Client généré manager_api_new/ : reliquats de désynchronisation, dont model/game_types.dart:28 (Escape = 2) absent du backend |
flutter analyze |
Ouvert, non bloquant |
Règle qui en découle :
flutter analyzeetnext devne prouvent rien. Seulsflutter build,npm run buildetdotnet testdisent la vérité. Reprise en checklist dans test-plan.md §0.
Fix suggéré pour B1 : typer explicitement les coordonnées plutôt que de caster au point d'usage —
coordinates?: number[] | number[][] | number[][][] dans types.ts:191 (et :121 pour MapAnnotation.geometry), puis vérifier les 7 sites. Alternative rapide : un helper asPoint(coords): [number, number] | null (le pattern existe déjà : extractStepPoint() dans ParcoursSection.tsx:2448) et l'utiliser partout.
7. Ordre de traitement suggéré avant déploiement
Bloquant (à faire avant toute mise en prod)
- B1 — build web
- Rotation des secrets Git (cf.
security/audit-securite-manager-service.md, toujours ouvert) - Fermeture du port PostgreSQL public
- M7 / W7 — géodéclenchement en mode contenu, ou décision assumée : "le mode escape indoor ne supporte pas la géolocalisation" + retirer les champs géo de l'UI quand
ShowMap = false
Important (écart client visible)
5. M1 (centrage carte mobile), M2 (horaires mobile)
6. W8 — assistant IA en web, si l'add-on est vendu sur le plan Essentiel
7. M6 — watermark trial mobile — écarté (l'essai est web-only, cf. §2)
8. M4 — trancher — résolu, champ supprimé du modèle (migration isHiddenInitiallyRemoveDeadGuidedStepFlags)
9. B2 — réparer les tests backend — fait le 2026-08-06, 124/124
Confort / cohérence 10. W1, W2 (carte web : provider + icônes de catégorie) 11. M3, M5, W3, W4, W5, W6, W9, W10 12. Nettoyage des champs morts (§4) 13. Tooltips ℹ️ manager-app (le seul reliquat réel du refacto SectionParcours §8)
8. Comment refaire cet audit
Le script d'extraction est jetable mais la méthode se reproduit :
- Extraire les champs de chaque
public XxxDTO ToDTO()dansmanager-service/ManagerService/Data/**. - Pour chaque champ, chercher le nom et sa variante
camelCasedans :manager-app/lib(horsmanager_api_new/etlib/api/),mymuseum-visitapp/lib,visitapp-web/src/lib(couche API) etvisitapp-web/src/{components,app,hooks}(couche UI) séparément — c'est la séparation API/UI qui révèle les champs "mappés mais jamais affichés". - Ne garder que
manager-app > 0et (mobile == 0ouweb UI == 0). - Lire chaque candidat : le taux de faux positifs est élevé (renommage, endpoint dédié, champ dérivé).