fix(frontend): confirm before dismissing the data table wizard mid-setup

Closing was guarded while a run was in flight and unguarded before one, which is backwards:
a run leaves a row to resume from, whereas a backdrop click on the review step threw away
the project, the pasted password and the folder with nothing to recover them from.

Backdrop, Escape and the close button now go through one path that asks first. It only asks
when there is something to lose -- no provider chosen yet, or a run that already produced a
result, closes immediately -- so the dialog does not become something to click through.
Continue in the background still leaves in one click; that exit was always the deliberate
one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-08-11 11:41:46 +02:00
co-authored by Claude Opus 5
parent 5da093bc31
commit 9e7d0a541b
@@ -312,6 +312,34 @@
onDone()
}
/**
* Whether closing would throw away work. Nothing is written before Finish, so the loss is
* only what was typed -- but that includes a pasted database password and a project about
* to be created, and a backdrop click is easy to do by accident. Once a run has produced a
* result there is nothing left to lose: it either finished, or left a row to resume from.
*/
function hasUnfinishedIntent(): boolean {
return wiz.provider !== undefined && !run.running && !run.result
}
/** Backdrop, Escape and the close button all arrive here. */
async function requestClose() {
if (preventClose) return
if (!hasUnfinishedIntent()) {
close()
return
}
// Held while the dialog is up so a second dismissal cannot stack another one.
preventClose = true
const confirmed = await confirmationModal.ask({
title: 'Leave without adding a database?',
children: 'Nothing has been created yet, and what you have filled in here will be lost.',
confirmationText: 'Discard'
})
preventClose = false
if (confirmed) close()
}
function close() {
opened = false
if (rowCreated && !run.result?.ok) {
@@ -389,8 +417,7 @@
bind:isOpen={
() => opened,
(v) => {
if (!v && preventClose) return
if (!v) close()
if (!v) requestClose()
else opened = v
}
}