diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index 41591854..d801f0e8 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -70,7 +70,7 @@ const Header = () => { title: t("common.settings"), icon: , }; - const authNavLink: NavLinkItem = { + const signInNavLink: NavLinkItem = { id: "header-auth", path: "/auth", title: t("common.sign-in"), @@ -79,7 +79,7 @@ const Header = () => { const navLinks: NavLinkItem[] = user ? [homeNavLink, dailyReviewNavLink, resourcesNavLink, exploreNavLink, archivedNavLink, settingNavLink] - : [exploreNavLink, authNavLink]; + : [exploreNavLink, signInNavLink]; return (
{ +const SignIn = () => { const t = useTranslate(); const globalStore = useGlobalStore(); const userStore = useUserStore(); @@ -57,11 +57,7 @@ const Auth = () => { const handleFormSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (systemStatus?.host) { - handleSignInButtonClick(); - } else { - handleSignUpButtonClick(); - } + handleSignInButtonClick(); }; const handleSignInButtonClick = async () => { @@ -89,31 +85,6 @@ const Auth = () => { actionBtnLoadingState.setFinish(); }; - const handleSignUpButtonClick = async () => { - if (username === "" || password === "") { - return; - } - - if (actionBtnLoadingState.isLoading) { - return; - } - - try { - actionBtnLoadingState.setLoading(); - await api.signup(username, password); - const user = await userStore.doSignIn(); - if (user) { - window.location.href = "/"; - } else { - toast.error(t("message.signup-failed")); - } - } catch (error: any) { - console.error(error); - toast.error(error.response.data.message || error.message || t("message.signup-failed")); - } - actionBtnLoadingState.setFinish(); - }; - const handleSignInWithIdentityProvider = async (identityProvider: IdentityProvider) => { const stateQueryParameter = `auth.signin.${identityProvider.name}-${identityProvider.id}`; if (identityProvider.type === "OAUTH2") { @@ -132,62 +103,62 @@ const Auth = () => {
-
- -

{systemStatus.customizedProfile.name}

+
+ +

{systemStatus.customizedProfile.name}

{!disablePasswordLogin && ( -
-
- - -
-
- {actionBtnLoadingState.isLoading && } - {!systemStatus.host ? ( - - ) : ( - <> - {systemStatus?.allowSignUp && ( - <> - - / - - )} - - - )} -
-
- )} - {!systemStatus.host && ( -

- {t("auth.host-tip")} -

+
+ + {systemStatus.allowSignUp && ( +

+ {"Don't have an account yet?"} + + {t("common.sign-up")} + +

+ )} + )} {identityProviderList.length > 0 && ( <> @@ -218,4 +189,4 @@ const Auth = () => { ); }; -export default Auth; +export default SignIn; diff --git a/web/src/pages/SignUp.tsx b/web/src/pages/SignUp.tsx new file mode 100644 index 00000000..1d7dc91d --- /dev/null +++ b/web/src/pages/SignUp.tsx @@ -0,0 +1,135 @@ +import { Button, Input } from "@mui/joy"; +import { useState } from "react"; +import { toast } from "react-hot-toast"; +import { Link } from "react-router-dom"; +import AppearanceSelect from "@/components/AppearanceSelect"; +import LocaleSelect from "@/components/LocaleSelect"; +import * as api from "@/helpers/api"; +import useLoading from "@/hooks/useLoading"; +import { useGlobalStore, useUserStore } from "@/store/module"; +import { useTranslate } from "@/utils/i18n"; + +const SignUp = () => { + const t = useTranslate(); + const globalStore = useGlobalStore(); + const userStore = useUserStore(); + const actionBtnLoadingState = useLoading(false); + const { appearance, locale, systemStatus } = globalStore.state; + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + + const handleUsernameInputChanged = (e: React.ChangeEvent) => { + const text = e.target.value as string; + setUsername(text); + }; + + const handlePasswordInputChanged = (e: React.ChangeEvent) => { + const text = e.target.value as string; + setPassword(text); + }; + + const handleLocaleSelectChange = (locale: Locale) => { + globalStore.setLocale(locale); + }; + + const handleAppearanceSelectChange = (appearance: Appearance) => { + globalStore.setAppearance(appearance); + }; + + const handleFormSubmit = (e: React.FormEvent) => { + e.preventDefault(); + handleSignUpButtonClick(); + }; + + const handleSignUpButtonClick = async () => { + if (username === "" || password === "") { + return; + } + + if (actionBtnLoadingState.isLoading) { + return; + } + + try { + actionBtnLoadingState.setLoading(); + await api.signup(username, password); + const user = await userStore.doSignIn(); + if (user) { + window.location.href = "/"; + } else { + toast.error(t("message.signup-failed")); + } + } catch (error: any) { + console.error(error); + toast.error(error.response.data.message || error.message || t("message.signup-failed")); + } + actionBtnLoadingState.setFinish(); + }; + + return ( +
+
+
+
+ +

{systemStatus.customizedProfile.name}

+
+

Create your account

+
+
+
+ {t("common.username")} + +
+
+ {t("common.password")} + +
+
+
+ +
+
+

+ {"Already has an account?"} + + {t("common.sign-in")} + +

+
+
+ + +
+
+
+ ); +}; + +export default SignUp; diff --git a/web/src/router/index.tsx b/web/src/router/index.tsx index e5114cb3..3ff51f63 100644 --- a/web/src/router/index.tsx +++ b/web/src/router/index.tsx @@ -1,20 +1,21 @@ import { lazy } from "react"; import { createBrowserRouter, redirect } from "react-router-dom"; import App from "@/App"; -import Archived from "@/pages/Archived"; -import DailyReview from "@/pages/DailyReview"; -import Resources from "@/pages/Resources"; -import Setting from "@/pages/Setting"; import { initialGlobalState, initialUserState } from "@/store/module"; const Root = lazy(() => import("@/layouts/Root")); -const Auth = lazy(() => import("@/pages/Auth")); +const SignIn = lazy(() => import("@/pages/SignIn")); +const SignUp = lazy(() => import("@/pages/SignUp")); const AuthCallback = lazy(() => import("@/pages/AuthCallback")); const Explore = lazy(() => import("@/pages/Explore")); const Home = lazy(() => import("@/pages/Home")); const UserProfile = lazy(() => import("@/pages/UserProfile")); const MemoDetail = lazy(() => import("@/pages/MemoDetail")); const EmbedMemo = lazy(() => import("@/pages/EmbedMemo")); +const Archived = lazy(() => import("@/pages/Archived")); +const DailyReview = lazy(() => import("@/pages/DailyReview")); +const Resources = lazy(() => import("@/pages/Resources")); +const Setting = lazy(() => import("@/pages/Setting")); const NotFound = lazy(() => import("@/pages/NotFound")); const initialGlobalStateLoader = (() => { @@ -58,7 +59,11 @@ const router = createBrowserRouter([ children: [ { path: "/auth", - element: , + element: , + }, + { + path: "/auth/signup", + element: , }, { path: "/auth/callback",