Commit Graph

1278 Commits

Author SHA1 Message Date
Marcel
3589e8659e fix(dashboard): bulk-load document titles in getActivity to avoid N+1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
bc762246e5 fix(dashboard): null-safe name join in toActorDTO
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
7f6380452f fix(dashboard): include ANNOTATION_CREATED in hero resume query
findMostRecentDocumentIdByActor only matched TEXT_SAVED events, so documents
where the user drew annotation bounding boxes (but typed no transcription text)
were invisible to the hero resume card. Extending the IN clause to include
ANNOTATION_CREATED lets annotation-only work surface in the card (0% progress,
no excerpt — the correct state before transcription begins).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
267380f714 fix(audit): submit afterCommit write to executor to avoid transaction sync conflict
AuditService.logAfterCommit() called writeLog() inline inside the afterCommit()
callback. At that point Spring's transaction synchronizations are still active on
the thread, so SimpleJpaRepository.save() throws IllegalStateException which the
catch block silently swallowed — leaving audit_log permanently empty.

Fix: submit writeLog() to auditExecutor so it runs on a fresh thread with no active
synchronization context. Also switch auditExecutor from CallerRunsPolicy to AbortPolicy
to prevent the bug from silently recurring when the queue fills under load.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
7506f8743a fix(dashboard): defensive null guard in ContributorStack; fix spec makeDoc factories 2026-04-20 07:45:16 +02:00
Marcel
520cca58b8 feat(dashboard): show contributor pill stack on each mission control queue item
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
4bd1ebfd1e feat(dashboard): add ContributorStack component for mission control pill stacks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
647a82b085 chore(types): regenerate API types with contributor fields on TranscriptionQueueItemDTO 2026-04-20 07:45:16 +02:00
Marcel
a3a9ad0471 test(dashboard): add empty-queue guard and boundary tests for contributor cap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
812053cd6b feat(dashboard): add contributors to TranscriptionQueueItemDTO with 5-cap and hasMore flag
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
20cac8f6d9 feat(dashboard): expose findContributorsPerDocument in AuditLogQueryService 2026-04-20 07:45:16 +02:00
Marcel
935a8b16d2 fix(dashboard): use LEFT JOIN users in findContributorsPerDocument for deleted-user resilience 2026-04-20 07:45:16 +02:00
Marcel
24b203ac80 feat(dashboard): add findContributorsPerDocument query and ContributorRow projection 2026-04-20 07:45:16 +02:00
Marcel
5a98edac86 feat(dashboard): complete frontend redesign for Issue #271
- +layout.svelte: Upload button in header (authenticated users only)
- +page.server.ts: call /api/dashboard/resume, /pulse, /activity;
  remove deprecated /api/documents/incomplete and /recent-activity
- +page.svelte: 2-col grid layout (main + 320px sidebar), greeting,
  DashboardFamilyPulse + DashboardActivityFeed in sidebar
- DashboardResumeStrip: refactored to use server data (resumeDoc prop),
  SVG thumbnail, progress bar with aria-*, empty state, CTA
- DashboardFamilyPulse: new component — weekly stats from audit_log
- DashboardActivityFeed: new component — activity feed with "für dich" badge
- Update specs for new data shapes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
d34e8986af feat(i18n): add dashboard i18n keys (de/en/es)
Greeting, resume card, mission control, family pulse, activity feed,
audit action verbs, and dropzone keys for the Issue #271 dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
06c75af96b chore(types): regenerate API types with dashboard endpoints
Adds DashboardResumeDTO, DashboardPulseDTO, ActivityFeedItemDTO,
ActivityActorDTO and the three /api/dashboard/* paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
ddd811c634 feat(dashboard): remove deprecated /incomplete and /recent-activity endpoints
GET /api/documents/incomplete and GET /api/documents/recent-activity are
superseded by the new dashboard endpoints (GET /api/dashboard/activity etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
250a00ff3c fix(migration): correct app_users → users table references in V46/V47
The AppUser entity is mapped to the 'users' table (not 'app_users').
V46 had a broken REFERENCES clause and hardcoded role in REVOKE; V47 and the
native query in AuditLogQueryRepository had the same wrong table name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
56a44bcef9 refactor(security): extract requireUserId to SecurityUtils
Both DocumentController and TranscriptionBlockController contained
identical private requireUserId helpers. Extracted to a shared static
utility in the security package ahead of DashboardController which
also needs actor resolution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
b3ae379be7 fix(audit): add blockId to TEXT_SAVED audit payload
Required for dashboard Pulse stat 2 (COUNT DISTINCT blockId).
Without it, two saves on different blocks on the same page
were indistinguishable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
74febd37f6 feat(user): add deterministic avatar color to AppUser
Adds color field assigned from an 8-colour palette keyed on the user's UUID
hash (Math.abs(id.hashCode()) % 8). Fires via @PrePersist/@PreUpdate/@PostLoad
so both new and existing users get the correct colour at runtime.

V47 migration adds the column and fixes the V46 REVOKE bug that hardcoded
role name 'app_user' instead of CURRENT_USER.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 07:45:16 +02:00
Marcel
da2ece986a fix(documents): WCAG text-size and API error pattern fixes
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m37s
CI / OCR Service Tests (pull_request) Successful in 29s
CI / Unit & Component Tests (push) Failing after 2m44s
CI / OCR Service Tests (push) Successful in 38s
CI / Backend Unit Tests (push) Failing after 2m45s
CI / Backend Unit Tests (pull_request) Failing after 2m45s
- DashboardResumeStrip: text-[10px] → text-xs on collaborator initials (WCAG 1.4.4)
- documents/+page.server.ts: use !result.response.ok per CLAUDE.md; keep narrow try/catch for network-level failures only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 01:17:04 +02:00
Marcel
f2bed92176 fix(review): cycle 3 — a11y, CSS injection, domain boundary
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m44s
CI / OCR Service Tests (push) Successful in 34s
CI / Unit & Component Tests (pull_request) Failing after 2m33s
CI / OCR Service Tests (pull_request) Successful in 35s
CI / Backend Unit Tests (push) Failing after 2m56s
CI / Backend Unit Tests (pull_request) Failing after 2m48s
- DocumentRow: add data-testid="search-snippet"; sanitize tag.color with safeTagColor()
- ContributorStack: add role="img" aria-label to overflow "…" badge
- DocumentList: year header text-[10px] → text-xs (WCAG 1.4.4 minimum 12px)
- DashboardResumeStrip: sanitize collab.color with safeColor()
- Extract TranscriptionBlockQueryService to fix cross-domain repository access in DocumentService
- Update unit test mocks to use TranscriptionBlockQueryService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 01:03:06 +02:00
Marcel
7f23e88b69 fix(documents): address review cycle 2 — a11y, CSS injection, debounce tests
Some checks failed
CI / Unit & Component Tests (pull_request) Failing after 2m33s
CI / OCR Service Tests (pull_request) Successful in 38s
CI / Unit & Component Tests (push) Failing after 2m40s
CI / OCR Service Tests (push) Successful in 36s
CI / Backend Unit Tests (push) Failing after 2m54s
CI / Backend Unit Tests (pull_request) Failing after 2m58s
- ContributorStack: text-xs for WCAG 1.4.4 (was text-[10px]), safeColor()
  validation to block CSS injection via actor.color, role="img" aria-label
  on empty placeholder, {#each} keyed by index
- ContributorStack spec: update empty-state assertion to getByRole('img')
- DocumentRow spec: add stopPropagation regression test for tag click
- documents/page.svelte.spec.ts: new — debounce, URL building, initial state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:40:48 +02:00
Marcel
b2ea9e74fe fix(a11y): add sr-only h1 to documents page for screen reader landmark
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m42s
CI / OCR Service Tests (push) Successful in 34s
CI / Backend Unit Tests (push) Failing after 3m1s
CI / Unit & Component Tests (pull_request) Failing after 2m41s
CI / OCR Service Tests (pull_request) Successful in 34s
CI / Backend Unit Tests (pull_request) Failing after 2m48s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:25:16 +02:00
Marcel
dc349947ef fix(a11y): add aria-hidden to decorative SVG and use text-xs for WCAG 12px minimum
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:24:36 +02:00
Marcel
80e10e1755 fix(docs): stop tag click propagation to prevent parent anchor navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:23:48 +02:00
Marcel
3707d34c62 test(docs): add DocumentRow unit tests — title, snippet, tags, sender, progress ring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:23:08 +02:00
Marcel
10833fbe6b feat(frontend): add /documents page with search, filter, and year-card list
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m35s
CI / OCR Service Tests (push) Successful in 36s
CI / Backend Unit Tests (push) Failing after 2m53s
CI / Unit & Component Tests (pull_request) Failing after 2m40s
CI / OCR Service Tests (pull_request) Successful in 31s
CI / Backend Unit Tests (pull_request) Failing after 2m46s
- New documents/+page.svelte wires SearchFilterBar + DocumentList with
  URL-driven navigation (goto + SvelteURLSearchParams)
- Reset button in SearchFilterBar now navigates to /documents
- Rename documents/+page.server.spec.ts → page.server.spec.ts to avoid
  SvelteKit route-file conflict on the + prefix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 00:06:25 +02:00
Marcel
4ba4e67bc5 feat(frontend): add /documents page.server.ts — search load function with all filter params
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:55:37 +02:00
Marcel
65bc859918 refactor(frontend): rewrite DocumentList as year-card orchestrator using DocumentSearchItem[]
Delegates row rendering to DocumentRow; groups by year; removes matchData and sort props.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:53:37 +02:00
Marcel
648aa2a742 feat(frontend): add DocumentRow component with two-column layout, highlights, progress, contributors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:44:31 +02:00
Marcel
ae0e3b271d feat(frontend): add ProgressRing component — SVG progress arc with percentage label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:34:30 +02:00
Marcel
6e888d9958 feat(frontend): simplify homepage to pure dashboard — remove search/filter dual-mode
The homepage now always renders the dashboard. Search and browse
moves to the dedicated /documents route (upcoming).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:31:57 +02:00
Marcel
eeca30e7a6 feat(frontend): add --header-height, bump initials to text-[10px], update nav to /documents
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:24:46 +02:00
Marcel
05d434fed3 chore(frontend): regenerate API types with DocumentSearchItem and updated DocumentSearchResult
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:23:08 +02:00
Marcel
71c02626f4 feat(migration): V48 add composite index on transcription_blocks(document_id, reviewed)
Speeds up the bulk completion percentage query added in previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:19:24 +02:00
Marcel
8df0c3a1ef feat(service): assemble DocumentSearchItem in DocumentService with completion and contributors
DocumentService.searchDocuments now fetches completion percentages and recent
contributors per document and zips them into DocumentSearchItem records.
Update affected tests to use the new items-based result shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:18:35 +02:00
Marcel
ab3a026feb feat(dto): add DocumentSearchItem record and refactor DocumentSearchResult to items-based shape
Replaces {documents, matchData, total} with {items: List<DocumentSearchItem>, total}
where each item collocates document + matchData + completionPercentage + contributors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:17:53 +02:00
Marcel
16614d1bfb feat(audit): add findRecentContributorsForDocuments query (max 4, recency order)
Adds a window-function query that returns at most 4 contributors per document
ordered by most-recent activity. Used by DocumentService to populate the
contributors field in DocumentSearchItem (issue #281).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:05:49 +02:00
Marcel
f197eb9752 feat(repository): add bulk completion stats query to TranscriptionBlockRepository
Adds findCompletionStatsForDocuments() returning reviewed-block percentage
per document in a single native SQL GROUP BY query. Needed for the new
DocumentSearchItem DTO in issue #281.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 23:00:20 +02:00
Marcel
70a2bbfaad refactor(audit): move AuditLogQueryService, AuditLogQueryRepository, and shared DTOs to audit package
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m48s
CI / OCR Service Tests (push) Successful in 34s
CI / Backend Unit Tests (push) Failing after 2m48s
CI / Unit & Component Tests (pull_request) Failing after 2m41s
CI / OCR Service Tests (pull_request) Successful in 32s
CI / Backend Unit Tests (pull_request) Failing after 2m50s
TranscriptionQueueService was importing ActivityActorDTO and AuditLogQueryService
from the dashboard package, creating an inverted dependency (service → dashboard).
Moving these to the audit package where AuditLog lives gives both DashboardService
and TranscriptionQueueService the correct dependency direction (→ audit).

Moved to audit:
- ActivityActorDTO, ActivityFeedRow, ContributorRow, PulseStatsRow (projections)
- AuditLogQueryRepository, AuditLogQueryService

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:43:30 +02:00
Marcel
5246638014 fix(dashboard): fix ContributorStack each-block key and add accessible avatar labels
- Replace (actor.name ?? actor.initials + i) with (actor.initials + '-' + actor.color)
  to fix operator-precedence bug that made keys order-dependent when name is null
- Add role="img" + aria-label={actor.name ?? actor.initials} so screen readers
  and touch users can access contributor names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:28:00 +02:00
Marcel
d6e5d3d1e8 fix(layout): replace hardcoded 'Hochladen' with m.upload_action() + aria-label
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:23:32 +02:00
Marcel
94823f85c8 test(dashboard): fix stale resume mock — use totalBlocks instead of page/pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:19:50 +02:00
Marcel
6494b13147 docs(spec): add /documents page design spec with mobile breakpoints
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m32s
CI / OCR Service Tests (push) Successful in 33s
CI / Backend Unit Tests (push) Failing after 2m47s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:44:45 +02:00
Marcel
2bb08b6877 fix(dashboard): i18n, a11y, security, and type-safety fixes from PR review
- Use @RequiredArgsConstructor in AuditLogQueryService; remove unused import
- Add 401/403 tests for /activity endpoint
- Add getPulseStats and findContributorsPerDocument integration tests
- Use m.pulse_headline/pulse_you in FamilyPulse; composite avatar keys
- Replace hover:text-accent with hover:text-ink in ActivityFeed (WCAG AA)
- Localise "Alle →" link with feed_show_all key + aria-label
- Gate DropZone behind {#if data.canWrite}
- Export DashboardResumeDTO, DashboardPulseDTO, ActivityFeedItemDTO from api.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:43:16 +02:00
Marcel
148710f2ed docs(spec): add /documents page design spec with mobile breakpoints
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m35s
CI / OCR Service Tests (push) Successful in 27s
CI / Backend Unit Tests (push) Failing after 1m26s
CI / OCR Service Tests (pull_request) Successful in 31s
CI / Backend Unit Tests (pull_request) Failing after 1m28s
CI / Unit & Component Tests (pull_request) Failing after 2m33s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:39:56 +02:00
Marcel
18e321b1e6 feat(dashboard): show block count instead of page numbers in resume strip
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:32:13 +02:00
Marcel
3aec856bac refactor(dashboard): remove page field from DashboardResumeDTO; rename pages to totalBlocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:27:07 +02:00