Commit Graph
3535 Commits
Author SHA1 Message Date
Marc-André Bombeck 9f4c7e48fe fix(ui): bake password-manager autofill guard into PasswordInput
Config/secret fields (BYOK API keys, webhook secrets, ntfy tokens) pass no
autocomplete token, so PasswordInput now defaults to autoComplete=off plus
data-bwignore / data-1p-ignore / data-lpignore for them — Bitwarden /
1Password / LastPass no longer offer to fill or save a config secret.
Credential fields (login, register, password change) pass a real token and
keep manager integration untouched. One change covers every secret call site.
2026-07-08 23:47:46 +02:00
Marc-André Bombeck dbc2b15b1b fix(coach,insights,achievements): honest error states + Plans discoverability
- Achievements: a failed load renders QueryErrorCard instead of falling
  through to zeroed summary tiles and a 'none unlocked yet' empty grid.
- /coach/conversations: a load failure now shows a retry instead of a false
  'no conversations yet' empty state.
- Insights overview error uses the shared QueryErrorCard.
- Coach Plans gains a header cross-link from /coach/conversations (and back),
  a skeleton loading state, and the shared EmptyState primitive.
2026-07-08 23:44:10 +02:00
Marc-André Bombeck 2e84498cf3 fix(notifications): repoint channel CTA, add error/loading primitives, cross-link surfaces
- The 'no channels' CTA now points at Settings → Integrations#channels
  (where channels are actually configured since v1.25.7) instead of the
  reminder-types screen, which had become a dead-end.
- A failed load renders the shared QueryErrorCard with Retry; the loading
  frame reserves a matrix skeleton instead of a lone spinner.
- The globally-disabled hint reads as a full sentence rather than the bare
  '(Disabled)' inline tag.
- The three notification surfaces (per-event matrix, reminder types,
  delivery channels) now cross-link each other.
- noChannels copy names its destination.
2026-07-08 23:39:59 +02:00
Marc-André Bombeck 2dc9947de2 fix(dashboard): link metric tiles to Insights detail + unify tile header
- Dashboard metric tiles link to their /insights/<slug> detail page
  (weight, blood pressure, pulse, glucose, mood, sleep, steps, VO2 max);
  tiles with no dedicated sub-page stay un-linked rather than misroute.
- Daily-briefing key findings now route hrv, resting HR, active energy,
  flights, distance, body temperature and recovery to their sub-pages —
  the METRIC_HREF map had gone stale as those routes shipped.
- RecentWorkoutsTile adopts the canonical TileHeader (foreground icon +
  title) instead of a muted hand-rolled row.
- Hero-strip header glyph moves off text-primary to the foreground tier.
2026-07-08 23:35:41 +02:00
Marc-André Bombeck 6ad2dc858a Merge pull request #438 from MBombeck/release/v1.27.28
Release v1.27.28 — share a document from the document
v1.27.28
2026-07-08 20:48:50 +02:00
Marc-André Bombeck 3829b80aca chore(release): v1.27.28 — share a document from the document 2026-07-08 20:38:27 +02:00
Marc-André Bombeck a887a03c01 Merge branch 'feat/document-share-entrypoint' into release/v1.27.28 2026-07-08 20:34:45 +02:00
Marc-André Bombeck a3276dcfeb Merge pull request #437 from MBombeck/release/v1.27.27
Release v1.27.27 — reorderable hero rings
v1.27.27
2026-07-08 20:33:17 +02:00
Marc-André Bombeck 1305e8d4bc docs(documents): correct the detail-sheet header comment for the Share action 2026-07-08 09:56:15 +02:00
Marc-André Bombeck 46c85e8671 test(documents): share entry point seeds the create flow with the document
Unit: the shared ShareLinkCreateForm mounts bare (Settings default) and
seeded (document flow) — a pre-attached document rides in as a chip with
the label pre-filled. e2e: the document detail Share action opens the
create flow with that document pre-attached and reaches the one-time QR
reveal; marked slow (two stacked sheets + a real create).
2026-07-08 09:50:26 +02:00
Marc-André Bombeck d4009a3587 feat(documents): share action on the document detail sheet
Extract the clinician share-link create flow (create form, document
picker mount, one-time QR + passphrase reveal) from the Settings sharing
section into a reusable ShareLinkCreateForm. Settings mounts it unchanged
alongside the active/revoked lists; the document detail sheet adds a
Share action beside Download that opens the same flow in a bottom sheet
with the current document pre-attached and its title pre-filled as the
label. The one-time QR reveal lands in that sheet, scannable on mobile.

No backend change: the shared POST /api/share-links handles the attached
document set as before.
2026-07-08 09:41:47 +02:00
Marc-André Bombeck 99f3824794 chore(release): v1.27.27 — reorderable hero rings 2026-07-08 09:40:58 +02:00
Marc-André Bombeck abb2db37ee test(dashboard-hero): cover hero-ring-order default + reorder; i18n hint
Add resolveHeroRingOrder unit coverage (default health-score-first,
reconcile against selected set, drop/append, dedupe) plus resolver/serializer
round-trip; hero SSR tests for the default lead + a persisted custom order +
dropped-when-unrendered; Settings picker tests for the pinned health-score
anchor row, its locked-on switch, drag/arrow reorder, and a persisted
non-leading order. Extend the score-rings picker description across six
locales.
2026-07-08 01:23:58 +02:00
Marc-André Bombeck abe688caba feat(dashboard-hero): health-score-first default + reorderable hero rings
Add a persisted hero ring ORDER over the health-score anchor ring plus the
selected score rings. Defaults to the health-score ring on the leading edge;
the user can reorder the full set (health score included) from Settings →
Darstellung with the existing drag-handle + arrow-button pair.

The order rides the dashboard-layout blob (dashboardWidgetsJson) — no
migration. resolveHeroRingOrder reconciles a stored order against the current
ring set on every read/serialize, so a deselected ring drops and a
newly-selected ring appends; the hero orders only the rings it actually
renders.
2026-07-08 01:18:00 +02:00
Marc-André Bombeck b00a2206c5 Merge pull request #436 from MBombeck/release/v1.27.26
Release v1.27.26 — hero and wellbeing card polish
v1.27.26
2026-07-08 00:57:18 +02:00
Marc-André Bombeck 53d8d30bd2 chore(release): v1.27.26 — hero and wellbeing card polish 2026-07-08 00:46:34 +02:00
Marc-André Bombeck 50b628abb8 Merge branch 'feat/hero-and-wellbeing-polish' into release/v1.27.26 2026-07-08 00:42:33 +02:00
Marc-André Bombeck 052ead6bf3 Merge pull request #435 from MBombeck/release/v1.27.25
Release v1.27.25 — documents filter dropdowns
v1.27.25
2026-07-08 00:33:08 +02:00
Marc-André Bombeck 5238fa9923 chore(release): v1.27.25 — documents filter dropdowns 2026-07-08 00:23:23 +02:00
Marc-André Bombeck 96860c69ab Merge branch 'feat/documents-filter-dropdown' into release/v1.27.25 2026-07-08 00:19:29 +02:00
Marc-André Bombeck 4b50ac8b6a feat(documents): make filter dropdowns responsive and update the e2e flows
Give each facet trigger a leading icon and show the text label from sm up
(and on phones only while the facet is active, truncated), so three facets
plus the search never overflow a 360-430px row — the bar stays one line
without a horizontal scroller. Drop the now-dead filter.groupLabel key and
point the vault e2e flows at the dropdown menu items instead of the removed
inline chips.
2026-07-08 00:03:04 +02:00
Marc-André Bombeck 56ca9094c0 Merge pull request #434 from MBombeck/release/v1.27.24
Release v1.27.24 — share documents
v1.27.24
2026-07-07 23:48:48 +02:00
Marc-André Bombeck 7cd317f1a5 feat(dashboard,mental-health): tighten hero gap, link hero rings, move wellbeing attribution to a header info popover
- Dashboard hero: pull the briefing closer to the ring row (md:gap-3 -> md:gap-2); mobile spacing unchanged.
- Each hero ring is now a focusable link to its natural detail: readiness/recovery/sleep -> their score-anatomy page, doses -> the medications surface, health score -> the Insights overview. Real anchors keep tap-vs-drag native on the mobile carousel.
- Mental-wellbeing: shorten the page title to the short noun form (matches the nav/module label) across all six locales.
- Instrument cards: move the required attribution/licence line off the space under the Start button to a discreet info control at the top-right of the header; a popover carries the verbatim citation. Start now stands alone.
2026-07-07 23:34:58 +02:00
Marc-André Bombeck f3a93b3439 test(e2e): mark the heavy share-lifecycle and med-wizard flows slow
Both are legitimately long end-to-end flows — the share test spans two
browser contexts (owner create + picker, recipient unlock, multiple
document serves, revoke), and the med wizard gates its Next button on
per-step validation. On a loaded CI runner the default 30s timeout races
the settle; test.slow() triples it so transient runner contention stops
flaking green flows.
2026-07-07 23:30:19 +02:00
Marc-André Bombeck 71698f3248 feat(documents): replace inline type-chip row with compact dropdown filters
The vault filter bar rendered the type/condition/year facets as a
horizontally-scrolling chip set beside the search field, which wrapped to a
second line on phones. Replace it with three compact dropdown facets — type
(multi-select), condition and year (single-select) — that share one
non-wrapping row with the flexing search and the clear control at every
width. The content-search hint keeps its own helper line below.
2026-07-07 23:29:24 +02:00
Marc-André Bombeck 97163d1e24 chore(release): v1.27.24 — share documents 2026-07-07 23:08:19 +02:00
Marc-André Bombeck 77f7caf4bf Merge branch 'feat/vault-p3-qa' into release/v1.27.24 2026-07-07 23:04:20 +02:00
Marc-André Bombeck 33c383b6b9 Merge pull request #433 from MBombeck/release/v1.27.23
Release v1.27.23 — dashboard hero polish
v1.27.23
2026-07-07 22:09:06 +02:00
Marc-André Bombeck 1face234cc test(share-links): end-to-end document share, serve, and revoke lifecycle
Adds a Playwright spec that drives the public sharing surface against the
production server and a real Postgres: the owner picks documents into a new
link through the Settings picker, a recipient unlocks it with the passphrase
and pulls each blob through the token-scoped serve route, and a revoke closes
it. Pins the load-bearing guarantees end to end — frozen-set confinement (an
owned but un-attached document 404s), the serving posture (Class A inline with
true type + nosniff + no-store, Class B opaque octet-stream attachment), the
EXIF/GPS strip on the shared image, revocation enforced at serve time, and the
scannable QR promotion at the mobile width.

Seeds a fixed share trio (inline PDF, Exif-bearing JPEG, download-only text)
through the same Postgres transport the vault fixture already uses.
2026-07-07 22:00:27 +02:00
Marc-André Bombeck 788996b934 test(share-links): drop the unused owner binding in the ownership guard
The foreign-document ownership test only needs owner A's upload to exist;
it seeded the user into a discarded local. Call it for its effect instead
so lint stays clean.
2026-07-07 22:00:14 +02:00
Marc-André Bombeck d2048fa171 chore(release): v1.27.23 — dashboard hero polish 2026-07-07 21:59:48 +02:00
Marc-André Bombeck 9298e7ca9f Merge branch 'feat/hero-ring-carousel' into release/v1.27.23 2026-07-07 21:56:00 +02:00
Marc-André Bombeck 829345a57b fix(sharing): keep the document cap literal client-side (no DB import leak) 2026-07-07 21:25:17 +02:00
Marc-André Bombeck e734c161f4 test(documents): clinician doc list + share picker/QR + i18n x6 2026-07-07 21:19:58 +02:00
Marc-André Bombeck fdc0f83a47 feat(documents): share document picker, clinician document list, mobile QR 2026-07-07 21:15:19 +02:00
Marc-André Bombeck 9ff39b5846 Merge branch 'feat/vault-p3-backend' into feat/vault-p3-integrated
# Conflicts:
#	prisma/schema.prisma
2026-07-07 21:03:04 +02:00
Marc-André Bombeck ee98a0c738 feat(dashboard): swipeable one-ring carousel on mobile hero, tighter desktop drop
Below md the hero wellness rings no longer wrap into a multi-row grid;
a dependency-free CSS scroll-snap track shows one ring centred at a time
with dot indicators, native touch/keyboard swipe, and reduced-motion
respected. At md and up the same nodes revert to the inline
right-aligned row unchanged. Desktop tightens the greeting/ring-row drop
to the briefing one step (md:gap-3).
2026-07-07 20:53:40 +02:00
Marc-André Bombeck 945e8b8df4 Merge pull request #432 from MBombeck/release/v1.27.22
Release v1.27.22 — document assist and content search
v1.27.22
2026-07-07 20:48:59 +02:00
Marc-André Bombeck 1545f3ec00 test(e2e): wait for the settled sidebar width on the iPad-portrait guard
The tablet icon-rail collapse is mounted-gated (SSR and the hydration
frame paint the expanded 256px shell to avoid a React #418 mismatch),
so the resolved layout lands on the first client render after hydration
settles. On a loaded runner that can arrive just after networkidle,
letting the guard measure a mid-hydration frame (main=512 instead of
~704). Wait for the content column to reach its settled width before
measuring; a real regression still fails via the wait timing out.
2026-07-07 20:38:32 +02:00
Marc-André Bombeck c0676564e6 chore(release): v1.27.22 — document assist and content search 2026-07-07 20:16:08 +02:00
Marc-André Bombeck 5dd8f23890 test(documents): e2e for AI assist, session-only summary, and content search
Six specs over the P2 surface: the review-first draft that only writes on
an explicit save (never auto-committed), the transient summary panel with
its not-saved / not-a-diagnosis note, whole-word content search reaching a
body-only word through the real blind-token index union, the corpus
index-all backfill CTA, the provider-unavailable settings pointer, and the
text-mode image-only refusal that fires before any OCR. The vault fixture
gains a seeded content-index row (tokens hashed under the same HKDF subkey
the server derives) plus a bland assist probe document.
2026-07-07 19:45:08 +02:00
Marc-André Bombeck 1c0c194820 fix(documents): scope the content-index gauge to live documents
The usage endpoint counted every DocumentContentIndex row for the user,
including those whose parent document has been soft-deleted (the index
row survives until the hard purge). Against totalCount, which filters
live documents, the gauge could read past 100 %. Scope the indexed count
to live documents so coverage never exceeds the corpus.

Also drop a redundant identical-branch ternary in the per-document
content-index status row.
2026-07-07 19:31:41 +02:00
Marc-André Bombeck 2319988a96 feat(documents): surface content search in the vault list
The list search already unions matches from the encrypted content index
server-side; surface it so it is legible and reachable:

- a subtle "search now looks inside your documents too (whole words)" hint
  under the search box once any document is indexed;
- a per-row "searchable" marker on indexed documents;
- an "index all for search" affordance that fires the corpus backfill when
  some documents are not yet indexed, driven off the usage coverage gauge.

All of it is gated on the same provider/consent precondition as the assist
layer — with no provider, search degrades to title and filename exactly as
before.
2026-07-07 19:14:59 +02:00
Marc-André Bombeck afeb693562 feat(documents): review-first AI assist and session-only summary in the vault detail sheet
Add an optional AI layer to a stored document, gated on the existing AI
provider and consent — no new toggle. All of it rides the shared OCR
capability probe so the client only posts a transport the endpoint accepts
(vision empty-body POST, or local-OCR text posted from the browser).

- "Suggest details" prefills the title / type / date as reviewable DRAFTS.
  Nothing is written on its own: the title lands in the editable field and
  is saved through the existing commit path; type and date each apply on an
  explicit tap. The card carries a visible "review before saving" affordance.
- A summary / extracted-text button opens a transient panel that renders the
  result once and always shows a "not saved / not a diagnosis" note; closing
  it discards the text. Nothing persists beyond the view.
- When no provider is configured the AI area is a calm pointer to the AI
  settings, never an error; the manual metadata form stays fully usable.

The detail-sheet AI area is lifted into a presentational section component so
the availability gate and the review-first wiring render without the sheet's
dialog portal and are pinned by static-render tests. Strings land in all six
locales.
2026-07-07 19:14:34 +02:00
Marc-André Bombeck 22feab46e8 test(share-links): end-to-end share document flow against Postgres
P3-1. Integration coverage the unit mocks cannot pin: migration 0229's join +
cascade, create-with-documents in one transaction, foreign-doc refusal at
create, the public serve route (passphrase gate, token confinement to the
frozen set, exact-bytes Class A posture, foreign id + revoked link → 404), and
membership cascade on document delete.
2026-07-07 18:56:52 +02:00
Marc-André Bombeck de84994a95 feat(share-links): expose the frozen document set on the share view + OpenAPI
P3-1-T6. loadShareViewData now returns the link's document set as metadata only
(id, title, kind, documentDate, byteSize, servingClass) — scoped to the
membership rows, the owner, and live rows; the blob column is never selected
(the serve route is the only decrypt path). Register documentCount on the share
summary, documentIds on create, and the public /c/{token}/d/{id} serve route in
the OpenAPI contract; regenerate docs/api/openapi.yaml.
2026-07-07 18:47:02 +02:00
Marc-André Bombeck 2653647f97 style(documents): prettier-format the list route test
P2-1. Formatting-only follow-up on the content-index mock additions.
2026-07-07 18:45:48 +02:00
Marc-André Bombeck 0885f331b0 test(share-links): pin the public share document serve route
P3-1-T4. Cover token confinement to the frozen set, EXIF strip on egress,
Class A inline vs Class B attachment posture, HTML/SVG never inline, foreign /
soft-deleted / owner-mismatch id → 404, revocation-and-passphrase-before-
decrypt, per-link rate limit, and fail-closed decrypt.
2026-07-07 18:45:00 +02:00
Marc-André Bombeck ce2f4808e2 test(documents): content-index integration coverage on real Postgres
P2-1. End-to-end: TEXT-mode indexing persists only ciphertext text + opaque
HMAC tokens (A4), a body-only word finds the document through the GIN token
overlap while the list still omits the blob, a word that appears nowhere does
not match, re-indexing upserts in place, and hasContentIndex + the usage gauge
reflect the index. Migration 0228 applies clean on the fresh testcontainer.
2026-07-07 18:43:39 +02:00
Marc-André Bombeck 844dc8457d feat(share-links): attach documents and serve them through the share token
P3-1-T2/T4/T5. Extend createShareLinkSchema with an optional, bounded
documentIds set; validate each id as the caller's own live document and create
the membership rows in the same transaction (frozen write-once). Summary DTOs
gain documentCount (never ids, never bytes).

Add the public share-scoped serve route /c/<token>/d/<id>: token-resolved
(never requireAuth), passphrase-gated via the token-scoped unlock cookie,
revocation/expiry re-checked before any decrypt, confined to the link's frozen
document set (foreign/deleted id → flat 404), fail-closed decrypt, identical
serving-class posture as the owner route (inline Class A / attachment Class B,
nosniff, no-store), EXIF-stripped for raster images on egress, per-link rate
limit.

Carve the serve path out of the proxy's blanket DENY posture with the same
document CSP as the owner route (default-src 'none'; frame-ancestors 'self'),
anchored to exactly two dynamic segments so no lookalike over-matches.
2026-07-07 18:43:13 +02:00