Commit Graph

23 Commits

Author SHA1 Message Date
Marcel
1aabd9826c test(frontend): update mock data for displayName and nullable firstName
Add displayName and personType to all Person mock objects in
component and page tests. Update assertions from reversed
"lastName, firstName" format to forward-order displayName.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:47:15 +02:00
Marcel
f11d8a38ed feat(frontend): replace all name concatenation with displayName
- Add displayName default method to PersonSummaryDTO
- Update native SQL queries to include title, person_type columns
- Add getInitials() utility to personFormat.ts
- Update abbreviateName/abbreviateCompact for nullable firstName
- Replace firstName+lastName concatenation with displayName in all
  person-displaying components and server load files
- Regenerate API types with displayName on Person and PersonSummaryDTO

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:22:30 +02:00
Marcel
1541afd470 feat(focus-rings): update all form inputs and document components to ring-focus-ring
Replaces focus:border-ink, focus:ring-ink, focus:ring-primary, focus:ring-accent
patterns with focus-visible:ring-2 focus-visible:ring-focus-ring focus:outline-none
across: PersonEditForm, profile forms, admin forms, document sections,
conversation filter bars, persons/documents new forms

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:22:11 +02:00
Marcel
a570dff4e9 fix(#95): stack save bar buttons full-width on mobile to prevent text wrap
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Long button labels (e.g. German "Speichern & Als überprüft markieren")
require ~515px at text-xs tracking-widest — impossible at 320px inline.

Both save bars (new document + edit document) now use flex-col on mobile
with w-full buttons and flex-row on sm+. Primary actions appear first
(top on mobile, right on desktop). Also fixes hardcoded border-gray-300/
text-gray-600 → border-line/text-ink-2 and bg-brand-navy/text-white →
bg-primary/text-primary-fg in these two components.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 16:23:12 +01:00
Marcel
fcff7fbdb1 fix(#94): replace text-white with text-primary-fg on all primary buttons
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
In dark mode --c-primary switches from navy (#012851) to mint (#a1dcd8).
Buttons using bg-primary+text-white showed white text on mint at 1.4:1
contrast — invisible. bg-brand-navy buttons were also invisible (navy on
near-black canvas, 1.3:1).

Replaced in 28 components app-wide:
- bg-primary ... text-white → text-primary-fg
- hover:bg-primary hover:text-white → hover:text-primary-fg
- bg-brand-navy ... text-white + hover:bg-brand-navy/90 →
  bg-primary ... text-primary-fg + hover:bg-primary/90

Light mode is unchanged: primary-fg = white in light mode.
Dark mode: primary-fg = navy (#012851) on mint bg = readable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 16:07:37 +01:00
Marcel
a7eaa40852 fix(#68): hide native file input, show selected filename in upload zone
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m47s
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (push) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
The native browser file input showed an untranslatable "Browse…" button
and "No file selected" text. The input is now sr-only; the large upload
zone label acts as the sole click target. When a file is selected its
name replaces the prompt text inside the zone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 07:04:54 +01:00
Marcel
c5e28ac18e feat(#68): lead new document form with file upload, all metadata optional
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m17s
CI / Backend Unit Tests (push) Failing after 9h3m48s
CI / E2E Tests (push) Failing after 28m15s
Restructure the "New Document" page so users can save quickly:

- FileSectionNew becomes the first element, redesigned as a prominent
  upload zone with an icon and large click target
- Title field is rendered standalone below the upload zone; it
  auto-populates from the filename (via parseFilename + stripExtension
  fallback) unless the user has already typed something
- All remaining metadata (who/when, description, transcription) moves
  into a collapsible "Weitere Details" section that auto-expands when
  URL prefill data or a form error is present, or when filename parsing
  detects a date/person
- title is no longer required — the form can be saved with only a file
- DescriptionSection gains a `hideTitle` prop for use in this layout
- `form_label_title` translation key no longer carries a hardcoded `*`;
  the asterisk is rendered by the template only when `titleRequired` is
  set (currently only the edit form)
- E2E tests added for all three scenarios from the issue

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:52:12 +01:00
Marcel
078bc1c886 feat(new-doc): pre-fill date, sender and title from parsed filename
When a file is selected on the new document page, parseFilename runs
on the filename and suggests date, sender name and title via the new
suggestedDateIso / suggestedSenderName / suggestedTitle props. Each
suggestion is applied only while the respective field is still clean
(not dirty), so manual input is never overwritten.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:17:47 +01:00
Marcel
aab9e9a4b0 feat(enrich): add metadata enrichment queue UI
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m19s
CI / Backend Unit Tests (pull_request) Successful in 2m11s
CI / E2E Tests (pull_request) Failing after 29m32s
CI / Unit & Component Tests (push) Successful in 2m21s
CI / Backend Unit Tests (push) Successful in 2m12s
CI / E2E Tests (push) Failing after 28m54s
Home page shows "Needs metadata" card when incomplete documents exist.
/enrich list shows all incomplete documents; /enrich/[id] provides a
split PDF-preview + compact form view with Skip / Save / Save & reviewed
actions that auto-advance through the queue.

New document page gets Save vs Save & reviewed split. Edit page gets
"Mark for review" secondary button to push a document back into the queue.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:45:16 +01:00
Marcel
1facf9cd60 refactor(documents): extract document form sub-components
Shared (src/lib/components/document/):
- WhoWhenSection.svelte: date/location/sender/receivers; owns date state
- DescriptionSection.svelte: title/archive-loc/tags/summary; owns tag binding
- TranscriptionSection.svelte: transcription textarea

Page-local:
- documents/[id]/edit/FileSectionEdit.svelte: current file + replace input
- documents/[id]/edit/SaveBar.svelte: sticky bar with two-step delete confirm
- documents/new/FileSectionNew.svelte: initial file upload input

documents/[id]/edit drops from 319 → ~40 lines.
documents/new drops from 254 → ~30 lines.
Date handling imported from \$lib/utils/date.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:20:34 +01:00
Marcel
162c58e8c5 fix(components): replace remaining unthemed gray classes with semantic tokens
Replace text-gray-*, bg-gray-*, border-gray-*, divide-gray-*, placeholder-gray-*,
focus:border-blue-*, focus:ring-blue-*, hover:bg-blue-*, and ring-brand-mint with
their semantic-token equivalents (text-ink, bg-muted, border-line, etc.) across
all pages and shared components so dark mode renders correctly everywhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:47:56 +01:00
Marcel
e4539ed0f0 refactor(components): replace all hardcoded colors with semantic tokens
Replaces bg-white, text-brand-navy, border-brand-sand, text-gray-*, bg-[#2A2A2A],
bg-brand-purple/15, hover:bg-brand-sand, etc. across all 35 .svelte files with
semantic token utilities (bg-surface, text-ink, border-line, bg-pdf-bg, bg-nav-active,
bg-muted, text-accent, bg-primary, ...).

Also adds CSS filter: invert(1) in layout.css for De Gruyter <img> icons in dark mode,
excluding icons that carry .invert already (to prevent double-inversion).

Closes #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:47:56 +01:00
Marcel
e83ba9b681 style(frontend): apply Prettier formatting to 26 pre-existing files
No logic changes — whitespace and indentation only. These were flagged
by the pre-commit hook when running lint after layout.css was modified.

Refs #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 13:47:56 +01:00
Marcel
b45ec744b2 feat: add PDF annotation feature (#40)
Backend:
- Add ANNOTATE_ALL permission
- Add ANNOTATION_NOT_FOUND and ANNOTATION_OVERLAP error codes
- V10 migration: document_annotations table with page/rect/color/owner
- DocumentAnnotation entity, AnnotationRepository, CreateAnnotationDTO
- AnnotationService: overlap detection (rectangle intersection), ownership enforcement on delete
- AnnotationController: GET (authenticated), POST/DELETE (ANNOTATE_ALL)
- 15 new tests (AnnotationServiceTest, AnnotationControllerTest) — TDD red/green

Frontend:
- AnnotationLayer.svelte: pointer-event drawing, colored rect overlays, delete buttons
- PdfViewer.svelte: annotate toggle, color picker, loads/saves/deletes annotations via API
- Disabled annotate button with tooltip for users without ANNOTATE_ALL
- canAnnotate exposed from layout server, passed to PdfViewer
- errors.ts + de/en/es translations for new error codes
- 3 new unit tests for AnnotationLayer — TDD red/green

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:27:21 +01:00
Marcel
f98792f10b fix(permissions): redirect read-only users from /documents/new to home
Some checks failed
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Successful in 2m5s
CI / Backend Unit Tests (push) Successful in 2m0s
CI / E2E Tests (push) Failing after 21m36s
throw error(403) kept the URL at /documents/new (the error page renders
in-place). Changed to throw redirect(303, '/') so the URL actually changes,
matching the E2E test expectation that a read-only user is redirected away.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:01:45 +01:00
Marcel
a65c69b0ce fix(tests): fix type errors in spec files after adding user to App.PageData
Some checks failed
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Successful in 1m57s
CI / Backend Unit Tests (pull_request) Successful in 1m55s
CI / E2E Tests (pull_request) Failing after 14m6s
Add user: undefined to baseData in conversations and documents/new specs.
Change null to undefined for filePath/transcription in makeDoc fixture.
Add form: null to render calls missing it.
Fix birthYear conversion from string to number in persons/[id] server action.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 23:05:08 +01:00
Marcel
0f8b582813 test(documents): add component tests for sender/receiver URL prefill on new document page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:32:23 +01:00
Marcel
4026bb9003 feat(documents): prefill sender and receiver from URL params on new document page
When navigating from the conversations page via the 'New document in this
correspondence' link, the senderId and receiverId query params are now read
in the server load, resolved to person names, and used to pre-populate the
sender typeahead and receiver multi-select on the form.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:32:23 +01:00
Marcel
db2fc33e99 fix(frontend): enforce lint locally and in CI, fix all pre-existing violations
Some checks failed
CI / Unit & Component Tests (push) Successful in 1m59s
CI / E2E Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
## Pre-commit hook
- Add .husky/pre-commit at repo root: runs `cd frontend && npm run lint`
- Update prepare script in package.json to auto-configure git hooks path
  on npm install (git -C .. config core.hooksPath .husky)
- Add lint step to CI unit-tests job so it catches issues before tests run
- Add generated dirs to .prettierignore (paraglide_bak*, test-results, .auth)
- Add src/lib/paraglide_bak* to .gitignore so ESLint can ignore them

## ESLint fixes (all pre-existing)
- Disable svelte/no-navigation-without-resolve: false positive in SvelteKit
  (rule targets Svelte 5 standalone routing, not SvelteKit <a href>)
- Fix svelte/require-each-key: add (item.id)/(item) keys to all {#each} blocks
  across 10 files — improves Svelte reconciliation performance
- Fix svelte/prefer-writable-derived in PersonTypeahead: $state+$effect → $derived
- Fix svelte/prefer-svelte-reactivity: URLSearchParams → SvelteURLSearchParams,
  Map → SvelteMap (enables Svelte reactive tracking)
- Fix @typescript-eslint/no-unused-vars: remove dead imports/variables

## Prettier
- Run npm run format to bring all source files in line with .prettierrc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 15:55:42 +01:00
Marcel
fa4bfb8e5c feat(routes): add server-side WRITE_ALL guard on write-only routes
Block direct URL navigation to /persons/new, /documents/new,
/documents/:id/edit for users without WRITE_ALL permission.
E2E tests verify admin user retains access to all write routes.

Closes #17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:47:52 +01:00
Marcel
0e76be5672 feat: implement i18n — extract all UI strings, add EN + ES-MX translations, add language selector
Some checks failed
CI / Unit & Component Tests (push) Successful in 9m36s
CI / Backend Unit Tests (push) Successful in 2m15s
CI / E2E Tests (push) Failing after 14m41s
Extract all hardcoded German strings from every .svelte file and component
into Paraglide message keys. Add complete translations for all keys in
messages/en.json (English) and messages/es.json (Spanish/Mexico).

Changes:
- messages/de.json: 100+ keys covering navigation, buttons, form labels,
  placeholders, section headings, empty states, and error messages
- messages/en.json, messages/es.json: complete translations for all keys
- project.inlang/settings.json: change baseLocale from "en" to "de"
- +layout.svelte: add DE/EN/ES language selector in header using setLocale();
  active language is bold, choice persists via Paraglide cookie strategy
- All 10 route pages + 3 shared components: replace hardcoded German with m.key()
- e2e/lang.spec.ts: E2E tests for language selector visibility, switching,
  persistence across navigation, and active state highlighting

Closes #2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:56 +01:00
Marcel
4417fc9828 refactor: migrate all Svelte components from Svelte 4 to Svelte 5 runes
- Replace `export let` with `$props()` and `$bindable()` across all components
- Replace `$:` reactive statements with `$derived()` and `$effect()`
- Replace `createEventDispatcher` with callback props (e.g. `onchange`)
- Replace `on:event` directives with inline event handlers (`onclick`, `oninput`, etc.)
- Replace `<slot />` with `{@render children()}` in layout
- Use `untrack()` for SSR-safe $state initialization from reactive props
- Replace `blur` + `setTimeout` anti-pattern in TagInput with `clickOutside` action
- Fix `page` store usage in layout to use `$app/state` directly
- 0 errors, 0 warnings after svelte-check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 11:43:26 +01:00
Marcel
6fcff3355d feat: add create document feature via web interface
- Backend: new POST /api/documents endpoint with DocumentService.createDocument()
  reusing DocumentUpdateDTO; handles file upload, tags, sender, receivers
- Frontend: new /documents/new route with same four-section form as edit page
  (Wer & Wann, Beschreibung, Transkription, Datei) but with empty fields
- Home page: subtle '+ Neues Dokument' link above the document list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 16:04:56 +01:00