Localization & stuff
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
"name": "admin-panel",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"shared": "link:../shared"
|
||||
"shared": "link:../shared",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"debug": "react-app-rewired start"
|
||||
"dev": "react-app-rewired start"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -3,7 +3,7 @@ import './res/adminlte.min.css';
|
||||
import './res/index.css';
|
||||
import API from "shared/api";
|
||||
import Icon from "shared/elements/icon";
|
||||
import {BrowserRouter, Route, Routes} from "react-router-dom";
|
||||
import {BrowserRouter, Routes} from "react-router-dom";
|
||||
import Dialog from "./elements/dialog";
|
||||
import Footer from "./elements/footer";
|
||||
import Header from "./elements/header";
|
||||
@@ -11,12 +11,14 @@ import Sidebar from "./elements/sidebar";
|
||||
import LoginForm from "./views/login";
|
||||
import {Alert} from "@material-ui/lab";
|
||||
import {Button} from "@material-ui/core";
|
||||
import {Locale} from "shared/locale";
|
||||
|
||||
export default class AdminDashboard extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.api = new API();
|
||||
this.locale = Locale.getInstance();
|
||||
this.state = {
|
||||
loaded: false,
|
||||
dialog: { onClose: () => this.hideDialog() },
|
||||
@@ -39,13 +41,19 @@ export default class AdminDashboard extends React.Component {
|
||||
|
||||
onInit() {
|
||||
this.setState({ ...this.state, loaded: false, error: null });
|
||||
this.api.info().then(data => {
|
||||
this.api.getLanguageEntries("general").then(data => {
|
||||
if (data.success) {
|
||||
this.setState({...this.state, info: data.info })
|
||||
this.api.fetchUser().then(data => {
|
||||
this.api.info().then(data => {
|
||||
if (data.success) {
|
||||
setInterval(this.onUpdate.bind(this), 60*1000);
|
||||
this.setState({...this.state, loaded: true});
|
||||
this.setState({...this.state, info: data.info })
|
||||
this.api.fetchUser().then(data => {
|
||||
if (data.success) {
|
||||
setInterval(this.onUpdate.bind(this), 60*1000);
|
||||
this.setState({...this.state, loaded: true});
|
||||
} else {
|
||||
this.setState({ ...this.state, error: data.msg })
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({ ...this.state, error: data.msg })
|
||||
}
|
||||
@@ -73,7 +81,6 @@ export default class AdminDashboard extends React.Component {
|
||||
callback(res);
|
||||
})
|
||||
} else {
|
||||
this.setState({ ...this.state, error: res.msg });
|
||||
callback(res);
|
||||
}
|
||||
});
|
||||
@@ -96,7 +103,6 @@ export default class AdminDashboard extends React.Component {
|
||||
callback(res);
|
||||
})
|
||||
} else {
|
||||
this.setState({ ...this.state, error: res.msg });
|
||||
callback(res);
|
||||
}
|
||||
});
|
||||
@@ -111,7 +117,6 @@ export default class AdminDashboard extends React.Component {
|
||||
callback(res);
|
||||
})
|
||||
} else {
|
||||
this.setState({ ...this.state, error: res.msg });
|
||||
callback(res);
|
||||
}
|
||||
});
|
||||
@@ -136,6 +141,7 @@ export default class AdminDashboard extends React.Component {
|
||||
showDialog: this.showDialog.bind(this),
|
||||
api: this.api,
|
||||
info: this.state.info,
|
||||
locale: this.locale,
|
||||
onUpdateLocale: this.onUpdateLocale.bind(this),
|
||||
onLogout: this.onLogout.bind(this),
|
||||
onLogin: this.onLogin.bind(this),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, {useState} from 'react';
|
||||
import {initLocale, L} from "shared/locale/locale";
|
||||
import {L} from "shared/locale";
|
||||
import {Box} from "@material-ui/core";
|
||||
import {makeStyles} from "@material-ui/core/styles";
|
||||
|
||||
@@ -20,8 +20,9 @@ export default function LanguageSelection(props) {
|
||||
const onSetLanguage = (code) => {
|
||||
api.setLanguageByCode(code).then((res) => {
|
||||
if (res.success) {
|
||||
initLocale(code);
|
||||
props.onUpdateLocale();
|
||||
} else {
|
||||
alert(res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -42,6 +43,6 @@ export default function LanguageSelection(props) {
|
||||
}
|
||||
|
||||
return <Box mt={1}>
|
||||
{L("Language") + ": "} { flags }
|
||||
{L("general.language") + ": "} { flags }
|
||||
</Box>
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import ReactDOM from "react-dom";
|
||||
import React from "react";
|
||||
import {createRoot} from "react-dom/client";
|
||||
import AdminDashboard from "./App";
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<AdminDashboard />,
|
||||
document.getElementById('admin-panel')
|
||||
);
|
||||
const root = createRoot(document.getElementById('admin-panel'));
|
||||
root.render(<AdminDashboard />);
|
||||
|
||||
1
react/admin-panel/src/res/adminlte.css.map
Normal file
1
react/admin-panel/src/res/adminlte.css.map
Normal file
File diff suppressed because one or more lines are too long
16
react/admin-panel/src/res/adminlte.min.css
vendored
16
react/admin-panel/src/res/adminlte.min.css
vendored
File diff suppressed because one or more lines are too long
1
react/admin-panel/src/res/adminlte.min.css.map
Normal file
1
react/admin-panel/src/res/adminlte.min.css.map
Normal file
File diff suppressed because one or more lines are too long
@@ -13,10 +13,11 @@ import {makeStyles} from '@material-ui/core/styles';
|
||||
import {Alert} from '@material-ui/lab';
|
||||
import React, {useCallback, useEffect, useState} from "react";
|
||||
import {Navigate} from "react-router-dom";
|
||||
import {L} from "shared/locale/locale";
|
||||
import {L} from "shared/locale";
|
||||
import ReplayIcon from '@material-ui/icons/Replay';
|
||||
import LanguageSelection from "../elements/language-selection";
|
||||
import {decodeText, encodeText, getParameter, removeParameter} from "shared/util";
|
||||
import Icon from "shared/elements/icon";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
paper: {
|
||||
@@ -79,13 +80,29 @@ export default function LoginForm(props) {
|
||||
let [tfaCode, set2FACode] = useState("");
|
||||
let [tfaState, set2FAState] = useState(0); // 0: not sent, 1: sent, 2: retry
|
||||
let [tfaError, set2FAError] = useState("");
|
||||
let [error, setError] = useState("");
|
||||
let [loaded, setLoaded] = useState(0);
|
||||
|
||||
const getNextUrl = () => {
|
||||
return getParameter("next") || "/admin";
|
||||
}
|
||||
|
||||
const onUpdateLocale = useCallback(() => {
|
||||
api.getLanguageEntries(["general", "account"]).then(data => {
|
||||
setLoaded(loaded + 1);
|
||||
if (!data.success) {
|
||||
alert(data.msg);
|
||||
}
|
||||
});
|
||||
}, [loaded]);
|
||||
|
||||
useEffect(() => {
|
||||
onUpdateLocale();
|
||||
}, []);
|
||||
|
||||
const onLogin = useCallback(() => {
|
||||
if (!isLoggingIn) {
|
||||
setError("");
|
||||
setLoggingIn(true);
|
||||
removeParameter("success");
|
||||
props.onLogin(username, password, rememberMe, (res) => {
|
||||
@@ -94,6 +111,7 @@ export default function LoginForm(props) {
|
||||
setPassword("");
|
||||
if (!res.success) {
|
||||
setEmailConfirmed(res.emailConfirmed);
|
||||
setError(res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -162,24 +180,24 @@ export default function LoginForm(props) {
|
||||
// 2FA
|
||||
if (api.loggedIn && api.user["2fa"]) {
|
||||
return <>
|
||||
<div>Additional information is required for logging in: {api.user["2fa"].type}</div>
|
||||
<div>{L("account.2fa_title")}: {api.user["2fa"].type}</div>
|
||||
{ api.user["2fa"].type === "totp" ?
|
||||
<TextField
|
||||
variant="outlined" margin="normal"
|
||||
id="code" label={L("6-Digit Code")} name="code"
|
||||
id="code" label={L("account.6_digit_code")} name="code"
|
||||
autoComplete="code"
|
||||
required fullWidth autoFocus
|
||||
value={tfaCode} onChange={(e) => set2FACode(e.target.value)}
|
||||
/> : <>
|
||||
Plugin your 2FA-Device. Interaction might be required, e.g. typing in a PIN or touching it.
|
||||
{L("account.2fa_text")}
|
||||
<Box mt={2} textAlign={"center"}>
|
||||
{tfaState !== 2
|
||||
? <CircularProgress/>
|
||||
: <div className={classes.error2FA}>
|
||||
<div>{L("Something went wrong:")}<br />{tfaError}</div>
|
||||
<div>{L("general.something_went_wrong")}:<br />{tfaError}</div>
|
||||
<Button onClick={() => set2FAState(0)}
|
||||
variant={"outlined"} color={"secondary"} size={"small"}>
|
||||
<ReplayIcon /> {L("Retry")}
|
||||
<ReplayIcon /> {L("general.retry")}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
@@ -187,7 +205,7 @@ export default function LoginForm(props) {
|
||||
</>
|
||||
}
|
||||
{
|
||||
props.error ? <Alert severity="error">{props.error}</Alert> : <></>
|
||||
error ? <Alert severity="error">{error}</Alert> : <></>
|
||||
}
|
||||
<Grid container spacing={2} className={classes.buttons2FA}>
|
||||
<Grid item xs={6}>
|
||||
@@ -196,7 +214,7 @@ export default function LoginForm(props) {
|
||||
color="inherit" size={"medium"}
|
||||
disabled={isLoggingIn}
|
||||
onClick={onCancel2FA}>
|
||||
{L("Go back")}
|
||||
{L("general.go_back")}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
@@ -206,8 +224,8 @@ export default function LoginForm(props) {
|
||||
disabled={isLoggingIn || api.user["2fa"].type !== "totp"}
|
||||
onClick={onSubmit2FA}>
|
||||
{isLoggingIn ?
|
||||
<>{L("Submitting…")}… <CircularProgress size={15}/></> :
|
||||
L("Submit")
|
||||
<>{L("general.submitting")}… <CircularProgress size={15}/></> :
|
||||
L("general.submit")
|
||||
}
|
||||
</Button>
|
||||
</Grid>
|
||||
@@ -218,35 +236,35 @@ export default function LoginForm(props) {
|
||||
return <>
|
||||
<TextField
|
||||
variant="outlined" margin="normal"
|
||||
id="username" label={L("Username")} name="username"
|
||||
id="username" label={L("account.username")} name="username"
|
||||
autoComplete="username" disabled={isLoggingIn}
|
||||
required fullWidth autoFocus
|
||||
value={username} onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
<TextField
|
||||
variant="outlined" margin="normal"
|
||||
name="password" label={L("Password")} type="password" id="password"
|
||||
name="password" label={L("account.password")} type="password" id="password"
|
||||
autoComplete="current-password"
|
||||
required fullWidth disabled={isLoggingIn}
|
||||
value={password} onChange={(e) => setPassword(e.target.value)}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={<Checkbox value="remember" color="primary"/>}
|
||||
label={L("Remember me")}
|
||||
label={L("account.remember_me")}
|
||||
checked={rememberMe} onClick={(e) => setRememberMe(!rememberMe)}
|
||||
/>
|
||||
{
|
||||
props.error ?
|
||||
<Alert severity="error">
|
||||
{props.error}
|
||||
error
|
||||
? <Alert severity="error">
|
||||
{error}
|
||||
{emailConfirmed === false
|
||||
? <> <Link href={"/resendConfirmation"}>Click here</Link> to resend the confirmation email.</>
|
||||
: <></>
|
||||
}
|
||||
</Alert> :
|
||||
successMessage
|
||||
</Alert>
|
||||
: (successMessage
|
||||
? <Alert severity="success">{successMessage}</Alert>
|
||||
: <></>
|
||||
: <></>)
|
||||
}
|
||||
<Button
|
||||
type={"submit"} fullWidth variant={"contained"}
|
||||
@@ -255,20 +273,20 @@ export default function LoginForm(props) {
|
||||
disabled={isLoggingIn}
|
||||
onClick={onLogin}>
|
||||
{isLoggingIn ?
|
||||
<>{L("Signing in")}… <CircularProgress size={15}/></> :
|
||||
L("Sign In")
|
||||
<>{L("account.signing_in")}… <CircularProgress size={15}/></> :
|
||||
L("account.sign_in")
|
||||
}
|
||||
</Button>
|
||||
<Grid container>
|
||||
<Grid item xs>
|
||||
<Link href="/resetPassword" variant="body2">
|
||||
{L("Forgot password?")}
|
||||
{L("account.forgot_password")}
|
||||
</Link>
|
||||
</Grid>
|
||||
{ props.info.registrationAllowed ?
|
||||
<Grid item>
|
||||
<Link href="/register" variant="body2">
|
||||
{L("Don't have an account? Sign Up")}
|
||||
{L("account.register_text")}
|
||||
</Link>
|
||||
</Grid> : <></>
|
||||
}
|
||||
@@ -276,6 +294,10 @@ export default function LoginForm(props) {
|
||||
</>
|
||||
}
|
||||
|
||||
if (loaded === 0) {
|
||||
return <b>{L("general.loading")}… <Icon icon={"spinner"}/></b>
|
||||
}
|
||||
|
||||
let successMessage = getParameter("success");
|
||||
return <Container maxWidth={"xs"} className={classes.container}>
|
||||
<div className={classes.paper}>
|
||||
@@ -287,7 +309,7 @@ export default function LoginForm(props) {
|
||||
</div>
|
||||
<form className={classes.form} onSubmit={(e) => e.preventDefault()}>
|
||||
{ createForm() }
|
||||
<LanguageSelection api={api} locale={props.locale} onUpdateLocale={props.onUpdateLocale}/>
|
||||
<LanguageSelection api={api} onUpdateLocale={onUpdateLocale} />
|
||||
</form>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
Reference in New Issue
Block a user