feat(bulk-edit): add BulkSelectionBar and Alle-X-editieren fast path
- BulkSelectionBar component: sticky bottom bar shown only when canWrite and selection is non-empty. Buttons meet WCAG 44px touch targets and iOS safe-area inset is honoured. - Bar mounted on /documents and /enrich. - Alle X editieren button on /documents replaces the selection with every UUID matching the active filter (via /api/documents/ids) and jumps to /documents/bulk-edit. Refs #225 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@ import { SvelteURLSearchParams } from 'svelte/reactivity';
|
||||
import SearchFilterBar from '../SearchFilterBar.svelte';
|
||||
import DocumentList from '../DocumentList.svelte';
|
||||
import Pagination from '$lib/components/Pagination.svelte';
|
||||
import BulkSelectionBar from '$lib/components/document/BulkSelectionBar.svelte';
|
||||
import { bulkSelectionStore } from '$lib/stores/bulkSelection.svelte';
|
||||
import * as m from '$lib/paraglide/messages.js';
|
||||
|
||||
let { data } = $props();
|
||||
@@ -138,6 +140,45 @@ $effect(() => {
|
||||
}
|
||||
});
|
||||
|
||||
let editingAll = $state(false);
|
||||
|
||||
/**
|
||||
* Fast path: replace the current selection with every document matching the
|
||||
* active filter (across all pages) and jump to the bulk-edit screen. The
|
||||
* /api/documents/ids endpoint is uncapped — chunking happens at PATCH time
|
||||
* inside the bulk-edit page's save handler.
|
||||
*/
|
||||
async function editAllMatching() {
|
||||
if (editingAll) return;
|
||||
editingAll = true;
|
||||
try {
|
||||
const params = buildSearchParams({
|
||||
q: data.q || '',
|
||||
from: data.from || '',
|
||||
to: data.to || '',
|
||||
senderId: data.senderId || '',
|
||||
receiverId: data.receiverId || '',
|
||||
tags: data.tags || [],
|
||||
sort: '',
|
||||
dir: '',
|
||||
tagQ: data.tagQ || '',
|
||||
tagOp: (data.tagOp as 'AND' | 'OR') || 'AND'
|
||||
});
|
||||
params.delete('sort');
|
||||
params.delete('dir');
|
||||
const res = await fetch(`/api/documents/ids?${params.toString()}`);
|
||||
if (!res.ok) {
|
||||
editingAll = false;
|
||||
return;
|
||||
}
|
||||
const ids: string[] = await res.json();
|
||||
bulkSelectionStore.setAll(ids);
|
||||
await goto('/documents/bulk-edit');
|
||||
} finally {
|
||||
editingAll = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep local filter state in sync with server data after navigation completes.
|
||||
// Guard q: skip overwrite while the user is actively typing.
|
||||
$effect(() => {
|
||||
@@ -181,6 +222,20 @@ $effect(() => {
|
||||
onblur={() => (qFocused = false)}
|
||||
/>
|
||||
|
||||
{#if data.canWrite && data.totalElements > 0}
|
||||
<div class="mb-2 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
onclick={editAllMatching}
|
||||
disabled={editingAll}
|
||||
class="inline-flex items-center gap-1 text-sm font-medium text-ink-2 transition-colors hover:text-ink disabled:opacity-50"
|
||||
data-testid="bulk-edit-all-x"
|
||||
>
|
||||
{m.bulk_edit_all_x({ count: data.totalElements })}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<DocumentList
|
||||
items={data.items}
|
||||
total={data.totalElements}
|
||||
@@ -192,3 +247,5 @@ $effect(() => {
|
||||
|
||||
<Pagination page={data.pageNumber} totalPages={data.totalPages} makeHref={buildPageHref} />
|
||||
</main>
|
||||
|
||||
<BulkSelectionBar canWrite={data.canWrite} />
|
||||
|
||||
Reference in New Issue
Block a user