fix(login): keep submit button disabled/loading during password set r… (#12429)

<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# Which Problems Are Solved

- On Login V2 after a user sets their password during the invite or
reset-password flows and clicks continue,
there is a few seconds delay before the redirect occurs because no
loading spinner is visible on the button
during this delay users assume nothing happened and click continue
again.
- Clicking the button again starts a duplicate form submission. Since
the first request was already fired the second request triggers an error
right before the redirect from the first request completion creating a
bad user experience

# How the Problems Are Solved

- Kept the `loading` state active `true` throughout the lifeline of the
request which includes the 2-second database consistency delay and the
subsequent `sendPassword` verification request.
- Added `setLoading(false)` calls only in early-return error handling
blocks and at the end of the
redirection flow ensuring the button remains disabled and shows the
spinner until the redirect happens.

# Additional Changes

- None

# Additional Context

- Solves  #12416

Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
Ayush
2026-07-14 10:05:09 +02:00
committed by GitHub
co-authored by Max Peintner
parent b429ec240a
commit aa76c3be27
+13 -16
View File
@@ -101,21 +101,20 @@ export function SetPasswordForm({
payload = { ...payload, code: values.code }; payload = { ...payload, code: values.code };
} }
const changeResponse = await changePassword(payload) const changeResponse = await changePassword(payload).catch(() => {
.catch(() => { setError(t("set.errors.couldNotSetPassword"));
setError(t("set.errors.couldNotSetPassword")); setLoading(false);
return; return;
}) });
.finally(() => {
setLoading(false);
});
if (changeResponse && "error" in changeResponse) { if (changeResponse && "error" in changeResponse) {
setLoading(false);
setError(changeResponse.error); setError(changeResponse.error);
return; return;
} }
if (!changeResponse) { if (!changeResponse) {
setLoading(false);
setError(t("set.errors.couldNotSetPassword")); setError(t("set.errors.couldNotSetPassword"));
return; return;
} }
@@ -138,15 +137,13 @@ export function SetPasswordForm({
password: { password: values.password }, password: { password: values.password },
}), }),
requestId, requestId,
}) }).catch(() => {
.catch(() => { setError(t("set.errors.couldNotVerifyPassword"));
setError(t("set.errors.couldNotVerifyPassword")); setLoading(false);
return; return;
}) });
.finally(() => {
setLoading(false);
});
setLoading(false);
handleServerActionResponse(passwordResponse as any, router, setSamlData, setError); handleServerActionResponse(passwordResponse as any, router, setSamlData, setError);
return; return;