6.1 KiB
manager-app
Interface de gestion de contenu Flutter (web/desktop) pour configurer tablet-app et mymuseum-visitapp.
Stack
- Flutter web (cible principale), support desktop partiel
- State management : Provider +
ChangeNotifier - Navigation : GoRouter
- Firebase Storage (upload de ressources)
Structure
lib/
├── main.dart # Entry point, init Firebase, GoRouter, breakpoints responsive
├── app_context.dart # Provider root wrapping ManagerAppContext
├── client.dart # Wrapper exposant les 12+ facades API générées
├── constants.dart # Couleurs, types de sections, langues, types de ressources
├── Models/ # ManagerAppContext (état central), Session, Menu...
├── Helpers/ # FileHelper (localStorage session), PDFHelper
├── Components/ # 48+ widgets réutilisables (inputs, pickers, players...)
└── Screens/ # Écrans par domaine
├── Main/ # Dashboard principal avec menu latéral
├── Configurations/ # Builder de configuration par type de section
│ └── SubSection/ # Map, Menu, Slider, Quiz, Article, PDF, Video, Weather, Event, Parcours, Game, Agenda
├── Resources/ # CRUD ressources (Image, Video, Audio, PDF, JSON...)
├── Applications/ # Liaison config ↔ app device
├── Kiosk_devices/ # Gestion des devices
├── Users/ # Gestion des utilisateurs
├── ApiKeys/ # Gestion des clés API
├── Notifications/ # Push notifications
└── Statistics/ # Analytics visiteurs
Client API généré (manager_api_new/)
C'est ici que le client est généré. Il est consommé via dépendance locale par tablet-app et mymuseum-visitapp.
- Éditer manuellement les fichiers de
manager_api_new/en miroir des changements backend — ne pas relancer le générateur OpenAPI, il écraserait des patches locaux déjà accumulés dans ce client. - 16 classes API : AuthenticationApi, ConfigurationApi, SectionApi, SectionMapApi, SectionEventApi, ResourceApi, DeviceApi, UserApi, StatsApi, AIApi, etc.
- 130+ DTOs générés dans
manager_api_new/lib/model/
Package layout partagé (myinfomate_layout/)
Package Dart pur (pas de dépendance Flutter) contenant bentoLayout() — le placement "bento" dense (chaque card occupe colSpan × lignes rowSpan sur une grille à N colonnes, même principe que grid-auto-flow: dense en CSS). Utilisé pour l'aperçu live de app_configuration_link_screen.dart ET par mymuseum-visitapp (même placement, pour que l'aperçu soit fidèle au rendu réel). Côté web (visitapp-web), le même rendu est obtenu nativement via CSS Grid (pas de portage JS nécessaire). Tests dans myinfomate_layout/test/ (fixtures bento_layout_cases.json).
La taille d'une card = deux entiers gridColSpan / gridRowSpan (1 ou 2), portés par AppConfigurationLinkDTO (donc réglables indépendamment par plateforme). Dans l'aperçu, ils se règlent via un sélecteur de forme par ligne (card_shape_selector.dart) OU par glisser du coin d'une card (drag 2D) — les deux écrivent le même champ.
État central (ManagerAppContext)
Contient : credentials, accessToken, instanceId, instanceDTO, référence au client API, configuration/section sélectionnée.
Accès via context.read<AppContext>() ou context.watch<AppContext>().
Flow d'authentification
FileHelper().readSessionWeb()→ localStorage- Si pas de session →
/login - POST
/api/Authentication/AuthenticateWithJson→ TokenDTO - Init
Clientavec le host backend - Fetch
InstanceDTO→ détermine les features (isMobile, isTablet, isWeb, isVR, isStatistic) - Redirect vers
/main/:view
Breakpoints responsive
- Mobile : 0–550px
- Tablet : 550–800px
- Desktop : 801–1920px
- 4K : 1921px+
Internationalisation — règle non négociable
Le manager est livré en FR / EN / NL. Toute chaîne visible par un utilisateur passe par
AppLocalizations : aucun littéral de texte dans un widget, jamais, y compris sur un écran
« qu'on traduira plus tard ». Un écran ajouté sans ses clés est un écran à refaire.
- Fichiers :
lib/l10n/app_fr.arb(template, cf.l10n.yaml),app_en.arb,app_nl.arb. Les trois doivent avoir exactement le même jeu de clés. - Après toute modification d'un
.arb:flutter gen-l10n(lesapp_localizations*.dartgénérés sont versionnés). - Lecture dans un widget :
AppLocalizations.of(context)!.maCle, oufinal l = AppLocalizations.of(context)!;en tête debuildquand il y en a plusieurs.
Les pièges qui reviennent
- Pas de contexte ? Ne pas replier sur un littéral. Passer
AppLocalizationsen paramètre — c'est ce que fontgetSectionTypeName(l, type),resourceTypeLabel(l, type),ProgressionMode.label(l)etAiTranslateException.localized(l). - Valeur par défaut d'un paramètre (
this.labelHint = "…") : impossible à traduire, un const n'a pas de contexte. Rendre le paramètre nullable et résoudre aubuild(widget.labelHint ?? l.selectLanguageHint). - Listes/enums de libellés construits en
initStateou en constante de fichier : les construire dansbuild, sinon la langue est figée au premier rendu. - Message d'erreur venant d'un service : le service ne traduit pas. Il porte un code ou un message backend, l'UI décide du texte.
- Ce qui ne se traduit pas : noms de marque (MyInfoMate, Unov), valeurs de données
(
"Point","Polygon", types d'API), messages deprint/StateErrorjamais affichés, et l'écran de réindexation du Guide IA (SuperAdmin uniquement, FR assumé — le commentaire dansguide_ia_screen.dartle dit).
Vérifier avant de pousser
flutter gen-l10n && flutter analyze --no-pub
# et relire le diff : tout `Text("…")`, `label:`, `hintText:`, `tooltip:`, `message:` littéral est un oubli
Commandes utiles
flutter run -d chrome # Lancer en web
flutter build web # Build web
flutter pub run build_runner build # Regénérer code généré