# 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()` ou `context.watch()`. ## 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 : 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` (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 ```bash flutter gen-l10n && flutter analyze --no-pub # et relire le diff : tout `Text("…")`, `label:`, `hintText:`, `tooltip:`, `message:` littéral est un oubli ``` ## Commandes utiles ```bash 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é ```