diff --git a/web/e2e/smoke.spec.js b/web/e2e/smoke.spec.js index 97cac17..6708c22 100644 --- a/web/e2e/smoke.spec.js +++ b/web/e2e/smoke.spec.js @@ -2211,3 +2211,50 @@ test('et tag finder sit projekt uanset store og små bogstaver', async ({ page } expect(trouble).toEqual([]); }); + +test('en opgave viser de noter, der nævner den, og linket åbner noten', async ({ page }) => { + const trouble = watchForTrouble(page); + await page.goto('/'); + + const box = page.getByLabel('Ny opgave'); + await box.fill('ring til leverandøren i dag'); + await box.press('Enter'); + await expect(page.getByText('ring til leverandøren', { exact: true })).toBeVisible(); + + // Asked for rather than read off the address bar: opening a task shows a drawer + // without navigating, so the URL is still the list's. + const taskId = await page.evaluate(async () => { + const r = await fetch('/api/v1/tasks?limit=100', { + credentials: 'include', + headers: { 'Sec-Fetch-Site': 'same-origin' } + }); + const j = await r.json(); + return (j.tasks ?? []).find((t) => t.content === 'ring til leverandøren')?.id; + }); + expect(taskId, 'opgaven blev ikke oprettet').toBeTruthy(); + + const sidebar = page.getByRole('navigation', { name: 'Hovedmenu' }); + await sidebar.getByRole('link', { name: 'Noter', exact: true }).click(); + await page.getByRole('button', { name: 'Ny note' }).click(); + const ed = page.getByRole('textbox', { name: 'Notens tekst' }); + await ed.click(); + await page.keyboard.type('Referat fra tirsdag'); + await page.keyboard.press('Enter'); + await page.keyboard.type('Aftalt at han ringer, se /opgave/' + taskId); + await ed.blur(); + + // Back on the task: the other direction of the same link. Without it the + // connection only works if you happen to be reading the note, which is the half + // nobody needs help with. + await page.goto('/opgave/' + taskId); + const linked = page.locator('.linked-notes').getByRole('link', { name: /Referat fra tirsdag/ }); + await expect(linked).toBeVisible(); + + // And it opens the note rather than dropping you on a list to find it again. + await linked.click(); + await expect(page.getByRole('textbox', { name: 'Notens tekst' })).toContainText( + 'Aftalt at han ringer' + ); + + expect(trouble).toEqual([]); +}); diff --git a/web/src/lib/components/TaskDetail.svelte b/web/src/lib/components/TaskDetail.svelte index a41f347..4d26252 100644 --- a/web/src/lib/components/TaskDetail.svelte +++ b/web/src/lib/components/TaskDetail.svelte @@ -43,6 +43,7 @@ let subtasks = $state([]); let comments = $state([]); let attachments = $state([]); + let notes = $state([]); let reminders = $state([]); let newSubtask = $state(''); @@ -80,6 +81,7 @@ subtasks = []; comments = []; attachments = []; + notes = []; reminders = []; const id = task.id; @@ -96,6 +98,11 @@ api.listReminders(id) .then((r) => id === seededId && (reminders = r.reminders)) .catch(() => {}); + // Quietly, because a task with no notes is the ordinary case and a failure + // here must not put an error in front of somebody reading a task. + api.notesLinking('task', id) + .then((r) => id === seededId && (notes = r.notes ?? [])) + .catch(() => {}); }); /** @@ -540,6 +547,22 @@ + + {#if notes.length} +
+

{t('notes.title')}

+ {#each notes as note (note.id)} + + {note.title || t('notes.untitled')} + + {/each} +
+ {/if} +

{t('detail.comments')}

@@ -582,6 +605,24 @@