Merge pull request #579 from MBombeck/release/v1.31.8

chore: release v1.31.8
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-07-21 18:20:40 +02:00
commit b01822be48
8 files changed
+65 -14

No files matched your search

+7
View File
@@ -2,9 +2,16 @@
## [Unreleased]
## [1.31.8] — 2026-07-21
- **MCP search cursors keep lab results stable across pages.** Distinct analyte
names are ordered before offset pagination, preventing repeated or skipped
lab entries when a client follows `nextCursor`.
- **Opening the medication wizard no longer risks clearing entered fields.**
The one-shot URL flag is removed without starting a second page navigation,
so slower browsers keep the active wizard instance and its form state.
No migrations. No breaking changes.
## [1.31.7] — 2026-07-20
+1 -1
View File
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: HealthLog API
version: 1.31.7
version: 1.31.8
description: >-
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
+3 -2
View File
@@ -20,8 +20,9 @@ import { STORAGE_STATE_PATH } from "./setup/global-setup";
/**
* v1.5.4 — Medication wizard end-to-end — daily cadence.
*
* Walks the 7-step daily path through the modal dialog. The dialog
* mounts when `/medications/new` redirects to `/medications?new=1`.
* Walks the 7-step daily path through the modal dialog. The shared helper
* opens `/medications?new=1` directly so cadence coverage is independent of
* the legacy `/medications/new` redirect.
* The DE label surface (the maintainer's primary locale) is the assertion
* surface — "Schritt N von 7" tracks the visible counter.
*/
+8 -3
View File
@@ -120,8 +120,9 @@ export async function stubDashboardAnalytics(page: Page): Promise<void> {
}
/**
* Open the wizard dialog by hitting `/medications/new` (which
* redirects to `/medications?new=1` and opens the create dialog).
* Open the wizard directly on `/medications?new=1`. The redirect from the
* legacy `/medications/new` route is a separate concern; these cadence specs
* exercise the wizard without racing that server navigation.
*/
export async function openCreateWizard(page: Page): Promise<void> {
// Pin the German locale for the wizard flow. The app default locale is
@@ -141,7 +142,11 @@ export async function openCreateWizard(page: Page): Promise<void> {
: "http://localhost:3000",
},
]);
await page.goto("/medications/new", { waitUntil: "domcontentloaded" });
await page.goto("/medications?new=1", { waitUntil: "load" });
await page.waitForURL(
(url) => url.pathname === "/medications" && !url.searchParams.has("new"),
{ timeout: 10_000 },
);
await expect(
page.locator('[data-slot="medication-wizard-dialog"]'),
).toBeVisible({ timeout: 10_000 });
+23
View File
@@ -33,6 +33,29 @@ import { STORAGE_STATE_PATH } from "./setup/global-setup";
test.describe("medication wizard — one-shot", () => {
test.use({ storageState: STORAGE_STATE_PATH });
test("does not replay the one-shot URL flag after history traversal", async ({
page,
}) => {
await stubDashboardAnalytics(page);
await openCreateWizard(page);
await page.keyboard.press("Escape");
await expect(
page.locator('[data-slot="medication-wizard-dialog"]'),
).toHaveCount(0);
await page.locator('a[href="/"]:visible').first().click();
await page.waitForURL((url) => url.pathname === "/");
await page.goBack();
await page.waitForURL(
(url) => url.pathname === "/medications" && !url.searchParams.has("new"),
);
await expect(
page.locator('[data-slot="medication-wizard-dialog"]'),
).toHaveCount(0);
});
test("creates a single-dose medication end-to-end", async ({ page }) => {
test.slow();
await stubDashboardAnalytics(page);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "healthlog",
"version": "1.31.7",
"version": "1.31.8",
"description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.",
"license": "PolyForm-Noncommercial-1.0.0",
"homepage": "https://healthlog.dev",
+1 -1
View File
@@ -36,7 +36,7 @@ try {
// v1.4.38.4 → v1.4.42. Do not hand-edit; bump `package.json` and rebuild.
const CACHE_VERSION =
(typeof self !== "undefined" && self.__APP_VERSION__) ||
/* @sw-version-fallback */ "v1.31.7";
/* @sw-version-fallback */ "v1.31.8";
const STATIC_CACHE = `healthlog-static-${CACHE_VERSION}`;
const PAGE_CACHE = `healthlog-pages-${CACHE_VERSION}`;
// v1.18.6 — read-only data cache for a curated allowlist of safe GET `/api/*`
+21 -6
View File
@@ -198,12 +198,27 @@ export default function MedicationsPageClient() {
const { layout, isLayoutLoading } = useMedicationListLayout(isAuthenticated);
useEffect(() => {
if (shouldOpenFromUrl) {
// Drop the query param so a refresh after closing the dialog
// doesn't keep reopening it.
router.replace("/medications");
}
}, [shouldOpenFromUrl, router]);
if (!shouldOpenFromUrl) return;
// Child effects may run before the App Router installs its native-history
// integration, while the responsive sheet also settles its first client
// layout. Wait until the following painted frame so replaceState preserves
// Next's internals and synchronizes useSearchParams without starting a
// router navigation that can remount the open wizard and discard fields.
let cleanupFrame: number | undefined;
const layoutFrame = window.requestAnimationFrame(() => {
cleanupFrame = window.requestAnimationFrame(() => {
window.history.replaceState(null, "", "/medications");
});
});
return () => {
window.cancelAnimationFrame(layoutFrame);
if (cleanupFrame !== undefined) {
window.cancelAnimationFrame(cleanupFrame);
}
};
}, [shouldOpenFromUrl]);
const {
data: medications,