fix(#145): deep-link notifications; show createdAt in recent docs

- Notification widget builds full link with ?commentId= and
  &annotationId= params, matching the bell notification behaviour
- Recent docs widget shows createdAt (upload date) instead of
  documentDate (the date on the original document)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-03-29 10:03:36 +02:00
parent c8da2224f8
commit 7734ce7bae
4 changed files with 31 additions and 17 deletions

View File

@@ -5,6 +5,8 @@ type NotificationDTO = {
id: string; id: string;
type: 'REPLY' | 'MENTION'; type: 'REPLY' | 'MENTION';
documentId?: string; documentId?: string;
referenceId?: string;
annotationId?: string;
read: boolean; read: boolean;
createdAt: string; createdAt: string;
actorName?: string; actorName?: string;
@@ -26,7 +28,9 @@ let { mentions }: Props = $props();
<div class="flex items-center gap-3 border-b border-line py-2 last:border-0"> <div class="flex items-center gap-3 border-b border-line py-2 last:border-0">
{#if mention.documentId} {#if mention.documentId}
<a <a
href="/documents/{mention.documentId}" href={mention.annotationId
? `/documents/${mention.documentId}?commentId=${mention.referenceId}&annotationId=${mention.annotationId}`
: `/documents/${mention.documentId}?commentId=${mention.referenceId}`}
class="font-serif text-sm text-ink hover:text-ink-2" class="font-serif text-sm text-ink hover:text-ink-2"
> >
{mention.actorName ?? ''} {mention.actorName ?? ''}

View File

@@ -10,6 +10,8 @@ type NotificationDTO = {
id: string; id: string;
type: 'REPLY' | 'MENTION'; type: 'REPLY' | 'MENTION';
documentId?: string; documentId?: string;
referenceId?: string;
annotationId?: string;
read: boolean; read: boolean;
createdAt: string; createdAt: string;
actorName?: string; actorName?: string;
@@ -20,6 +22,7 @@ function makeMention(overrides: Partial<NotificationDTO> = {}): NotificationDTO
id: 'notif-1', id: 'notif-1',
type: 'MENTION', type: 'MENTION',
documentId: 'doc-abc', documentId: 'doc-abc',
referenceId: 'comment-xyz',
read: false, read: false,
createdAt: '2026-01-15T10:00:00Z', createdAt: '2026-01-15T10:00:00Z',
actorName: 'Anna Schmidt', actorName: 'Anna Schmidt',
@@ -40,15 +43,22 @@ describe('DashboardMentions', () => {
await expect.element(widget).toBeInTheDocument(); await expect.element(widget).toBeInTheDocument();
}); });
it('renders one row per mention with link to document', async () => { it('builds link with commentId param when no annotationId', async () => {
const mentions = [ render(DashboardMentions, {
makeMention({ id: 'n1', documentId: 'doc-1', actorName: 'Anna' }), mentions: [makeMention({ documentId: 'doc-1', referenceId: 'cmt-1' })]
makeMention({ id: 'n2', documentId: 'doc-2', actorName: 'Bob' }) });
]; const link = page.getByRole('link');
render(DashboardMentions, { mentions }); await expect.element(link).toHaveAttribute('href', '/documents/doc-1?commentId=cmt-1');
const links = page.getByRole('link'); });
await expect.element(links.nth(0)).toHaveAttribute('href', '/documents/doc-1');
await expect.element(links.nth(1)).toHaveAttribute('href', '/documents/doc-2'); it('builds link with commentId and annotationId when annotationId is present', async () => {
render(DashboardMentions, {
mentions: [makeMention({ documentId: 'doc-2', referenceId: 'cmt-2', annotationId: 'ann-9' })]
});
const link = page.getByRole('link');
await expect
.element(link)
.toHaveAttribute('href', '/documents/doc-2?commentId=cmt-2&annotationId=ann-9');
}); });
it('shows actor name in each row', async () => { it('shows actor name in each row', async () => {

View File

@@ -5,7 +5,7 @@ import { getLocale } from '$lib/paraglide/runtime.js';
type Document = { type Document = {
id: string; id: string;
title: string; title: string;
documentDate?: string; createdAt?: string;
sender?: { id: string; firstName: string; lastName: string }; sender?: { id: string; firstName: string; lastName: string };
}; };
@@ -20,7 +20,7 @@ function formatDate(dateStr: string): string {
day: 'numeric', day: 'numeric',
month: 'long', month: 'long',
year: 'numeric' year: 'numeric'
}).format(new Date(dateStr + 'T12:00:00')); }).format(new Date(dateStr));
} }
</script> </script>
@@ -37,12 +37,12 @@ function formatDate(dateStr: string): string {
> >
{doc.title} {doc.title}
</a> </a>
{#if doc.documentDate} {#if doc.createdAt}
<span <span
data-testid="doc-date-{doc.id}" data-testid="doc-date-{doc.id}"
class="ml-2 shrink-0 font-sans text-xs text-gray-400" class="ml-2 shrink-0 font-sans text-xs text-gray-400"
> >
{formatDate(doc.documentDate)} {formatDate(doc.createdAt)}
</span> </span>
{/if} {/if}
</div> </div>

View File

@@ -9,12 +9,12 @@ afterEach(cleanup);
type Document = { type Document = {
id: string; id: string;
title: string; title: string;
documentDate?: string; createdAt?: string;
sender?: { id: string; firstName: string; lastName: string }; sender?: { id: string; firstName: string; lastName: string };
}; };
function makeDoc(id: string, title: string, date?: string): Document { function makeDoc(id: string, title: string, createdAt?: string): Document {
return { id, title, documentDate: date }; return { id, title, createdAt };
} }
describe('DashboardRecentDocuments', () => { describe('DashboardRecentDocuments', () => {