HormoneTrack-web/docs/CHANGELOG.md
Siphonight 0dd5fa7dfd v1.11.0 (web) : inactifs regroupés en bas de la page Traitements + cartes atténuées — miroir Android
- Helper pur treatmentsForDisplay dans js/data/models.js (miroir du Kotlin,
  tri STABLE : actifs d'abord, inactifs après, ordre relatif conservé dans
  chaque groupe) — demande : « peu discernables, à placer à la fin ».
- Rendu : en-tête « Traitements inactifs » (seulement s'il en existe),
  cartes opacity 0.55 en plus du badge — lisibles et cliquables.
- Clés i18n inactive_section FR/EN ; WEB_VERSION alignée sur 1.11.0.
- 4 tests treatments-display.test.js (miroir TreatmentsDisplayTest.kt).

184 tests + E2E verts (check.sh).
2026-09-29 07:30:59 +02:00

23 KiB
Raw Blame History

CHANGELOG — HormoneTrack Web

Journal des versions de l'app web (séparé du CHANGELOG Android — docs/CHANGELOG.md à la racine du dépôt). Convention Keep a Changelog : section la plus récente EN PREMIER ; ce fichier est lu par le dialog « Nouveautés » de l'app web (fetch docs/CHANGELOG.md).

[1.11.0] — 2026-09-29

Ajouté — les traitements inactifs regroupés en bas de la page Traitements

  • Miroir de l'Android v1.11.0 (demande : « les traitements inactifs sont peu discernables — les placer à la fin, et peut-être les rendre plus distincts ») : les ACTIFS d'abord, les INACTIFS regroupés tout en bas sous un en-tête « Traitements inactifs » (seulement s'il en existe), cartes atténuées (opacity 0,55) en plus du badge.
  • Tri STABLE : l'ordre relatif à l'intérieur de chaque groupe est conservé. Helper pur treatmentsForDisplay dans js/data/models.js (miroir du Kotlin, node-safe) — 4 tests (tests/treatments-display.test.js, miroir de TreatmentsDisplayTest.kt).

Vérifié : 184 tests verts + E2E (check.sh).

[1.10.0] — 2026-09-27

Corrigé — la recommandation de prise de sang précédait la stabilisation (#68)

  • Miroir de l'Android v1.10.0 (remontée + données réelles → 6ᵉ régression épinglée côté Android) : le « creux » était le minimum de la fenêtre entière (créneau N−1 → créneau N). Pour un ester à montée lente (EEn : pic ~J+5 ≈ intervalle 7 j), ce minimum tombe juste après l'injection PRÉCÉDENTE (creux d'absorption, ~3 h) — la date proposée était gouvernée par l'injection N−1, antérieure à la stabilisation, et le texte « juste avant ton injection du … » était faux de plusieurs jours.
  • Fix : le creux = niveau PRÉ-INJECTION du créneau (dernier point de courbe avant l'injection) — restaure le principe v1.8.0 documenté et rend la carte cohérente. Pour les esters à t½ courte (EV), le point pré-injection EST le minimum de fenêtre : comportement inchangé.
  • Tests : la suite lab-timing (12) passe inchangée — sa sémantique épinglée (« creux juste avant l'injection, écart ≤ 2 h ») était déjà celle du fix ; 180 tests + E2E verts (check.sh).

Les autres changements de la v1.10.0 Android (dialog fantôme #69, rappel sauté si dose déjà loggée) n'ont pas d'équivalent web (notifications et intents de notification sont spécifiques Android).

[1.9.7] — 2026-09-20

Corrigé — la suggestion de prise de sang « change tout le temps » à chaque injection

  • Miroir de l'Android v1.9.7 (remontée + données réelles) : la règle de stabilisation comparait l'écart inter-doses EXACTEMENT à l'écart précédent — un log 30 min plus tard cassait le régime et repoussait la stabilisation de 5 × t½ à chaque injection (creux recommandé fuyant).
  • Fix (tolérance) : chaque écart doit rester dans l'intervalle de Posologie ± 24 h (GAP_TOLERANCE_MS, fenêtre vs l'intervalle THÉORIQUE). Un vrai changement de créneau (2 j au lieu de 7 j) reste hors fenêtre. Tests : « interval change » ré-épinglé (+21 j) + 2 tests de tolérance miroirs (12 verts lab-timing).

[1.9.6] — 2026-09-20

Modifié — la carte de l'accueil liste TOUTES les prochaines prises

  • Miroir de l'Android v1.9.6 (remontée : le rappel quotidien CPA masquait le rappel hebdomadaire EEn — « je ne le vois quasiment pas ») : la carte « Prochaine dose » devient « Prochaines doses » (titre pluriel dès 2 lignes) — une ligne par traitement, TRIÉES par prochaine prise ; la 1ʳᵉ ligne (la plus proche) reste mise en avant. Format de ligne inchangé (Xj Yh · sam. 26 10:00 (nom), XhMM · HH:mm, N min).
  • Moteur : helper PUR nextDoseLineText dans format.js (miroir du Kotlin nextDoseLine) — 3 tests miroirs dans misc.test.js.
  • Les NOTIFICATIONS de rappel sont inchangées.

[1.9.5] — 2026-09-20

Corrigé — revue de code post-v1.9.4 (audit lecture-seule des deux dépôts)

  • Garde de cutoffHours pour ESE (parité du Kotlin) : la branche ESE ne vérifiait pas hasModel(ester) — un traitement IMPORTÉ (le backup accepte toute string de pkModel/ester) avec un ester non couvert (ex. ECS/PEP en ESE) levait TypeError et laissait un écran vide. Retombe maintenant sur la garde Bateman (≥ 24 h), comme TFS/WHS le faisaient déjà.
  • min/max en UNE passe : les spreads Math.min(...allPoints) (≈ 5 par frame) lèvent RangeError au-delà de ~10⁵ arguments — atteignable avec le Nuage actif × zoom max (≈ 230 000 points) — et allouaient un tableau intermédiaire par appel. Remplacés par une boucle (chart.js + chart-canvas.js).
  • Init MCMC non bloquante : le démarrage n'attend plus le fetch de l'asset (seul le nuage lit le posterior, qui renvoie [] proprement) ; l'init re-rend à la fin et pose data-ready — la garantie E2E « data-ready ⇒ asset chargé » est maintenant RÉELLE (le commentaire antérieur était faux : le marqueur était posé avant le fetch).
  • Portage des optimisations Android v1.8.2 (la dérive de miroir était réelle) : contexte moteur pré-calculé (prepareE2Context/e2AtCtx — doses groupées, cutoffs par traitement, Bateman paresseux), computeCurve ne recalcule plus cutoffHours PAR POINT × traitement ni le scan complet de doseLogs. Résultats identiques (175 tests verts).
  • E2E version paramétrée : les asserts lisent data-version posé par l'app au lieu de strings codées en dur à bump à chaque release (l'oubli a produit le bug v1.9.3).

Documenté — miroir nettoyé

  • En-tête de pk-engine.js à jour (ESE analytique v1.9.0, plus de tables ODS) ; JSDoc « troncature » de pk-calibration.js alignée sur Math.round (fix v1.9.2) ; dialogs.js (ESE=6) ; import mort et JSDoc orpheline de lab-timing.js retirés ; couleurs de l'accueil via COLORS (source unique avec le canvas).
  • pk_profiles.json exclu du zip de release (.gitattributes export-ignore, −548 Ko livrés) : plus fetché au runtime depuis v1.9.0 — l'asset ne survit que pour le test de fidélité RMS (miroir Android), pk-profile-store.js documenté test-only.

[1.9.4] — 2026-09-19

Ajouté — les presets du modèle ESE couvrent ses 6 esters injectables

  • Remontée : depuis l'ajout du modèle source Estrannaise (v1.9.0), les presets de création n'offraient toujours que EV/EU/EEn — « les presets n'ont pas été mis à jour lors de l'ajout du modèle source pour ESE ».
  • Presets : 3 presets ESE ajoutés (25 entrées, miroir de PKPresets.kt) — EC huile (5 mg IM), EB (1 mg IM), EUCS undécylate suspension cristalline (10 mg SC, ester exclusif à ce fit). Champs Bateman informatifs = t½ terminales des fits (pas utilisés au calcul).
  • Éditeur : la liste des esters suit la couverture RÉELLE du modèle choisi (choicesForModel, parité Android v1.9.0) — ESE liste ses 6 esters injectables ; au changement de modèle, si l'ester courant sort de la couverture (ex. EUCS + TFS), il rebascule sur EV (garde anti « 0 pg/mL ») et le formulaire se re-rend.
  • Corrigé (portage v1.9.0 incomplet) : Esters.EUCS manquait dans pk-engine.js (le preset référençait undefined) et choicesForModel (js/data/models.js) était resté sur la couverture des tables ODS (3 esters) avec sa KDoc obsolète « produirait 0 pg/mL ».
  • Tests : tests/presets.test.js (nouveau, 6 tests) — couverture des 6 esters ESE, unicité, clés i18n EN/FR, TFS (7) / WHSAH (6) inchangés, choicesForModel aligné sur les dispatchs. Suite web : 175 tests.

[1.9.3] — 2026-09-19

Corrigé — le numéro de version annoncé

  • Le dialog « Nouveautés » et Paramètres annonçaient 1.9.1 au lieu de 1.9.2 (le commit de fix du nuage n'incluait pas la mise à jour de WEB_VERSION) — méta-correction de version, aucune autre différence avec 1.9.2 (mêmes fonctionnalités que l'Android 1.9.2).

[1.9.2] — 2026-09-19

Corrigé — le nuage d'incertitude exigeait un traitement STOCKÉ en ESE

  • Miroir du fix Android v1.9.2 : le filtre du nuage testait le pkModel STOCKÉ du traitement, alors que la courbe ESE affichée redessine toutes les doses E2 avec modelOverride = "ESE" — le nuage restait vide dès qu'aucun traitement n'était stocké en ESE.
  • Fix : le nuage couvre toutes les doses E2 à profil injectable dont l'ester effectif (override compris) est couvert par le fit Estrannaise — indépendamment du modèle stocké. L'exclusivité ESE reste portée par le chip. L'oral Bateman reste hors nuage.
  • Test réécrit : « les doses d'un traitement TFS sont couvertes quand ESE est affiché » (épinglé des deux plateformes) ; oral seul → vide.

[1.9.1] — 2026-09-19

Corrigé — le nuage d'incertitude entoure maintenant la courbe Estrannaise calibrée

  • Miroir du fix Android v1.9.1 : la courbe ESE affichée est calibrée (scalePerEster des labs) mais le nuage était tracé brut → échelles différentes. Le nuage reçoit maintenant le MÊME scalePerEster que la courbe ESE.
  • Harmonisation de l'arrondi de la médiane de calibration (le web avait la troncature historique) : les niveaux estimés web et Android convergent. Le niveau correct = l'ARRONDI (plus juste que la troncature, cf CHANGELOG Android 1.9.1).

[1.9.0] — 2026-09-18

Modèle Estrannaise ANALYTIQUE + nuage d'incertitude (portage Android v1.9.0, versions sync)

  • Le modèle ESE n'est plus les tables horaires ODS : il est désormais la forme close 3C publiée par estrannaise.js (les sources originelles dont l'ODS n'était que l'échantillonnage horaire) — fidélité épinglée : RMS 0,00 vs les tables sur 0→200 j, écart de pic ≤ 0,1 %. Les tables ne sont plus fetchées au démarrage (−550 Ko, démarrage plus rapide) ; pk_profiles.json reste uniquement comme référence des tests.
  • 6 esters injectables couverts (EV, EU, EEn, EC, EB + EUCS, undécylate suspension cristalline, exclusif à ce fit) — l'ancien ESE n'en couvrait que 3.
  • NUAGE D'INCERTITUDE (exclusif ESE) : Estrannaise publie le posterior MCMC de ses paramètres (313 échantillons/ester) — chip Nuage (off par défaut, activable à volonté, actif SEULEMENT si ESE est affiché) → nuage diffus de courbes montrant la plage d'imprécision du modèle, comme sur le site estrannaise. TFS/WHSAH ne publient pas de posterior → pas de nuage pour eux.
  • t½ terminale ESE désormais analytique (ln2/min(k1,k2,k3)) — la recommandation de prise de sang n'estime plus la t½ par pente de table.
  • Tests : fidélité (RMS, pics), cas dégénérés, MCMC (313 × 6), nuage (32 courbes, dispersion, exclusivité ESE), t½ analytique.

[1.8.1] — 2026-09-17

Corrigé — la stabilisation ignorait les changements récents (dose/ester/intervalle)

  • Bug remonté : la recommandation disait « stabilisée depuis février » alors que l'ester, le dosage ET la posologie venaient de changer — l'ancien calcul partait de la 1ʳᵉ dose du traitement (proxy aveugle aux changements).
  • Fix : le régime courant = la séquence terminale de doses à (ester effectif, dose mg, écart inter-doses) constants — tout changement récent réinitialise la stabilisation : la recommandation saute au premier creux après la stabilisation du nouveau régime (règle des ~5 demi-vies conservée). L'encart porte l'ester effectif de la dernière dose (override compris).
  • Nouveau : encart sur l'Accueil (demande) — la suggestion de prochaine prise de sang est désormais aussi visible directement sur la page d'accueil (creux daté + créneau associé, mention de stabilisation si le régime n'était pas déjà stable).
  • 3 nouveaux tests miroirs (155 au total) : changement de dose / de posologie (intervalle) / d'ester → réinitialisation épinglée.

[1.8.0] — 2026-09-17

Prochaine prise de sang recommandée — portage de l'Android v1.8.0 (versions sync)

  • Nouvel encart sur la page Analyses : « Prochaine prise de sang (suggestion) » — quand un traitement E2 injectable actif a une Posologie, l'app déduit de ses courbes le creux estimé juste avant l'injection suivante (heure exacte + créneau associé + mention « ou simplement la veille »), au premier creux où l'ester est stabilisé (~5 demi-vies terminales après le dernier changement — on saute les creux « de transition »).
  • js/pk/lab-timing.js : miroir fidèle du Kotlin (créneaux generateForecastDoses, courbe E2 fine sans calibration, filtres maintenant/+6 h/dernière prise/stabilisation). Le modèle Estrannaise lit sa demi-vie dans les tables (pkProfileStore.terminalHalfLifeDays, refactor miroir), TFS/WHSAH analytiquement.
  • Sans Posologie sur l'injectable actif : encart remplacé par l'invite « renseigne une Posologie… » (demande v1.8.0) — le lien est découvrable.
  • 7 tests miroirs (152 au total) + scénario E2E (encart rendu sur #labs avec la démo EEn 7 j : creux daté, créneau, stabilisation).

[1.7.1] — 2026-09-16

Corrigé — une seule note sur deux s'affichait dans les Analyses

  • Bug remonté (identique Android) : dans une prise de sang E2+T, quand les deux entrées portent des notes distinctes (ex. une note clinique sur l'E2, une note « DHT » sur la T), une seule s'affichait — l'autre était bien conservée mais perdue à l'affichage.
  • Cause : l'ancien code ne prenait que la première note non vide du groupe (hypothèse historique « toutes identiques » — vraie à l'époque du dialog de création, fausse depuis l'édition unitaire).
  • Fix : labNotesForDisplay (helper PUR dans format.js, miroir du Kotlin) — notes distinctes → une ligne par note préfixée du marqueur (« E2 : … » / « T : … ») ; note identique → une seule ligne ; vides ignorées.
  • Épinglé par : 5 tests unitaires (miroir du Kotlin) + scénario E2E (la dernière paire de démo porte des notes distinctes, les deux s'affichent sur #labs) + régression data-driven Android sur l'export réel.

[1.7.0] — 2026-09-13

Unités des axes du graphique — portage de l'Android v1.7.0 (versions sync)

  • pg/mL (E2, axe gauche) et ng/mL (T, axe droit) affichées au sommet des colonnes de labels du graphique de simulation — miroir exact du fix Android (padTop élargi 12 → 26 px, texte gris dans la couleur des labels).
  • Universelles, PAS des clés i18n (déjà en dur dans les légendes).

Note de parité : la sauvegarde automatique quotidienne de l'Android v1.7.0 n'est PAS portée — le navigateur ne peut pas écrire périodiquement dans un dossier arbitraire sans intervention (voir §12 Limites) ; l'export/import manuel du backup couvre la même donnée.

[1.6.1] — 2026-09-13

Corrigé — les options du graphique se réinitialisaient « toutes seules »

  • Bug remonté : sur l'écran Graphiques, les options sélectionnées (modèles affichés, Tracé labs, Prolonger, zoom, panoramique…) revenaient d'elles-mêmes aux défauts « alors qu'on n'a pas changé de menu ».
  • Cause (divergence de portage) : le shell web re-crée l'écran ENTIER à chaque tick minute (60 s), mutation du store ou resize — et l'état des options vivait dans une variable LOCALE de la fonction de rendu, donc retombait aux défauts à chaque re-crétion. Côté Android, l'équivalent vit dans des remember { mutableStateOf } qui survivent à toutes les recompositions : le bug est structurellement impossible côté APK — c'était un piège du portage, pas de la logique.
  • Fix : l'état d'interaction du graphique vit au niveau MODULE et le shell signale les re-rendus du même écran (preserveState: true) → options préservées pendant qu'on regarde le graphique ; à l'entrée depuis la navigation (quitter puis revenir), retour aux défauts — comportement identique à l'Android.
  • Épinglé par l'E2E : re-render déclenché (resize — même code path que le tick) → chip et légende toujours en place ; navigation → reset.

Note de parité : fix web-only (pas de changement Android) — les fonctionnalités portées restent celles de l'Android v1.6.0.

[1.6.0] — 2026-09-12

« Tracé labs » prolongé au-delà du dernier lab — portage de l'Android v1.6.0

  • Nouveau chip Prolonger (à côté de Tracé labs, off par défaut, désactivé tant que Tracé labs est off) : la courbe ancrée sur tes prises de sang se PROLONGE au-delà du dernier lab — la limite v1.5.0 rendait invisible justement la période la plus récente.
  • Recette : au-delà du dernier lab significatif, courbe(t) = M(t) × ρ_dernier lab — le modèle PK (dosages, esters injectés depuis, pics et descentes classiques) × le ratio mesuré au dernier lab, supposé constant. Les doses loguées APRÈS le dernier lab font repartir la courbe en pic (override d'ester compris).
  • Horizon honnête : la prolongation s'arrête quand le MODÈLE lui-même s'éteint (dernière dose + cutoff moteur : 10 t½ terminales V3C/WHS, fin de table ODS, 30 t½ Bateman) — jamais de ligne à zéro inventée.
  • Lisibilité : partie prolongée dessinée SÉPARÉMENT (même rose, atténué) avec sa légende dédiée + AVERTISSEMENT visible sous la légende : simple simulation sans garantie de correspondre au réel, extrapolée à partir de tes résultats de laboratoire — qui peuvent eux-mêmes être erronés. Fie-toi à ta prochaine prise de sang.
  • Garde anti-ratés inchangée (#61) : un lab hors fenêtre d'action ne prolonge pas la courbe et n'est jamais traversé.
  • Portage fidèle : extensionHorizonEndMs/lastAnchorMs (miroir du Kotlin), 8 nouveaux tests unitaires miroirs + scénario E2E navigateur (activation des chips, légende et avertissement rendus).

[1.5.0] — 2026-09-11

« Tracé labs » — portage de la version Android v1.5.0 (versions synchronisées)

  • Nouvelle courbe superposable (chip Tracé labs, off par défaut)
    courbe(t) = M(t) × ρ(t) — le modèle PK garde la forme biologique (Tmax, queues, accumulation), l'amplitude est recalée sur CHAQUE prise de sang — passage EXACT garanti sur chaque lab, transition log-linéaire d'un lab au suivant (jamais de saut vertical).
  • Garde anti-ratés (#61) : un lab hors fenêtre d'action n'est pas un ancrage ; E2 uniquement ; fenêtre bornée [1er ; dernier lab] — la courbe s'arrête au dernier lab (pas d'extrapolation).
  • Indépendante de la calibration (sa propre référence) ; hors pics/creux ; n'entre JAMAIS dans niveau accueil/alertes.
  • La rangée de chips du graphique défile horizontalement (4ᵉ chip — miroir du fix Android v1.5.0).
  • 11 tests miroirs de LabTrajectoryModelTest.kt (Android).

[1.4.10] — 2026-09-08

Version initiale de la version web — portage navigateur de l'app Android v1.4.10, 100 % locale (données dans le localStorage, aucun serveur applicatif, aucune télémétrie).

Moteur PK (fidèle à l'Android, testé)

  • Trois modèles superposables : Estrannaise (tables ODS de pk_profiles.json, même asset que l'app Android), Transfem Science (forme close V3C de la méta-analyse, 7 esters), WHSAH (fit license-free de Mona, 6 esters — PEP non couvert) + Bateman paramétrable (gel/patch/oral).
  • Pics de l'article TFS épinglés (±2 %) ; équilibre EV 5 mg/7 j = Figure 11 (Cmax 384 / Cmin 142 / Cavg 269) ; fidélité Mona WHSAH (EV 367,5 @ 1,69 j, EEn 187,9 @ 4,99 j, t½ 7,34 j).
  • Invariants Android préservés : bisection computeKa dans le bon sens (#19), modèle strict + extrapolation terminale ≥ 1 % du pic (#20/#21), lookup insensible à la casse (#22), conversion des labs T (#23/#26), garde de significativité de calibration (#61), créneaux de prévision passés sautés (#35), rappels sur la grille Posologie (#52).
  • Calibration par période d'ester et par modèle affiché (#60) : auto-calibration optionnelle (désactivée par défaut) + calibration manuelle par traitement ; k T par période d'ester.

Fonctionnalités

  • Accueil : niveau actuel estimé (E2 pg/mL + T ng/mL) avec delta « vs il y a 6 h » réel (fix #53), cartes d'alerte de seuil, carte prochaine dose (jours au-delà de 24 h, fix v1.4.1), log rapide, mini-graphique 24 h.
  • Graphiques : 24 h/7 j/30 j, zoom 6 h → 300 j (pinch, molette, boutons, focal du pinch conservée), panoramique avec cumul fractionnaire des deltas (fix #62), prévision avec extension de fenêtre sans saut (fix #54) et horizon borné 30 j–1 an, toggles 3 modèles alignés sur les traitements (fix #58), pics/creux avec valeurs, marqueurs de doses réelles et projetées (fix #55), labels X à minuit local dans le fuseau choisi (v1.4.5), labs T convertis en ng/mL au rendu.
  • Doses : historique groupé par jour, Δ jours depuis la dose précédente du même traitement, édition (override d'ester par injection selon le modèle), en-tête « temps sous THS » (3 placeholders, bug #47 épinglé).
  • Analyses : prise de sang E2 + T en une entrée, groupement par timestamp, suppression par prise entière, unités T multiples (ng/mL, ng/dL, ng/L, nmol/L).
  • Traitements : 22 presets, éditeur complet (ester/modèle/Bateman, calibration, Posologie, rappel), traitement inactif = drapeau administratif (la simulation reste, bug v1.2.4).
  • Rappels web : notifications navigateur tant que la page est ouverte (bannière in-app + actions « Loguer maintenant » / « Reporter 1 h »). Limite documentée : pas de notification onglet fermé.
  • Seuils d'alerte : évaluation du niveau estimé, notification web, anti-spam par état persisté (même codec que l'app Android).
  • Sauvegarde JSON compatible Android dans les deux sens (BackupData v2, rétrocompat v1, import en mode écrasement avec IDs conservés) ; paramètres utilisateur transportés (langue, auto-cal, seuils).
  • Logs de diagnostic exportables (buffer 500 lignes, même format que l'app Android).
  • FR + EN (langue par app, indépendante du système), dialog « Nouveautés » après mise à jour.

Processus

  • 121 tests Node (node --test) : profils ODS (pins ODS identiques à l'Android), fidélité TFS/WHSAH, moteur, calibration, backup (dont import des exports Android réels de local-test-data/ si présents), helpers du graphique, alertes, rappels, changelog, store.
  • scripts/check.sh : syntaxe de tous les modules + cohérence i18n FR/EN + tests + smoke HTTP optionnel.
  • Dépôt GIT SÉPARÉ de l'app Android (HormoneTrack-web) — historique 100 % propre (aucune dette de confidentialité), versions alignées sur l'Android, changelogs totalement indépendants.

Non porté (impossible ou hors périmètre navigateur)

  • Événements d'agenda récurrents (pas de CalendarProvider dans un navigateur) — l'éditeur affiche une note explicative.
  • Notifications quand l'onglet est fermé (aucun scheduler système côté web).
  • Verrou biométrique, widget — non implémentés non plus côté Android v1.