Frontend UX: delete-object dialog allows double-submit; login button gives no in-flight feedback #70

Closed
opened 2026-06-09 21:26:49 +00:00 by logaritmisk · 1 comment
Owner

Two pending-state gaps left over after #46 (object-form double-submit) and #48 (auth feedback):

Delete dialog: confirm button stays enabled during the request

web/src/objects/delete-object-dialog.tsx:58<AlertDialogAction onClick={onConfirm}> is never disabled while del.isPending. A double-click fires two DELETEs (the second 404s and flips the dialog into the error state even though the delete succeeded). Compare publish-control.tsx, which already does disabled={setVisibility.isPending} on every action.

Fix:

  • disabled={del.isPending} on the action (and arguably the cancel button),
  • optional: pending label {del.isPending ? t("form.saving") : t("actions.delete")} or a dedicated actions.deleting ("Deleting…" / "Tar bort…").

Check whether the shared components/delete-confirm-dialog.tsx (used by field/vocab/term/authority rows) has the same gap — its onConfirm is also a mutation.

Login: no "Signing in…" state

web/src/auth/login-page.tsx:79-81 — the button correctly disables on login.isPending, but the label stays "Sign in", so on a slow network there is zero feedback that anything is happening. i18n already has auth.signingOut: "Signing out…" but no signingIn.

Fix: add auth.signingIn ("Signing in…" / "Loggar in…") to en.json + sv.json and render {login.isPending ? t("auth.signingIn") : t("auth.signIn")}.

(Keep en/sv key parity — the #60 test will catch a one-sided addition.)

Two pending-state gaps left over after #46 (object-form double-submit) and #48 (auth feedback): ## Delete dialog: confirm button stays enabled during the request `web/src/objects/delete-object-dialog.tsx:58` — `<AlertDialogAction onClick={onConfirm}>` is never disabled while `del.isPending`. A double-click fires two `DELETE`s (the second 404s and flips the dialog into the error state even though the delete succeeded). Compare `publish-control.tsx`, which already does `disabled={setVisibility.isPending}` on every action. Fix: - `disabled={del.isPending}` on the action (and arguably the cancel button), - optional: pending label `{del.isPending ? t("form.saving") : t("actions.delete")}` or a dedicated `actions.deleting` (`"Deleting…"` / `"Tar bort…"`). Check whether the shared `components/delete-confirm-dialog.tsx` (used by field/vocab/term/authority rows) has the same gap — its `onConfirm` is also a mutation. ## Login: no "Signing in…" state `web/src/auth/login-page.tsx:79-81` — the button correctly disables on `login.isPending`, but the label stays "Sign in", so on a slow network there is zero feedback that anything is happening. i18n already has `auth.signingOut: "Signing out…"` but **no `signingIn`**. Fix: add `auth.signingIn` (`"Signing in…"` / `"Loggar in…"`) to `en.json` + `sv.json` and render `{login.isPending ? t("auth.signingIn") : t("auth.signIn")}`. (Keep en/sv key parity — the #60 test will catch a one-sided addition.)
Author
Owner

Fixed in 27205c6 (merged as 78f5afa).

  • DeleteObjectDialog: cancel + confirm now disabled={del.isPending}, confirm label swaps to the new actions.deleting while in flight — double-click double-DELETE is no longer possible.
  • The shared DeleteConfirmDialog had the same gap (as suspected); it now tracks a local pending state around the awaited onConfirm() (cleared in finally so the in-use error path re-enables the buttons) with the same disabled + label treatment. This covers the field/vocab/term/authority delete rows too.
  • Login button shows auth.signingIn ("Signing in…" / "Loggar in…") while login.isPending.
  • i18n: actions.deleting + auth.signingIn added to both en.json and sv.json (parity test green).

Each fix has a test that gates the request (MSW handler awaiting a promise, or an unresolved onConfirm), asserts the disabled + "…"-labelled state mid-flight, then releases and asserts the happy path completes.

Fixed in 27205c6 (merged as 78f5afa). - `DeleteObjectDialog`: cancel + confirm now `disabled={del.isPending}`, confirm label swaps to the new `actions.deleting` while in flight — double-click double-DELETE is no longer possible. - The shared `DeleteConfirmDialog` had the same gap (as suspected); it now tracks a local `pending` state around the awaited `onConfirm()` (cleared in `finally` so the in-use error path re-enables the buttons) with the same disabled + label treatment. This covers the field/vocab/term/authority delete rows too. - Login button shows `auth.signingIn` ("Signing in…" / "Loggar in…") while `login.isPending`. - i18n: `actions.deleting` + `auth.signingIn` added to both `en.json` and `sv.json` (parity test green). Each fix has a test that gates the request (MSW handler awaiting a promise, or an unresolved `onConfirm`), asserts the disabled + "…"-labelled state mid-flight, then releases and asserts the happy path completes.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logaritmisk/biggus-dickus#70