Commit Graph

20 Commits

Author SHA1 Message Date
Marcel
25014cce2d refactor(admin/users): extract user form sub-components
Shared (src/lib/components/user/):
- UserProfileSection.svelte: name/birth-date/email/contact fields
- UserGroupsSection.svelte: group checkboxes
- UserPasswordSection.svelte: new/confirm password fields

Page-local:
- admin/users/new/AccountSection.svelte: username + initial password

admin/users/[id] drops from 224 → ~35 lines.
admin/users/new drops from 191 → ~30 lines.
Date utilities imported from \$lib/utils/date.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:17:55 +01:00
Marcel
af59ed4de4 refactor(admin): split admin page into tab sub-components
Split admin/+page.svelte (573 lines) into:
- UsersTab.svelte: user table with delete action
- TagsTab.svelte: tag list with inline rename and delete
- GroupsTab.svelte: groups table with inline edit + create form
- SystemTab.svelte: backfill buttons with own state

Page drops from 573 → ~40 lines.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:13:06 +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
0ec86220d3 feat(backend): add POST /api/admin/backfill-file-hashes endpoint
- DocumentRepository: findByFileHashIsNullAndFilePathIsNotNull()
- AnnotationRepository: findByDocumentIdAndFileHashIsNull()
- FileService: downloadFileBytes() downloads raw bytes from S3 for hashing
- AnnotationService: backfillAnnotationFileHashForDocument() sets hash on null-hash annotations
- DocumentService: backfillFileHashes() iterates documents with null hash,
  downloads bytes, computes SHA-256, saves doc, then propagates hash to annotations
- AdminController: POST /api/admin/backfill-file-hashes delegates to DocumentService

Closes #56

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 17:32:29 +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
47b8cc9340 feat(frontend): add System tab to admin panel with backfill-versions action
Some checks failed
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Unit & Component Tests (push) Has been cancelled
CI / Unit & Component Tests (pull_request) Successful in 2m13s
CI / Backend Unit Tests (pull_request) Successful in 2m17s
CI / E2E Tests (pull_request) Failing after 22m45s
Admin can trigger an initial history snapshot for all documents without
version history. Shows count of backfilled documents after completion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 12:33:39 +01:00
Marcel
70d858b65a fix(tests): add missing user/canWrite/form props to admin spec fixtures
After the layout load function started injecting user+canWrite into all
page data, the admin spec files failed svelte-check with missing property
errors. Add user:undefined, canWrite:true, and form:null to all fixture
data objects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 23:01:25 +01:00
Marcel
fb4f8e820c feat(admin): add dedicated routes for admin user management (#37)
Some checks failed
CI / Unit & Component Tests (push) Successful in 2m4s
CI / Backend Unit Tests (push) Successful in 1m59s
CI / E2E Tests (push) Failing after 18m4s
CI / Unit & Component Tests (pull_request) Successful in 2m2s
CI / Backend Unit Tests (pull_request) Successful in 2m0s
CI / E2E Tests (pull_request) Failing after 16m10s
- New GET /admin/users/new page: create user with all profile fields
  (login, password, firstName, lastName, birthDate, email, contact, groups)
- New GET /admin/users/[id] page: edit user profile, groups, and
  optional password change without requiring current password
- New PUT /api/users/{id} backend endpoint (ADMIN_USER permission)
  with AdminUpdateUserRequest DTO for admin-override user updates
- Refactored admin users tab: replaced inline editing with edit links
  to dedicated routes; create button now links to /admin/users/new
- Extended CreateUserRequest with profile fields so new users can be
  created with full profile data in a single request
- Added 28 component tests across 3 new spec files (TDD)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 16:33:50 +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
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
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
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
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
62189d8bb3 fix: show names in admin tag panel 2026-03-15 21:08:06 +00: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
4cc86de143 feat: replace raw error messages with structured error codes
Backend now returns { code: ErrorCode, message: string } for all errors,
making it language-agnostic. Frontend maps codes to localised strings via
Paraglide (en/de/es), so translations live in messages/*.json.

- Add ErrorCode enum and DomainException with static factory methods
- Update GlobalExceptionHandler to return ErrorResponse(code, message)
- Replace ResponseStatusException throughout controllers/services/aspects
- Add frontend errors.ts with parseBackendError() and getErrorMessage()
- getErrorMessage() delegates to Paraglide m.error_*() functions
- Add error_* keys to messages/en.json, de.json, es.json
- Update all page.server.ts files to use the new error utilities
- Fix hardcoded localhost URLs in admin and login pages
- Fix missing baseUrl in deleteTag action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 13:15:28 +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