Commit Graph

12 Commits

Author SHA1 Message Date
Marcel
0a1075e03f feat(conversations): add summary with document count and year range (#31)
Shows a summary line above the conversation listing with total document
count and the year span, e.g. "4 Dokumente · 1923–1965".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:32:23 +01:00
Marcel
ca212e871f feat(conversations): add swap button (#32)
Adds a button between the two person typeaheads that swaps sender and
receiver, then reloads the conversation view.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:32:23 +01:00
Marcel
0525e66d55 feat(conversations): filter person typeahead to correspondents of selected person
All checks were successful
CI / E2E Tests (push) Successful in 18m17s
CI / Unit & Component Tests (push) Successful in 3m37s
CI / Backend Unit Tests (push) Successful in 2m15s
CI / Unit & Component Tests (pull_request) Successful in 2m12s
CI / Backend Unit Tests (pull_request) Successful in 2m1s
CI / E2E Tests (pull_request) Successful in 15m17s
Closes #29

Backend:
- Add PersonRepository.findCorrespondents / findCorrespondentsWithFilter
  (native SQL, orders by shared document count DESC, limit 10)
- Add PersonService.findCorrespondents(personId, q) delegating to the
  correct repository method based on whether a query string is present
- Expose GET /api/persons/{id}/correspondents?q= in PersonController

Frontend:
- Add optional restrictToCorrespondentsOf prop to PersonTypeahead
- On focus with the prop set, fetch correspondents immediately (no typing
  required) — opens the dropdown showing top correspondents
- On input with the prop set, hit the correspondents endpoint with q= param
- Without the prop, keep existing /api/persons?q= behaviour unchanged
- Wire the prop bidirectionally in /conversations: sender restricts receiver
  and vice versa

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:23:11 +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
11f6f9e2a2 refactor(frontend): apply formatDate utility and fix derived/error handling
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m37s
CI / Backend Unit Tests (push) Successful in 2m2s
CI / E2E Tests (push) Has started running
- Replace 5 inline Intl.DateTimeFormat blocks with formatDate() across
  home, conversations, persons detail, and document detail pages
- Fix coCorrespondents: $derived(() => ...) → $derived.by(...) —
  the old form typed the value as a function, breaking template call sites
- Persons list: throw error on API failure instead of silently returning []

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:11:42 +01:00
Marcel
7988c62246 fix(e2e): fix strict mode violation and conversations sort toggle
- Add exact: true to all language button selectors in lang.spec.ts to
  prevent Playwright from matching "Abmelden" (contains "de") alongside
  the DE language button
- Fix goto in conversations applyFilters to use absolute path
  /conversations?... instead of relative ?... so URL updates correctly
- Set locale: 'de-DE' in playwright.config.ts so Accept-Language header
  is consistent and locale detection defaults to German during tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 20:34:40 +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
775e185e0f fix: format conversation date with Intl API (20. Dezember 2026)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:15:23 +01:00
Marcel
8a9e7bd9eb fix: resolve all 47 svelte-check errors and 10 a11y warnings
Root cause 1 — OpenAPI types: add @Schema(requiredMode=REQUIRED) to
non-nullable fields on Person, Tag, Document, AppUser, UserGroup;
regenerate api.ts so required fields are no longer optional.

Root cause 2 — Stale types: api.ts regenerated, picking up the Tag
endpoint fix from commit 62189d8 (List<Tag> instead of List<String>).

Root cause 3 — openapi-fetch error pattern: replace `if (apiError)`
(broken when error type is never/undefined) with `if (!result.response.ok)`
across all +page.server.ts files. Cast error via `unknown` to satisfy TS.

Root cause 4 — FormData casts: add `as string` / `as string[]` to
FormData.get() / FormData.getAll() calls in admin/+page.server.ts.

Standalone fixes:
- +page.server.ts: return error field so home page template compiles
- documents/[id]/+page.svelte: type loadFile param, remove invalid iframe `type`
- conversations: type documents as Document[] instead of unknown[]
- persons/[id]: non-null assert person data after ok-check

a11y: aria-label on all icon-only buttons in TagInput and admin page,
replace invalid <label> with <p> for compound controls, remove autofocus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 12:08:25 +01:00
Marcel
d76248cffd refactor: migrate all page.server.ts files to typed API client
All server-side fetch calls now go through createApiClient() from
$lib/api.server.ts, which wraps openapi-fetch with the generated OpenAPI
types. This means backend changes are reflected in the frontend after
running npm run generate:api.

- Add stub src/lib/generated/api.ts (replaced by generate:api output)
- Fix GroupController: missing /api prefix and ResponseStatusException
- Root, conversations, persons, documents pages all use typed client
- Error handling uses apiError.code directly (no parseBackendError needed)
- Edit page load uses typed client; PUT action keeps raw fetch (multipart)
- Login keeps raw fetch (explicit Authorization header, not cookie auth)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 13:39:15 +01:00
Marcel
e63adb964d restructure: flatten workspace nesting, move devcontainer to root
- backend/workspaces/backend/ → backend/
- backend/workspaces/frontend/ → frontend/
- backend/.devcontainer/ + .vscode/ → repo root (where VS Code expects them)
- loose scripts/SQL files → scripts/
- replace nested git repo with single repo at project root
- update docker-compose.yml build context and devcontainer.json path
- add root .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:47:58 +01:00