Commit Graph
9818 Commits
Author SHA1 Message Date
Marc-André Bombeck 46b1cd6f34 refactor(insights): classify the derived BMI through classifyBMI
The derived BMI card kept its own copy of the WHO cut-offs beside
`classifyBMI`, which the targets panel, the status text and the
overview use. It now maps that one classifier onto its four-word
vocabulary, so a later change to the cut-offs has one place to land.
2026-11-11 20:00:38 +01:00
Marc-André Bombeck 4fa5e6381e fix(insights): take weight and BMI bands from the record on screen
Inside a shared or managed record the weight page shaded the band for
the viewer's own height, and the record's own target never arrived
because `/api/user/thresholds` refuses under a switch: a parent saw
their own healthy range behind a child's weights. The BMI page decided
from the viewer's height whether there was anything to show, and the
target gear opened an editor that could neither load nor save there.

The weight page now reads the record's target and height from
`/api/insights/targets`, the payload the reference panel under the
chart already uses, and paints the same wings as before. The BMI page
reads the record's height there inside somebody else's record. The
gear stays hidden in another person's record, as the BMI chart
already draws nothing there rather than a wrong figure.
2026-11-11 19:56:49 +01:00
Marc-André Bombeck 2c7a0dc3c2 fix(insights): show small lab changes and print them like the labs page
"What changed since your last panel" rounded every delta to two
decimals, so TSH 0.004 to 0.009 read "no change", and it printed values
and deltas through `String()`: a full stop in every language and every
digit of a unit conversion (41.999198717948715 ng/mL).

The summary now reads each reading at the precision it was recorded at
(`recordedPrecision`, with the report's printed figure for a converted
value, which the route now selects) and rounds the delta to the finer
of the two, so its direction agrees with the printed figures. The card
prints both through `useLabNumber()`. 5.9 - 5.6 still comes out as 0.3,
so the existing marker-pairing test stands as it was.
2026-11-11 19:52:13 +01:00
Marc-André Bombeck 9d7c99e60c fix(vaccinations): date a dose by its calendar day on every surface
A dose date is stored at UTC midnight. The health record PDF, the share
page, the related-records list, the document link chips and both dose
pickers formatted it as an instant in the reader's zone, so across the
Americas each of them showed the day before the vaccination list did.
The booster anchor added the interval to the stored instant, which put
the first due day a day early there too.

The report and share page print dose dates through the calendar-date
formatter, the link surfaces through `useCalendarDate()`, and the
booster counts its interval from the dose's date to the start of that
day in the person's zone.
2026-11-11 19:50:34 +01:00
Marc-André Bombeck 9afcfd4351 fix(day): read dose and allergy dates as calendar dates
A dose date is stored at UTC midnight and an allergy onset at noon UTC;
neither has a time of day. The day view, the day index, the visit
preparation and the timeline treated both as instants in the person's
zone: west of UTC a dose landed on the previous day, and the day view
printed a clock time nobody entered (01:00 for a dose in Berlin, 13:00
for an onset).

These readers now take the date part, as the vaccination list and the
allergy form already do: the day and the index query the UTC calendar
day (the window documents already use), the events carry no instant,
and the timeline dates both kinds by their key. Correct for midnight
and noon rows in every zone, without touching stored data.
2026-11-11 19:47:53 +01:00
Marc-André Bombeck 1d0a2a8128 fix(doctor-report): print the date of birth on its own day west of UTC
The date of birth is a calendar date stored at UTC midnight. The PDF
cover and the share page formatted it as an instant in the owner's
zone, so everyone in the Americas read the day before; settings and
the FHIR export, which take the date part, already showed the right
day. Both now print it through a calendar-date formatter (read in UTC,
the owner's date order), which is right in every zone, unlike a noon
anchor that moves the day east of UTC+12.
2026-11-11 19:45:12 +01:00
Marc-André Bombeck 9a02dfff2e fix(settings): build the avatar initials from the display name
The settings avatar took its fallback letters and its alt text from the
login, while the top bar and the sidebar spell the display name, so the
same account showed different initials on two screens. The three
copies of the initials function become one `nameInitials()` beside
`accountName()`, and the settings avatar reads the same name.
2026-11-11 19:43:21 +01:00
Marc-André Bombeck 5cf4a7d213 fix(doctor-report): name a managed record by its display name
The report cover, the PDF author field, the FHIR Patient and the share
page printed `fullName ?? username`, and the collector never read the
display name. A managed record has no way to get a full name, so its
generated `managed-<hex>` login landed on every clinical document made
for it; an own account printed the login instead of its display name.

The collector now resolves the name once (`patientName()`: full name,
display name, login, never a managed record's generated handle) and the
five readers print that one field. `patient.username` and
`patient.fullName` leave the report payload; nothing else read them.
2026-11-11 19:42:17 +01:00
Marc-André Bombeck aae029d94e Merge pull request #1365 from MBombeck/release/v1.47.2
release: v1.47.2
v1.47.2
2026-11-11 07:21:17 +01:00
Marc-André Bombeck 0a0df4946a chore(release): v1.47.2 2026-11-11 07:01:25 +01:00
Marc-André Bombeck 9ef3331f85 test(pwa): give the sw-version script tests time to start node 2026-11-11 01:56:44 +01:00
Marc-André Bombeck a1cfb570c1 test(browser): wait for the file's own iframe before the first command
Every browser test iframe is named "vitest-iframe" and the Playwright
provider looks the frame up by that name. Once a page runs a second
file, the first file's iframe can still be found until its detach event
arrives, so the second file's first click lands in a frame that is going
away and fails with "Frame was detached". With fifteen browser files and
at most twelve pages, three pages now run two files each, and a full
local run failed this way in four of five tries. The setup now waits
until the frame found by name is its own.
2026-11-11 01:47:48 +01:00
Marc-André Bombeck 13fd7edfd8 docs(changelog): do not promise a unit change on a (calc) marker 2026-11-11 01:20:25 +01:00
Marc-André Bombeck 3745a0207d test(gki): check the chart's series colour without a browser
In the full run the browser version made a neighbouring browser file
fail with a detached frame, while each passed alone. Render the chart in
Node with Recharts stubbed, as the coach result chart tests do.
2026-11-11 01:11:27 +01:00
Marc-André Bombeck 8b607ab501 test: drive the revoke race against Postgres and type the new fixtures
The revoke audit is now asserted in the integration race test where a
rotation commits while the turn-off waits, instead of a route test that
mocked the handler and the client. The other new tests type their
fixtures rather than casting them away.
2026-11-11 01:00:23 +01:00
Marc-André Bombeck 8aa8379861 fix(gki): draw the index chart in a chart token
The custom metric chart drew its line in --primary, which put the index
in the action colour right under the ketone series drawn in a chart
token. Give the chart an optional series colour, default primary so a
custom metric looks as before, and draw the index in --chart-4.
2026-11-11 00:52:41 +01:00
Marc-André Bombeck 35b4e75a53 refactor(reminders): share the satisfiable select with the Telegram button
The select is checked against SatisfiableReminder, so a column the
reschedule needs cannot be left out of a narrowed read again, and the
handler is back under its size baseline.
2026-11-11 00:52:40 +01:00
Marc-André Bombeck 0b3c013af7 fix(i18n): name the period in the forecast style hints
Only the selected style's hint is shown, so "it may start" and "they
may fall" had no noun on screen to point back to. Name the period in
every locale that left it implied, and put the Korean hints in the same
register as the rest of the card.
2026-11-11 00:49:02 +01:00
Marc-André Bombeck b4a5442fb5 fix(labs): leave "/" to the browser when no search field is on screen
The shortcut called preventDefault before checking for the field, so on
a labs or documents page still loading, in error, or with nothing
recorded, "/" did nothing and the browser lost its own use of the key.
2026-11-11 00:48:16 +01:00
Marc-André Bombeck ee1eb921a9 fix(ui): announce a filter bar's result count
The count beside every filter bar was a plain span, so a screen reader
heard nothing while a list narrowed, only the empty state once nothing
was left. Make it a polite status region.
2026-11-11 00:47:27 +01:00
Marc-André Bombeck 128cf55ab6 fix(day): fit the GKI tile's pair line in its column
The single-reading line spelled out both readings with their names and
units and was cut off after the glucose in every layout. Show the two
numbers with one unit, which fits the tile, and keep the worded pair for
assistive tech.
2026-11-11 00:46:30 +01:00
Marc-André Bombeck a7e98b980e fix(cycle): audit the feeds a revoke actually turned off
DELETE audited the feed it read before its two revoke passes. When a
rotation committed in between, that feed was already revoked by the
rotation and the second pass revoked its successor, so the trail showed
the old feed revoked twice and the new one never. Read back the rows
carrying this request's revokedAt stamp and audit each.
2026-11-11 00:45:22 +01:00
Marc-André Bombeck 5ebfac1259 fix(gki): build the index from the glucose shown beside it
A reading entered in mmol/L is stored as whole mg/dL, and the index
divided the unrounded conversion, so a 5.2 / 0.3 pair printed beside a
17.4 rather than 17.3. Round the glucose to the decimal it is shown with
first and divide that. The tiles also printed a two-decimal ketone
reading to one decimal; show it as recorded.
2026-11-11 00:44:29 +01:00
Marc-André Bombeck c8b32ccaea fix(documents): judge the review flag on the unit as stored
A "1 (calc)" unit is stored as "1", the explicit no-unit value, but the
dimensionless check ran on the raw string and missed it, so the read
went straight through instead of to review.
2026-11-11 00:42:05 +01:00
Marc-André Bombeck 6c8458d911 fix(reminders): keep a course window's end through every reschedule
The first due slot of a Coach course honoured endsOn, but the tick's
roll-on, satisfy and skip all built their schedule input without it, so
a seven-day twice-daily course kept rolling on indefinitely and never
reached the null nextDueAt the expired-course cleanup waits for.
Carry endsOn on RollableReminder and SatisfiableReminder and select it
wherever a caller narrows the row.
2026-11-11 00:41:31 +01:00
Marc-André Bombeck f521cdbbaf docs(changelog): correct the checkup repeat wording and note the zlib update 2026-11-11 00:27:46 +01:00
Marc-André Bombeck f20efca500 Merge PR #1370: biomarker filtering and search in the labs list 2026-11-11 00:25:12 +01:00
Marc-André Bombeck ba3ee968c7 style(labs): format the filter rail files 2026-11-11 00:22:26 +01:00
Marc-André Bombeck c59f4d7e65 docs(changelog): describe the labs search and filters 2026-11-11 00:18:50 +01:00
Marc-André Bombeck 142307acf5 fix(i18n): put the Korean biomarker count after the noun
"바이오마커 {count}개" reads as a count of biomarkers; the counter
before the noun read like a fragment.
2026-11-11 00:18:27 +01:00
Marc-André Bombeck cc9cbc4640 fix(labs): read odd filter links kindly and match names without accents
A link with the date bounds reversed showed an empty list; it now reads
as the range it means. The search ignores accents as well as case, so
"hamoglobin" finds "Hämoglobin". The category pill only appears when
there are categories to choose between, or when a link already filters
by the only one, so the filter can still be cleared where it shows.
2026-11-11 00:18:17 +01:00
Marc-André Bombeck a40c9e4ea4 fix(labs): keep keyboard focus where it belongs around the search field
Clearing the search with its button removed the button under the
focus and dropped focus onto the page; it now stays in the field. The
/ shortcut no longer pulls focus out of an open dialog, menu or list,
and ignores a key another handler already took or one typed while an
input method is composing.
2026-11-11 00:16:39 +01:00
Marc-André Bombeck 74464184c5 fix(labs): keep what is typed in the search field when the URL catches up
The labs search writes its trimmed query to the URL after a short pause,
and every change of the URL query replaced the field's text. The
field's own write came back as well: a trailing space vanished, so
"vitamin " followed by "d" searched for "vitamind", and a letter
typed while the write landed was lost. A query the field wrote itself
now leaves the field alone; one from back/forward, a link or Reset
still replaces it.
2026-11-11 00:15:24 +01:00
Marc-André Bombeck bcf411f38b chore(deps): move node-forge to 1.4.3
The override in pnpm-workspace.yaml now floors node-forge at 1.4.3, which
clears the five advisories against 1.4.2 and leaves pnpm audit --prod
clean. It arrives only under @parse/node-apn, which pins 1.4.2 exactly;
the APNs sender uses token auth, so the certificate path never runs.
2026-11-11 00:14:35 +01:00
Marc-André Bombeck 04a4353e50 fix(labs): put the search field beside the filter rail, not in it
Inside the rail, the full-width search field wrapped on phones and left
the filter icon alone on a line above it, and the shared FilterBar grew
a class override and a second layout rule to make room. The search
field now sits next to the unchanged rail: above it on phones, where
the pills keep the usual scroll row, and in one line with it from sm
up. FilterBar and docs/ui-guidelines.md are back as they were, so the
filter-bar grammar stays the one ui-standards.md defers to.
2026-11-11 00:13:41 +01:00
Marc-André Bombeck 2c63bf7a3a chore(labs): tidy the filter rail follow-ups
Drop a local-only ignore line, end the search placeholder with the
ellipsis character the other placeholders use, and put the
UrlFilterSyncOptions interface above the hook's doc comment so that
comment documents the hook.
2026-11-11 00:12:04 +01:00
Marc-André Bombeck fbbcaf4417 docs(api): keep the labs category status rule in the contract
The description of a labs category lost the status rule and the
sentence that a client never rebuilds the category, the order or the
status. Both are back as they were, with one line added: a client
that shows only the members matching a filter may summarise that
subset by the same rule.
2026-11-11 00:11:35 +01:00
Marc-André Bombeck 82ad1e2b45 fix(labs): call the date filter "Last measured"
The filter compares only the day of a marker's newest reading, so a
marker measured inside the range and again later drops out. "Date
range" promised more than that. The pill now reads "Last measured" in
every locale, and the from/to semantics are written down where the
filter is defined.
2026-11-11 00:10:44 +01:00
Marc-André Bombeck 2e2a0ac4a1 fix(labs): keep groups with a match open while filtering
With groups opening only when flagged, the open state was worked out
from the filtered rows. Filtering to in-range markers, or searching for
an unremarkable one, closed the header while the counter reported
matches. A group's default now follows the server's status for the
whole category again, and while any filter is active every group with
a match is open. A group closed by hand during filtering stays closed
while the filter changes, is not stored, and the saved state returns
once the filter is cleared.
2026-11-11 00:09:54 +01:00
Marc-André Bombeck b3e22269cb revert(labs): keep the shared list-preference precedence unchanged
The saved-entry check and the reordered legacy precedence in
useModuleListPrefs reach the illness and checkup lists as well, and
the filter rail does not need them now that the sort pill is gone.
The hook is back to what the release line ships; the precedence
question can be raised on its own.
2026-11-11 00:08:29 +01:00
Marc-André Bombeck 2f4da62c46 refactor(labs): drop the sort pill from the filter rail
Settings > Labs already holds the list's sort order. The pill added a
second, URL-only one that was lost on the next visit, and Reset cleared
it while the rail counted it as no filter at all. The list now sorts
the way Settings says, and a ?sort param is ignored.
2026-11-11 00:07:58 +01:00
Marc-André Bombeck a486c3c8a3 Merge remote-tracking branch 'origin/release/v1.47.2' into takeover/1370
# Conflicts:
#	CHANGELOG.md
#	src/lib/module-list-prefs.ts
2026-11-11 00:04:51 +01:00
Marc-André Bombeck ea3fcce80c fix(checkups): keep a missed rolling reminder due until it is measured
A rolling reminder ("every N days") is anchored on its last reading, but
the reminder tick rolled any cycle of a week or less on after a delivered
reminder, re-anchoring it on the send. A missed every-two-days blood
pressure reading therefore moved two days ahead each evening, read "in 2
days" on the card, and never showed as today or overdue.

A rolling reminder now stays due after its reminder, whatever its
interval, until a reading lands or it is done, skipped or snoozed. While
it stays open the repeat follows its interval, capped at a week, still in
its notify hour and under the per-day claim. Calendar rules that recur
within a week (a twice-daily course) keep rolling on; appointments are
unchanged.

holdsOpenAfterReminder stays the one definition for the tick and the
edit route. The daily digest asks the narrower question underneath it,
hasLongCycle, so a short rhythm still waits for its time of day on the
start page and is not pinned under the rail cap.

Refs #1369
2026-11-10 22:14:02 +01:00
Konrad Garbal b4ac67ee7c feat(labs): add biomarker filtering and sorting 2026-11-10 20:46:57 +01:00
Marc-André Bombeck d110f0456c chore(bundle): baseline the ketones page 2026-11-10 20:07:51 +01:00
Marc-André Bombeck 4dcb041612 fix(labs): review a unit that was only a calc note, keep unseen category places
A document unit that held only "(calc)" read as no unit and went straight to the record; it now goes to review. Saving a category order keeps the saved keys not on screen after the shown ones. The changelog says what each fix does and no more.
2026-11-10 19:53:47 +01:00
Marc-André Bombeck 69ba8bec91 fix(measurements): read the GKI's glucose from the ketone side
The correlated EXISTS over a time range had no equality to drive it from the ketones, so a person with years of sensor glucose walked every glucose row in the window. Each ketone reading now reads its glucose through a LATERAL index range. A full tie between two glucose readings resolves by id, the day tile names the unit, and the empty text says a ketone reading above zero.
2026-11-10 19:53:47 +01:00
Marc-André Bombeck c14cc9ab47 fix(cycle): turning the feed off wins against a rotation in either order
DELETE revoked the one feed it had read, so a rotation committing while it waited left the successor live behind a 200. It now revokes every live feed of the record in two passes. A rotation that loses to a turn-off answers 404 cycle.feed.none instead of 409; PATCH documents its 409. The in-repo feed guide describes the three forecast styles.
2026-11-10 19:53:47 +01:00
Marc-André Bombeck c62abad965 fix(sync): give every drained domain a fresh watermark two minutes back
A domain with no new rows kept its last row's stamp, so once that was older than the tombstone horizon every pull answered cursorExpired and the client resynced from scratch on every pull. A domain whose page runs dry now takes serverNow minus the overlap on every page where its watermark has not passed that line, and on the last page every row domain does, raising a quiet domain and holding back one that delivered rows in the stretch. A domain drained early in a long walk is covered as well. The overlap grows from 60 to 120 seconds, above the 60-second timeout of the longest transactions that write served rows.
2026-11-10 19:49:30 +01:00
Marc-André Bombeck 62f4578874 Merge branch 'feat/v1472-ketones' into feat/cycle-feed-forecast-style
# Conflicts:
#	CHANGELOG.md
2026-11-10 19:31:31 +01:00