- FIX #70 : generateForecastDoses garde !isActive — un traitement archivé à Posologie ne projette plus de créneaux ; l'horizon du chip Prévision (chart.js) ne considère que les Posologies ACTIVES. §6.bis inchangé : l'historique reste simulé, seul le FUTUR s'arrête. 3 tests miroirs. - Page Doses : routeGlyph (💊💉💧🩹🧴, miroir routeIcon) + encarts « prise de sang le même jour » 🧪↑/🧪↓ (labMarkersForDose, miroir) ; i18n FR/EN ; 10 tests doses-extras.test.js (⚠️ makeDose(ts, amount, overrides)). 194 tests + E2E verts (check.sh). WEB_VERSION alignée sur 1.12.0.
446 lines
24 KiB
Markdown
446 lines
24 KiB
Markdown
# 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.12.0] — 2026-09-29
|
||
|
||
### Corrigé — un traitement inactif à Posologie projetait encore ses injections (#70)
|
||
|
||
- Miroir de l'Android v1.12.0 (remontée : « un traitement mis en inactif ne
|
||
devrait plus être pris en compte pour les futures prédictions ») :
|
||
`generateForecastDoses` (js/pk/pk-engine.js) garde `!isActive` — et
|
||
l'horizon du chip Prévision du graphique (js/ui/chart.js) ne considère
|
||
que les Posologies ACTIVES.
|
||
- §6.bis inchangé : l'historique d'un inactif reste simulé — seul le FUTUR
|
||
s'arrête. 3 tests miroirs (inactif vide, actif inchangé, avant/après
|
||
archivage).
|
||
|
||
### Ajouté — page Doses : glyphes de forme de prise + marqueurs « prise de sang le même jour »
|
||
|
||
- Chaque ligne de dose porte le glyphe de sa forme (💊 oral, 💉 injection
|
||
IM/SC, 💧 gel, 🩹 patch, 🧴 autre — helper pur `routeGlyph` dans
|
||
js/data/models.js, miroir de `routeIcon`).
|
||
- Une analyse de sang LE MÊME JOUR qu'une dose affiche un encart latéral
|
||
🧪↑ (avant la dose) et/ou 🧪↓ (après) — helper pur `labMarkersForDose`
|
||
(js/util/format.js, jour calendaire LOCAL ; lab à l'heure exacte =
|
||
APRÈS, convention documentée).
|
||
- 10 tests (`tests/doses-extras.test.js`, miroirs de DosesExtrasTest.kt).
|
||
|
||
Vérifié : 194 tests verts + E2E (`check.sh`).
|
||
|
||
## [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.
|