v1.9.6 (web) : la carte Accueil liste TOUTES les prochaines doses (triées) — miroir Android
- helper PUR nextDoseLineText dans format.js (miroir du Kotlin nextDoseLine) : « Xj Yh · sam. 26 10:00 (nom) » > 24 h, « XhMM » sinon, « N min » dans l'heure - home.js : une ligne par traitement, triées par prochaine prise, 1ʳᵉ en avant, titre pluriel « Prochaines doses » dès 2 lignes (i18n EN/FR) - 3 tests miroirs dans misc.test.js (178 verts + E2E) - WEB_VERSION 1.9.6 + CHANGELOG/README
This commit is contained in:
parent
be4d26b3e5
commit
048a76e540
@ -17,12 +17,12 @@
|
||||
> Fie-toi toujours à tes analyses de sang et aux consignes de ton
|
||||
> endocrinologue.
|
||||
|
||||
- **Statut** : web v1.9.5 — parité fonctionnelle avec l'Android v1.9.5
|
||||
- **Statut** : web v1.9.6 — parité fonctionnelle avec l'Android v1.9.6
|
||||
(modèle Estrannaise analytique + nuage d'incertitude MCMC exclusif ESE ;
|
||||
presets des 6 esters injectables ESE ; l'auto-backup journalier Android
|
||||
reste structurellement non porté, cf §12 — l'export manuel couvre la
|
||||
donnée) ·
|
||||
**175 tests verts** · E2E navigateur ✅ · conteneur testé en
|
||||
**178 tests verts** · E2E navigateur ✅ · conteneur testé en
|
||||
configuration durcie ✅ · lint/i18n ✅
|
||||
- **Journal des versions web** : [docs/CHANGELOG.md](docs/CHANGELOG.md)
|
||||
- **Releases** : [gitea.cloudyfy.fr/Siphonight/HormoneTrack-web/releases](https://gitea.cloudyfy.fr/Siphonight/HormoneTrack-web/releases)
|
||||
|
||||
@ -5,6 +5,20 @@
|
||||
> 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.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)
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
import { computeCurve, autoCalibrated, nextReminderFireFor, Alerts, TConfig, pointHoursBefore, nextBloodDrawRecommendation } from '../pk/index.js';
|
||||
import { store } from '../data/store.js';
|
||||
import { t } from '../util/i18n.js';
|
||||
import { formatDose, formatValue, formatTime, formatWeekdayTime, formatDateTime, HrtDuration } from '../util/format.js';
|
||||
import { formatDose, formatValue, formatDateTime, formatWeekdayTime, nextDoseLineText } from '../util/format.js';
|
||||
import { el } from './components.js';
|
||||
import { drawChart, COLORS } from './chart-canvas.js';
|
||||
import { openDoseDialog } from './dialogs.js';
|
||||
@ -109,7 +109,14 @@ export function renderHome(container, ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Carte « prochaine dose » (grille Posologie via le moteur, fix #52) ───
|
||||
// ── Carte « prochaines doses » (grille Posologie via le moteur, fix #52) ──
|
||||
// v1.9.6 (demande) : la carte liste TOUTES les prochaines prises, UNE
|
||||
// ligne par traitement TRIÉES par prochaine prise (helper pur
|
||||
// nextDoseLineText, miroir du Kotlin nextDoseLine) — l'ancienne carte ne
|
||||
// montrait que la plus proche, et une prise quotidienne (CPA) masquait
|
||||
// totalement le rappel de l'injection hebdomadaire. Le titre passe au
|
||||
// pluriel dès qu'il y a au moins deux lignes ; la 1ʳᵉ ligne (la plus
|
||||
// proche) reste mise en avant (font-weight 600).
|
||||
const fires = [];
|
||||
for (const tr of treatments) {
|
||||
const fire = nextReminderFireFor(tr, doseLogs, nowMs);
|
||||
@ -117,24 +124,12 @@ export function renderHome(container, ctx) {
|
||||
}
|
||||
if (fires.length) {
|
||||
fires.sort((a, b) => a[1] - b[1]);
|
||||
const [nextTreatment, next] = fires[0];
|
||||
const deltaMs = next - nowMs;
|
||||
const h = Math.floor(deltaMs / HOUR_MS);
|
||||
const m = Math.floor((deltaMs % HOUR_MS) / 60000);
|
||||
const card = el('div', { class: 'card' });
|
||||
card.appendChild(el('div', { class: 'label' }, t('next_dose')));
|
||||
if (h >= 24) {
|
||||
// v1.4.1 : au-delà de 24 h, afficher en JOURS + jour de semaine
|
||||
const [days, remH] = HrtDuration.daysAndHours(h);
|
||||
card.appendChild(el('div', { style: 'font-weight:600;' },
|
||||
t('next_dose_days', days, remH, formatWeekdayTime(next), nextTreatment.name)));
|
||||
} else if (h > 0) {
|
||||
card.appendChild(el('div', { style: 'font-weight:600;' },
|
||||
`${h}h${String(m).padStart(2, '0')} · ${formatTime(next)} (${nextTreatment.name})`));
|
||||
} else {
|
||||
card.appendChild(el('div', { style: 'font-weight:600;' },
|
||||
`${m} min · ${formatTime(next)} (${nextTreatment.name})`));
|
||||
}
|
||||
card.appendChild(el('div', { class: 'label' }, t(fires.length === 1 ? 'next_dose' : 'next_doses')));
|
||||
fires.forEach(([treatment, next], idx) => {
|
||||
card.appendChild(el('div', { style: idx === 0 ? 'font-weight:600;' : '' },
|
||||
nextDoseLineText(nowMs, next, treatment.name)));
|
||||
});
|
||||
col.appendChild(card);
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ import { el, showDialog, field, switchRow, filterChip, toast } from './component
|
||||
* (APK + web au MÊME numéro, tags appariés `vX.Y.Z` + `vX.Y.Z-web`).
|
||||
* v1.4.10 = portage initial de l'Android v1.4.10.
|
||||
*/
|
||||
export const WEB_VERSION = '1.9.5';
|
||||
export const WEB_VERSION = '1.9.6';
|
||||
|
||||
/** Parse un champ numérique FR (virgule → point) ; null si vide/invalide. */
|
||||
function parseNumOrNull(text) {
|
||||
|
||||
@ -113,6 +113,33 @@ export function formatLongDate(ms, timeZone = null) {
|
||||
}), timeZone).format(new Date(ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ligne « prochaine prise » d'un traitement (v1.9.6 — miroir du helper
|
||||
* `nextDoseLine` Kotlin) : « Xj Yh · sam. 26 10:00 (nom) » au-delà de 24 h
|
||||
* (v1.4.1 : les heures deviennent peu lisibles), « XhMM · HH:mm (nom) » en
|
||||
* dessous, « N min · HH:mm (nom) » dans l'heure. PUR, testé dans
|
||||
* tests/misc.test.js — sert à CHAQUE ligne de la carte « Prochaines doses »
|
||||
* de l'accueil (une ligne par traitement, triées par prochaine prise).
|
||||
*
|
||||
* @param {number} nowMs maintenant
|
||||
* @param {number} fireMs prochaine prise du traitement
|
||||
* @param {string} name nom du traitement
|
||||
* @param {string} [timeZone=null]
|
||||
* @returns {string}
|
||||
*/
|
||||
export function nextDoseLineText(nowMs, fireMs, name, timeZone = null) {
|
||||
const deltaMs = fireMs - nowMs;
|
||||
const h = Math.floor(deltaMs / 3600000);
|
||||
const m = Math.floor((deltaMs % 3600000) / 60000);
|
||||
if (h >= 24) {
|
||||
const days = Math.floor(h / 24);
|
||||
const remH = h % 24;
|
||||
return `${days}j ${remH}h · ${formatWeekdayTime(fireMs, timeZone)} (${name})`;
|
||||
}
|
||||
if (h > 0) return `${h}h${String(m).padStart(2, '0')} · ${formatTime(fireMs, timeZone)} (${name})`;
|
||||
return `${m} min · ${formatTime(fireMs, timeZone)} (${name})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* « EEE d HH:mm » (carte prochaine dose au-delà de 24 h — jour de semaine
|
||||
* inclus, cf v1.4.1).
|
||||
|
||||
@ -42,6 +42,7 @@ const EN = {
|
||||
no_data_short: 'No data — log an injection to see estimates',
|
||||
delta_6h: '{0} {1} pg/mL vs 6 h ago',
|
||||
next_dose: 'Next dose',
|
||||
next_doses: 'Upcoming doses',
|
||||
next_dose_days: '{0} d {1} h · {2} ({3})',
|
||||
logged_today: 'Quick log',
|
||||
home_chart_title: 'Last 24 h',
|
||||
@ -252,6 +253,7 @@ const FR = {
|
||||
no_data_short: 'Aucune donnée — logue une injection pour voir les estimations',
|
||||
delta_6h: '{0} {1} pg/mL vs il y a 6 h',
|
||||
next_dose: 'Prochaine dose',
|
||||
next_doses: 'Prochaines doses',
|
||||
next_dose_days: '{0} j {1} h · {2} ({3})',
|
||||
logged_today: 'Log rapide',
|
||||
home_chart_title: 'Dernières 24 h',
|
||||
|
||||
@ -14,7 +14,7 @@ import { nextReminderFireFor, nextReminderFireMs } from '../js/pk/pk-reminders.j
|
||||
import { detectExtrema } from '../js/pk/pk-extrema.js';
|
||||
import { LevelPoint } from '../js/pk/pk-engine.js';
|
||||
import { isVersionNewer, sectionsSince } from '../js/util/changelog.js';
|
||||
import { HrtDuration } from '../js/util/format.js';
|
||||
import { HrtDuration, nextDoseLineText } from '../js/util/format.js';
|
||||
import { makeTreatment, makeDose, DAY_MS, HOUR_MS, assertClose } from './helpers.js';
|
||||
|
||||
/** Met (h, m) sur le jour calendaire de [ms] (locale — miroir du module testé). */
|
||||
@ -212,3 +212,30 @@ describe('HrtDuration (temps sous THS)', () => {
|
||||
assert.throws(() => assertClose(300, 100, 0.02));
|
||||
});
|
||||
});
|
||||
|
||||
describe('nextDoseLineText (carte « Prochaines doses » — v1.9.6, miroir NextDoseLineTest.kt)', () => {
|
||||
// Hors DST : les heures attendues sont construites avec les MÊMES
|
||||
// primitives (Intl fuseau système) que le module testé.
|
||||
const now = new Date(2026, 8, 21, 12, 0, 0).getTime(); // lundi 21/09/2026 12:00
|
||||
const timeOf = (h, m) => new Date(2026, 8, 21, h, m, 0).getTime();
|
||||
|
||||
test('au-delà de 24 h — jours + jour de semaine', () => {
|
||||
// fire = samedi 26/09 10:00 (118 h plus tard)
|
||||
const fire = now + 118 * HOUR_MS;
|
||||
const line = nextDoseLineText(now, fire, 'Injection EEn 5mg 7d');
|
||||
assert.match(line, /^4j 22h · .+ \(Injection EEn 5mg 7d\)$/);
|
||||
// le jour de semaine est présent (le format Intl diffère selon la locale
|
||||
// système : on épingle la STRUCTURE, pas le libellé)
|
||||
assert.ok(!line.startsWith('118'), 'delta affiché en jours, pas en heures');
|
||||
});
|
||||
|
||||
test('entre 1 h et 24 h — heures + minutes', () => {
|
||||
const fire = timeOf(14, 5);
|
||||
assert.equal(nextDoseLineText(now, fire, 'CPA'), `2h05 · 14:05 (CPA)`);
|
||||
});
|
||||
|
||||
test('moins d\'une heure — minutes', () => {
|
||||
const fire = timeOf(12, 7);
|
||||
assert.equal(nextDoseLineText(now, fire, 'CPA'), `7 min · 12:07 (CPA)`);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user