From 4998fe4c94c0c75e2b6770a641d0cf09dfcc2596 Mon Sep 17 00:00:00 2001 From: gorkem-bwl Date: Mon, 20 Apr 2026 13:50:36 -0400 Subject: [PATCH] fix: hide register tip on login and redirect to setup when no super admin (#3556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes - Removed the "Register here" link from the login page — it misled users into thinking public registration was open. - Added redirect from /login to /register when no super admin exists (initial setup). - Added an info alert on the register page clarifying the page is only for initial setup; additional users are invited from team settings. Hidden for the invite-token flow. - Extracted shared useSuperAdminRedirect hook to dedupe identical fetch/redirect logic across Login and Register. ## Benefits - Clearer UX: login page no longer suggests self-registration is available post-setup. - Correct routing on fresh installs: users land on the registration page instead of a login page they can't use. --- client/src/Hooks/useSuperAdminRedirect.ts | 24 +++++++++++++++++++++++ client/src/Pages/Auth/Login/index.tsx | 14 +++++++------ client/src/Pages/Auth/Register/index.tsx | 21 ++++++++++---------- client/src/locales/en.json | 1 + 4 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 client/src/Hooks/useSuperAdminRedirect.ts diff --git a/client/src/Hooks/useSuperAdminRedirect.ts b/client/src/Hooks/useSuperAdminRedirect.ts new file mode 100644 index 000000000..341208ac3 --- /dev/null +++ b/client/src/Hooks/useSuperAdminRedirect.ts @@ -0,0 +1,24 @@ +import { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { useGet } from "@/Hooks/UseApi"; + +type Options = { + redirectTo: string; + redirectWhen: boolean; + skip?: boolean; +}; + +export const useSuperAdminRedirect = ({ redirectTo, redirectWhen, skip }: Options) => { + const navigate = useNavigate(); + const { data: superAdminExists, isLoading } = useGet( + skip ? null : "/auth/users/superadmin" + ); + + useEffect(() => { + if (!skip && superAdminExists === redirectWhen) { + navigate(redirectTo, { replace: true }); + } + }, [superAdminExists, redirectWhen, redirectTo, skip, navigate]); + + return { superAdminExists, isLoading }; +}; diff --git a/client/src/Pages/Auth/Login/index.tsx b/client/src/Pages/Auth/Login/index.tsx index ec95a0ce3..fd0eff130 100644 --- a/client/src/Pages/Auth/Login/index.tsx +++ b/client/src/Pages/Auth/Login/index.tsx @@ -10,6 +10,7 @@ import { useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import { setAuthState } from "@/Features/Auth/authSlice"; import { usePost } from "@/Hooks/UseApi"; +import { useSuperAdminRedirect } from "@/Hooks/useSuperAdminRedirect"; const LoginPage = () => { const { t } = useTranslation(); @@ -17,6 +18,11 @@ const LoginPage = () => { const navigate = useNavigate(); const { post, loading } = usePost(); + const { isLoading: isCheckingAdmin } = useSuperAdminRedirect({ + redirectTo: "/register", + redirectWhen: false, + }); + const { schema, defaults } = useLoginForm(); const { control, handleSubmit } = useForm({ @@ -24,6 +30,8 @@ const LoginPage = () => { defaultValues: defaults, }); + if (isCheckingAdmin) return null; + const onSubmit = async (data: LoginFormData) => { if (loading) return; @@ -82,12 +90,6 @@ const LoginPage = () => { linkText={t("pages.auth.login.links.forgotPassword.linkText")} href="/forgot-password" /> - ); }; diff --git a/client/src/Pages/Auth/Register/index.tsx b/client/src/Pages/Auth/Register/index.tsx index a2a439023..787f7da0e 100644 --- a/client/src/Pages/Auth/Register/index.tsx +++ b/client/src/Pages/Auth/Register/index.tsx @@ -1,11 +1,13 @@ import { BaseAuthPage } from "@/Components/design-elements"; import { Button, TextField } from "@/Components/inputs"; +import { Alert } from "@mui/material"; import { useForm, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod/dist/zod.js"; import { useRegisterForm } from "@/Hooks/useRegisterForm"; import type { RegisterFormData } from "@/Validation/register"; import { useTranslation } from "react-i18next"; -import { usePost, useGet } from "@/Hooks/UseApi"; +import { usePost } from "@/Hooks/UseApi"; +import { useSuperAdminRedirect } from "@/Hooks/useSuperAdminRedirect"; import { setAuthState } from "@/Features/Auth/authSlice"; import { useDispatch } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; @@ -32,21 +34,17 @@ const RegisterPage = () => { const { post: verifyToken } = usePost<{ token: string }, InviteVerifyResponse>(); const hasVerified = useRef(false); - const { data: superAdminExists, isLoading: isCheckingAdmin } = useGet( - token ? null : "/auth/users/superadmin" - ); + const { isLoading: isCheckingAdmin } = useSuperAdminRedirect({ + redirectTo: "/login", + redirectWhen: true, + skip: !!token, + }); const { control, handleSubmit, setError, reset } = useForm({ resolver: zodResolver(schema), defaultValues: defaults, }); - useEffect(() => { - if (superAdminExists === true) { - navigate("/login", { replace: true }); - } - }, [superAdminExists, navigate]); - useEffect(() => { if (!token || hasVerified.current) return; hasVerified.current = true; @@ -92,6 +90,9 @@ const RegisterPage = () => { title={t("pages.auth.register.title")} subtitle={t("pages.auth.register.subtitle")} > + {!token && ( + {t("pages.auth.register.setupNotice")} + )}