diff --git a/react/admin-panel/src/elements/language-selection.js b/react/admin-panel/src/elements/language-selection.js
deleted file mode 100644
index a3e20ca..0000000
--- a/react/admin-panel/src/elements/language-selection.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import React, {useCallback, useContext, useState} from 'react';
-import {Box} from "@mui/material";
-import {LocaleContext} from "shared/locale";
-
-/*
-const useStyles = makeStyles((theme) => ({
- languageFlag: {
- margin: theme.spacing(0.2),
- cursor: "pointer",
- border: 0,
- }
-}));
-*/
-
-export default function LanguageSelection(props) {
-
- const api = props.api;
- // const classes = useStyles();
- const classes = {};
- const [languages, setLanguages] = useState(null);
- const {translate: L, setLanguageByCode} = useContext(LocaleContext);
-
- const onSetLanguage = useCallback((code) => {
- setLanguageByCode(api, code).then((res) => {
- if (!res.success) {
- alert(res.msg);
- }
- });
- }, []);
-
- let flags = [];
- if (languages === null) {
- api.getLanguages().then((res) => {
- if (res.success) {
- setLanguages(res.languages);
- } else {
- setLanguages({});
- alert(res.msg);
- }
- });
- } else {
- for (const language of Object.values(languages)) {
- let key = `lang-${language.code}`;
- flags.push();
- }
- }
-
- return
- {L("general.language") + ": "} { flags }
-
-}
\ No newline at end of file
diff --git a/react/admin-panel/src/views/login.jsx b/react/admin-panel/src/views/login.jsx
deleted file mode 100644
index 6445db4..0000000
--- a/react/admin-panel/src/views/login.jsx
+++ /dev/null
@@ -1,310 +0,0 @@
-import {
- Box,
- Button,
- Checkbox, CircularProgress, Container,
- FormControlLabel,
- Grid,
- Link,
- TextField,
- Typography
-} from "@mui/material";
-
-import {Alert} from '@mui/lab';
-import React, {useCallback, useContext, useEffect, useState} from "react";
-import ReplayIcon from '@mui/icons-material';
-import LanguageSelection from "../elements/language-selection";
-import {decodeText, encodeText, getParameter, removeParameter} from "shared/util";
-import Icon from "shared/elements/icon";
-import {LocaleContext} from "shared/locale";
-
-/*
-const useStyles = makeStyles((theme) => ({
- paper: {
- marginTop: theme.spacing(8),
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- },
- avatar: {
- margin: theme.spacing(2),
- width: "60px",
- height: "60px"
- },
- form: {
- width: '100%', // Fix IE 11 issue.
- marginTop: theme.spacing(1),
- },
- submit: {
- margin: theme.spacing(3, 0, 2),
- },
- logo: {
- marginRight: theme.spacing(3)
- },
- headline: {
- width: "100%",
- },
- container: {
- marginTop: theme.spacing(5),
- paddingBottom: theme.spacing(1),
- borderColor: theme.palette.primary.main,
- borderStyle: "solid",
- borderWidth: 1,
- borderRadius: 5
- },
- buttons2FA: {
- marginTop: theme.spacing(1),
- marginBottom: theme.spacing(1),
- },
- error2FA: {
- marginTop: theme.spacing(2),
- marginBottom: theme.spacing(2),
- "& > div": {
- fontSize: 16
- },
- "& > button": {
- marginTop: theme.spacing(1)
- }
- }
-}));
- */
-
-export default function LoginForm(props) {
-
- const api = props.api;
- // const classes = useStyles();
- const classes = {};
- let [username, setUsername] = useState("");
- let [password, setPassword] = useState("");
- let [rememberMe, setRememberMe] = useState(true);
- let [isLoggingIn, setLoggingIn] = useState(false);
- let [emailConfirmed, setEmailConfirmed] = useState(null);
- 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(false);
-
- const {translate: L, currentLocale, requestModules} = useContext(LocaleContext);
-
- const onUpdateLocale = useCallback(() => {
- requestModules(api, ["general", "account"], currentLocale).then(data => {
- setLoaded(true);
- if (!data.success) {
- alert(data.msg);
- }
- });
- }, [currentLocale]);
-
- useEffect(() => {
- onUpdateLocale();
- }, [currentLocale]);
-
- const onLogin = useCallback(() => {
- if (!isLoggingIn) {
- setError("");
- setLoggingIn(true);
- removeParameter("success");
- api.login(username, password, rememberMe).then((res) => {
- set2FAState(0);
- setLoggingIn(false);
- setPassword("");
- if (!res.success) {
- setEmailConfirmed(res.emailConfirmed);
- setError(res.msg);
- } else {
- props.onLogin();
- }
- });
- }
- }, [api, isLoggingIn, password, props, rememberMe, username]);
-
- const onSubmit2FA = useCallback(() => {
- setLoggingIn(true);
- props.onTotp2FA(tfaCode, (res) => {
- setLoggingIn(false);
- });
- }, [tfaCode, props]);
-
- const onCancel2FA = useCallback(() => {
- props.onLogout();
- }, [props]);
-
- useEffect(() => {
- if (!api.loggedIn || !api.user) {
- return;
- }
-
- let twoFactor = api.user["2fa"];
- if (!twoFactor || !twoFactor.confirmed ||
- twoFactor.authenticated || twoFactor.type !== "fido") {
- return;
- }
-
- if (tfaState === 0) {
- set2FAState(1);
- set2FAError("");
- navigator.credentials.get({
- publicKey: {
- challenge: encodeText(window.atob(twoFactor.challenge)),
- allowCredentials: [{
- id: encodeText(window.atob(twoFactor.credentialID)),
- type: "public-key",
- }],
- userVerification: "discouraged",
- },
- }).then((res) => {
- let credentialID = res.id;
- let clientDataJson = decodeText(res.response.clientDataJSON);
- let authData = window.btoa(decodeText(res.response.authenticatorData));
- let signature = window.btoa(decodeText(res.response.signature));
- props.onKey2FA(credentialID, clientDataJson, authData, signature, res => {
- if (!res.success) {
- set2FAState(2);
- }
- });
- }).catch(e => {
- set2FAState(2);
- set2FAError(e.toString());
- });
- }
- }, [api.loggedIn, api.user, tfaState, props]);
-
- const createForm = () => {
-
- // 2FA
- if (api.loggedIn && api.user["2fa"]) {
- return <>
-
{L("account.2fa_title")}: {api.user["2fa"].type}
- { api.user["2fa"].type === "totp" ?
- set2FACode(e.target.value)}
- /> : <>
- {L("account.2fa_text")}
-
- {tfaState !== 2
- ?
- :
-
{L("general.something_went_wrong")}:
{tfaError}
-
-
- }
-
- >
- }
- {
- error ? {error} : <>>
- }
-
-
-
-
-
-
-
-
- >
- }
-
- return <>
- setUsername(e.target.value)}
- />
- setPassword(e.target.value)}
- />
- }
- label={L("account.remember_me")}
- checked={rememberMe} onClick={(e) => setRememberMe(!rememberMe)}
- />
- {
- error
- ?
- {error}
- {emailConfirmed === false
- ? <> Click here to resend the confirmation email.>
- : <>>
- }
-
- : (successMessage
- ? {successMessage}
- : <>>)
- }
-
-
-
-
- {L("account.forgot_password")}
-
-
- { props.info.registrationAllowed ?
-
-
- {L("account.register_text")}
-
- : <>>
- }
-
- >
- }
-
- if (!loaded) {
- return {L("general.loading")}…
- }
-
- let successMessage = getParameter("success");
- return
-
-
-
-
- {props.info.siteName}
-
-
-
-
-
-}
diff --git a/react/shared/elements/language-selection.js b/react/shared/elements/language-selection.js
index c6983aa..616d2bc 100644
--- a/react/shared/elements/language-selection.js
+++ b/react/shared/elements/language-selection.js
@@ -1,16 +1,12 @@
import React, {useCallback, useContext, useState} from 'react';
-import {Box, Button} from "@mui/material";
+import {Box, styled} from "@mui/material";
import {LocaleContext} from "shared/locale";
-/*
-const useStyles = makeStyles((theme) => ({
- languageFlag: {
- margin: theme.spacing(0.2),
- cursor: "pointer",
- border: 0,
- }
+const LanguageFlag = styled(Box)((props) => ({
+ display: "inline-block",
+ marginRight: props.theme.spacing(0.5),
+ cursor: "pointer"
}));
-*/
export default function LanguageSelection(props) {
@@ -39,10 +35,10 @@ export default function LanguageSelection(props) {
} else {
for (const language of Object.values(languages)) {
let key = `lang-${language.code}`;
- flags.push();
+ flags.push(
+ onSetLanguage(language.code)} />
+
+ );
}
}
diff --git a/react/shared/views/login.jsx b/react/shared/views/login.jsx
index e37e98d..fa58581 100644
--- a/react/shared/views/login.jsx
+++ b/react/shared/views/login.jsx
@@ -1,76 +1,43 @@
-import {Box,
+import {
+ Box,
Button,
Checkbox, CircularProgress, Container,
FormControlLabel,
Grid,
- Link,
+ Link, styled,
TextField,
Typography
} from "@mui/material";
import {Alert} from '@mui/lab';
-import React, {useCallback, useContext, useEffect, useState} from "react";
+import React, {useCallback, useContext, useEffect, useRef, useState} from "react";
import ReplayIcon from '@mui/icons-material/Replay';
import LanguageSelection from "../elements/language-selection";
import {decodeText, encodeText, getParameter, removeParameter} from "shared/util";
import {LocaleContext} from "shared/locale";
-/*
-const useStyles = makeStyles((theme) => ({
- paper: {
- marginTop: theme.spacing(8),
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center',
- },
- avatar: {
- margin: theme.spacing(2),
+
+const LoginContainer = styled(Container)((props) => ({
+ marginTop: props.theme.spacing(5),
+ paddingBottom: props.theme.spacing(1),
+ borderColor: props.theme.palette.primary.main,
+ borderStyle: "solid",
+ borderWidth: 1,
+ borderRadius: 5,
+ "& h1 > img": {
+ marginRight: props.theme.spacing(2),
width: "60px",
height: "60px"
- },
- form: {
- width: '100%', // Fix IE 11 issue.
- marginTop: theme.spacing(1),
- },
- submit: {
- margin: theme.spacing(3, 0, 2),
- },
- logo: {
- marginRight: theme.spacing(3)
- },
- headline: {
- width: "100%",
- },
- container: {
- marginTop: theme.spacing(5),
- paddingBottom: theme.spacing(1),
- borderColor: theme.palette.primary.main,
- borderStyle: "solid",
- borderWidth: 1,
- borderRadius: 5
- },
- buttons2FA: {
- marginTop: theme.spacing(1),
- marginBottom: theme.spacing(1),
- },
- error2FA: {
- marginTop: theme.spacing(2),
- marginBottom: theme.spacing(2),
- "& > div": {
- fontSize: 16
- },
- "& > button": {
- marginTop: theme.spacing(1)
- }
}
}));
-*/
+
+const ResponseAlert = styled(Alert)((props) => ({
+ marginBottom: props.theme.spacing(2),
+}));
export default function LoginForm(props) {
const api = props.api;
- // const classes = useStyles();
- const classes = { };
// inputs
let [username, setUsername] = useState("");
@@ -91,6 +58,9 @@ export default function LoginForm(props) {
let [isLoggingIn, setLoggingIn] = useState(false);
let [loaded, setLoaded] = useState(false);
+ // ui
+ let passwordRef = useRef();
+
const {translate: L, currentLocale, requestModules} = useContext(LocaleContext);
const onUpdateLocale = useCallback(() => {
@@ -203,9 +173,9 @@ export default function LoginForm(props) {
value={tfaCode} onChange={(e) => set2FACode(e.target.value)}
/>
{
- tfaToken.error ? {tfaToken.error} : <>>
+ tfaToken.error ? {tfaToken.error} : <>>
}
-
+