diff --git a/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx b/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx index 8d36d1e79..47b8a7f7f 100644 --- a/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx +++ b/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx @@ -4,7 +4,7 @@ import type { PropsWithChildren } from "react"; import { useCallback, useEffect, useRef, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Anchor, Button, Card, Code, Collapse, Divider, PasswordInput, Stack, Text, TextInput } from "@mantine/core"; -import { useDisclosure } from "@mantine/hooks"; +import { useDisclosure, useMounted } from "@mantine/hooks"; import { z } from "zod/v4"; import { signIn } from "@homarr/auth/client"; @@ -32,6 +32,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c const searchParams = useSearchParams(); const isError = searchParams.has("error"); const router = useRouter(); + const isMounted = useMounted(); const [isPending, setIsPending] = useState(false); const form = useZodForm(extendedValidation, { initialValues: { @@ -132,7 +133,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c {providers.includes("credentials") && ( - + {t("action.login.label")} @@ -140,7 +141,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c )} {providers.includes("ldap") && ( - + {t("action.login.labelWith", { provider: "LDAP" })} )} @@ -151,7 +152,7 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c )} {providers.includes("oidc") && ( - )} @@ -161,12 +162,13 @@ export const LoginForm = ({ providers, oidcClientName, isOidcAutoLoginEnabled, c }; interface SubmitButtonProps { + isMounted: boolean; isPending: boolean; form: UseFormReturnType; provider: "credentials" | "ldap"; } -const SubmitButton = ({ isPending, form, provider, children }: PropsWithChildren) => { +const SubmitButton = ({ isMounted, isPending, form, provider, children }: PropsWithChildren) => { const isCurrentProviderActive = form.getValues().provider === provider; return ( @@ -176,7 +178,7 @@ const SubmitButton = ({ isPending, form, provider, children }: PropsWithChildren fullWidth onClick={() => form.setFieldValue("provider", provider)} loading={isPending && isCurrentProviderActive} - disabled={isPending && !isCurrentProviderActive} + disabled={!isMounted || (isPending && !isCurrentProviderActive)} > {children} diff --git a/e2e/assistant-management.spec.ts b/e2e/assistant-management.spec.ts index 025b1c820..df2c00f61 100644 --- a/e2e/assistant-management.spec.ts +++ b/e2e/assistant-management.spec.ts @@ -44,7 +44,7 @@ describe("Assistant management", () => { await page.getByLabel("Username").fill(adminCredentials.username); await page.locator("#password").fill(adminCredentials.password); await page.getByRole("button", { name: "Login" }).click(); - await page.waitForURL(baseUrl, { timeout: 15_000 }); + await expect(page).not.toHaveURL(/\/auth\/login/, { timeout: 30_000 }); await page.goto(`${baseUrl}/manage/assistant`); await expect(page.getByRole("heading", { name: "Assistant" })).toBeVisible();