manager-app/CLAUDE.md

6.1 KiB
Raw Blame History

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

  1. FileHelper().readSessionWeb() → localStorage
  2. Si pas de session → /login
  3. POST /api/Authentication/AuthenticateWithJson → TokenDTO
  4. Init Client avec le host backend
  5. Fetch InstanceDTO → détermine les features (isMobile, isTablet, isWeb, isVR, isStatistic)
  6. Redirect vers /main/:view

Breakpoints responsive

  • Mobile : 0550px
  • Tablet : 550800px
  • Desktop : 8011920px
  • 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 (les app_localizations*.dart générés sont versionnés).
  • Lecture dans un widget : AppLocalizations.of(context)!.maCle, ou final l = AppLocalizations.of(context)!; en tête de build quand il y en a plusieurs.

Les pièges qui reviennent

  • Pas de contexte ? Ne pas replier sur un littéral. Passer AppLocalizations en paramètre — c'est ce que font getSectionTypeName(l, type), resourceTypeLabel(l, type), ProgressionMode.label(l) et AiTranslateException.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 au build (widget.labelHint ?? l.selectLanguageHint).
  • Listes/enums de libellés construits en initState ou en constante de fichier : les construire dans build, 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 de print/StateError jamais affichés, et l'écran de réindexation du Guide IA (SuperAdmin uniquement, FR assumé — le commentaire dans guide_ia_screen.dart le 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é