diff --git a/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte b/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte index 8aceb7c2..d9576c2c 100644 --- a/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte +++ b/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte @@ -134,6 +134,16 @@ async function handleDiscard() { }); if (!ok) return; } + if (mode === 'edit') { + // In edit mode the file map IS the user's bulk selection — discarding + // must clear the upstream store and bounce back to the list, otherwise + // the user is left on /documents/bulk-edit with an empty form and a + // stale count in the bottom bar (issue #225 Bulk-Edit Panel table). + bulkSelectionStore.clear(); + discardAll(); + await goto('/documents'); + return; + } discardAll(); } diff --git a/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte.spec.ts b/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte.spec.ts index ea7306e2..5fb57cac 100644 --- a/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte.spec.ts +++ b/frontend/src/lib/components/document/BulkDocumentEditLayout.svelte.spec.ts @@ -315,6 +315,30 @@ describe('BulkDocumentEditLayout', () => { // ─── mode="edit" ───────────────────────────────────────────────────────────── +describe('BulkDocumentEditLayout — mode="edit" discard', () => { + it('discard in edit mode clears the selection store and navigates back to /documents', async () => { + const { bulkSelectionStore } = await import('$lib/stores/bulkSelection.svelte'); + bulkSelectionStore.setAll(['doc-1']); + + const { container } = render(BulkDocumentEditLayout, { + mode: 'edit', + initialEditEntries: [ + { id: 'doc-1', title: 'Brief 1', pdfUrl: '/api/documents/doc-1/file' }, + { id: 'doc-2', title: 'Brief 2', pdfUrl: '/api/documents/doc-2/file' } + ] + }); + + const discardBtn = container.querySelector( + 'button[data-testid="discard-all-btn"]' + ) as HTMLButtonElement; + expect(discardBtn).not.toBeNull(); + discardBtn.click(); + + await vi.waitFor(() => expect(goto).toHaveBeenCalledWith('/documents'), { timeout: 1000 }); + expect(bulkSelectionStore.size).toBe(0); + }); +}); + describe('BulkDocumentEditLayout — mode="edit"', () => { const editEntry = (i: number) => ({ id: `doc-${i}`, diff --git a/frontend/src/lib/components/document/DescriptionSection.svelte b/frontend/src/lib/components/document/DescriptionSection.svelte index dcde5a7a..3714076f 100644 --- a/frontend/src/lib/components/document/DescriptionSection.svelte +++ b/frontend/src/lib/components/document/DescriptionSection.svelte @@ -11,7 +11,6 @@ let { archiveBox = $bindable(''), archiveFolder = $bindable(''), initialTitle = '', - initialDocumentLocation = '', initialSummary = '', titleRequired = false, suggestedTitle = '', @@ -24,7 +23,6 @@ let { archiveBox?: string; archiveFolder?: string; initialTitle?: string; - initialDocumentLocation?: string; initialSummary?: string; titleRequired?: boolean; suggestedTitle?: string; @@ -32,14 +30,11 @@ let { editMode?: boolean; } = $props(); +// currentTitle seeds from initialTitle once at mount; subsequent edits flow +// through the oninput handler that flips titleDirty. let titleDirty = $state(false); currentTitle = untrack(() => initialTitle); const titleValue = $derived(titleDirty ? currentTitle : suggestedTitle || currentTitle); - -// Initialize controlled location field once from the legacy initial-* props so -// callers that haven't switched to the bindable form keep their existing -// pre-fill behaviour. -documentLocation = untrack(() => documentLocation || initialDocumentLocation);
diff --git a/frontend/src/lib/components/document/WhoWhenSection.svelte b/frontend/src/lib/components/document/WhoWhenSection.svelte index 6bfb2bb7..ed7e25d7 100644 --- a/frontend/src/lib/components/document/WhoWhenSection.svelte +++ b/frontend/src/lib/components/document/WhoWhenSection.svelte @@ -13,8 +13,6 @@ let { senderId = $bindable(''), selectedReceivers = $bindable([]), dateIso = $bindable(''), - initialDateIso = '', - initialLocation = '', initialSenderName = '', suggestedDateIso = '', suggestedSenderName = '', @@ -24,8 +22,6 @@ let { senderId?: string; selectedReceivers?: Person[]; dateIso?: string; - initialDateIso?: string; - initialLocation?: string; initialSenderName?: string; suggestedDateIso?: string; suggestedSenderName?: string; @@ -33,8 +29,9 @@ let { editMode?: boolean; } = $props(); -let dateDisplay = $state(untrack(() => isoToGerman(initialDateIso))); -dateIso = untrack(() => initialDateIso); +// Seed dateDisplay from the bindable's current value once at mount; subsequent +// edits flow through handleDateInput which writes back to dateIso. +let dateDisplay = $state(untrack(() => isoToGerman(dateIso))); let dateDirty = $state(false); const dateInvalid = $derived(dateDirty && dateDisplay.length > 0 && dateIso === ''); @@ -122,7 +119,6 @@ $effect(() => { id="location" type="text" name="location" - value={initialLocation} placeholder={m.form_placeholder_location()} class="block w-full rounded border border-line px-2 py-3 text-sm shadow-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring" />