diff --git a/STATUS.md b/STATUS.md index cda07b0..40e4282 100644 --- a/STATUS.md +++ b/STATUS.md @@ -335,6 +335,19 @@ Restait le contenu réel du lot, les deux avertissements du build : **NDK 27.0.1 **Ce que K5 débloque** : **D0** (test-plan §21 sur device), donc la mesure des bugs offline D2-D5 et la confirmation du volet visiteur de D1 — qui ne tient aujourd'hui que sur `flutter analyze`. +**✅ K3 et K7 livrés le 2026-08-12 — `flutter build apk --debug` vert, APK produit.** La borne couvre désormais **12 des 13 types** : `SectionArticle` (K7) et `SectionEvent` (K3) sont branchés dans `main_view.getContent`. Seul `SectionParcours` reste dehors, et **définitivement** — un parcours guidé fait marcher le visiteur. + +Les deux écrans suivent la maquette `DOCS/claude design/kiosk-paysage-article-event.html` : rail média ancré + colonne de lecture plafonnée pour l'article, bande héros à 26 % + programme + carte vive pour l'événement, détail d'un bloc **sous la carte** plutôt qu'en `showModalBottomSheet`. + +**Quatre choses que le code a dictées, contre ce que la maquette ou mymuseum annonçaient :** + +- ⚠️ **La barre de pied de la maquette n'appartient pas aux écrans.** `section_page_detail:184/198` dessine déjà le bouton retour, avec les clés `back`/`menu` selon `isFromMenu`. Les écrans qui auraient dessiné le leur en auraient affiché deux. La maquette est à corriger sur ce point. +- ⚠️ **Un seul constructeur de marqueurs au lieu des quatre de mymuseum.** `MapAnnotationDTO` et `MapAnnotation` ont des champs **rigoureusement identiques** mais sont deux types Dart distincts — d'où la duplication là-bas. Normalisé ici par une classe interne à deux fabriques. C'est **L5** appliqué au front, et il porte d'autant plus que la convention `[lng, lat]` est l'endroit exact où une divergence **ne lève aucune erreur**. +- ⚠️ **L'audio d'article se résout dans `contents` avant l'API.** `ContentDTO` porte son `resource` complet — idiome déjà utilisé par `marker_view` — donc l'audio est trouvé sans appel réseau quand il est embarqué, donc **hors ligne aussi**. `resourceGetDetail` n'est qu'un repli. Mymuseum appelle l'API systématiquement en ligne. +- ⚠️ **Nouvelle clé i18n `event.live` dans les 10 langues.** Sans les 10, `getFromLocale` renvoie `""` et la pastille « en cours » rendrait une boîte vide. **FR, EN, NL, DE, IT, ES sont sûres ; PL, CN, UK et AR sont de ma main et demandent une relecture humaine.** + +⚠️ **Ce qui n'est pas prouvé** : le rendu. Aucun des deux écrans n'a été vu à l'œil — build vert ne veut pas dire mise en page juste, c'est exactement la leçon du §1bis appliquée au design. Et **aucun `SectionEvent` n'existe en base** (le type est né avec Postgres v3) : c'est le §19.13 cas E qui en créera un. + **Périmètre kiosk tranché le 2026-08-12** : `tablet-app` couvre 11 des 13 types — ⛔ **corrigé le 2026-08-12 : c'est 10, et le compte cachait un vrai manque.** Le `switch` de `main_view.getContent` traite Map, Slider, Video, Web, Menu, Quiz, Pdf, Game, Agenda, Weather. **`SectionArticle` (type 6) n'est pas traité** : son `case` est commenté « TODO » depuis l'origine et `Screens/Article/article_view.dart` fait 1 Ko. Un article tombe donc dans le `default` — « Ce type n'est pas supporté ». Contrairement à `SectionEvent` et `SectionParcours`, **ce type-là existait dans Mongo** : c'est un vrai retard, pas un type né avec Postgres v3. À trancher — le supporter ou l'assumer — mais pas à compter comme couvert. `SectionParcours` est **exclu définitivement** (un parcours guidé fait marcher le visiteur — pas de sens sur borne fixe) ; `SectionEvent` est **à supporter** (borne d'accueil affichant le programme du jour, §19.13 cas E). Ni l'un ni l'autre n'est un retard : **les deux types sont nés avec Postgres v3**. S'ajoute le renommage `SectionPuzzle` → `SectionGame` et le nouveau `SlidingPuzzle`, à récupérer depuis `mymuseum-visitapp/lib/Screens/Sections/Game/` — sa version est moins buguée que le `Puzzle/` de tablet-app. **Ce que ça coûte** : +3 à 5 jours. Ce n'est pas du périmètre ajouté, c'est du travail déjà dû que personne n'avait vu. diff --git a/claude design/kiosk-paysage-article-event.html b/claude design/kiosk-paysage-article-event.html index e615650..f35c551 100644 --- a/claude design/kiosk-paysage-article-event.html +++ b/claude design/kiosk-paysage-article-event.html @@ -623,13 +623,16 @@
C

- Barre de pied, hauteur de doigt. Un seul bouton, cible à - 56 px minimum : on tape debout, parfois avec un enfant au bout du bras. - Pas de sélecteur de langue — elle est choisie en amont, la réafficher ici - rouvrirait une décision déjà prise. Et rien d'autre : SectionPageDetail ne - reçoit qu'une section et un booléen isFromMenu, jamais la liste de ses - voisines. Un repère du type « 3 / 8 » n'est donc adossé à aucune donnée — il faudrait - descendre la liste jusqu'ici pour le calculer. + Barre de pied — et elle n'appartient pas à ces écrans. + ⛔ Corrigé le 12/08 en implémentant : section_page_detail la dessine + déjà pour les treize types, avec les clés back / menu selon qu'on + vient d'un menu. Les deux écrans ne rendent que leur contenu — dessiner ce pied de page + aurait affiché deux boutons retour. Elle reste dans les maquettes parce que + le visiteur la voit, mais c'est la coquille qui la fournit. + Pas de sélecteur de langue — elle est choisie en amont. Et rien d'autre : + SectionPageDetail ne reçoit qu'une section et un booléen + isFromMenu, jamais la liste de ses voisines, donc un repère du type « 3 / 8 » + n'est adossé à aucune donnée.

diff --git a/kanban.html b/kanban.html index 3b59c7e..694948c 100644 --- a/kanban.html +++ b/kanban.html @@ -454,13 +454,13 @@
-
2Urgent
+
1Urgent
2Migration v3
5Bugs ouverts
6À tester
22Planifié
8Bascule prod
-
40Fait récemment
+
41Fait récemment
@@ -486,7 +486,7 @@
-

Urgent

2
+

Urgent

1
@@ -496,15 +496,6 @@ todo-features.md — Sécurité réseau
-
-
tablet-appLot K · périmètre kiosk
-

Types de section manquants sur le kiosk

-

tablet-app couvre 10 des 13 types — corrigé le 12/08, les docs disaient 11. Le switch de main_view.getContent traite Map, Slider, Video, Web, Menu, Quiz, Pdf, Game, Agenda et Weather. SectionEvent et SectionParcours ne sont pas un retard — ils sont nés avec Postgres v3 et n'ont jamais existé dans Mongo. Mais SectionArticle (type 6) en est un : son case est commenté « TODO » depuis l'origine et Screens/Article/article_view.dart est un fichier d'1 Ko. Un article tombe donc dans le default — « Ce type n'est pas supporté » — alors que l'article existait dans Mongo et est du contenu kiosk parfaitement légitime. Décidé le 12/08 : le supporter, c'est K7 au plan.

-

Le travail n'est pas la copie, c'est le paysage. Reprendre mymuseum-visitapp/lib/Screens/Sections/Article/ est mécanique ; les écrans de mymuseum sont pensés pour un téléphone tenu debout, alors qu'une borne est large — un article en colonne unique sur 1280 px est illisible. K3 (SectionEvent) pose exactement la même question — héros, programme, carte — et la trancher deux fois donnerait deux mises en page différentes sur la même borne. Une seule passe de design pour les deux, même raisonnement d'économie que L5 et L15.

-

Tranché le 12/08SectionParcours : exclu définitivement, un parcours guidé fait marcher le visiteur avec géodéclenchement, ça n'a pas de sens sur borne fixe. SectionEvent : à supporter, une borne d'accueil qui affiche le programme du jour et le plan est le cas d'usage kiosk par excellence (§19.13 cas E).

-

Plus le renommage SectionPuzzleSectionGame et le nouveau SlidingPuzzle : reprendre mymuseum-visitapp/lib/Screens/Sections/Game/ (game_page.dart, sliding_puzzle_piece.dart), dont la version est moins buguée que le Puzzle/ de tablet-app. Récupération, pas réécriture.

- v1-plan.md lot K — K3, K4 -
@@ -875,9 +866,17 @@

Fait récemment

-

Quarante chantiers clos entre le 5 et le 12 août 2026.

+

Quarante et un chantiers clos entre le 5 et le 12 août 2026.

+
+ La borne couvre 12 des 13 types — SectionArticle (K7) et SectionEvent (K3) livrés + flutter build apk --debug vert, APK produit. Seul SectionParcours reste dehors, et définitivement — un parcours guidé fait marcher le visiteur. Les deux écrans suivent la maquette paysage : rail média ancré + colonne de lecture plafonnée pour l'article, bande héros à 26 % + programme + carte vive pour l'événement, détail d'un bloc sous la carte et non en showModalBottomSheet. + ⚠️ La barre de pied de la maquette n'appartenait pas aux écrans : section_page_detail dessine déjà le bouton retour, avec les clés back/menu selon isFromMenu. Les dessiner aurait donné deux boutons. ⚠️ Un seul constructeur de marqueurs au lieu des quatre de mymuseum : MapAnnotationDTO et MapAnnotation ont des champs rigoureusement identiques mais sont deux types Dart distincts. Normalisé par une classe interne à deux fabriques — c'est L5 appliqué au front, et il porte d'autant plus que la convention [lng, lat] est l'endroit exact où une divergence ne lève aucune erreur. + ⚠️ L'audio d'article se résout dans contents avant l'API : ContentDTO porte son resource complet, donc l'audio est trouvé sans appel réseau quand il est embarqué — donc hors ligne aussi. Mymuseum appelle l'API systématiquement en ligne. ⚠️ Clé i18n event.live ajoutée aux 10 langues — sans les 10, getFromLocale renvoie "" et la pastille rendrait une boîte vide. PL, CN, UK et AR sont de ma main et demandent une relecture humaine. + ⚠️ Ce qui n'est pas prouvé : le rendu. Aucun des deux écrans n'a été vu à l'œil — un build vert ne dit rien d'une mise en page, c'est la leçon du §1bis appliquée au design. Et aucun SectionEvent n'existe en base, le type étant né avec Postgres v3 : c'est le §19.13 cas E qui en créera un. +
+
K5 — l'APK mymuseum-visitapp se construit, et le lot n'était pas ce qu'on croyait La prémisse était périmée. Trois documents annonçaient « flutter build apk échoue côté Gradle/NDK », revérifié le 11/08. Les trois flavors (dev, mdlf, fortsaintheribert) construisent en exit 0, APK à l'appui. Sa config Android était déjà à niveau — Gradle 8.11.1, AGP 8.9.0, -Xmx4096M, enableJetifier=false, aucun forçage d'androidx.lifecycle. Les dix crans du 12/08 ont amené tablet-app jusqu'à mymuseum ; il ne restait pas la même migration à refaire ici. Le « réflexe du diff » noté la veille donnait la réponse en deux cat — appliqué, il l'a donnée. diff --git a/v1-plan.md b/v1-plan.md index 51234e4..78ea154 100644 --- a/v1-plan.md +++ b/v1-plan.md @@ -280,11 +280,11 @@ Ordre du §2 de STATUS.md, corrigé par **L12**. |---|---|---| | ~~**K2**~~ ✅ **2026-08-12** | **Portage livré. Les 132 erreurs de contrat sont tombées** ; `flutter analyze lib` ne renvoie plus que les 6 erreurs `PuzzleDTO`, qui sont K4. Fait : `TabletAppContext.currentAppConfigurationLink` (copie du getter mymuseum), `lib/Helpers/geo.dart` avec l'extension `lat`/`lng`, 67 accès `roundedValue` rebranchés, `main_view` et les 4 vues carte portées.
⚠️ **Deux bugs latents trouvés — c'est ce qui justifiait de le faire à la main.**
**(1)** `applicationInstanceDTO` n'était assigné **que si `instanceDTO.isAssistant == true`** (`main_view:339`) : il servait de drapeau d'assistant. Ce DTO portant désormais les `AppConfigurationLink`, le getter aurait renvoyé `null` sur **toute instance sans IA — donc MDLF et le Fort** — et les cinq réglages seraient retombés sur leurs défauts, sans erreur ni log. Corrigé : assignation inconditionnelle + drapeau explicite `isAssistantEnabled`, qui **préserve le ET** instance × canal (les deux gardes de `AiController:315/360`).
**(2)** `ConfigurationDTO.isTablet` a disparu : le filtre « configurations pour tablette » de `getConfigurations` n'avait plus de source. Il porte désormais sur les `AppConfigurationLink` du canal `AppType.Tablet`, même appel que `main_view`. ⚠️ **À valider sur device (§0/§18)** : si un client a des configurations non rattachées à son canal tablette, l'écran de sélection sera **vide**. L'ancien code avait le même angle mort, mais la donnée qui pilote le filtre a changé.
⚠️ **Piège de vérification** : `flutter analyze` écrit `error - `, pas `error • ` — un grep sur la mauvaise forme a fait conclure « 0 erreur » alors qu'il en restait 10 | | ~~K2 — notes de conception~~ | ⚠️ **Deux conventions de coordonnées coexistent** dans le projet — `GeoPointDTO` en `[lng, lat]`, géométries de `GuidedStep` en `[lat, lng]`. Une confusion **ne lève aucune erreur**, elle déplace les points. Détail et tableau dans **STATUS.md §1sexies**. Fondations déjà posées le 2026-08-12 : `TabletAppContext.currentAppConfigurationLink` et `lib/Helpers/geo.dart`.
**Portage du contrat d'API** — **132 erreurs Dart, 3 causes** : `ConfigurationDTO.roundedValue`/`isDate`/`isHour`/`isSectionImageBackground`/`screenPercentageSectionsMainPage` ont migré vers **`AppConfigurationLinkDTO`** (73 err.) · `GeoPointDTO.latitude/longitude` → **`GeometryDTO? geometry`**, effet de PostGIS (40 err.) · les `min()` sur `dynamic` en découlent (19 err.) | **Le modèle est déjà écrit** : `mymuseum-visitapp` a fait ce portage (`visitAppContext.currentAppConfigurationLink?.roundedValue`, `Models/visitContext.dart`). Copier, ne pas concevoir. Les 40 sites `latitude`/`longitude` se traitent par **une extension Dart sur `GeoPointDTO`** plutôt qu'un à un | -| **K3** | **Périmètre kiosk — tranché le 2026-08-12.** ⛔ **`tablet-app` couvre 10 des 13 types, pas 11 — recompté dans le `switch` le 2026-08-12.** `main_view.getContent` traite Map, Slider, Video, Web, Menu, Quiz, Pdf, Game, Agenda, Weather. **`SectionArticle` (type 6) manque** : son `case` est commenté « TODO » et `Screens/Article/article_view.dart` fait 1 Ko — un article tombe dans le `default`, « Ce type n'est pas supporté ». **Ce type-là existait dans Mongo**, contrairement aux deux ci-dessous : c'est un vrai retard, à trancher (le supporter ou l'assumer) et non à compter comme couvert. Énoncé d'origine, qui reste valable : `tablet-app` couvre **11 des 13** types. **`SectionParcours` : exclu définitivement** — un parcours guidé fait marcher le visiteur avec géodéclenchement, ça n'a aucun sens sur borne fixe. **`SectionEvent` : à supporter** — une borne d'accueil qui affiche le programme du jour et le plan est le cas d'usage kiosk par excellence, et c'est le scénario du §19.13 cas E | Ni l'un ni l'autre n'est un retard : les deux types **sont nés avec Postgres v3**, ils n'ont jamais existé dans Mongo (cf. lot G, écart a) | +| ~~**K3**~~ ✅ **2026-08-12** | **`SectionEvent` livré sur le kiosk**, `flutter build apk --debug` vert, APK produit. `Screens/Event/event_view.dart` : bande héros à 26 %, programme à gauche avec bloc « en cours », carte `flutter_map` vive à droite, détail d'un bloc **sous la carte** et non en `showModalBottomSheet`. Volet parcours exclu, conformément à l'arbitrage ci-dessous.
⚠️ **Un seul constructeur de marqueurs au lieu des quatre de mymuseum.** `MapAnnotationDTO` et `MapAnnotation` portent des champs **rigoureusement identiques** mais sont deux types Dart distincts — d'où `_buildMarkers`/`_buildBlockMarkers` et `_buildPolylines`/`_buildBlockPolylines` là-bas. Normalisé ici par une classe interne à deux fabriques : c'est le raisonnement de **L5** appliqué au front, et il porte d'autant plus que **la convention de coordonnées est l'endroit exact où une divergence ne lève aucune erreur**.
⚠️ **Nouvelle clé i18n `event.live` dans les 10 langues** de `Helpers/translations.dart`. **FR, EN, NL, DE, IT, ES sont sûres ; PL, CN, UK et AR sont de ma main et demandent une relecture humaine.** Sans les 10, `getFromLocale` renvoie `""` et la pastille rendrait une boîte vide | **Périmètre kiosk — tranché le 2026-08-12.** ⛔ **`tablet-app` couvre 10 des 13 types, pas 11 — recompté dans le `switch` le 2026-08-12.** `main_view.getContent` traite Map, Slider, Video, Web, Menu, Quiz, Pdf, Game, Agenda, Weather. **`SectionArticle` (type 6) manque** : son `case` est commenté « TODO » et `Screens/Article/article_view.dart` fait 1 Ko — un article tombe dans le `default`, « Ce type n'est pas supporté ». **Ce type-là existait dans Mongo**, contrairement aux deux ci-dessous : c'est un vrai retard, à trancher (le supporter ou l'assumer) et non à compter comme couvert. Énoncé d'origine, qui reste valable : `tablet-app` couvre **11 des 13** types. **`SectionParcours` : exclu définitivement** — un parcours guidé fait marcher le visiteur avec géodéclenchement, ça n'a aucun sens sur borne fixe. **`SectionEvent` : à supporter** — une borne d'accueil qui affiche le programme du jour et le plan est le cas d'usage kiosk par excellence, et c'est le scénario du §19.13 cas E | Ni l'un ni l'autre n'est un retard : les deux types **sont nés avec Postgres v3**, ils n'ont jamais existé dans Mongo (cf. lot G, écart a) | | ~~**K4**~~ ✅ **2026-08-12** | **Écran Game repris de mymuseum.** Les 6 fichiers de `Screens/Sections/Game/` sont dans `tablet-app/lib/Screens/Game/`, contexte adapté ; `lib/Screens/Puzzle/` supprimé ; `main_view` bascule sur `SectionType.Game` / `GameDTO` / `GamePage`. **Gains** : le puzzle glissant (mélange par mouvements valides, donc toujours soluble), le bouton d'indice, et un dimensionnement qui respecte le ratio de l'image — 508 lignes contre 237 à l'ancien `puzzle_view`.
⚠️ **Une décision de rendu à valider à l'œil** : mymuseum code ses couleurs **en dur par flavor client** (`kMainColor0/1/2` — rouge MDLF, bleu Fort). `tablet-app` n'a pas de flavor : un seul APK sert tous les clients et la charte vient de la configuration choisie au pincode. Recopier ces constantes aurait affiché du bleu chez MDLF. Le dégradé et le bouton d'indice sont donc **dérivés de `configuration.primaryColor`** (même parsing que `audio_player`/`loading_common`, repli `kTestSecondColor`, 3 nuances par `Color.lerp`). Plus juste sur le fond, mais **le rendu diffère de la version mobile** : si le dégradé dessiné à la main est préféré, figer les trois couleurs | | ~~K4 — énoncé d'origine~~ | **`SectionPuzzle` → `SectionGame` + le type glissant.** Le serveur a renommé et porte `GameTypes { Puzzle, SlidingPuzzle }` ; `tablet-app` a encore un écran `Puzzle/` qui ne connaît que l'ancien. **Reprendre le code de `mymuseum-visitapp/lib/Screens/Sections/Game/`** — `game_page.dart` et `sliding_puzzle_piece.dart` existent déjà là-bas, et cette version est **moins buguée** que celle de tablet-app | Récupération, pas réécriture | | ~~**K5**~~ ✅ **2026-08-12** | **Les trois flavors de `mymuseum-visitapp` construisent** (`dev`, `mdlf`, `fortsaintheribert`), exit 0, APK à l'appui. **D0 est débloqué.**
⛔ **L'énoncé était faux : il n'y avait pas de « même rattrapage » à faire.** Sa config Android était **déjà à niveau** — Gradle 8.11.1, AGP 8.9.0, `-Xmx4096M`, `enableJetifier=false`, aucun forçage d'`androidx.lifecycle`. Les dix crans de K1 ont amené **`tablet-app` jusqu'à `mymuseum`** ; le lot supposait la symétrie du problème, elle n'existait pas. Le réflexe du diff annoncé en K1 a donné la réponse en deux `cat`.
**Ce qui restait, et qui est fait** : les deux avertissements du build. NDK **27.0.12077973 → 28.2.13676358** (`speech_to_text` le réclame nommément) et Kotlin **2.1.0 → 2.3.10** (seuil 2.2.20 annoncé comme rupture ; c'est la version de `tablet-app`, donc un alignement). Six builds — trois avant, trois après. APK de **382 à 349 Mo**.
⚠️ **Un cran de plus existe, délibérément non pris** : Kotlin réglé, Flutter réclame Gradle 8.14.0 et AGP 8.11.1. C'est du **terrain neuf pour les deux repos** (`tablet-app` est à 8.11.1/8.9.1, mêmes avertissements latents) — le faire ici seul **désaligne** les deux apps. À faire d'un bloc sur les deux, ou pas du tout : c'est la dette d'outillage déjà parquée en V2 | Son code était déjà porté sur le nouveau contrat — **c'est ce qui aurait dû mettre la puce à l'oreille** : le repo qui a servi de modèle au portage K2 n'avait pas de raison d'être en retard d'outillage sur celui qui le copiait | -| **K7** | **`SectionArticle` sur le kiosk — ajouté le 2026-08-12**, en recomptant les types couverts. Son `case` est commenté « TODO » dans `main_view.getContent` et `Screens/Article/article_view.dart` fait 1 Ko : un article rend « Ce type n'est pas supporté ». **Contrairement à `SectionEvent` et `SectionParcours`, ce type existait dans Mongo** — c'est donc un vrai retard, pas un type né avec Postgres v3, et il sortira de la migration avec du contenu réel.
**Le code est une reprise de `mymuseum-visitapp/lib/Screens/Sections/Article/`**, pas une conception. **Le travail n'est pas là** : il est dans le **passage en paysage**. Les écrans de mymuseum sont pensés pour un téléphone tenu debout ; une borne est large, et un article rendu en colonne unique sur 1280 px de large est illisible. C'est le seul endroit du lot K où il y a une décision de mise en page à prendre, pas une copie à faire.
✅ **Maquette faite et validée le 2026-08-12** — `DOCS/claude design/kiosk-paysage-article-event.html`, une seule passe pour K3 et K7. Six règles communes (deux colonnes ancrage/flux, texte à 65 caractères, héros à 26 % au lieu de 52 %, plus de `showModalBottomSheet`, carte sans « Agrandir », accents dérivés de `configuration.primaryColor` selon le précédent K4) et quatre décisions tranchées : **fond clair** aux valeurs réelles de l'app (`kBackgroundColor` / `kBackgroundLight`), **pas de sélecteur de langue** (choisie en amont), **hors dates : aucune prose**, et le **retour automatique** sorti du périmètre (ligne suivante).
⚠️ **Deux vérifications faites dans le code plutôt que supposées.** Les **statistiques sont déjà acquises** : `section_page_detail.dart:60/72` émet `sectionView` et `sectionLeave` avec durée, et il enveloppe les treize types — l'article sera compté dès que son `case` entre dans le `switch`, sans une ligne à écrire. ⛔ **Et une erreur à ne pas rouvrir : j'avais écrit que `tablet-app` n'a aucun i18n. C'est faux.** `lib/Helpers/translations.dart` porte une table de **10 langues** (FR, EN, NL, DE, IT, ES, PL, CN, AR, UK), ~150 entrées, lue par `TranslationHelper.getFromLocale` à **19 endroits**, avec déjà une clé `back`. Ce n'est pas de l'ARB : chercher `l10n/` ou `AppLocalizations` ne le trouve pas — c'est une table à la main, le même pattern que mymuseum, décrit dans le `CLAUDE.md` du repo. **Conséquence** : un libellé traduit coûte une clé × 10 langues, pas un chantier. La règle « hors dates, aucune prose » reste défendable — rien à traduire vaut mieux que quelque chose à traduire — mais c'est désormais un **choix, pas une contrainte**, et le motif d'origine était faux. ⚠️ Corollaire pour l'implémentation : le bouton « Retour » passe par la clé `back`, il ne s'écrit pas en dur | **À traiter avec K3** : `SectionEvent` pose exactement la même question de paysage (héros, programme, carte), et la trancher deux fois donnerait deux mises en page différentes sur la même borne. Une seule passe de design pour les deux, même raisonnement d'économie que **L5** et **L15**.
**Trois états incomplets couverts par la maquette** : sans audio (le dock tombe, l'image reprend la hauteur), sans photo (le rail tombe, l'audio passe en tête de la colonne de lecture), texte seul (une colonne centrée, toujours plafonnée). ⚠️ **`audioIds` est une `List`** : « sans audio » est un état **du visiteur**, pas de l'article. Règle actée le 2026-08-12 — **pas d'entrée pour la langue choisie, pas de lecteur du tout** : ni lecteur grisé, ni repli sur une autre langue. Et **`isContentTop` existe déjà** ; en paysage il n'y a plus de « dessus », donc **le drapeau devient le côté** : `isContentTop == true` → texte à gauche et rail média à droite, sinon média à gauche. Le rail change de bord, pas de contenu. À câbler sous peine de rendre sans effet un réglage déjà offert dans `manager-app` | +| ~~**K7**~~ ✅ **2026-08-12** | **`SectionArticle` livré**, `flutter build apk --debug` vert. `Screens/Article/article_view.dart` remplace le stub `Text("TODO Article")` et le `case` est décommenté — **l'import de `ArticleView` était déjà en tête de `main_view`**, laissé par celui qui avait écrit le TODO. Rail média à 38 %, colonne de lecture plafonnée à 720 px, les trois états incomplets, `isContentTop` qui inverse les colonnes.
⚠️ **La zone C de la maquette n'appartient pas à l'écran** : `section_page_detail:184/198` dessine déjà le bouton retour, avec les clés `back`/`menu` selon `isFromMenu`. Un article qui aurait dessiné son pied de page en aurait affiché deux. **À corriger dans la maquette.**
⚠️ **L'audio se résout d'abord dans `contents`, l'API en repli** : `ContentDTO` porte son `resource` complet (idiome déjà utilisé par `marker_view`), donc l'audio est trouvé **sans appel réseau** quand il est embarqué — donc hors ligne aussi. `resourceGetDetail` n'intervient qu'à défaut. Plus robuste que mymuseum, qui appelle l'API systématiquement en ligne.
Énoncé d'origine : **`SectionArticle` sur le kiosk — ajouté le 2026-08-12**, en recomptant les types couverts. Son `case` est commenté « TODO » dans `main_view.getContent` et `Screens/Article/article_view.dart` fait 1 Ko : un article rend « Ce type n'est pas supporté ». **Contrairement à `SectionEvent` et `SectionParcours`, ce type existait dans Mongo** — c'est donc un vrai retard, pas un type né avec Postgres v3, et il sortira de la migration avec du contenu réel.
**Le code est une reprise de `mymuseum-visitapp/lib/Screens/Sections/Article/`**, pas une conception. **Le travail n'est pas là** : il est dans le **passage en paysage**. Les écrans de mymuseum sont pensés pour un téléphone tenu debout ; une borne est large, et un article rendu en colonne unique sur 1280 px de large est illisible. C'est le seul endroit du lot K où il y a une décision de mise en page à prendre, pas une copie à faire.
✅ **Maquette faite et validée le 2026-08-12** — `DOCS/claude design/kiosk-paysage-article-event.html`, une seule passe pour K3 et K7. Six règles communes (deux colonnes ancrage/flux, texte à 65 caractères, héros à 26 % au lieu de 52 %, plus de `showModalBottomSheet`, carte sans « Agrandir », accents dérivés de `configuration.primaryColor` selon le précédent K4) et quatre décisions tranchées : **fond clair** aux valeurs réelles de l'app (`kBackgroundColor` / `kBackgroundLight`), **pas de sélecteur de langue** (choisie en amont), **hors dates : aucune prose**, et le **retour automatique** sorti du périmètre (ligne suivante).
⚠️ **Deux vérifications faites dans le code plutôt que supposées.** Les **statistiques sont déjà acquises** : `section_page_detail.dart:60/72` émet `sectionView` et `sectionLeave` avec durée, et il enveloppe les treize types — l'article sera compté dès que son `case` entre dans le `switch`, sans une ligne à écrire. ⛔ **Et une erreur à ne pas rouvrir : j'avais écrit que `tablet-app` n'a aucun i18n. C'est faux.** `lib/Helpers/translations.dart` porte une table de **10 langues** (FR, EN, NL, DE, IT, ES, PL, CN, AR, UK), ~150 entrées, lue par `TranslationHelper.getFromLocale` à **19 endroits**, avec déjà une clé `back`. Ce n'est pas de l'ARB : chercher `l10n/` ou `AppLocalizations` ne le trouve pas — c'est une table à la main, le même pattern que mymuseum, décrit dans le `CLAUDE.md` du repo. **Conséquence** : un libellé traduit coûte une clé × 10 langues, pas un chantier. La règle « hors dates, aucune prose » reste défendable — rien à traduire vaut mieux que quelque chose à traduire — mais c'est désormais un **choix, pas une contrainte**, et le motif d'origine était faux. ⚠️ Corollaire pour l'implémentation : le bouton « Retour » passe par la clé `back`, il ne s'écrit pas en dur | **À traiter avec K3** : `SectionEvent` pose exactement la même question de paysage (héros, programme, carte), et la trancher deux fois donnerait deux mises en page différentes sur la même borne. Une seule passe de design pour les deux, même raisonnement d'économie que **L5** et **L15**.
**Trois états incomplets couverts par la maquette** : sans audio (le dock tombe, l'image reprend la hauteur), sans photo (le rail tombe, l'audio passe en tête de la colonne de lecture), texte seul (une colonne centrée, toujours plafonnée). ⚠️ **`audioIds` est une `List`** : « sans audio » est un état **du visiteur**, pas de l'article. Règle actée le 2026-08-12 — **pas d'entrée pour la langue choisie, pas de lecteur du tout** : ni lecteur grisé, ni repli sur une autre langue. Et **`isContentTop` existe déjà** ; en paysage il n'y a plus de « dessus », donc **le drapeau devient le côté** : `isContentTop == true` → texte à gauche et rail média à droite, sinon média à gauche. Le rail change de bord, pas de contenu. À câbler sous peine de rendre sans effet un réglage déjà offert dans `manager-app` | | **K8** | **Retour automatique à l'accueil après inactivité — ajouté le 2026-08-12**, sorti de la maquette K3/K7. Un visiteur part sans fermer l'écran ; le suivant trouve l'article du précédent. Délai proposé : **5 minutes**.
**À écrire une seule fois pour les treize types**, dans `section_page_detail` — il enveloppe déjà toutes les sections et porte déjà les crochets `initState`/`dispose`. Bénéfice de bord : le retour passant par le même `dispose`, il émet un `sectionLeave` **avec sa durée réelle** au lieu de laisser une consultation ouverte jusqu'au prochain visiteur. **La statistique devient juste, en plus de l'écran** | Ce n'est ni K3 ni K7 : c'est un comportement de borne qui vaut pour tous les types. À ne pas glisser dans le port des deux écrans, sinon il sera écrit deux fois puis une troisième | | **K6** | **Publier les APK à jour**, puis vérifier le renouvellement du parc | **L19**. Les tablettes kiosk sont des appareils que tu gères — mise à jour maîtrisée. **Les téléphones des visiteurs, non** : aucune manœuvre technique ne force une mise à jour, d'où la stratégie de coexistence du lot I |