feat(bulk-upload): guard discard-all with confirm dialog

Uses getConfirmService() (optional — null fallback when context is absent so
unit tests that don't exercise the discard path need no CONFIRM_KEY context)
and the new bulk_discard_confirm i18n key.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-25 11:26:05 +02:00
parent e5fe2fc5c6
commit 46dae8a826
4 changed files with 27 additions and 2 deletions

View File

@@ -858,6 +858,7 @@
"bulk_save_cta_one": "Speichern →", "bulk_save_cta_one": "Speichern →",
"bulk_save_cta": "{count} speichern →", "bulk_save_cta": "{count} speichern →",
"bulk_discard_all": "Alle verwerfen", "bulk_discard_all": "Alle verwerfen",
"bulk_discard_confirm": "Alle Dateien und eingegebenen Daten verwerfen? Diese Aktion kann nicht rückgängig gemacht werden.",
"bulk_add_more": "Weitere hinzufügen", "bulk_add_more": "Weitere hinzufügen",
"bulk_scope_per_file_label": "Nur diese Datei", "bulk_scope_per_file_label": "Nur diese Datei",
"bulk_scope_shared_label": "Gilt für alle {count}", "bulk_scope_shared_label": "Gilt für alle {count}",

View File

@@ -858,6 +858,7 @@
"bulk_save_cta_one": "Save →", "bulk_save_cta_one": "Save →",
"bulk_save_cta": "Save {count} →", "bulk_save_cta": "Save {count} →",
"bulk_discard_all": "Discard all", "bulk_discard_all": "Discard all",
"bulk_discard_confirm": "Discard all files and entered data? This action cannot be undone.",
"bulk_add_more": "Add more", "bulk_add_more": "Add more",
"bulk_scope_per_file_label": "This file only", "bulk_scope_per_file_label": "This file only",
"bulk_scope_shared_label": "Applies to all {count}", "bulk_scope_shared_label": "Applies to all {count}",

View File

@@ -858,6 +858,7 @@
"bulk_save_cta_one": "Guardar →", "bulk_save_cta_one": "Guardar →",
"bulk_save_cta": "Guardar {count} →", "bulk_save_cta": "Guardar {count} →",
"bulk_discard_all": "Descartar todo", "bulk_discard_all": "Descartar todo",
"bulk_discard_confirm": "¿Descartar todos los archivos y datos introducidos? Esta acción no se puede deshacer.",
"bulk_add_more": "Añadir más", "bulk_add_more": "Añadir más",
"bulk_scope_per_file_label": "Solo este archivo", "bulk_scope_per_file_label": "Solo este archivo",
"bulk_scope_shared_label": "Para todos los {count}", "bulk_scope_shared_label": "Para todos los {count}",

View File

@@ -3,6 +3,8 @@ import { SvelteMap } from 'svelte/reactivity';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { onDestroy, untrack } from 'svelte'; import { onDestroy, untrack } from 'svelte';
import { m } from '$lib/paraglide/messages.js'; import { m } from '$lib/paraglide/messages.js';
import { getConfirmService } from '$lib/services/confirm.svelte.js';
import type { ConfirmService } from '$lib/services/confirm.svelte.js';
import BulkDropZone from './BulkDropZone.svelte'; import BulkDropZone from './BulkDropZone.svelte';
import FileSwitcherStrip from './FileSwitcherStrip.svelte'; import FileSwitcherStrip from './FileSwitcherStrip.svelte';
import type { FileEntry } from './FileSwitcherStrip.svelte'; import type { FileEntry } from './FileSwitcherStrip.svelte';
@@ -17,6 +19,14 @@ import type { components } from '$lib/generated/api';
type Person = components['schemas']['Person']; type Person = components['schemas']['Person'];
// Optional — not available in unit tests that don't provide CONFIRM_KEY context.
let _confirmService: ConfirmService | null;
try {
_confirmService = getConfirmService();
} catch {
_confirmService = null;
}
let { let {
initialSenderId = '', initialSenderId = '',
initialSenderName = '', initialSenderName = '',
@@ -77,6 +87,18 @@ function discardAll() {
chunkProgress = undefined; chunkProgress = undefined;
} }
async function handleDiscard() {
if (_confirmService) {
const ok = await _confirmService.confirm({
title: m.bulk_discard_all(),
body: m.bulk_discard_confirm(),
destructive: true
});
if (!ok) return;
}
discardAll();
}
onDestroy(() => { onDestroy(() => {
for (const entry of files.values()) { for (const entry of files.values()) {
if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl); if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl);
@@ -177,7 +199,7 @@ async function save() {
<button <button
type="button" type="button"
data-testid="discard-all-btn" data-testid="discard-all-btn"
onclick={discardAll} onclick={handleDiscard}
class="text-xs font-medium text-red-600/70 hover:text-red-700" class="text-xs font-medium text-red-600/70 hover:text-red-700"
> >
{m.bulk_discard_all()} {m.bulk_discard_all()}
@@ -289,7 +311,7 @@ async function save() {
fileCount={files.size} fileCount={files.size}
chunkProgress={chunkProgress} chunkProgress={chunkProgress}
onSave={save} onSave={save}
onDiscard={discardAll} onDiscard={handleDiscard}
disabled={saving} disabled={saving}
/> />
</div> </div>