aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd')
-rw-r--r--FrontEnd/src/App.tsx8
-rw-r--r--FrontEnd/src/pages/login/index.css4
-rw-r--r--FrontEnd/src/pages/login/index.tsx2
-rw-r--r--FrontEnd/src/pages/register/index.css5
-rw-r--r--FrontEnd/src/pages/register/index.tsx138
-rw-r--r--FrontEnd/src/views/login/index.css8
-rw-r--r--FrontEnd/src/views/login/index.tsx159
7 files changed, 152 insertions, 172 deletions
diff --git a/FrontEnd/src/App.tsx b/FrontEnd/src/App.tsx
index 8f2bf6b0..f638f5e8 100644
--- a/FrontEnd/src/App.tsx
+++ b/FrontEnd/src/App.tsx
@@ -7,8 +7,8 @@ import LoadingPage from "./views/common/LoadingPage";
import AboutPage from "./pages/about";
import SettingPage from "./pages/setting";
import Center from "./views/center";
-import Login from "./pages/login";
-import Register from "./views/register";
+import LoginPage from "./pages/login";
+import RegisterPage from "./pages/register";
import TimelinePage from "./views/timeline";
import Search from "./views/search";
import Admin from "./views/admin";
@@ -22,8 +22,8 @@ export default function App() {
<div style={{ height: 56 }} />
<Routes>
<Route path="center" element={<Center />} />
- <Route path="login" element={<Login />} />
- <Route path="register" element={<Register />} />
+ <Route path="login" element={<LoginPage />} />
+ <Route path="register" element={<RegisterPage />} />
<Route path="settings" element={<SettingPage />} />
<Route path="about" element={<AboutPage />} />
<Route path="search" element={<Search />} />
diff --git a/FrontEnd/src/pages/login/index.css b/FrontEnd/src/pages/login/index.css
index d78b3587..ef97359c 100644
--- a/FrontEnd/src/pages/login/index.css
+++ b/FrontEnd/src/pages/login/index.css
@@ -7,4 +7,8 @@
.login-page-welcome {
text-align: center;
font-size: 2em;
+}
+
+.login-page-error {
+ color: var(--cru-danger-color);
} \ No newline at end of file
diff --git a/FrontEnd/src/pages/login/index.tsx b/FrontEnd/src/pages/login/index.tsx
index 9aee455f..a09e32c3 100644
--- a/FrontEnd/src/pages/login/index.tsx
+++ b/FrontEnd/src/pages/login/index.tsx
@@ -111,7 +111,7 @@ export default function LoginPage() {
<div className="login-page-container">
<div className="login-page-welcome">{c("welcome")}</div>
<InputGroup {...inputGroupProps} />
- {error ? <p className="cru-color-danger">{c(error)}</p> : null}
+ {error ? <p className="login-page-error">{c(error)}</p> : null}
<div className="login-page-button-row">
<LoadingButton
loading={process}
diff --git a/FrontEnd/src/pages/register/index.css b/FrontEnd/src/pages/register/index.css
new file mode 100644
index 00000000..c0078b28
--- /dev/null
+++ b/FrontEnd/src/pages/register/index.css
@@ -0,0 +1,5 @@
+.register-page {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
diff --git a/FrontEnd/src/pages/register/index.tsx b/FrontEnd/src/pages/register/index.tsx
new file mode 100644
index 00000000..bc474adb
--- /dev/null
+++ b/FrontEnd/src/pages/register/index.tsx
@@ -0,0 +1,138 @@
+import { useState, useEffect } from "react";
+import { useTranslation } from "react-i18next";
+import { useNavigate } from "react-router-dom";
+
+import { HttpBadRequestError } from "@/http/common";
+import { getHttpTokenClient } from "@/http/token";
+import { userService, useUser } from "@/services/user";
+
+import { LoadingButton } from "@/views/common/button";
+import {
+ useInputs,
+ InputErrorDict,
+ InputGroup,
+} from "@/views/common/input/InputGroup";
+
+import "./index.css";
+
+export default function RegisterPage() {
+ const navigate = useNavigate();
+
+ const { t } = useTranslation();
+
+ const user = useUser();
+
+ const { hasErrorAndDirty, confirm, setAllDisabled, inputGroupProps } =
+ useInputs({
+ init: {
+ scheme: {
+ inputs: [
+ {
+ key: "username",
+ type: "text",
+ label: "register.username",
+ },
+ {
+ key: "password",
+ type: "text",
+ label: "register.password",
+ password: true,
+ },
+ {
+ key: "confirmPassword",
+ type: "text",
+ label: "register.confirmPassword",
+ password: true,
+ },
+ {
+ key: "registerCode",
+
+ type: "text",
+ label: "register.registerCode",
+ },
+ ],
+ validator: ({
+ username,
+ password,
+ confirmPassword,
+ registerCode,
+ }) => {
+ const result: InputErrorDict = {};
+ if (username === "") {
+ result["username"] = "register.error.usernameEmpty";
+ }
+ if (password === "") {
+ result["password"] = "register.error.passwordEmpty";
+ }
+ if (confirmPassword !== password) {
+ result["confirmPassword"] = "register.error.confirmPasswordWrong";
+ }
+ if (registerCode === "") {
+ result["registerCode"] = "register.error.registerCodeEmpty";
+ }
+ return result;
+ },
+ },
+ dataInit: {},
+ },
+ });
+
+ const [process, setProcess] = useState<boolean>(false);
+ const [resultError, setResultError] = useState<string | null>(null);
+
+ useEffect(() => {
+ if (user != null) {
+ navigate("/");
+ }
+ }, [navigate, user]);
+
+ return (
+ <div className="container register-page">
+ <InputGroup {...inputGroupProps} />
+ {resultError && <div className="cru-color-danger">{t(resultError)}</div>}
+ <LoadingButton
+ text="register.register"
+ loading={process}
+ disabled={hasErrorAndDirty}
+ onClick={() => {
+ const confirmResult = confirm();
+ if (confirmResult.type === "ok") {
+ const { username, password, registerCode } = confirmResult.values;
+ setProcess(true);
+ setAllDisabled(true);
+ void getHttpTokenClient()
+ .register({
+ username: username as string,
+ password: password as string,
+ registerCode: registerCode as string,
+ })
+ .then(
+ () => {
+ void userService
+ .login(
+ {
+ username: username as string,
+ password: password as string,
+ },
+ true,
+ )
+ .then(() => {
+ navigate("/");
+ });
+ },
+ (error) => {
+ if (error instanceof HttpBadRequestError) {
+ setResultError("register.error.registerCodeInvalid");
+ } else {
+ setResultError("error.network");
+ }
+ setProcess(false);
+ setAllDisabled(false);
+ },
+ );
+ }
+ }}
+ />
+ </div>
+ );
+}
diff --git a/FrontEnd/src/views/login/index.css b/FrontEnd/src/views/login/index.css
deleted file mode 100644
index aefe57e8..00000000
--- a/FrontEnd/src/views/login/index.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.login-container {
- max-width: 25em;
-}
-
-.login-container input[type="text"],
-.login-container input[type="password"] {
- width: 100%;
-}
diff --git a/FrontEnd/src/views/login/index.tsx b/FrontEnd/src/views/login/index.tsx
deleted file mode 100644
index cc1d9865..00000000
--- a/FrontEnd/src/views/login/index.tsx
+++ /dev/null
@@ -1,159 +0,0 @@
-import * as React from "react";
-import { Link, useNavigate } from "react-router-dom";
-import { useTranslation, Trans } from "react-i18next";
-
-import { useUser, userService } from "@/services/user";
-
-import AppBar from "../common/AppBar";
-import LoadingButton from "../common/button/LoadingButton";
-
-import "./index.css";
-
-const LoginPage: React.FC = () => {
- const { t } = useTranslation();
-
- const navigate = useNavigate();
-
- const [username, setUsername] = React.useState<string>("");
- const [usernameDirty, setUsernameDirty] = React.useState<boolean>(false);
- const [password, setPassword] = React.useState<string>("");
- const [passwordDirty, setPasswordDirty] = React.useState<boolean>(false);
- const [rememberMe, setRememberMe] = React.useState<boolean>(true);
- const [process, setProcess] = React.useState<boolean>(false);
- const [error, setError] = React.useState<string | null>(null);
-
- const user = useUser();
-
- React.useEffect(() => {
- if (user != null) {
- const id = setTimeout(() => navigate("/"), 3000);
- return () => {
- clearTimeout(id);
- };
- }
- }, [navigate, user]);
-
- if (user != null) {
- return (
- <>
- <AppBar />
- <p>{t("login.alreadyLogin")}</p>
- </>
- );
- }
-
- const submit = (): void => {
- if (username === "" || password === "") {
- setUsernameDirty(true);
- setPasswordDirty(true);
- return;
- }
-
- setProcess(true);
- userService
- .login(
- {
- username: username,
- password: password,
- },
- rememberMe
- )
- .then(
- () => {
- if (history.length === 0) {
- navigate("/");
- } else {
- navigate(-1);
- }
- },
- (e: Error) => {
- setProcess(false);
- setError(e.message);
- }
- );
- };
-
- const onEnterPressInPassword: React.KeyboardEventHandler = (e) => {
- if (e.key === "Enter") {
- submit();
- }
- };
-
- return (
- <div className="login-container container-fluid mt-2">
- <h1 className="cru-text-center cru-color-primary">{t("welcome")}</h1>
- <div className="cru-operation-dialog-group">
- <label className="cru-operation-dialog-label" htmlFor="username">
- {t("user.username")}
- </label>
- <input
- id="username"
- type="text"
- disabled={process}
- onChange={(e) => {
- setUsername(e.target.value);
- setUsernameDirty(true);
- }}
- value={username}
- />
- {usernameDirty && username === "" && (
- <div className="cru-operation-dialog-error-text">
- {t("login.emptyUsername")}
- </div>
- )}
- </div>
- <div className="cru-operation-dialog-group">
- <label className="cru-operation-dialog-label" htmlFor="password">
- {t("user.password")}
- </label>
- <input
- id="password"
- type="password"
- disabled={process}
- onChange={(e) => {
- setPassword(e.target.value);
- setPasswordDirty(true);
- }}
- value={password}
- onKeyDown={onEnterPressInPassword}
- />
- {passwordDirty && password === "" && (
- <div className="cru-operation-dialog-error-text">
- {t("login.emptyPassword")}
- </div>
- )}
- </div>
- <div className="cru-operation-dialog-group">
- <input
- id="remember-me"
- type="checkbox"
- checked={rememberMe}
- onChange={(e) => {
- setRememberMe(e.currentTarget.checked);
- }}
- />
- <label className="cru-operation-dialog-inline-label">
- {t("user.rememberMe")}
- </label>
- </div>
- {error ? <p className="cru-color-danger">{t(error)}</p> : null}
- <div className="cru-text-end">
- <LoadingButton
- loading={process}
- onClick={(e) => {
- submit();
- e.preventDefault();
- }}
- disabled={username === "" || password === "" ? true : undefined}
- >
- {t("user.login")}
- </LoadingButton>
- </div>
- <Trans i18nKey="login.noAccount">
- 0<Link to="/register">1</Link>2
- </Trans>
- </div>
- );
-};
-
-export default LoginPage;