Commit Graph

2031 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
acf6fc05ad fix(search): prevent stale navigation from clobbering focused search input
Some checks failed
CI / E2E Tests (push) Waiting to run
CI / Unit & Component Tests (push) Successful in 10m14s
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (pull_request) Failing after 13m49s
CI / Backend Unit Tests (pull_request) Failing after 13m59s
CI / Unit & Component Tests (pull_request) Failing after 14m9s
The sync $effect on the home page unconditionally overwrote the local `q`
state with the URL value after every navigation. When users typed faster
than a navigation round-trip (debounce fires → goto() → data reloads),
the completed navigation wrote the stale URL value back into the input,
dropping the characters typed in the interim.

Guard the `q` assignment in the effect with a `qFocused` flag (set via
onfocus/onblur on the text input). Covers issue #34.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:57:23 +01:00
Marcel
f950e4e826 docs(codestyle): add Svelte 5 specific rules with examples
All checks were successful
CI / Unit & Component Tests (push) Successful in 1m52s
CI / Backend Unit Tests (push) Successful in 1m59s
CI / E2E Tests (push) Successful in 16m21s
Document the three key rules enforced by eslint-plugin-svelte:
- svelte/require-each-key: why position-based tracking silently corrupts state
- svelte/prefer-writable-derived: why $state+$effect is wrong for computed values
- svelte/prefer-svelte-reactivity: why SvelteMap/SvelteURLSearchParams are needed

Each rule includes bad/good code examples and a technical reason.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 15:58:40 +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
28dea45cc3 fix(i18n): remove trailing comma from all three message files
All checks were successful
CI / Unit & Component Tests (push) Successful in 1m39s
CI / Backend Unit Tests (push) Successful in 1m51s
CI / E2E Tests (push) Successful in 16m21s
Standard JSON does not allow trailing commas. The comma after the last
key in de/en/es.json caused paraglide to fail compilation, which meant
messages.js was never generated and all component tests crashed on import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:33:55 +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
4771832492 refactor(frontend): extract toActionResult helper and formatDate utility
- Admin page: replace 7 identical error-handling blocks with a single
  toActionResult() helper — DRY without over-abstraction
- New date.ts util: formatDate(isoDate) centralises the T12:00:00
  timezone guard and Intl.DateTimeFormat locale config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:10:04 +01:00
Marcel
c006113db9 refactor(backend): replace 7-parameter updatePerson with PersonUpdateDTO
Reduces parameter count from 7 to 2 (id + dto), keeping all validation
and trimming logic in the service. Controller now binds request JSON
directly to the DTO via @RequestBody.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:52:16 +01:00
Marcel
5160009175 refactor(backend): fix typos, dead debug logs, and bare orElseThrow calls
- UserService: remove debug log dumping all DB groups ("Groupds in DB"),
  fix indentation of createUserOrUpdate, clean up log messages
- DocumentService: fix typo reciever → receiver in searchDocuments parameter,
  remove broken log.info("Tags", tags) with missing format specifier,
  replace bare orElseThrow() with DomainException in updateDocumentTags
  and createDocument, remove what-comments on Lombok annotations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:50:16 +01:00
Marcel
4009781064 docs: add CODESTYLE.md with Clean Code, DRY/KISS, and SOLID principles
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m26s
CI / Backend Unit Tests (push) Successful in 1m59s
CI / E2E Tests (push) Failing after 15m42s
Covers naming, function design, guard clauses, comment policy, command-query
separation, DRY vs KISS trade-offs (KISS wins), and SOLID applied to the
Java backend and TypeScript/Svelte frontend. Linked from CLAUDE.md and
COLLABORATING.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:36:34 +01:00
Marcel
761c903111 refactor(person): remove redundant conversations link from header
Some checks failed
CI / Unit & Component Tests (push) Failing after 1m17s
CI / Backend Unit Tests (push) Successful in 1m52s
CI / E2E Tests (push) Has started running
The co-correspondent chips already link directly to the conversation view
pre-filled with both persons, making the generic "Konversationen anzeigen"
header link redundant. Removed the link and the person_btn_conversations
i18n key from all three locales.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:27:25 +01:00
Marcel
931a8dac95 refactor(person): move sort button into each section heading, sort independently
Some checks are pending
CI / Unit & Component Tests (push) Successful in 1m43s
CI / Backend Unit Tests (push) Successful in 1m57s
CI / E2E Tests (push) Has started running
Replaced the single shared sort control with per-section sort buttons placed
inline in each heading row (right-aligned via ml-auto). Each section now sorts
independently, which matches user expectation and keeps the control visually
anchored to the list it affects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:22:56 +01:00
Marcel
3f717e3266 refactor(person): fold year range into section headings, remove standalone stats bar
Some checks are pending
CI / Unit & Component Tests (push) Successful in 1m39s
CI / Backend Unit Tests (push) Successful in 2m8s
CI / E2E Tests (push) Has started running
The floating stats bar was visually disconnected and showed a combined document
count already visible from the per-section badges. Replaced it with a year range
shown inline next to each section heading (e.g. "Gesendete Dokumente · 12 · 1921–1945"),
making the range contextually relevant per direction.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:16:22 +01:00
Marcel
203b7d2b08 fix(auth): proxy document file requests server-side to prevent Basic Auth popup
Some checks are pending
CI / Unit & Component Tests (push) Successful in 1m55s
CI / Backend Unit Tests (push) Successful in 2m8s
CI / E2E Tests (push) Has started running
Client-side fetch('/api/documents/{id}/file') bypassed the handleFetch hook
that injects the Authorization header, causing the browser to receive a 401
with WWW-Authenticate: Basic and show a native auth dialog.

Added a SvelteKit server route at /api/documents/[id]/file that proxies the
request through the server, where handleFetch injects the auth cookie correctly.

Also fixed E2E default password (admin → admin123) to match application.yaml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:01:31 +01:00
Marcel
e9b03ee6a9 fix(person): add i18n for show-more button and limit doc lists to 5
All checks were successful
CI / Unit & Component Tests (push) Successful in 1m59s
CI / Backend Unit Tests (push) Successful in 2m9s
CI / E2E Tests (push) Successful in 16m33s
- Add person_show_more key (DE/EN/ES)
- Limit sent/received document lists to 5 with a translated "show more" button
- Co-correspondent chips now link to /conversations?senderId=...&receiverId=...

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:13:38 +01:00
Marcel
ba04e62f87 fix(person): remove redundant role badges from document lists
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
The Gesendet/Empfangen badge is redundant since documents already appear
in separate Gesendete/Empfangene sections.

Refs #21
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:05:19 +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
fde75f3fcf feat(ui): hide write UI from users without WRITE_ALL permission
Wrap write-only elements with {#if data.canWrite} in:
- Home page: Neues Dokument link
- Persons list: Neue Person link
- Document detail: Bearbeiten button
- Person detail: edit button, edit form, merge section

Refs #17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:47:45 +01:00
Marcel
03a1a86cdb feat(layout): expose canWrite flag from layout server load
Derives canWrite from WRITE_ALL permission in user groups, available
as page.data.canWrite on every page without per-page boilerplate.

Refs #17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:47:37 +01:00
Marcel
55ffaa1c5c feat(person): show received docs, role badges, stats bar, co-correspondents
- Split document list into Gesendete / Empfangene Dokumente sections
- Add role badges (Gesendet / Empfangen) on each document card
- Add statistics strip showing total count and year range
- Add co-correspondents section with frequency-sorted chips
- Single sort toggle applies to both sections

Closes #1 Closes #19 Closes #21 Closes #22
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:50 +01:00
Marcel
1fdde95b09 feat(frontend): load sent and received documents for person detail
Split single documents load into sentDocuments and receivedDocuments,
fetched in parallel via Promise.all.

Refs #1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:37 +01:00
Marcel
c056d804e6 feat(i18n): add translations for received docs, role badges, and co-correspondents
Add keys: person_received_docs_heading, person_no_received_docs,
person_role_sender, person_role_receiver, person_co_correspondents_heading
in DE, EN, ES.

Refs #1 Refs #21 Refs #22
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:30 +01:00
Marcel
490382b5de feat(api): regenerate TypeScript types with received-documents endpoint
Refs #1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:24 +01:00
Marcel
557b62ac5c feat(backend): add received-documents endpoint for persons
Add findByReceiversId to DocumentRepository, getDocumentsByReceiver
to DocumentService, and GET /api/persons/{id}/received-documents
to PersonController. Tests added for both service and controller layers.

Closes #1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 09:39:18 +01:00
Marcel
4ccc8d69d0 docs(collab): add atomic commits rule to commit message guidelines
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
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:47:52 +01:00
Marcel
c3f487f16c fix(e2e+i18n): add missing DE translation, fix E2E test selectors
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
- Add missing person_btn_conversations translation to de.json
- Fix birth/death year test: exclude /persons/new link + wait for hydration
- Fix lang test switching back to DE: wait for hydration + clear locale cookie
  (headless Chromium doesn't reliably delete cookies via document.cookie)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:45:56 +01:00
Marcel
6e6663376d fix(migrations): make V5/V6 idempotent with IF NOT EXISTS
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
Avoids Flyway errors when columns already exist in the DB due to
migration history mismatches from parallel feature branches.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:14:44 +01:00
Marcel
041bbdc2e6 merge(feat/person-birth-death-years): resolve conflicts with main, bump migration to V6
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 1m45s
CI / Backend Unit Tests (pull_request) Successful in 2m4s
CI / E2E Tests (pull_request) Failing after 18m40s
CI / Unit & Component Tests (push) Successful in 1m57s
CI / Backend Unit Tests (push) Successful in 2m13s
CI / E2E Tests (push) Failing after 18m39s
Resolves merge conflicts with main (feat/person-notes merged first).
Combines both features: birth/death years and notes field on person detail.
Renames migration V5__add_birth_death_years to V6 to avoid Flyway conflict.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:04:44 +01:00
Marcel
08f7ae9a5c feat(persons): add notes field to person profile (issue #23)
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
V5 Flyway migration adds TEXT notes column; Person entity, service, and
controller updated to persist notes. Frontend edit form adds textarea and
view mode renders the notes section. Backed by 2 new service unit tests
(persist + blank clears).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:57:05 +01:00
Marcel
c01a07bd82 fix(e2e): fix conversations link test — exclude /persons/new and use specific link text
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 9h3m35s
CI / Backend Unit Tests (pull_request) Successful in 3m51s
CI / E2E Tests (pull_request) Failing after 17m8s
CI / Unit & Component Tests (push) Successful in 1m52s
CI / Backend Unit Tests (push) Successful in 2m3s
CI / E2E Tests (push) Failing after 16m23s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:48:58 +01:00
Marcel
cccc12429c chore: resolve merge conflict with main (keep both sort and conversations tests)
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 1m48s
CI / Backend Unit Tests (pull_request) Successful in 2m8s
CI / E2E Tests (pull_request) Failing after 3m40s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:46:47 +01:00
Marcel
b07391541b feat(persons): add birth/death year fields (issue #18)
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 1m48s
CI / Backend Unit Tests (pull_request) Successful in 2m3s
CI / E2E Tests (pull_request) Failing after 17m10s
V5 Flyway migration adds birth_year and death_year INTEGER columns.
Service validates birthYear <= deathYear (400 otherwise). Frontend edit
form adds year number inputs; view mode renders * year / † year. Backed
by 3 backend service tests and 1 E2E test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:45:02 +01:00
Marcel
fb08eb30a4 feat(persons): add sort toggle to person document list (issue #24)
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 1m55s
CI / Backend Unit Tests (pull_request) Successful in 2m9s
CI / E2E Tests (pull_request) Successful in 18m8s
Extracted sortDocumentsByDate utility with full Vitest coverage (6 tests),
wired it into the person detail page with a DESC/ASC toggle button, and
added an E2E smoke test for the toggle interaction.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:24:46 +01:00
Marcel
371d92f52a feat(person): add conversations quick-link (#20)
Some checks failed
CI / E2E Tests (push) Failing after 17m44s
CI / Backend Unit Tests (push) Has been cancelled
CI / Unit & Component Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Successful in 1m51s
CI / Backend Unit Tests (pull_request) Successful in 2m3s
CI / E2E Tests (pull_request) Failing after 17m7s
Add a "Konversationen anzeigen" link to the person detail page header
that navigates to /conversations?senderId={id}, pre-filling the person
as Person A. Includes i18n in de/en/es and an E2E test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 21:19:36 +01:00
Marcel
fe9b4a9569 fix(e2e): fix locale cookie httpOnly and add hydration waits
Some checks failed
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (pull_request) Has been cancelled
CI / Unit & Component Tests (push) Successful in 2m3s
CI / Backend Unit Tests (push) Successful in 2m13s
CI / E2E Tests (push) Successful in 17m49s
Paraglide's client-side setLocale writes the locale via document.cookie,
which silently fails for HttpOnly cookies. SvelteKit's cookies.set()
defaults to httpOnly: true, so locale switching never worked in tests.
Fix by setting httpOnly: false on the locale cookie (it's a UI preference,
not a credential — no security concern).

Add waitForSelector('[data-hydrated]') before any click that relies on
SvelteKit JavaScript event handlers. Without this, the click fires before
hydration and the onclick handler is not yet registered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 20:34:40 +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
a998ef4550 test(i18n): add unit tests for locale detection + extract to module
Extract detectLocale() from hooks.server.ts into src/lib/server/locale.ts
so it can be tested in isolation. Add 7 unit tests covering:
- German, English, Spanish browser preferences
- Fallback when primary language is unsupported
- Quality value (q=) ordering
- Fully unsupported language → null
- Empty Accept-Language header → null

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 20:34:40 +01:00
Marcel
c4be2eb46e feat(i18n): detect browser language as default locale
On first visit (no PARAGLIDE_LOCALE cookie), parse the Accept-Language
request header and set the cookie to the best matching supported locale
(de/en/es). The user's manual choice via the switcher always takes
precedence since the detection is skipped when the cookie exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 20:34:40 +01:00
Marcel
20313de4e9 feat(i18n): fix remaining hardcoded strings and add login page switcher
- Add 9 missing translation keys to de/en/es.json:
  doc_file_error_preview, doc_download_title, doc_tag_filter_title,
  doc_conversation_title, doc_preview_iframe_title, doc_image_alt,
  doc_no_date, person_merge_will_be_deleted, admin_user_delete_confirm

- documents/[id]/+page.svelte: replace 6 hardcoded strings with m.*()
- persons/[id]/+page.svelte: replace "wird gelöscht." and "Kein Datum"
- admin/+page.svelte: replace confirm() string with m.admin_user_delete_confirm()
- login/+page.svelte: add top-right DE/EN/ES language switcher (Option B)
  and wire existing login_* keys to the form labels

Closes #12

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 20:34:40 +01:00
Marcel
4142c7cd83 devops: fix upload-artifact and use Playwright Docker image for unit tests
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 1m54s
CI / Backend Unit Tests (pull_request) Successful in 2m4s
CI / E2E Tests (pull_request) Failing after 17m35s
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (push) Has been cancelled
- Switch unit-tests job to mcr.microsoft.com/playwright:v1.58.2-noble
  container; Chromium and all system deps are pre-installed so the
  browser install/cache dance is eliminated entirely (closes #13)

- Downgrade upload-artifact@v4 → v3 in both unit-tests and e2e-tests
  jobs; v4 is not supported on Gitea (GHES) and was causing jobs to
  report failure even when all tests passed, and prevented the
  Playwright browser cache from ever being saved (closes #14)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:17:53 +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
db6dc28528 fix(ci): pin DOCKER_API_VERSION=1.43 for e2e job
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
The runner's Docker client negotiates API 1.53 but the daemon on the
NAS only supports up to 1.43. Pin the version for all docker commands
in the e2e job, including the new network connect step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:17 +01:00
Marcel
208dc87d69 fix(ci): connect job container to compose network for DB/MinIO access
The act_runner job runs inside a Docker container. Docker Compose port
mappings bind to the Docker host, not the job container's localhost —
so localhost:5433 was always refused.

Fix: after compose starts, connect the job container (identified by
/etc/hostname) to the archive-net compose network. Then switch the
backend startup args to use service names db:5432 and minio:9000
instead of host-mapped ports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:17 +01:00
Marcel
d943372ea7 fix(ci): pass docker-compose.ci.yml override to e2e compose commands
The e2e job was calling plain `docker compose up` without the CI
override file, so it used the base compose bind-mount for MinIO
(./data/minio) which doesn't exist on the runner. The CI override
replaces bind mounts with ephemeral named volumes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:17 +01:00
Marcel
4e8de3658f fix(api): use API_INTERNAL_URL in tags and persons proxy routes
Both SvelteKit API proxy routes were hardcoding http://localhost:8080,
breaking typeahead search in Docker environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:17 +01:00
Marcel
b6361e6cbc fix(auth): use API_INTERNAL_URL in userGroup hook
The userGroup hook was hardcoding http://localhost:8080 instead of
reading API_INTERNAL_URL from the environment. In Docker this caused
the /api/users/me fetch to fail silently, leaving event.locals.user
unset and triggering the handleAuth guard to redirect every page to
/login — including the login form action itself, creating an infinite
redirect loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:13:17 +01:00
Marcel
a3948b6a0f fix(test): add missing user, createdAt, updatedAt, and tag id fields to page spec fixtures
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 11m27s
CI / Backend Unit Tests (push) Successful in 2m11s
CI / E2E Tests (push) Failing after 56s
2026-03-19 12:51:35 +01:00
Marcel
0918e75803 docs: add Red/Green TDD workflow to COLLABORATING.md
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 8m48s
CI / Backend Unit Tests (pull_request) Successful in 1m56s
CI / E2E Tests (pull_request) Failing after 58s
CI / Unit & Component Tests (push) Successful in 8m22s
CI / Backend Unit Tests (push) Successful in 1m59s
CI / E2E Tests (push) Failing after 52s
2026-03-19 12:03:50 +01:00