From 2944fb5a0552c7a530805cef2ab84783ead9d470 Mon Sep 17 00:00:00 2001 From: Thomas Fransolet Date: Thu, 10 Sep 2026 12:07:04 +0200 Subject: [PATCH] =?UTF-8?q?QR=20/=20nom=20d'app=20/=20proximit=C3=A9=20:?= =?UTF-8?q?=20STATUS,=20plan=20de=20test=20=C2=A723,=20kanban=20r=C3=A9g?= =?UTF-8?q?=C3=A9n=C3=A9r=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le §23 couvre les réglages du manager, les trois formats de QR (id brut du Fort, URL MDLF, nouvelle URL /download), la page de téléchargement et les notifications de proximité. §9 mis à jour : au premier plan c'est la popup, la notification n'arrive qu'écran verrouillé, et meterZoneGPS sert aux zones GPS, pas aux beacons. Co-Authored-By: Claude Opus 5 (1M context) --- STATUS.md | 1 + kanban.html | 58 +++++++++++++++++++++++++++++++++++++++++----- test-plan.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 8 deletions(-) diff --git a/STATUS.md b/STATUS.md index 8f6b786..efa4dc9 100644 --- a/STATUS.md +++ b/STATUS.md @@ -14,6 +14,7 @@ Résumé de la table de suivi de ce fichier : - **Fait** : SectionEvent, Escape/Puzzle games, GuidedPath + progression carte, push, stats tracking, sync agenda/météo, traduction IA, quotas (IA/stockage/stats) par plan, audit log backend fiabilisé, refacto SectionParcours/GuidedPath/SectionGame (backend, vérifié 2026-07-15). - **Partiel** : Beacons/geofencing (manager-app UI ⚠️), AI Assistant (manager-app ⚠️), ~~**mode offline**~~ ✅ **corrigé le 2026-08-11 (D1) puis complété le 13/08 (D5)** — le pipeline de collecte n'est plus commenté, une visite embarque désormais contenus, audios et icônes, et une visite incomplète ne s'annonce plus téléchargée. **Reste à confirmer sur device** (§21 / D0), c'est le seul point encore ouvert du volet offline. Repasse UI globale, refacto SectionParcours côté manager-app/visitapp-web/mymuseum-visitapp (UI présente mais sous-comportements non revérifiés en détail — voir section "Refactoring majeur" de todo-features.md). - **🧪 Implémenté mais jamais testé (2026-08-05)** : **onboarding self-service complet** — inscription sur myinfomate-landing (`/[lang]/signup`), création auto de l'instance + du premier user, essai 14j, Stripe (customer/Checkout/webhook/Tax), 9 emails Resend (domaine vérifié, envoi réel validé), job Hangfire du cycle d'essai, watermark « Aperçu » visitapp-web, écran Abonnement manager-app, mot de passe oublié + invitation user, plafond IA d'essai. Les 4 repos compilent, migrations appliquées en base locale, **aucun parcours exécuté** → checklist de test dans `todo-features.md` (section "🧪 Checklist de test end-to-end"). Reste à faire côté config : alias mail `onboarding@myinfomate.be`, et `whsec_` de la Stripe CLI pour tester le webhook en local. +- **🧪 Codé et committé le 2026-09-10 (rien de poussé), pas encore testé** : scan d'id brut (QR du Fort) depuis l'accueil, drapeau « afficher le scan QR » et **nom de l'application** par app (Mobile/Web), liens stores (SuperAdmin), QR du manager vers la nouvelle page **`app.myinfomate.be/download/…`**, suggestions de proximité beacon + GPS avec **notification écran verrouillé** (mobile) et suggestion GPS dans la page (web). Tests : [test-plan.md §23](test-plan.md) ; restes hors code (redirection `web.mymuseum.be`, déclarations stores) : carte kanban « QR codes, nom d'application… ». - **À faire** : dashboard super-admin V2, facturation V2. ✅ **Le Stripe Customer Portal est complet le 2026-08-13** (backend + bouton manager-app). - ⚠️ **Cette ligne était périmée sur six points, revérifiés dans le code le 2026-08-13.** Sont **faits** : écran audit log manager-app (front livré le 12/08), **rate limiting API Keys** (livré, pas seulement décidé — politique `ai` dans `Startup.cs:279`, `UseRateLimiter()` en `:344`, les deux endpoints décorés), gestion des users par instance (plafond serveur + compteur UI, 12/08), nettoyage `SectionEvent.ParcoursIds` (11/08), et le **backend** du Customer Portal (13/08). L'**unification `QuestionType`** n'est pas « à faire » mais **écartée** : le trio TextLibre/Digicode/ExpectedAnswer n'a jamais existé dans le code, l'enum a trois valeurs et le digicode est un comportement dérivé, pas un type — reste une propreté front, elle aussi livrée (alias nommés, 11/08). - **📦 Reporté en V2 (décidé le 2026-08-07)** : **SectionForm**, **ressource 360°**, **AR image tracking (Mind AR)**. Les trois sont spécifiés en détail dans [todo-features.md](todo-features.md) mais sortent du périmètre V1 — aucun n'est un prérequis de la migration Postgres ni de la mise en prod. À reprendre tels quels quand la V1 sera stabilisée. diff --git a/kanban.html b/kanban.html index cbc1085..d8c1aa5 100644 --- a/kanban.html +++ b/kanban.html @@ -461,10 +461,10 @@
2Urgent
1Migration v3
2Bugs ouverts
-
11À tester
-
36Planifié
+
12À tester
+
38Planifié
9Bascule prod
-
62Fait récemment
+
63Fait récemment
@@ -560,7 +560,7 @@
-

À tester

11
+

À tester

12
@@ -660,12 +660,33 @@ conversation du 2026-09-08 — captures du Fort de Saint-Héribert
+
+
§23manager-servicemanager-appvisitapp-webmymuseum-visitappCodé 10/09 · device requis · redirection serveur à faire
+

QR codes, nom d'application, page /download et notifications de proximité

+

Codé dans les quatre repos, rien n'est committé :

+
    +
  • ApplicationInstance (par app, Mobile ≠ Web) : IsQRCodeEnabled (défaut true), AppName (traductions), AppStoreUrl/PlayStoreUrl (modifiables par un SuperAdmin seulement, contrôlé côté API). Migration AddAppNameQrAndStoresToApplicationInstance.
  • +
  • Scan mobile : id brut (QR du Fort) résolu via la section puis sa visite, y compris depuis l'accueil ; les URL web.mymuseum.be (MDLF) restent lues ; nouvelle URL app.myinfomate.be/download/…. QR d'une autre instance refusé.
  • +
  • QR du manager : app.myinfomate.be/download/{instance}/{config}/{section} — ou la section web directement pour une instance web seule.
  • +
  • visitapp-web : page /download (nom, image principale, liens stores), bouton de scan conditionné, nom de l'app sur l'accueil et dans l'onglet, suggestion de proximité GPS dans la page. Slugs download/demo/api réservés.
  • +
  • Proximité mobile : beacon + zones GPS (meterZoneGPS). App ouverte → popup ; écran verrouillé pendant une visite → notification, dont le tap ouvre la section. Android : service de premier plan location ; iOS : mode d'arrière-plan location. App tuée : rien, par choix.
  • +
+

⚠️ Reste à faire hors code :

+
    +
  • Redirection 301 web.mymuseum.be/{i}/{c}/{s}app.myinfomate.be/download/{i}/{c}/{s}, pour les QR MDLF scannés à l'appareil photo. Le domaine n'est routé dans aucun compose des repos : à poser là où il est servi.
  • +
  • app.myinfomate.be n'est pas encore déployé (carte 210) : les QR générés par le manager y mènent, les deux partent en prod ensemble.
  • +
  • Stores : déclarer le service de premier plan de type location dans la Play Console ; justifier le mode location à la revue Apple.
  • +
+

Bug corrigé au passage : la popup beacon (BeaconArticleFound) lisait les maps JSON brutes de currentSections comme des SectionDTO et plantait.

+ test-plan.md §23 +
+
-

Planifié

36
+

Planifié

38
@@ -846,14 +867,25 @@
-
myinfomate-landingNouveau 28/08 · priorité 1
+
myinfomate-landing10/09 : Thomas ne veut pas afficher de montant

Chiffrer les frais de mise en place sur la page tarifs

Le défaut est de confiance, pas de design. La carte Pro annonce €99/mois et, juste dessous, setupFeeNote = « Frais de mise en place + engagement 12 mois (app mobile) » — sans aucun chiffre (translations.ts:272, repris tel quel en EN/NL/DE). L'acheteur budgète 1 200 €/an, puis découvre le setup en rendez-vous : c'est le pire moment pour l'apprendre. Remède : « à partir de X € selon le volume de contenu », dans les 4 langues. Priorité n°1 de la passe.

+

⚖️ Arbitré le 10/09 : pas de montant affiché. Thomas ne veut pas publier de plancher. Mesure de repli appliquée le même jour, dans les 4 langues : setupFeeNote devient « Frais de mise en place uniques, chiffrés dans le devis + engagement 12 mois », et la FAQ de la nouvelle page /tarifs précise ce que couvrent ces frais (configuration et publication sur les stores) et qu'ils sont chiffrés avant signature. L'acheteur sait donc qu'une ligne s'ajoute, et quand il en connaîtra le montant. Ce qui reste ouvert : si un plancher est un jour décidé, c'est cette carte qui le porte.

Secondaire, même passe : badge « IA incluse » sur Premium — le badge « Recommandé » reste sur Pro (HomeClient.tsx:525), il ne se déplace pas ; regrouper assistant IA + traduction automatique + audio sous un intitulé unique type « Studio IA » plutôt que trois lignes de features indistinctes ; remplacer le stockage en GB (1 / 15 / 50 GB, en dur dans HomeClient.tsx et SegmentPageClient.tsx) par une métrique que l'acheteur sait évaluer : POI / parcours / langues / sites. Personne ne sait ce que pèsent ses contenus en gigaoctets.

⚠️ Le « Bientôt disponible » sur Essentiel n'existe pas dans le code — vérifié le 28/08 : le seul badge « Bientôt » de la landing est sur le 4e mode de déploiement (XR, HomeClient.tsx:154), et la carte Essentiel pointe déjà vers /signup. Ce qui bloque réellement Essentiel est le déploiement de app.myinfomate.be (carte dédiée dans ce tableau). L'arbitrage est donc : déployer, ou ajouter un badge honnête en attendant — pas retirer un badge absent.

myinfomate-landing/src/data/translations.ts · HomeClient.tsx §pricing · todo-features.md — Site vitrine
+
+
myinfomate-landingseolinkedinDémarré 10/09 · actions 1-9 faites
+

Plan SEO/GEO de la landing et lancement LinkedIn

+

Audit du 09/09 : 21 actions classées par rapport impact/effort. Faites le 10/09 et vérifiées dans le HTML servi : titres h2/h3, schema Organization ancré en Belgique avec la TVA, trois offres dans le schema, les 9 modules de la home enfin dans le HTML, liens morts, llms.txt en français, prérendu statique rétabli (58 pages, le layout racine est passé dans [lang]) et une page /tarifs avec FAQ tarifaire. Stagé, pas committé.

+

/assistant-ia est écrite le 10/09, en français seulement (~850 mots, 6 FAQ, schema FAQPage) : en attente de relecture, puis traduction NL/EN/DE. Les autres langues répondent 404, c'est volontaire.

+

Suite : vocabulaire acheteur dans les 6 segments, puis page marchés publics — preuve à mettre en avant, confirmée le 10/09 : Visit Namur a été gagné sur cahier des charges publié, mais ⚠️ aucun document d'appel d'offres n'existe encore, donc ne rien promettre. Les frais de mise en place restent sans montant, par décision de Thomas (carte 180).

+

LinkedIn : vitrine MyInfoMate créée sous Unov, fiche produit soumise. Huit posts rédigés en deux vagues : ⚠️ ceux qui poussent à l'inscription attendent la bascule prod.

+ myinfomate-landing/audit-seo-geo.md §0 · DOCS/linkedin-posts-lancement.md +
+
priorité 4V2

Génération de visites à la demande

@@ -861,6 +893,15 @@ plan-import-ia-stats-subsides.md §3
+
+
juridiquemyinfomate-landingCGU publiées le 10/09 sans relecture juridique
+

Faire valider les CGU par un juriste belge

+

Les CGU sont en ligne depuis le 10/09 — publier valait mieux que faire cocher « j'accepte » sur un lien mort — mais elles n'ont jamais été relues par un juriste, ce que le document source dit lui-même depuis avril. Deux points appellent un œil professionnel : le §8 (RGPD), réécrit le 11/08 quand le guide IA s'est mis à enregistrer les questions des visiteurs, et le §9 (SLA à 99 %), qui est un engagement chiffré tenu par une infrastructure mono-serveur.

+

Deux sources à garder synchrones : DOCS/cgu-myinfomate.md fait foi, la page Next en est le rendu. Toute correction du juriste passe par les deux.

+

⏱️ À faire avant d'ouvrir l'inscription self-service à de vrais clients payants, pas avant la mise en prod technique. Prévoir aussi le contrat de traitement des données (DPA) qu'un acheteur public demandera — il n'existe pas encore.

+ DOCS/cgu-myinfomate.md (note d'en-tête) · myinfomate-landing/src/app/(legal)/conditions-generales/page.tsx +
+
guide IAV2

Guides multiples & guide thématique

@@ -1456,6 +1497,11 @@ Les CGU décrivaient des plans abandonnés (Starter/Standard/Premium 69/99/199 €). Alignées sur la landing, avec un quota IA défini en tokens renvoyant au back-office plutôt qu'un chiffre figé dans un contrat. Landing : « req/mois » → « questions de visiteurs / mois » en 4 langues.
+
+ Le lien « conditions générales » de l'inscription ne mène nulle part +

Relevé et corrigé le 10/09. Les CGU sont désormais publiées sur myinfomate.be/conditions-generales (groupe (legal), page statique), la case de consentement de /signup pointe dessus, et le lien figure aussi dans les pieds de page des 4 langues et dans le sitemap. Deux contradictions du document source ont été corrigées au passage : le §6.1 citait les plans abandonnés (Starter 2 Go / Standard 10 Go) et le §7.1 réservait l'assistant IA aux « plans Standard et Premium ». ⚠️ La validation juridique reste due — carte dédiée.

+
+
Écran Guide IA — onglet Configuration livré Migration additive (4 colonnes nullable), champs sur Instance, client généré étendu à la main, écran avec 35 clés i18n FR/EN/NL. dotnet build et flutter build web passent. Menu conditionné à isAssistant, le même drapeau que la garde d'AiController. diff --git a/test-plan.md b/test-plan.md index ccead60..3d1eb18 100644 --- a/test-plan.md +++ b/test-plan.md @@ -50,6 +50,7 @@ tests fonctionnels ci-dessous. 19. [**Parité manager-app → apps visiteur (par type de section)**](#19-parité-manager-app--apps-visiteur-par-type-de-section) 20. [Écarts de parité connus — à rejouer après correction](#20-écarts-de-parité-connus--à-rejouer-après-correction) 21. [Visite hors ligne — diagnostic terrain](#21-visite-hors-ligne--diagnostic-terrain) ⚠️ **bugs suspectés, à jouer tôt** +23. [QR codes, nom d'application, page de téléchargement, proximité](#23-qr-codes-nom-dapplication-page-de-téléchargement-proximité) --- @@ -274,14 +275,14 @@ Voir [v2/stats-screen-plan.md](v2/stats-screen-plan.md) § État d'implémentati | # | Action | Résultat attendu | ✓ | |---|--------|-----------------|---| -| 9.1 | Visitapp en range d'un iBeacon connu | Notification locale `beaconFound` affichée | | +| 9.1 | Visitapp en range d'un iBeacon connu | App au premier plan : popup de suggestion, **sans** notification. App en arrière-plan / écran verrouillé : notification locale `beaconFound` (cf. §23) | | | 9.2 | Notification beacon | Titre/corps dans la langue active (clé `beaconFound` / `beaconFoundBody`) | | | 9.3 | Entrée dans une zone geo d'un `GuidedStep` | Notification locale `geoZone` affichée | | | 9.4 | Filtre par `minorId` + `accuracy` + cooldown | Pas de spam de notifications pour le même beacon | | | 9.5 | `beacon_scanner: ^0.0.4` | Scanner opérationnel sur Android et iOS | | | 9.6 | **iOS** — balise émettant un `proximityUUID` **autre** que `FDA50693-A4E2-4FB1-AFCF-C6EB07647825` | Le cas doit **échouer** : la région iOS est filtrée sur cet UUID en dur (`geo_beacon_trigger_service.dart:148-155`). Si ça déclenche quand même, le code ne fait pas ce qu'il dit — Android, lui, scanne sans filtre | | | 9.7 | Déclenchement **proactif de l'assistant** par balise (chemin distinct de 9.1) | Le prompt part depuis `geo_beacon_trigger_service._onBeaconResult`, uniquement si le mode proactif est actif | | -| 9.8 | Distance réelle de déclenchement, mètre en main | ⚠️ `configuration_page.dart:43` = `meterToBeacon = 100` alors que le commentaire dit 15 m, et `Section.meterZoneGPS` est ignoré. Noter la distance **mesurée**, pas celle attendue | | +| 9.8 | Distance réelle de déclenchement, mètre en main | ⚠️ `proximity_suggestion_service.dart` = `beaconMaxDistanceMeters = 100` (accuracy du ranging). `Section.meterZoneGPS` sert désormais aux zones **GPS**, pas aux beacons. Noter la distance **mesurée**, pas celle attendue | | | 9.9 | Stationner 2 min devant un POI | Une seule notification (cooldown), pas de répétition | | | 9.10 | Balise avec `major` porteur d'un niveau de batterie | Aucun effet sur l'identification du POI — l'app ne lit que le `minorId`. C'est ce qui rend le `major` utilisable pour la télémétrie | | @@ -918,6 +919,66 @@ Les comptages ne disent rien des traductions, des médias liés ni de l'ordre. * --- +## 23. QR codes, nom d'application, page de téléchargement, proximité + +Prérequis : migration `AddAppNameQrAndStoresToApplicationInstance` appliquée ; visitapp buildée après `flutter clean` (empreinte de `libapp.so` vérifiée). + +### 23.1 — Réglages dans manager-app (écrans Mobile et Web) + +| # | Action | Résultat attendu | ✓ | +|---|--------|-----------------|---| +| 23.1.1 | Saisir le **nom de l'application** en FR/EN/NL, recharger | Valeurs conservées, par app (Mobile ≠ Web) | | +| 23.1.2 | Décocher **Afficher le scan de QR code**, recharger | Case restée décochée | | +| 23.1.3 | En **SuperAdmin**, saisir les liens App Store / Play Store (écran Mobile) | Enregistrés | | +| 23.1.4 | En **InstanceAdmin** | Les champs de liens stores n'apparaissent pas ; un `PUT` forgé avec d'autres liens ne les modifie pas | | +| 23.1.5 | QR d'une section (instance avec app mobile) | Encode `https://app.myinfomate.be/download/{instanceId}/{configId}/{sectionId}` | | +| 23.1.6 | QR d'une section (instance **web seule**) | Encode `https://app.myinfomate.be/{slug}/{configId}/sections/{sectionId}` | | + +### 23.2 — Scan dans l'app mobile + +| # | QR scanné | Depuis | Résultat attendu | ✓ | +|---|-----------|--------|-----------------|---| +| 23.2.1 | Id brut d'une section (QR du **Fort**) | Accueil | La visite s'ouvre directement sur la section | | +| 23.2.2 | Id brut d'une section de la visite ouverte | Visite | La section s'ouvre | | +| 23.2.3 | Id brut d'une section d'une autre visite | Visite | Popup « autre visite » puis ouverture sur la section | | +| 23.2.4 | URL `web.mymuseum.be/…` d'un vrai QR **MDLF** imprimé | Accueil et visite | Même comportement qu'avant | | +| 23.2.5 | Nouvelle URL `app.myinfomate.be/download/…` | Accueil et visite | Ouvre la section | | +| 23.2.6 | QR d'une **autre instance** | Accueil | « QR code invalide » | | +| 23.2.7 | Visite hors ligne, mode avion, id brut | Accueil | Résolu depuis la base locale | | +| 23.2.8 | Scan QR désactivé dans le manager | — | Bouton absent sur l'accueil, dans la visite et dans un menu | | +| 23.2.9 | Nom de l'app renseigné | — | En-tête de l'accueil = nom de l'app (langue active) ; sans nom : titre de la 1re visite comme avant | | + +### 23.3 — Web (visitapp-web) + +| # | Action | Résultat attendu | ✓ | +|---|--------|-----------------|---| +| 23.3.1 | Ouvrir `/download/{instanceId}/{c}/{s}` (téléphone, appareil photo) | Image principale, nom de l'app, boutons stores renseignés, textes dans la langue du navigateur | | +| 23.3.2 | Même URL sans liens stores | « Bientôt disponible sur les stores » | | +| 23.3.3 | Instance sans app mobile | 404 | | +| 23.3.4 | Scanner un QR `download` depuis l'app web | Ouvre la section web | | +| 23.3.5 | Scan QR désactivé sur l'app **Web** | Bouton absent (accueil et visite) | | +| 23.3.6 | Nom de l'app renseigné | Titre sur l'accueil et dans l'onglet | | +| 23.3.7 | Visite avec sections géolocalisées, activer le bouton de proximité, forcer la position dans les DevTools | Carte « À proximité » une fois par section et par session, 20 s minimum entre deux | | +| 23.3.8 | ⚠️ **Prendre l'`instanceId` d'un vrai QR MDLF imprimé** et ouvrir `/download/{cet id}/…` | La page **de MDLF** s'affiche (son nom, son image, ses liens). Si elle ne trouve rien, l'id a changé à la migration Mongo → Postgres : la redirection ne servirait à rien, à traiter avant de la poser | | +| 23.3.9 | Instance sans **nom d'application** renseigné | Le titre retombe sur le nom de l'instance, jamais sur « Application mobile » | | +| 23.3.10 | Ancien QR d'une instance **web seule** redirigé vers `/download/…` | Redirection vers la visite web à la bonne section | | + +### 23.4 — Proximité et notifications (mobile, device réel) + +| # | Action | Résultat attendu | ✓ | +|---|--------|-----------------|---| +| 23.4.1 | Visite avec zones GPS (sans beacon) | Le bouton de proximité apparaît une fois les sections chargées | | +| 23.4.2 | Activer, entrer dans une zone (mock location Android), app ouverte | Popup de suggestion, **pas** de notification | | +| 23.4.3 | Même chose écran verrouillé | Notification « Contenu à proximité » + titre de la section | | +| 23.4.4 | Toucher la notification | L'app s'ouvre sur la section | | +| 23.4.5 | Rester dans la zone écran verrouillé | Une seule notification par section et par visite | | +| 23.4.6 | Android : pendant le scan | Notification persistante « Suggestions de contenu à proximité activées » ; disparaît en coupant le bouton ou en quittant la visite | | +| 23.4.7 | iOS : écran verrouillé | Indicateur de localisation en arrière-plan ; notification reçue (beacon **et** GPS) | | +| 23.4.8 | App tuée | Aucune notification (hors périmètre, par choix) | | +| 23.4.9 | Mode vocal lunettes sans permission de localisation (Android 14+) | Pas de crash ; le service de premier plan ne démarre pas | | + +--- + ## Hors scope (non implémenté) | Feature | Statut |