mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-03-27 14:09:20 +01:00
Implemented register page in react
This commit is contained in:
parent
f6d8b618eb
commit
f36ed678a3
@ -248,6 +248,11 @@ public class PageFactory {
|
||||
}
|
||||
|
||||
public Page registerPage() throws IOException {
|
||||
if (config.get().isTrue(PluginSettings.FRONTEND_BETA)) {
|
||||
String reactHtml = getResource("index.html");
|
||||
return () -> reactHtml;
|
||||
}
|
||||
|
||||
return new LoginPage(getResource("register.html"), serverInfo.get(), locale.get(), theme.get(), versionChecker.get());
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,7 @@ public enum HtmlLang implements Lang {
|
||||
REGISTER_COMPLETE_INSTRUCTIONS_2("html.register.completion2", "Code expires in 15 minutes"),
|
||||
REGISTER_COMPLETE_INSTRUCTIONS_3("html.register.completion3", "Use the following command in game to finish registration:"),
|
||||
REGISTER_COMPLETE_INSTRUCTIONS_4("html.register.completion4", "Or using console:"),
|
||||
REGISTER_SUCCESS("html.register.success", "Registered a new user successfully! You can now login."),
|
||||
REGISTER_FAILED("html.register.error.failed", "Registration failed: "),
|
||||
REGISTER_CHECK_FAILED("html.register.error.checkFailed", "Checking registration status failed: "),
|
||||
|
||||
|
@ -28,7 +28,6 @@ const ServerPvpPve = React.lazy(() => import("./views/server/ServerPvpPve"));
|
||||
const PlayerbaseOverview = React.lazy(() => import("./views/server/PlayerbaseOverview"));
|
||||
const ServerPlayers = React.lazy(() => import("./views/server/ServerPlayers"));
|
||||
const ServerGeolocations = React.lazy(() => import("./views/server/ServerGeolocations"));
|
||||
const LoginPage = React.lazy(() => import("./views/layout/LoginPage"));
|
||||
const ServerPerformance = React.lazy(() => import("./views/server/ServerPerformance"));
|
||||
const ServerPluginData = React.lazy(() => import("./views/server/ServerPluginData"));
|
||||
const ServerWidePluginData = React.lazy(() => import("./views/server/ServerWidePluginData"));
|
||||
@ -50,6 +49,8 @@ const QueryPage = React.lazy(() => import("./views/layout/QueryPage"));
|
||||
const NewQueryView = React.lazy(() => import("./views/query/NewQueryView"));
|
||||
const QueryResultView = React.lazy(() => import("./views/query/QueryResultView"));
|
||||
|
||||
const LoginPage = React.lazy(() => import("./views/layout/LoginPage"));
|
||||
const RegisterPage = React.lazy(() => import("./views/layout/RegisterPage"));
|
||||
const ErrorsPage = React.lazy(() => import("./views/layout/ErrorsPage"));
|
||||
const SwaggerView = React.lazy(() => import("./views/SwaggerView"));
|
||||
|
||||
@ -90,6 +91,7 @@ function App() {
|
||||
<Route path="" element={<MainPageRedirect/>}/>
|
||||
<Route path="/" element={<MainPageRedirect/>}/>
|
||||
<Route path="/login" element={<Lazy><LoginPage/></Lazy>}/>
|
||||
<Route path="/register" element={<Lazy><RegisterPage/></Lazy>}/>
|
||||
<Route path="/player/:identifier" element={<Lazy><PlayerPage/></Lazy>}>
|
||||
<Route path="" element={<Lazy><OverviewRedirect/></Lazy>}/>
|
||||
<Route path="overview" element={<Lazy><PlayerOverview/></Lazy>}/>
|
||||
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useMetadata} from "../../hooks/metadataHook";
|
||||
import {Modal} from "react-bootstrap-v5";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faHandPointRight} from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
const FinalizeRegistrationModal = ({show, toggle, registerCode}) => {
|
||||
const {t} = useTranslation();
|
||||
const {mainCommand} = useMetadata();
|
||||
|
||||
return (
|
||||
<Modal id={"finalizeModal"}
|
||||
aria-labelledby={"finalizeModalLable"}
|
||||
show={show}
|
||||
onHide={toggle}
|
||||
>
|
||||
<Modal.Header className="bg-white">
|
||||
<Modal.Title id={"finalizeModalLabel"}>
|
||||
<Fa icon={faHandPointRight}/> {t('html.register.completion')}
|
||||
</Modal.Title>
|
||||
<button aria-label="Close" className="btn-close" onClick={toggle}/>
|
||||
</Modal.Header>
|
||||
<Modal.Body className={"bg-white"}>
|
||||
<p>{t('html.register.completion1')} {t('html.register.completion2')}</p>
|
||||
<p>{t('html.register.completion3')}</p>
|
||||
<p><code>/{mainCommand} register --code {registerCode}</code></p>
|
||||
<p>{t('html.register.completion4')}</p>
|
||||
<p><code>{mainCommand} register superuser --code {registerCode}</code></p>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
)
|
||||
};
|
||||
|
||||
export default FinalizeRegistrationModal
|
@ -8,4 +8,14 @@ export const fetchWhoAmI = async () => {
|
||||
export const fetchLogin = async (username, password) => {
|
||||
const url = '/auth/login';
|
||||
return doSomePostRequest(url, [standard200option], `user=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`);
|
||||
}
|
||||
|
||||
export const postRegister = async (username, password) => {
|
||||
const url = '/auth/register';
|
||||
return doSomePostRequest(url, [standard200option], `user=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`);
|
||||
}
|
||||
|
||||
export const fetchRegisterCheck = async (code) => {
|
||||
const url = `/auth/register?code=${encodeURIComponent(code)}`;
|
||||
return doGetRequest(url);
|
||||
}
|
@ -125,6 +125,7 @@ const LoginPage = () => {
|
||||
|
||||
const [forgotPasswordModalOpen, setForgotPasswordModalOpen] = useState(false);
|
||||
|
||||
const [successMessage, setSuccessMessage] = useState('')
|
||||
const [failMessage, setFailMessage] = useState('');
|
||||
const [redirectTo, setRedirectTo] = useState(undefined);
|
||||
|
||||
@ -138,6 +139,9 @@ const LoginPage = () => {
|
||||
const cameFrom = urlParams.get('from');
|
||||
if (cameFrom) setRedirectTo(cameFrom);
|
||||
|
||||
const registerSuccess = urlParams.get('registerSuccess');
|
||||
if (registerSuccess) setSuccessMessage(t('html.register.success'))
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("bg-plan", "plan-bg-gradient");
|
||||
}
|
||||
@ -189,8 +193,9 @@ const LoginPage = () => {
|
||||
<Logo/>
|
||||
<LoginCard>
|
||||
{failMessage && <Alert className='alert-danger'>{failMessage}</Alert>}
|
||||
{successMessage && <Alert className='alert-success'>{successMessage}</Alert>}
|
||||
<LoginForm login={login}/>
|
||||
<hr className="bg-secondary"/>
|
||||
<hr className="col-secondary"/>
|
||||
<ForgotPasswordButton onClick={togglePasswordModal}/>
|
||||
<CreateAccountLink/>
|
||||
<ColorChooserButton/>
|
||||
|
200
Plan/react/dashboard/src/views/layout/RegisterPage.js
Normal file
200
Plan/react/dashboard/src/views/layout/RegisterPage.js
Normal file
@ -0,0 +1,200 @@
|
||||
import React, {useCallback, useEffect, useState} from 'react';
|
||||
|
||||
import logo from '../../Flaticon_circle.png'
|
||||
import {Alert, Card, Col, Row} from "react-bootstrap-v5";
|
||||
import {Link, useNavigate} from "react-router-dom";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {FontAwesomeIcon as Fa} from "@fortawesome/react-fontawesome";
|
||||
import {faPalette} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useTheme} from "../../hooks/themeHook";
|
||||
import ColorSelectorModal from "../../components/modal/ColorSelectorModal";
|
||||
import {useAuth} from "../../hooks/authenticationHook";
|
||||
import FinalizeRegistrationModal from "../../components/modal/FinalizeRegistrationModal";
|
||||
import {fetchRegisterCheck, postRegister} from "../../service/authenticationService";
|
||||
import {baseAddress} from "../../service/backendConfiguration";
|
||||
|
||||
const Logo = () => {
|
||||
return (
|
||||
<Col md={12} className='mt-5 text-center'>
|
||||
<img alt="logo" className="w-15" src={logo}/>
|
||||
</Col>
|
||||
)
|
||||
};
|
||||
|
||||
const RegisterCard = ({children}) => {
|
||||
return (
|
||||
<Row className="justify-content-center container-fluid">
|
||||
<Col xl={6} lg={7} md={9}>
|
||||
<Card className='o-hidden border-0 shadow-lg my-5'>
|
||||
<Card.Body className='p-0'>
|
||||
<Row>
|
||||
<Col lg={12}>
|
||||
<div className='p-5'>
|
||||
{children}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
const RegisterForm = ({register}) => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const onRegister = useCallback(event => {
|
||||
event.preventDefault();
|
||||
register(username, password).then(() => setPassword(''));
|
||||
}, [username, password, setPassword, register]);
|
||||
|
||||
return (
|
||||
<form className="user">
|
||||
<div className="mb-3">
|
||||
<input autoComplete="username" className="form-control form-control-user"
|
||||
id="inputUser"
|
||||
placeholder={t('html.login.username')} type="text"
|
||||
value={username} onChange={event => setUsername(event.target.value)}/>
|
||||
<div className={"form-text"}>{t('html.register.usernameTip')}</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<input autoComplete="current-password" className="form-control form-control-user"
|
||||
id="inputPassword" placeholder={t('html.login.password')} type="password"
|
||||
value={password} onChange={event => setPassword(event.target.value)}/>
|
||||
<div className={"form-text"}>{t('html.register.passwordTip')}</div>
|
||||
</div>
|
||||
<button className="btn bg-plan btn-user w-100" id="register-button" onClick={onRegister}>
|
||||
{t('html.register.register')}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
const ColorChooserButton = () => {
|
||||
const {t} = useTranslation();
|
||||
const {toggleColorChooser} = useTheme();
|
||||
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<button className="btn col-plan" onClick={toggleColorChooser}
|
||||
title={t('html.label.themeSelect')}>
|
||||
<Fa icon={faPalette}/>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const LoginLink = () => {
|
||||
const {t} = useTranslation();
|
||||
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<Link to='/login' className='col-plan small'>{t('html.register.login')}</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RegisterPage = () => {
|
||||
const {t} = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const {authLoaded, authRequired, loggedIn} = useAuth();
|
||||
|
||||
const [finalizeRegistrationModalOpen, setFinalizeRegistrationModalOpen] = useState(false);
|
||||
|
||||
const [registerCode, setRegisterCode] = useState(undefined);
|
||||
const [failMessage, setFailMessage] = useState('');
|
||||
|
||||
const toggleRegistrationModal = useCallback(() => setFinalizeRegistrationModalOpen(!finalizeRegistrationModalOpen),
|
||||
[setFinalizeRegistrationModalOpen, finalizeRegistrationModalOpen])
|
||||
|
||||
useEffect(() => {
|
||||
document.body.classList.add("bg-plan", "plan-bg-gradient");
|
||||
|
||||
return () => {
|
||||
document.body.classList.remove("bg-plan", "plan-bg-gradient");
|
||||
}
|
||||
}, []);
|
||||
|
||||
const checkRegistration = async (code) => {
|
||||
if (!code) {
|
||||
setFinalizeRegistrationModalOpen(false);
|
||||
return setFailMessage("Register code was not received.");
|
||||
}
|
||||
if (!finalizeRegistrationModalOpen) {
|
||||
setFinalizeRegistrationModalOpen(true);
|
||||
}
|
||||
|
||||
const {data, error} = await fetchRegisterCheck(code);
|
||||
if (error) {
|
||||
setFailMessage(t('html.register.error.checkFailed') + error)
|
||||
} else if (data && data.success) {
|
||||
navigate(baseAddress + '/login?registerSuccess=true');
|
||||
} else {
|
||||
setTimeout(() => checkRegistration(code), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
const register = async (username, password) => {
|
||||
if (!username || username.length < 1) {
|
||||
return setFailMessage(t('html.register.error.noUsername'));
|
||||
}
|
||||
if (username.length > 50) {
|
||||
return setFailMessage(t('html.register.error.usernameLength') + username.length);
|
||||
}
|
||||
if (!password || password.length < 1) {
|
||||
return setFailMessage(t('html.register.error.noPassword'));
|
||||
}
|
||||
|
||||
const {data, error} = await postRegister(username, password);
|
||||
|
||||
if (error) {
|
||||
setFailMessage(t('html.register.error.failed') + (error.data && error.data.error ? error.data.error : error.message));
|
||||
} else if (data && data.code) {
|
||||
setRegisterCode(data.code);
|
||||
setFinalizeRegistrationModalOpen(true);
|
||||
setTimeout(() => checkRegistration(data.code), 10000);
|
||||
} else {
|
||||
setFailMessage(t('html.register.error.failed') + data ? data.error : t('generic.noData'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!authLoaded) {
|
||||
return <></>
|
||||
}
|
||||
|
||||
if (!authRequired || loggedIn) {
|
||||
navigate('../');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<main className="container">
|
||||
<Logo/>
|
||||
<RegisterCard>
|
||||
<div className="text-center">
|
||||
<h1 className="h4 text-gray-900 mb-4">{t('html.register.createNewUser')}</h1>
|
||||
</div>
|
||||
{failMessage && <Alert className='alert-danger'>{failMessage}</Alert>}
|
||||
<RegisterForm register={register}/>
|
||||
<hr className="col-secondary"/>
|
||||
<LoginLink/>
|
||||
<ColorChooserButton/>
|
||||
</RegisterCard>
|
||||
</main>
|
||||
<aside>
|
||||
<ColorSelectorModal/>
|
||||
<FinalizeRegistrationModal
|
||||
show={finalizeRegistrationModalOpen}
|
||||
toggle={toggleRegistrationModal}
|
||||
registerCode={registerCode}
|
||||
/>
|
||||
</aside>
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default RegisterPage
|
Loading…
Reference in New Issue
Block a user