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.)
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.
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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 whiledel.isPending. A double-click fires twoDELETEs (the second 404s and flips the dialog into the error state even though the delete succeeded). Comparepublish-control.tsx, which already doesdisabled={setVisibility.isPending}on every action.Fix:
disabled={del.isPending}on the action (and arguably the cancel button),{del.isPending ? t("form.saving") : t("actions.delete")}or a dedicatedactions.deleting("Deleting…"/"Tar bort…").Check whether the shared
components/delete-confirm-dialog.tsx(used by field/vocab/term/authority rows) has the same gap — itsonConfirmis also a mutation.Login: no "Signing in…" state
web/src/auth/login-page.tsx:79-81— the button correctly disables onlogin.isPending, but the label stays "Sign in", so on a slow network there is zero feedback that anything is happening. i18n already hasauth.signingOut: "Signing out…"but nosigningIn.Fix: add
auth.signingIn("Signing in…"/"Loggar in…") toen.json+sv.jsonand render{login.isPending ? t("auth.signingIn") : t("auth.signIn")}.(Keep en/sv key parity — the #60 test will catch a one-sided addition.)
Fixed in
27205c6(merged as78f5afa).DeleteObjectDialog: cancel + confirm nowdisabled={del.isPending}, confirm label swaps to the newactions.deletingwhile in flight — double-click double-DELETE is no longer possible.DeleteConfirmDialoghad the same gap (as suspected); it now tracks a localpendingstate around the awaitedonConfirm()(cleared infinallyso 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.auth.signingIn("Signing in…" / "Loggar in…") whilelogin.isPending.actions.deleting+auth.signingInadded to bothen.jsonandsv.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.