From e454c8635408a94840f3dabb87793db7da17ec85 Mon Sep 17 00:00:00 2001 From: fy59 Date: Wed, 7 Oct 2026 10:20:12 +0200 Subject: [PATCH] fix(report): keep sport times chronological within each day --- CHANGELOG.md | 5 +++-- docs/current_state.md | 5 +++-- docs/sleep_diary.md | 5 +++-- docs/tests.md | 7 ++++--- web/src/report/sleepDiaryPdf.test.ts | 5 ++++- web/src/report/sleepDiaryPdf.ts | 3 ++- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 883a205..41ee55a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ## Unreleased — 0.1.7 -- Present Sleep PDF nights and the completed-sport appendix newest first while - preserving chronological timed content within each night. Add a restrained +- Present Sleep PDF nights and completed-sport days newest first while + preserving chronological timed content within each night and sport day. + Add a restrained printable header, period and generation date, and pale row separation. Remove unhelpful validation labels from agenda rows and PDF warnings; the editor now names the exact-revision synchronization publication action and diff --git a/docs/current_state.md b/docs/current_state.md index 0919ce3..587d01f 100644 --- a/docs/current_state.md +++ b/docs/current_state.md @@ -263,8 +263,9 @@ full-width heart-rate detail; it performs no database mutation. PDF preview and download instead share an explicit inclusive range over night start dates, initialized to the loaded history bounds and independent of detail selection. The vector PDF now presents selected nights newest first, while retaining -chronological timed content within each night; its sport appendix is also -newest first. A light header, period, generation date and pale alternating rows +chronological timed content within each night; its sport appendix shows newest +days first and chronological sessions within each day. A light header, period, +generation date and pale alternating rows improve print hierarchy in color and grayscale. Agenda rows and PDF omit the publication/validation labels. The editor still exposes the exact-revision publication action and state required for synchronization; PDF selection and diff --git a/docs/sleep_diary.md b/docs/sleep_diary.md index 9480dfd..80b4995 100644 --- a/docs/sleep_diary.md +++ b/docs/sleep_diary.md @@ -66,8 +66,9 @@ on dated pages instead of truncating silently. Review the chosen range before export; a PDF is a local copy of potentially sensitive information. The PDF prints the newest selected night first and orders its sport appendix by -actual session start, newest first. Events and medication intakes within each -night keep their chronological order. A light, printable header shows the +actual session day, newest first, with sessions chronological within each day. +Events and medication intakes within each night keep their chronological order. +A light, printable header shows the selected period and generation date; alternating pale rows and a restrained accent distinguish nights. Publication status and global validation warnings are omitted from the report. This presentation does not alter Sleep facts, diff --git a/docs/tests.md b/docs/tests.md index acc06d2..1b8c429 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -409,9 +409,10 @@ subset summaries cannot inherit facts from unselected nights. The same real Firefox scenario exports a seven-night French PDF from the embedded production bundle, extracts its text with `pdftotext` and rasterizes its A4 landscape page with `pdftoppm` for retained visual inspection. -The 0.1.7 PDF polish tests also assert newest-first night and sport appendix -order, unchanged chronological intervals and medication intakes within a night, -7/14/21/28-night pagination, and absence of global validation warnings. Web +The 0.1.7 PDF polish tests also assert newest-first night and sport day order, +chronological sessions within each day, unchanged chronological intervals and +medication intakes within a night, 7/14/21/28-night pagination, and absence of +global validation warnings. Web tests keep the exact-revision publication action and editing while excluding publication labels from agenda rows. diff --git a/web/src/report/sleepDiaryPdf.test.ts b/web/src/report/sleepDiaryPdf.test.ts index 62b87d0..df848ff 100644 --- a/web/src/report/sleepDiaryPdf.test.ts +++ b/web/src/report/sleepDiaryPdf.test.ts @@ -332,6 +332,8 @@ describe("sleep diary PDF", () => { it("prints factual sport bands and a separate legible listing without changing sleep totals", async () => { const sessions = [ + { identity: "se_previous", session_type: "training", label: "Yoga", + started_at: "2026-09-20T19:00:00+02:00", ended_at: "2026-09-20T19:30:00+02:00" }, { identity: "se_morning", session_type: "training", label: "Musculation", started_at: "2026-09-21T05:00:00+02:00", ended_at: "2026-09-21T06:00:00+02:00" }, { identity: "se_evening", session_type: "training", label: "Étirements", @@ -344,7 +346,8 @@ describe("sleep diary PDF", () => { expect(content).toContain("Musculation"); expect(content).toContain("05:00"); expect(content).toContain("Étirements"); - expect(content.indexOf("Étirements")).toBeLessThan(content.indexOf("Musculation")); + expect(content.indexOf("Musculation")).toBeLessThan(content.indexOf("Étirements")); + expect(content.indexOf("Étirements")).toBeLessThan(content.indexOf("Yoga")); expect(content).toContain("sommeil non renseigné"); expect(content).toContain("1 nuit"); expect((source.match(/\/Type \/Page /g) ?? []).length).toBe(2); diff --git a/web/src/report/sleepDiaryPdf.ts b/web/src/report/sleepDiaryPdf.ts index abb3107..af9d71a 100644 --- a/web/src/report/sleepDiaryPdf.ts +++ b/web/src/report/sleepDiaryPdf.ts @@ -527,7 +527,8 @@ export function buildSleepDiaryPdf( right.night_start_date.localeCompare(left.night_start_date) || left.entry_id.localeCompare(right.entry_id)); const orderedSport = [...sport].sort((left, right) => - Date.parse(right.started_at) - Date.parse(left.started_at) || + right.started_at.slice(0, 10).localeCompare(left.started_at.slice(0, 10)) || + Date.parse(left.started_at) - Date.parse(right.started_at) || left.identity.localeCompare(right.identity)); const first = reportStart ?? orderedEntries[orderedEntries.length - 1]?.night_start_date ?? "—"; const last = reportEnd ?? orderedEntries[0]?.night_start_date ?? "—";