100 Commits
Author SHA1 Message Date
logaritmisk 97c63ac25b merge: UI polish bundle (#73)
CI / web (push) Successful in 6m4s
2026-06-10 13:47:28 +02:00
logaritmiskandClaude Opus 4.8 62c569741f fix(web): UI polish — select placeholder, locked-field note, list overflow, sidebar toggle, heading wrap (#73)
Five small design/layout nits from the UI sweep:

- form.selectPlaceholder "— select —" → "Select…" / "Välj…", matching
  the affordance style of every other placeholder (Filter…, Search…).
- FieldForm in edit mode now explains its locked controls with a muted
  fields.lockedNote caption ("Key and type can't be changed after
  creation.") instead of leaving four silently disabled inputs.
- FieldList rows truncate long labels (min-w-0 on the row button +
  truncate on the label, shrink-0 on the badge and required marker)
  instead of overflowing the 20rem column.
- The sidebar collapse toggle is hidden on narrow viewports (hidden
  md:flex) instead of rendered permanently disabled/grayed — the rail
  is forced collapsed there anyway.
- PageTitle gains text-balance so long titles wrap evenly.

Closes #73

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:47:22 +02:00
logaritmisk 3ad0e56ecd merge: deep-linkable field selection (#72)
CI / web (push) Successful in 6m14s
2026-06-10 13:44:08 +02:00
logaritmiskandClaude Opus 4.8 ada5d06dad feat(web): deep-linkable field selection via /fields/:key (#72)
FieldsPage kept the selected field definition in component state, so
reload lost the selection, fields couldn't be linked/shared, and
back/forward didn't navigate selections — inconsistent with
/vocabularies/:id and /objects/:id.

Move selection into the URL: the route becomes /fields/:key?
(optional segment), FieldList selection navigates, cancel/done
navigates back to /fields, and the page derives the selected def from
the already-cached field-defs query. An unknown or stale key (e.g.
after deleting the selected field) falls back to the create form.

Tests: deep link opens the locked edit form, select→cancel round-trips
through the URL, unknown key falls back to create.

Closes #72

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:44:02 +02:00
logaritmisk 3a57c0a77c merge: reduced-motion support + overscroll containment (#71)
CI / web (push) Successful in 4m48s
2026-06-10 13:38:44 +02:00
logaritmiskandClaude Opus 4.8 9a896bb5f6 fix(web): honor prefers-reduced-motion; contain overscroll in modal surfaces (#71)
Nothing in the app respected prefers-reduced-motion — the kit's
data-open/closed animations, the skeleton pulse, and the sidebar width
transition all ran unconditionally. Add a global base-layer rule that
collapses animation/transition durations to a single frame when the OS
asks for reduced motion; one rule covers current and future additions.

Add overscroll-y-contain to the scrollable modal/popup surfaces
(DrawerContent, SelectContent, ComboboxPopup) so flicking past the end
of their content no longer chain-scrolls the page beneath, and to
AlertDialogContent for when it gains scrollable content.

Verified in the built CSS: the media query and
overscroll-behavior-y:contain both compile into dist/assets.

Closes #71

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:38:37 +02:00
logaritmisk 78f5afad35 merge: pending-state feedback for delete confirms + login (#70)
CI / web (push) Successful in 4m37s
2026-06-10 13:35:34 +02:00
logaritmiskandClaude Opus 4.8 27205c65ef fix(web): disable delete confirms while pending + Signing in… feedback (#70)
The delete dialogs (DeleteObjectDialog and the shared
DeleteConfirmDialog) left their confirm button enabled during the
in-flight request, so a double-click fired a second DELETE that 404'd
and surfaced a spurious error. Disable cancel + confirm while pending
and swap the confirm label to a new actions.deleting ("Deleting…" /
"Tar bort…").

The login button disabled itself during login.isPending but kept the
"Sign in" label; it now shows auth.signingIn ("Signing in…" /
"Loggar in…") so slow networks get visible feedback.

Each fix is covered by a gated-MSW (or gated-promise) test asserting
the pending label + disabled state before releasing the request.

Closes #70

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:35:27 +02:00
logaritmisk 091a1a651d merge: focus-visible rings + live search count (#69)
CI / web (push) Successful in 5m7s
2026-06-10 13:31:15 +02:00
logaritmiskandClaude Opus 4.8 ec11c9dc76 fix(web): focus-visible rings on remaining controls + live search count (#69)
Keyboard focus was invisible on the objects-table sort headers and
page-size select, breadcrumb links, the external-URI link, and the
combobox input/clear/trigger. Apply the shared focusRing helper in app
code and the kit's inline focus-visible classes (matching input.tsx)
in ui/combobox.

Make the search result count a role="status" live region so screen
readers announce updated counts while typing; the existing search test
now asserts the count through getByRole("status").

Closes #69

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:29:27 +02:00
logaritmisk 1d19ddfd96 merge: dark-mode tokens for popup primitives + theme-color/color-scheme sync (#68)
CI / web (push) Successful in 5m47s
2026-06-10 13:23:47 +02:00
logaritmiskandClaude Opus 4.8 79a6567530 fix(web): dark-mode tokens for popup primitives + theme-color/color-scheme sync (#68)
Tooltip, toast, and combobox popups still hardcoded light colors
(bg-white, neutral-*, indigo-50) and rendered as white boxes in dark
mode; the objects-table page-size select did the same in app code.
Swap all of them to theme tokens (popover/accent/muted/destructive/
success) and replace the toast's literal "×" with the lucide X icon.

Wire browser chrome into the theme: color-scheme via CSS on
:root/.dark (follows the in-app toggle, not just the OS), a
theme-color meta kept in sync by the preload script and applyTheme(),
plus a unit test for the meta sync.

Extend check-no-raw-colors to also flag shadeless white/black
utilities outside components/ui/ so the objects-table case can't
recur.

Closes #68

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 09:42:57 +02:00
logaritmiskandClaude Opus 4.8 fe448034ac merge: instance-timezone timestamp formatter (#42)
CI / web (push) Successful in 5m29s
Add shared formatTimestamp(value, timeZone, locale) helper — date + short
time in the instance tz/locale, with a UTC fallback on an invalid IANA zone.
Route the objects-table 'Updated' column through it (was inline, date-only,
unguarded). Display-only; storage stays UTC.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:14:45 +02:00
logaritmisk 67c5da57bf feat(web): render objects 'Updated' as a tz-aware timestamp via formatTimestamp (#42) 2026-06-09 21:11:11 +02:00
logaritmisk 53405d7831 feat(web): formatTimestamp helper (instance tz + locale, UTC fallback) (#42) 2026-06-09 21:07:13 +02:00
logaritmisk e615260422 docs(plans): instance-timezone timestamp formatter — 2-task plan (#42) 2026-06-09 20:58:34 +02:00
logaritmisk 3b6441688f docs(specs): instance-timezone timestamp formatter (#42) 2026-06-09 15:34:02 +02:00
logaritmisk a0b7dcdc2d merge: responsive master/detail for vocabularies, search, fields (#58)
CI / web (push) Successful in 5m3s
2026-06-09 15:22:46 +02:00
logaritmisk 7f9cf9fe60 feat(web): responsive Fields page (stacks on narrow) (#58) 2026-06-09 15:18:39 +02:00
logaritmisk b83149e0bb feat(web): responsive Search master/detail (drawer on narrow) (#58) 2026-06-09 15:15:44 +02:00
logaritmisk 80c2aad298 feat(web): responsive Vocabularies master/detail (drawer on narrow) (#58) 2026-06-09 15:12:45 +02:00
logaritmisk b5756e16b5 refactor(web): shared DetailDrawer; objects-page uses it (#58) 2026-06-09 15:09:37 +02:00
logaritmisk b3f061ced7 docs(plans): responsive master/detail — 4-task plan (#58) 2026-06-09 15:06:42 +02:00
logaritmisk eec3a261b4 docs(specs): responsive master/detail for vocab/search/fields (#58) 2026-06-09 14:11:14 +02:00
logaritmisk 390f6897a8 merge: bundle vendor-split + test-gap fills (#67)
CI / web (push) Successful in 5m13s
2026-06-09 13:48:46 +02:00
logaritmisk 8b881f369b test(web): add a Storybook story for the combobox primitive (#67) 2026-06-09 12:32:06 +02:00
logaritmisk aef5000543 test(web): cover prune-fields, labels, format-date, delete-in-use dialog (#67) 2026-06-09 12:28:48 +02:00
logaritmisk 878db9a37b build(web): split framework deps into cache-stable vendor chunks (#67) 2026-06-09 12:24:47 +02:00
logaritmisk 0b44bc0855 docs(plans): bundle vendor-split + test gaps — 3-task plan (#67) 2026-06-09 12:16:23 +02:00
logaritmisk 79ee402b33 docs(specs): bundle vendor-split + test-gap fills (#67) 2026-06-09 12:09:02 +02:00
logaritmisk 64f35e5a57 merge: fix CI — Node 22, Playwright install, deterministic pending-state tests, testTimeout (#25)
CI / web (push) Successful in 7m32s
2026-06-09 11:57:32 +02:00
logaritmiskandClaude Opus 4.8 3aff10557c ci: make the logout pending-state test deterministic (gate, not delay)
CI / web (push) Successful in 4m35s
Same timing-window race as object-new-page: the 50ms delay let the logout
resolve (menu unmounts on me=null) before findByText caught 'Signing out…'
on the slow CI runner. Hold the logout open with a promise released only
after the pending state is asserted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 11:51:35 +02:00
logaritmiskandClaude Opus 4.8 e8fe24f755 ci: raise vitest testTimeout to 20s for the resource-constrained runner
CI / web (push) Failing after 3m50s
The 'narrow: detail renders inside a portaled drawer' test lazy-loads the
drawer chunk and exceeded the 5s default on the slow CI container (setup
alone took ~486s). Bump testTimeout on both vitest projects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 11:44:30 +02:00
logaritmiskandClaude Opus 4.8 fc170ccf10 ci: install Playwright chromium for the storybook vitest project; deterministic in-flight test
CI / web (push) Failing after 4m5s
- CI runs the @vitest/browser-playwright (storybook) project, which needs the
  chromium browser downloaded — add 'playwright install --with-deps chromium'.
- object-new-page in-flight test held the create mutation open with a 50ms
  delay and raced the pending-state assertion (failed under CI timing); gate it
  on a promise released only after asserting 'saving…', so it's deterministic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 11:37:20 +02:00
logaritmiskandClaude Opus 4.8 3ae9d87e6e ci: bump Node 20 → 22 so pnpm 11 (needs Node ≥22.13) runs
CI / web (push) Failing after 2m25s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 11:29:58 +02:00
logaritmisk 3dbede6bc2 and use correct container image
CI / web (push) Failing after 55s
2026-06-09 11:25:18 +02:00
logaritmisk ba238ca962 run ci on correct runner
CI / web (push) Failing after 4s
2026-06-09 11:24:08 +02:00
logaritmisk 7cabebc338 merge: design-kit consistency — useLang, class recipes, kit adoption (#66)
CI / web (push) Has been cancelled
2026-06-09 00:02:33 +02:00
logaritmiskandClaude Opus 4.8 74cde67a54 refactor(web): kit consistency — focusRing, PageTitle, Badge, size-4, icon buttons (#66)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 23:49:35 +02:00
logaritmisk 900f85f8ac refactor(web): adopt useLang + segmentClass/rowStateClass across sites (#66) 2026-06-08 23:45:24 +02:00
logaritmisk 00a7ce772e feat(web): useLang + segmentClass/rowStateClass helpers; delete dead Card (#66) 2026-06-08 23:41:08 +02:00
logaritmisk 71dee23028 docs(plans): design-kit consistency — 3-task plan (#66) 2026-06-08 23:31:35 +02:00
logaritmisk 91716e628a docs(specs): design-kit consistency — useLang, class recipes, kit adoption (#66) 2026-06-08 22:32:34 +02:00
logaritmisk 002af9d1f8 merge: split queries.ts — errors + key factory + domain modules; invalidate search on object writes (#65)
CI / web (push) Has been cancelled
2026-06-08 22:26:41 +02:00
logaritmisk d8d8035850 refactor(web): split queries.ts into api/queries/ domain modules behind a barrel (#65) 2026-06-08 21:35:02 +02:00
logaritmiskandClaude Opus 4.8 704b159d48 refactor(web): central query-key factory + invalidate search on object writes (#65)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 21:30:57 +02:00
logaritmisk c1bddb47c4 refactor(web): extract API error classes to api/errors.ts (#65) 2026-06-08 21:27:21 +02:00
logaritmisk a21ab85576 docs(plans): split queries.ts — 3-task plan (#65) 2026-06-08 20:46:29 +02:00
logaritmisk 7ddf6967ce docs(specs): split queries.ts — errors + key factory + domain modules (#65) 2026-06-08 20:37:11 +02:00
logaritmisk 404cf67f35 merge: unify vocabulary + authority CRUD into shared components (#64)
CI / web (push) Has been cancelled
2026-06-08 20:22:36 +02:00
logaritmisk 50d2512123 refactor(web): term/authority rows + pages adopt shared CRUD components (#64) 2026-06-08 20:16:17 +02:00
logaritmisk c689b8c0e9 feat(web): shared FilteredRecordList component (#64) 2026-06-08 20:11:29 +02:00
logaritmisk acdaf8d07f feat(web): shared LabelledRecordCreateForm component (#64) 2026-06-08 20:08:10 +02:00
logaritmisk 77c56f7a9d feat(web): shared LabelledRecordRow component (#64) 2026-06-08 20:05:05 +02:00
logaritmisk 030472c2da docs(plans): unify vocab + authority CRUD — 4-task plan (#64) 2026-06-08 19:56:42 +02:00
logaritmisk f1eb6a9ba5 docs(specs): unify vocabulary + authority CRUD (#64) 2026-06-08 19:52:35 +02:00
logaritmisk 285a1323ad merge: accessibility defect bundle — label-id, table rows, drawer/breadcrumb names, announced states (#62)
CI / web (push) Has been cancelled
2026-06-08 19:47:58 +02:00
logaritmisk da3e078fbc fix(web): objects-table a11y — real-link rows, pill focus ring, announced load/error (#62) 2026-06-08 19:07:00 +02:00
logaritmisk 0def81ab42 fix(web): a11y labelling — useId, named drawer/breadcrumb, translated combobox (#62) 2026-06-08 19:00:28 +02:00
logaritmisk 546680017d docs(plans): a11y defect bundle — 2-task plan (#62) 2026-06-08 18:52:21 +02:00
logaritmisk 3efb7e175d docs(specs): accessibility defect bundle (#62) 2026-06-08 18:49:27 +02:00
logaritmisk 56076c4daa merge: consistent status-aware mutation error feedback (#63)
CI / web (push) Has been cancelled
2026-06-08 18:41:37 +02:00
logaritmisk aeb1b084d9 feat(web): adopt MutationError across create/object forms; distinguish edit-form fetch error (#63) 2026-06-08 17:32:36 +02:00
logaritmisk 6e02ac874f feat(web): inline status-aware errors on term/authority edit rows + delete dialog (#63) 2026-06-08 17:27:02 +02:00
logaritmisk dd131ee740 feat(web): mutations throw HttpError(status) so failures are status-aware (#63) 2026-06-08 17:21:50 +02:00
logaritmisk cad5a980c5 feat(web): shared status-aware error-message helper + MutationError component (#63) 2026-06-08 17:17:14 +02:00
logaritmisk 17bfd3e9d8 docs(plans): mutation error feedback — 4-task plan (#63) 2026-06-08 16:43:10 +02:00
logaritmisk d90aa75468 docs(specs): consistent status-aware mutation error feedback (#63) 2026-06-08 16:22:45 +02:00
logaritmisk 7a43f794e5 merge: session-expiry soft redirect + auth feedback (#48)
CI / web (push) Has been cancelled
2026-06-08 15:13:03 +02:00
logaritmisk af3f1a5367 feat(web): return-to-destination on auth redirect; logout pending state (#48) 2026-06-08 15:06:50 +02:00
logaritmisk ec6e90ef5b feat(web): login reason banner + return-to + empty-field guard (#48) 2026-06-08 15:02:55 +02:00
logaritmisk 3c59f47f81 feat(web): soft-redirect to login on 401 via a navigate bridge (#48) 2026-06-08 14:58:25 +02:00
logaritmisk 76f65a95dd docs(plans): session-expiry soft redirect — 3-task plan (#48) 2026-06-08 14:56:44 +02:00
logaritmisk a0aab6571f docs(specs): session-expiry soft redirect + auth feedback (#48) 2026-06-08 14:52:16 +02:00
logaritmisk 6e72f24f0a merge: group object-form flexible fields by definition group (#45 follow-up)
CI / web (push) Has been cancelled
2026-06-08 14:21:00 +02:00
logaritmiskandClaude Sonnet 4.6 d447e2d8a8 feat(web): group object-form flexible fields by definition group (#45)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 14:03:33 +02:00
logaritmiskandClaude Sonnet 4.6 a9a0c4d477 refactor(web): extract groupDefinitions helper; object-detail uses it (#45)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 13:58:12 +02:00
logaritmisk c0c86a5859 docs(plans): object-form field grouping — 2-task plan (#45) 2026-06-08 13:56:07 +02:00
logaritmisk faca2670a4 docs(specs): object-form flexible-field grouping via shared helper (#45 follow-up) 2026-06-08 13:54:53 +02:00
logaritmisk c68bbb9460 merge: search-row recording_date + softened estimated count (#61)
CI / web (push) Has been cancelled
2026-06-08 13:49:31 +02:00
logaritmisk 30da072d96 feat(web): show recording_date on search rows; flag estimated count as approximate (#61) 2026-06-08 13:45:35 +02:00
logaritmiskandClaude Opus 4.8 1cdfa21259 feat(search): index + return recording_date on search hits (#61)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 13:41:17 +02:00
logaritmisk d37ac821f0 docs(plans): search-row recording_date + count copy — 2-task plan (#61) 2026-06-08 13:38:13 +02:00
logaritmisk 150ca63fc0 docs(specs): search-row recording_date + softened estimated count (#61) 2026-06-08 10:03:15 +02:00
logaritmisk d082836529 merge: a11y — focus rings, route focus, skip link, honest semantics, html lang (#52)
CI / web (push) Has been cancelled
2026-06-08 09:54:17 +02:00
logaritmisk 69d3d2be15 feat(web): skip link + route focus management + html lang sync (#52) 2026-06-08 09:46:17 +02:00
logaritmisk 57504c941d feat(web): focus-visible rings on custom controls; honest authority links + lang group (#52) 2026-06-08 09:42:33 +02:00
logaritmisk 4530004d87 docs(plans): a11y focus/route/skip/semantics — 2-task plan (#52) 2026-06-08 09:39:28 +02:00
logaritmisk 1948d09d16 docs(specs): a11y — focus rings, route focus, skip link, honest semantics, html lang (#52) 2026-06-08 09:36:44 +02:00
logaritmisk 4c24f0387c merge: enforce en/sv i18n key parity test (#60)
CI / web (push) Has been cancelled
2026-06-08 09:31:14 +02:00
logaritmiskandClaude Opus 4.8 0209638552 docs: consolidate frontend guardrails + test-harness gotchas
CI / web (push) Has been cancelled
Adds web/GUARDRAILS.md capturing the recurring CI-guardrail and
test-harness lessons in one place: the check:size (250 KB-gz largest
chunk) and check:colors (design-token) guards, the jsdom/storybook
vitest split, MSW onUnhandledRequest:"error" overrides, RTL
accessible-name collisions, Storybook nested-router/portal handling,
and the components/ui code-style split. Wires a pointer from CLAUDE.md.

All claims verified against the live scripts, ci.yaml, and src/test/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:21:16 +02:00
logaritmisk 2b6ea1b4a4 test(web): enforce en/sv i18n key parity + non-empty values (#60) 2026-06-08 09:15:35 +02:00
logaritmisk 3575282dc2 merge: reference-data scannability + parity — sort/filter/external_uri/counts (#50)
CI / web (push) Has been cancelled
2026-06-08 09:10:33 +02:00
logaritmisk 882d0c828f feat(web): field-list filter, within-group label sort, group order, count badges (#50) 2026-06-08 09:06:17 +02:00
logaritmiskandClaude Opus 4.8 75e7cf9047 feat(web): authorities sort+filter, create external_uri, external_uri in rows, url input (#50)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:02:13 +02:00
logaritmiskandClaude Opus 4.8 76b2cbde1d feat(web): vocab list/terms sort+filter, external_uri in rows, rename guard, url input (#50)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 08:57:52 +02:00
logaritmisk 6c2fa63cac feat(web): collator sort helpers + ExternalUriLink + filter/uri i18n (#50) 2026-06-08 08:54:04 +02:00
logaritmisk a4fb05a175 docs(plans): reference-data scannability + parity — 4-task plan (#50) 2026-06-08 08:18:58 +02:00
logaritmisk 0678cefd13 docs(specs): reference-data scannability + parity (sort/filter/uri/counts) (#50) 2026-06-08 07:37:40 +02:00
logaritmisk 53c98102d2 merge: standardize loading states on shared Skeleton recipes (#53)
CI / web (push) Has been cancelled
2026-06-08 06:58:28 +02:00