Commit Graph

291 Commits

Author SHA1 Message Date
Marcel
afebaf4c53 fix(#91): add px-4 base padding and fix admin tab overflow at 320px
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m42s
CI / Backend Unit Tests (pull_request) Successful in 2m23s
CI / E2E Tests (pull_request) Failing after 29m0s
CI / Unit & Component Tests (push) Successful in 3m20s
CI / Backend Unit Tests (push) Successful in 2m21s
CI / E2E Tests (push) Failing after 29m37s
Home and Admin had no horizontal padding below the sm breakpoint (640px),
causing content to bleed to viewport edges. Admin's flex justify-between
row with h1 + 4 tab buttons overflowed by ~110px at 320px.

- +page.svelte: add px-4 to <main> (sm:px-6 lg:px-8 unchanged)
- admin/+page.svelte: add px-4 to outer container; stack header row
  vertically on mobile (flex-col sm:flex-row); reduce tab button padding
  to px-2 on mobile (sm:px-4 on desktop)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:50:03 +01:00
Marcel
1bfe0ab022 fix(#96): remove off-brand lavender accent bar from all pages
Some checks failed
CI / Unit & Component Tests (push) Successful in 2m33s
CI / Backend Unit Tests (push) Successful in 2m15s
CI / E2E Tests (push) Failing after 28m13s
CI / Unit & Component Tests (pull_request) Successful in 2m44s
CI / Backend Unit Tests (pull_request) Successful in 2m16s
CI / E2E Tests (pull_request) Failing after 24m21s
The h-1 bg-brand-purple strip (#b4b9ff) is not a De Gruyter brand
color and was added as a rough placeholder. Removed from +layout.svelte
and the three auth pages (login, forgot-password, reset-password).
Also removed the unused --palette-purple and --color-brand-purple CSS
tokens from layout.css.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:49:13 +01:00
Marcel
6ebae19984 feat(#90): add hamburger menu and mobile nav drawer below 640px
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m26s
CI / Backend Unit Tests (pull_request) Successful in 2m11s
CI / E2E Tests (pull_request) Failing after 25m59s
CI / Unit & Component Tests (push) Has been cancelled
CI / Backend Unit Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Nav links were completely hidden on mobile (sm:flex / hidden split).
Adds a 44×44px hamburger toggle, a fixed overlay panel with full-width
nav links (min-h-[44px] touch targets), backdrop-click and Escape to
close, and a $effect that auto-closes on route change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:38:52 +01:00
Marcel
fa9577052d fix(e2e): fix 4 failing e2e tests — strict mode locator and nested form
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 2m23s
CI / Backend Unit Tests (push) Successful in 2m11s
CI / E2E Tests (push) Failing after 29m1s
documents.spec.ts: replace getByText with getByRole('heading') to avoid
Svelte's #svelte-announcer matching the same text (strict mode violation).

SaveBar.svelte: move <form id="mark-for-review-form"> out of the component
and into +page.svelte as a sibling of delete-form. The form was previously
nested inside <form id="update-form">, which is invalid HTML. The browser
auto-repaired it, causing a Svelte hydration mismatch that broke the edit
form's use:enhance, preventing version snapshots from being recorded —
leaving history tests with 0 versions instead of the expected 2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 10:04:21 +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
d6f4ea05d9 feat(#68): fall back to filename as title when createDocument gets no title
When a document is created without an explicit title (null or blank),
the service now derives the title from the uploaded filename using the
same titleFromFilename() logic already used by storeDocument — stripping
the extension for plain names and formatting structured names as
"Firstname Lastname (DD.MM.YYYY)".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:51:24 +01:00
Marcel
065dd8fabd fix(e2e): fix two flaky annotation tests
Test 6 (delete annotation): the mouse-draw test can create multiple
annotations in CI. Changed the assertion to `countBefore - 1` instead
of a hard-coded 0, so the test is resilient to any pre-existing count.

Test 7 (hash versioning): `[data-testid^="annotation-"]` matched both
real annotation elements AND `annotation-outdated-notice` (which also
starts with "annotation-"), inflating the count to 2 instead of 0.
Added `:not([data-testid="annotation-outdated-notice"])` to exclude the
notice from the count assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:32:58 +01:00
Marcel
a967483cd9 fix(e2e): update tests to match current UI and fix panel persistence
Some checks failed
CI / Unit & Component Tests (push) Successful in 2m35s
CI / Backend Unit Tests (push) Successful in 2m15s
CI / E2E Tests (push) Failing after 27m18s
Code:
- Persist panelOpen to localStorage so panel stays open after reload
- Auto-open panel to Metadaten when document has no file (no prior state)

Tests:
- Nav active state: check bg-nav-active instead of text-brand-navy
  (nav uses semantic tokens since dark mode refactor)
- Save button: use exact:true to avoid matching "Speichern & abschließen"
  (new button was added alongside the plain "Speichern" button)

Note: annotation tests (documents.spec.ts:324, 356) are pre-existing
flaky failures due to test data contamination, not caused by this PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:26:03 +01:00
Marcel
5d0a2a2c9c fix: use semantic color tokens for enrich hint box
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m29s
CI / Backend Unit Tests (pull_request) Successful in 2m12s
CI / E2E Tests (pull_request) Failing after 24s
CI / Unit & Component Tests (push) Successful in 2m17s
CI / Backend Unit Tests (push) Successful in 2m1s
CI / E2E Tests (push) Has started running
Replaced hardcoded brand-navy/brand-mint palette constants with
semantic tokens (ink, accent, accent-bg) so the hint box themes
correctly in dark mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:47:44 +01:00
Marcel
0f0d74eb2f fix(#81): use text-primary-fg for badge text so dark mode reads correctly
In dark mode --c-primary flips to mint (#a1dcd8), making text-white
unreadable. text-primary-fg is already paired correctly in both modes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 19:41:10 +01:00
Marcel
20f6de4424 refactor(#81): replace nudge button with always-visible count badge
Show the discussion count badge on every state (including 0) instead of
a separate nudge button. Simpler, less intrusive, and works without
needing an extra element near the panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:43:48 +01:00
Marcel
bf82ebfe1d feat(#81): improve discussion discoverability
- Add comment count badge on the Discussion tab (seeded from SSR, updated live)
- Add 'Diskussion starten' nudge above collapsed panel when no comments exist
- Add empty state hint with speech-bubble icon inside the discussion panel
- Fix CommentThread to fire onCountChange with SSR-seeded count on mount
- Add tests for all three behaviours in CommentThread and DocumentBottomPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:19:38 +01:00
Marcel
c6984e49ee fix(dropzone): vertical layout, larger icon, improved copy
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m23s
CI / Backend Unit Tests (pull_request) Successful in 2m9s
CI / E2E Tests (pull_request) Failing after 28m26s
CI / E2E Tests (push) Failing after 28m40s
CI / Backend Unit Tests (push) Successful in 2m12s
CI / Unit & Component Tests (push) Successful in 2m28s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:47:08 +01:00
Marcel
150bc2f171 feat(dropzone): replace upload icon with multi-file icon and clearer hint text
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 2m22s
CI / Backend Unit Tests (pull_request) Successful in 2m4s
CI / E2E Tests (pull_request) Failing after 26m49s
Swaps the generic upload arrow for Display-Pages-MD (stack of pages) and
shortens the hint text to convey that multiple files are welcome at a glance.

Closes #79
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 17:32:15 +01:00
Marcel
41c311249b fix(enrich): use fixed positioning for fullscreen layout and fix done icon
Some checks failed
CI / Unit & Component Tests (push) Successful in 2m16s
CI / Backend Unit Tests (push) Successful in 2m10s
CI / E2E Tests (push) Failing after 27m43s
Align enrich/[id] with the document detail page pattern: position fixed
with runtime header height measurement instead of a hardcoded calc value.
The root layout is reverted to its original simple form with no per-route
detection. Also replaces the missing check icon on the done page with
Check-Double-LG from the icon library.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 16:56:39 +01:00
Marcel
2efa790243 Revert "fix(enrich): restore fixed-position layout and done icon"
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m28s
CI / Backend Unit Tests (pull_request) Successful in 2m13s
CI / E2E Tests (pull_request) Failing after 27m56s
CI / Unit & Component Tests (push) Successful in 2m24s
CI / Backend Unit Tests (push) Successful in 2m12s
CI / E2E Tests (push) Failing after 28m17s
This reverts commit 648bdffe4f.
2026-03-26 15:52:47 +01:00
Marcel
648bdffe4f fix(enrich): restore fixed-position layout and done icon
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 3m39s
CI / Backend Unit Tests (pull_request) Successful in 2m39s
CI / E2E Tests (pull_request) Failing after 30m26s
Re-applies the scroll fix from 0d3c557 which was missing from this branch:
- measure header height at mount, use it as top offset instead of hardcoded 68px
- fix done page icon to Check-Double-LG

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:51:15 +01:00
Marcel
99e3163c0e feat(quick-upload): pre-fill date and sender from structured filename
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 2m28s
CI / Backend Unit Tests (pull_request) Successful in 2m14s
CI / E2E Tests (pull_request) Failing after 28m25s
storeDocument() now uses the ParsedFilename record to also set
documentDate and sender on new quick-uploads. Sender lookup is
an exact case-insensitive first+last name match — no new persons
are created. Unmatched filenames behave as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:43:39 +01:00
Marcel
f0940524e7 feat(filename): support compound last names like de Gruyter
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 2m17s
CI / Backend Unit Tests (pull_request) Successful in 2m13s
CI / E2E Tests (pull_request) Failing after 25m0s
Replace the four fixed regexes with a split-based algorithm:
- first segment = date → last segment = firstName, rest = lastName parts
- last segment = date → second-to-last = firstName, rest = lastName parts

18881025_de_Gruyter_Walter.pdf now correctly yields "Walter de Gruyter".
Simple two-segment names behave identically to before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:33:21 +01:00
Marcel
a302f96560 feat(quick-upload): generate better title from structured filename
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 2m16s
CI / Backend Unit Tests (pull_request) Successful in 2m8s
CI / E2E Tests (pull_request) Failing after 26m22s
titleFromFilename() mirrors the same four patterns as the frontend
parseFilename() utility. Dropzone uploads to Mueller_Hans_19650312.pdf
now land with title "Hans Mueller (12.03.1965)" instead of the raw
stripped filename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:18:34 +01:00
Marcel
654e736f8a feat(dropzone): add filename hint showing supported naming pattern
Shows a concrete example (2024-03-15_Mueller_Hans.pdf) so users know
which filenames will be auto-parsed during bulk upload.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:18: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
8555193a79 feat(filename): add parseFilename utility with full-pattern-only matching
Supports four patterns: date_lastname_firstname and lastname_firstname_date,
both with ISO (YYYY-MM-DD) and compact (YYYYMMDD) date formats.
Returns dateIso, personName and a formatted suggestedTitle.
Partial matches are rejected — unrecognised filenames return {}.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:17:16 +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
0ce18e1eed feat(documents): add metadataComplete flag and enrichment queue endpoints
Adds a metadata_complete column (default true for existing rows) to drive
the enrichment queue. New drop-zone uploads always start as false; createDocument
uses an explicit DTO flag or a heuristic (any of date/sender/receivers present →
true); the mass importer applies the same heuristic per row.

New endpoints: GET /api/documents/incomplete-count, /incomplete, /incomplete/next.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 13:25:57 +01:00
Marcel
2bfbf45eba refactor(types): extract shared types to \$lib/types.ts
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m22s
CI / Backend Unit Tests (pull_request) Successful in 2m16s
CI / E2E Tests (pull_request) Failing after 31m23s
CI / Unit & Component Tests (push) Successful in 2m24s
CI / Backend Unit Tests (push) Successful in 2m6s
CI / E2E Tests (push) Failing after 30m19s
Eliminates type duplication across 6 files by introducing a single
shared types module:

- Comment + CommentReply: were identically defined in CommentThread,
  PanelDiscussion, and DocumentBottomPanel
- DocumentPanelTab: was identically defined in DocumentBottomPanel
  and documents/[id]/+page.svelte
- Annotation: was defined in both AnnotationLayer and PdfViewer
  (PdfViewer's variant with fileHash? is now the canonical definition)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:47:08 +01:00
Marcel
40f01a7712 refactor(comments): extract commentEntry snippet to remove duplicated markup
The root-comment and reply rendering blocks were near-identical (view mode
with author/time/edit-delete, and edit mode with textarea/save/cancel).
Extracted a local {#snippet commentEntry(comment, threadId, showReplyButton)}
that handles both states, introducing Svelte 5 snippets to the codebase.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:42:39 +01:00
Marcel
0db68da00c refactor(persons): extract PersonCard, PersonMergePanel, CoCorrespondentsList, PersonDocumentList
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 / 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
Split the 610-line person detail page into four focused co-located components:
- PersonCard: view/edit card with inline form (owns editMode)
- PersonMergePanel: merge target typeahead + two-step confirm (state reset via {#key})
- CoCorrespondentsList: frequency-ranked correspondent chips linking to conversations
- PersonDocumentList: reusable sorted/paginated document list (used for sent + received)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:32:01 +01:00
Marcel
e831de4f85 refactor(home): extract SearchFilterBar, DropZone, and DocumentList
Split the 580-line home page into three focused co-located components:
- SearchFilterBar: full-text search + collapsible advanced filters
- DropZone: drag-and-drop / click-to-upload with progress and messages
- DocumentList: document list with new-doc link and empty state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:28:18 +01:00
Marcel
90e94b350a refactor(conversations): extract filter bar and timeline sub-components
Split conversations/+page.svelte (346 lines) into:
- ConversationFilterBar.svelte: person A/B typeaheads, swap button, date range, sort toggle
- ConversationTimeline.svelte: summary bar, chat bubbles, year dividers, new-doc link

Page drops from 346 → ~70 lines; navigation logic and filter state stay in the page.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:22:38 +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
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
6f71682454 refactor(profile): extract PersonalInfoForm and PasswordChangeForm
Split profile/+page.svelte (240 lines) into:
- PersonalInfoForm.svelte: name/birth-date/email/contact with own date state
- PasswordChangeForm.svelte: current/new/confirm password fields

Page drops from 240 → ~25 lines.
Date utilities now imported from \$lib/utils/date instead of duplicated inline.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:14:20 +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
d46764ef4f refactor(layout): extract AppNav and UserMenu sub-components
Split +layout.svelte (205 lines) into:
- AppNav.svelte: logo + nav links with active-state styling
- UserMenu.svelte: avatar button, dropdown, click-outside handler

Layout drops from 205 → 80 lines.

Part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:10:54 +01:00
Marcel
d40d4b21e1 refactor(utils): consolidate date utilities into \$lib/utils/date.ts
Move isoToGerman and germanToIso from utils.ts into utils/date.ts alongside
formatDate, and add handleGermanDateInput for the shared date field handler.
Make utils.ts a re-export shim so existing imports continue to work.

Closes part of #75

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 12:07:46 +01:00
Marcel
1ea84e4dc8 feat(upload): show progress bar in drop zone during upload
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 2m23s
CI / Backend Unit Tests (push) Successful in 2m13s
CI / E2E Tests (push) Failing after 29m59s
Replaces fetch with XMLHttpRequest to get upload progress events.
The drop zone shows a filling progress bar and percentage while
files are uploading, then reverts to the normal hint when done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:37:28 +01:00
Marcel
d078ad8224 feat(upload): warn on duplicate filename with link to existing document
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- storeDocument now returns StoreResult(document, isNew) to distinguish
  new uploads from updates to existing documents
- QuickUploadResult gains an `updated` list alongside `created`
- Frontend shows an amber warning with a "View document" link for duplicates
  instead of silently re-uploading and leaving the user confused

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:31:31 +01:00
Marcel
9d5c57b49b fix(dropzone): replace broken degruyter upload icon with inline SVG
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:14:37 +01:00
Marcel
0795e4099f fix(delete): add cascade deletes and fix SvelteKit named action conflict
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- Add V14 migration: ON DELETE CASCADE for document_tags and document_receivers
  so deleting a document removes its join-table rows automatically
- Rename default form action to 'update' in the edit page — SvelteKit forbids
  mixing a default action with named actions (was causing 500 on delete)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:12:21 +01:00
Marcel
1413058ae7 fix(documents): style delete button as red outlined button with inline trash icon
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Replace the subtle link-style delete trigger and broken degruyter icon
with a proper red outlined button and an inline SVG trash bin icon.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 11:00:06 +01:00
Marcel
91a29d501d feat(documents): add delete button to document edit form
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- DELETE /api/documents/{id} endpoint (204 No Content, WRITE_ALL required)
- DocumentService.deleteDocument() — throws 404 if not found, cascades
  via DB foreign keys (versions, annotations, comments all ON DELETE CASCADE)
- Delete form action in edit page server: redirects to / on success
- Two-step confirmation in the save bar: first click reveals inline
  "Wirklich löschen?" + confirm/cancel, avoiding native browser dialogs
- i18n key doc_delete_confirm added to de/en/es

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:52:43 +01:00
Marcel
963807ff05 fix(upload): structured error codes for quick-upload, fix duplicate filename crash
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
- Switch errors from plain strings to { filename, code } objects so the
  frontend can show translated messages instead of raw exception text
- Add UNSUPPORTED_FILE_TYPE error code end-to-end (Java enum → errors.ts
  → de/en/es messages)
- Fix IncorrectResultSizeDataAccessException when a filename exists more
  than once in the DB: use findFirstByOriginalFilename instead of
  findByOriginalFilename in storeDocument()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:38:30 +01:00
Marcel
6a663cefe6 fix(search): sort document overview by createdAt DESC instead of documentDate ASC
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) Has been cancelled
CI / Backend Unit Tests (pull_request) Has been cancelled
CI / E2E Tests (pull_request) Has been cancelled
Newly uploaded documents (from bulk drop-zone or Excel import) have no
documentDate, so they were sinking to the bottom. Sorting by createdAt
DESC puts the most recently added documents first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:32:46 +01:00
Marcel
db103ca1ab fix(test): add invalidateAll to $app/navigation mock in home page spec
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 2m24s
CI / Backend Unit Tests (pull_request) Successful in 2m11s
CI / E2E Tests (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:20:03 +01:00
Marcel
3ec680b812 feat(upload): expand drop zone when dragging file over browser window
Adds window-level dragenter/dragleave/drop listeners that detect when
the user drags any file into the browser. The drop zone expands from
py-3 to py-10 with a softened highlight, giving a clear visual cue
that dropping is possible anywhere on the page.

Uses a drag-counter to correctly handle the dragenter/dragleave storm
that fires as the pointer moves across child elements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:19:34 +01:00
Marcel
50e3f948c7 fix(upload): use border-ink/20 and primary color for drop zone visibility
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) Failing after 1m59s
CI / Backend Unit Tests (pull_request) Successful in 2m16s
CI / E2E Tests (pull_request) Failing after 30m10s
In light mode, border-line-2 (#eeede8) was nearly invisible and
accent (#a1dcd8, mint) was too light for hover text. Switch to:
- border-ink/20 — navy-tinted dashed border, readable in both modes
- hover:border-primary / hover:text-primary — navy in light, mint in dark

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:09:17 +01:00
Marcel
bbfef9a22d feat(upload): add drag-and-drop bulk upload zone to home page
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m25s
CI / Backend Unit Tests (push) Successful in 2m26s
CI / E2E Tests (push) Has started running
CI / Unit & Component Tests (pull_request) Failing after 1m49s
CI / Backend Unit Tests (pull_request) Successful in 2m2s
CI / E2E Tests (pull_request) Failing after 30m19s
Adds a compact, unobtrusive drop zone between the search card and the
document list. Only visible to users with WRITE_ALL permission.

- Drag-and-drop or click-to-select multiple files at once
- Client-side MIME type validation with per-file error messages
- POSTs to /api/documents/quick-upload; refreshes list via invalidateAll()
- Inline feedback: success count + per-file errors
- i18n keys added to de/en/es message files

Closes #66 (frontend part)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:00:19 +01:00
Marcel
332b5b3c40 feat(upload): add POST /api/documents/quick-upload endpoint for bulk file upload
Adds a new multipart endpoint that accepts multiple files and creates one
document per file without requiring any form metadata. Each document gets
title = filename-without-extension and status = UPLOADED.

- Fix storeDocument() to strip the file extension from the document title
- Validate content type (PDF/JPEG/PNG/TIFF) server-side; unsupported files
  are skipped and returned as per-file errors in QuickUploadResult
- Tests cover 401/403 auth, success path, and unsupported file type

Closes #66 (backend part)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 09:59:59 +01:00