From 5f2cf5f2c296ec0979ad041fbbe28ff3f61eec5a Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 14 Jun 2026 09:00:45 +0200 Subject: [PATCH] fix(timeline): gate event delete behind the confirm dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The delete
combined onsubmit={confirmDelete} with use:enhance. SvelteKit's enhance ignores event.defaultPrevented, so the DELETE fired on the bare click — before the dialog was answered — and the post-DELETE redirect ran regardless of the user's choice. Reading getConfirmService() lazily inside the handler also threw (Svelte context is init-only), so the dialog never appeared even with mounted. Capture confirm at init and run the gate inside the enhance submit phase, calling cancel() on "no". Clear dirty in the result callback so the beforeNavigate guard no longer prompts "unsaved changes" on the post-delete redirect. Co-Authored-By: Claude Opus 4.8 --- frontend/src/lib/timeline/EventForm.svelte | 45 ++++++++---- .../src/lib/timeline/EventForm.svelte.spec.ts | 69 +++++++++++++++---- 2 files changed, 85 insertions(+), 29 deletions(-) diff --git a/frontend/src/lib/timeline/EventForm.svelte b/frontend/src/lib/timeline/EventForm.svelte index 3cce8d3e..74231eb1 100644 --- a/frontend/src/lib/timeline/EventForm.svelte +++ b/frontend/src/lib/timeline/EventForm.svelte @@ -82,6 +82,10 @@ let selectedDocuments = $state( const isEdit = $derived(event !== undefined); +// Captured at init — Svelte context is init-only, so reading it lazily inside an +// event handler throws even when is mounted. Gates the delete. +const { confirm } = getConfirmService(); + let titleTouched = $state(false); let submitting = $state(false); let dirty = $state(false); @@ -109,18 +113,6 @@ beforeNavigate(({ cancel }) => { function markDirty() { dirty = true; } - -async function confirmDelete(e: SubmitEvent) { - e.preventDefault(); - const { confirm } = getConfirmService(); - const ok = await confirm({ - title: m.event_editor_delete_confirm_title(), - body: m.event_editor_delete_confirm_body(), - destructive: true, - confirmLabel: m.event_editor_delete() - }); - if (ok) (e.target as HTMLFormElement).requestSubmit(); -}
@@ -309,9 +301,32 @@ async function confirmDelete(e: SubmitEvent) { {#if isEdit} - + The confirm gate runs inside the enhance submit phase: enhance ignores an + onsubmit preventDefault(), so awaiting confirm() and calling cancel() is the + only thing that actually stops the destructive POST. --> + { + const ok = await confirm({ + title: m.event_editor_delete_confirm_title(), + body: m.event_editor_delete_confirm_body(), + destructive: true, + confirmLabel: m.event_editor_delete() + }); + if (!ok) { + cancel(); + return; + } + return async ({ update }) => { + // Clear dirtiness so beforeNavigate doesn't prompt "unsaved changes" + // on the post-delete redirect. + dirty = false; + await update(); + }; + }} + class="mt-4" + >