DOCS/parity-manager-visitapp.md
Thomas Fransolet a5a8ecdb20 Documentation interne MyInfoMate / Unov
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>
2026-08-11 11:17:01 +02:00

20 KiB
Raw Permalink Blame History

Parité manager-app → apps visiteur

Question à laquelle ce document répond : tout ce qu'un client peut configurer dans manager-app est-il réellement visible dans mymuseum-visitapp (mobile) et visitapp-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 (renommages snake_casecamelCase, champs chargés via un endpoint dédié, champs dérivés type imageResourceIdimageUrl).

tablet-app est 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 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é
M1 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
M2 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
M4 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
M6 Watermark "Preview" pendant l'essai gratuit backend onboarding Non applicable app/[slug]/layout.tsx:29TrialWatermark 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
M7 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
M8 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ésmap/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
W2 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
W6 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
W7 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
W8 Assistant IA (Instance.isAssistant / ApplicationInstance.isAssistant) Implémenté le 2026-08-06components/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, IsHiddenInitially et FactContent supprimés du modèle (migration RemoveDeadGuidedStepFlags). IsStepLocked était non seulement redondant avec RequireSuccessToAdvance, mais rendait l'étape définitivement infranchissable même après réussite du défi (_canAdvance renvoyait false).
  • 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).
  • HideNextStepsUntilComplete appliqué aussi en mode contenu, mobile et web.
  • IsLinear appliqué 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 quand IsLinear = 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.dart centralise 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 dans marker_view.dart ; les icônes de catégorie web utilisent categorie.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 ShowMap seul (parcours_page.dart _showMap, ParcoursSection.tsx useMapView). BaseSectionMapId est 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 / gridRowSpan ne sont pas un écart — le web les mappe en colSpan/rowSpan (client.ts:171-172) et les rend en CSS Grid (ConfigurationGrid.tsx:38-48). Idem SectionQuiz.bad_level… → badLevel, et SectionParcours.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èteGameTypes 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-web build en prod, et dotnet test passe 124/124. Seul B3 (tablet-app, Gradle) reste ouvert. Voir STATUS.md §1bis.

# Constat Commande État
B0 Les 3 apps Flutter ne compilaient plusError: 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 buildFailed 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 analyze et next dev ne prouvent rien. Seuls flutter build, npm run build et dotnet test disent 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)

  1. B1 — build web
  2. Rotation des secrets Git (cf. security/audit-securite-manager-service.md, toujours ouvert)
  3. Fermeture du port PostgreSQL public
  4. 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 isHiddenInitiallyrésolu, champ supprimé du modèle (migration RemoveDeadGuidedStepFlags) 9. B2 — réparer les tests backendfait 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 :

  1. Extraire les champs de chaque public XxxDTO ToDTO() dans manager-service/ManagerService/Data/**.
  2. Pour chaque champ, chercher le nom et sa variante camelCase dans : manager-app/lib (hors manager_api_new/ et lib/api/), mymuseum-visitapp/lib, visitapp-web/src/lib (couche API) et visitapp-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".
  3. Ne garder que manager-app > 0 et (mobile == 0 ou web UI == 0).
  4. Lire chaque candidat : le taux de faux positifs est élevé (renommage, endpoint dédié, champ dérivé).