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 -
-
- - {"Logo"} - {props.info.siteName} - -
-
e.preventDefault()}> - { createForm() } - - -
-
-} 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( + {key} 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} : <> } - + - + } - +