Adds simulateDragDrop helper and three tests covering the splice/insertAt
index arithmetic in handlePointerUp:
- move-to-end (insertAt path where target > fromIdx)
- move-to-start (insertAt path where target <= fromIdx)
- move-down-by-one (verifies the off-by-one dropTargetIdx - 1 branch)
Fixes @saraholt: "reorder calculation in handlePointerUp is untested"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
text-[9px] is below WCAG practical minimum and unreadable for senior users.
Changed all three occurrences (tablet button count, desktop link label,
flyout link label) to text-[11px].
Fixes @leonievoss: "text-[9px] is below 12px minimum"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds MockEventSource.simulate() helper and two tests covering:
- unread notification via SSE prepends to list and increments unreadCount
- read notification via SSE adds to list but does not increment unreadCount
Fixes @saraholt: "SSE event handling not tested"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NotificationDropdown was importing relativeTime through notifications.ts,
creating an accidental coupling to a module unrelated to timestamp formatting.
Now imports directly from the canonical \$lib/utils/time module.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move the identical isDirty / beforeNavigate / discard pattern out of the
three admin detail pages (groups, tags, users) into a reusable
createUnsavedWarning() hook and a UnsavedWarningBanner presentational
component.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move blob URL lifecycle management into a reusable createFileLoader()
hook that owns revoke-before-create and revoke-on-destroy. Replace
identical inline logic in documents/[id] and enrich/[id] with the hook.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Calling loadFile a second time previously leaked the previous object URL.
Add URL.revokeObjectURL(fileUrl) before creating a new one and in
onDestroy so all URLs are freed. Revoke behavior will be covered by the
useFileLoader hook tests in the next commit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add format?: 'short'|'long' (default 'long') to date.ts formatDate and
remove the duplicate from personFormat.ts. Update DocumentTopBar to
import from date.ts directly. Move the formatDate tests from
personFormat.spec to date.spec.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Unify the initials-extraction logic: the new string-based getInitials()
splits on whitespace, takes the first char of the first and last word
uppercased — matching the pattern that was already inlined in
CommentThread. Update PersonChip, DocumentMetadataDrawer, and
CommentThread to use the shared function.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move relativeTime from notifications.ts (Intl.RelativeTimeFormat) to a
new time.ts that uses the Paraglide comment_time_* message keys — the
same logic that was already in CommentThread's timeAgo(). Remove the
duplicate timeAgo() from CommentThread and re-export relativeTime from
notifications.ts for backwards compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the inline attachClickOutside attachment in NotificationBell with
the shared use:clickOutside action from $lib/actions/clickOutside. The
inline implementation was functionally identical to the existing action.
Guard the onclickoutside handler so it only calls closeDropdown() when
the notification panel is already open, preventing the bell button from
stealing focus from other interactive elements (e.g. the user avatar menu).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The action already checks event.defaultPrevented before dispatching
clickoutside, but that branch had no test. Add the missing case and
add a one-line comment explaining why capture phase is used.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove the old conversations page that was superseded by briefwechsel/.
No navigation link pointed to /conversations; it was unreachable through
the UI. Deletes 5 files, removes 14 orphaned i18n keys from de/en/es
message bundles, and removes E2E tests that navigated to /conversations.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>