diff --git a/src/frontend/src/components/SplashScreen.tsx b/src/frontend/src/components/SplashScreen.tsx
index 79bb9992ac..8c8e783d57 100644
--- a/src/frontend/src/components/SplashScreen.tsx
+++ b/src/frontend/src/components/SplashScreen.tsx
@@ -1,4 +1,5 @@
import { BackgroundImage } from '@mantine/core';
+import { useEffect } from 'react';
import { generateUrl } from '../functions/urls';
import { useServerApiState } from '../states/ApiState';
@@ -7,10 +8,20 @@ import { useServerApiState } from '../states/ApiState';
*/
export default function SplashScreen({
children
-}: {
+}: Readonly<{
children: React.ReactNode;
-}) {
- const [server] = useServerApiState((state) => [state.server]);
+}>) {
+ const [server, fetchServerApiState] = useServerApiState((state) => [
+ state.server,
+ state.fetchServerApiState
+ ]);
+
+ // Fetch server data on mount if no server data is present
+ useEffect(() => {
+ if (server.server === null) {
+ fetchServerApiState();
+ }
+ }, [server]);
if (server.customize?.splash) {
return (
diff --git a/src/frontend/src/components/forms/AuthenticationForm.tsx b/src/frontend/src/components/forms/AuthenticationForm.tsx
index 982b386265..a615d728c8 100644
--- a/src/frontend/src/components/forms/AuthenticationForm.tsx
+++ b/src/frontend/src/components/forms/AuthenticationForm.tsx
@@ -7,7 +7,6 @@ import {
Loader,
PasswordInput,
Stack,
- Text,
TextInput
} from '@mantine/core';
import { useForm } from '@mantine/form';
@@ -329,47 +328,3 @@ export function RegistrationForm() {
>
);
}
-
-export function ModeSelector({
- loginMode,
- changePage
-}: Readonly<{
- loginMode: boolean;
- changePage: (state: string) => void;
-}>) {
- const [sso_registration, registration_enabled] = useServerApiState(
- (state) => [state.sso_registration_enabled, state.registration_enabled]
- );
- const both_reg_enabled =
- registration_enabled() || sso_registration() || false;
-
- if (both_reg_enabled === false) return null;
- return (
-
- {loginMode ? (
- <>
- Don't have an account?{' '}
- changePage('register')}
- >
- Register
-
- >
- ) : (
- changePage('login')}
- >
- Go back to login
-
- )}
-
- );
-}
diff --git a/src/frontend/src/components/forms/InstanceOptions.tsx b/src/frontend/src/components/forms/InstanceOptions.tsx
index 091887275d..7b1606d869 100644
--- a/src/frontend/src/components/forms/InstanceOptions.tsx
+++ b/src/frontend/src/components/forms/InstanceOptions.tsx
@@ -1,13 +1,5 @@
import { Trans, t } from '@lingui/macro';
-import {
- ActionIcon,
- Divider,
- Group,
- Paper,
- Select,
- Table,
- Text
-} from '@mantine/core';
+import { ActionIcon, Divider, Group, Select, Table, Text } from '@mantine/core';
import { useToggle } from '@mantine/hooks';
import {
IconApi,
@@ -18,11 +10,11 @@ import {
IconServerSpark
} from '@tabler/icons-react';
+import { Wrapper } from '../../pages/Auth/Layout';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
import type { HostList } from '../../states/states';
import { EditButton } from '../buttons/EditButton';
-import { StylishText } from '../items/StylishText';
import { HostOptionsForm } from './HostOptionsForm';
export function InstanceOptions({
@@ -54,46 +46,42 @@ export function InstanceOptions({
}
return (
- <>
-
- {t`Select Server`}
-
-
-
-
- }
- />
-
+
+
+
+
+ }
+ />
+
- {HostListEdit ? (
- <>
-
-
- Edit host options
-
-
- >
- ) : (
- <>
-
-
- >
- )}
-
- >
+ {HostListEdit ? (
+ <>
+
+
+ Edit host options
+
+
+ >
+ ) : (
+ <>
+
+
+ >
+ )}
+
);
}
diff --git a/src/frontend/src/components/items/LanguageToggle.tsx b/src/frontend/src/components/items/LanguageToggle.tsx
index 75248c2260..5874683afa 100644
--- a/src/frontend/src/components/items/LanguageToggle.tsx
+++ b/src/frontend/src/components/items/LanguageToggle.tsx
@@ -22,6 +22,7 @@ export function LanguageToggle() {
onClick={() => toggle.toggle()}
size='lg'
variant='transparent'
+ aria-label='Language toggle'
>
diff --git a/src/frontend/src/functions/auth.tsx b/src/frontend/src/functions/auth.tsx
index c94b3cec09..b714bf2e92 100644
--- a/src/frontend/src/functions/auth.tsx
+++ b/src/frontend/src/functions/auth.tsx
@@ -1,5 +1,5 @@
import { t } from '@lingui/macro';
-import { notifications } from '@mantine/notifications';
+import { notifications, showNotification } from '@mantine/notifications';
import axios from 'axios';
import type { AxiosRequestConfig } from 'axios';
import type { Location, NavigateFunction } from 'react-router-dom';
@@ -358,3 +358,172 @@ export function authApi(
// use normal api
return api(url, requestConfig);
}
+
+export const getTotpSecret = async (setTotpQr: any) => {
+ await authApi(apiUrl(ApiEndpoints.auth_totp), undefined, 'get').catch(
+ (err) => {
+ if (err.status == 404 && err.response.data.meta.secret) {
+ setTotpQr(err.response.data.meta);
+ } else {
+ const msg = err.response.data.errors[0].message;
+ showNotification({
+ title: t`Failed to set up MFA`,
+ message: msg,
+ color: 'red'
+ });
+ }
+ }
+ );
+};
+
+export function handleVerifyTotp(
+ value: string,
+ navigate: NavigateFunction,
+ location: Location
+) {
+ return () => {
+ authApi(apiUrl(ApiEndpoints.auth_totp), undefined, 'post', {
+ code: value
+ }).then(() => {
+ followRedirect(navigate, location?.state);
+ });
+ };
+}
+
+export function handlePasswordReset(
+ key: string | null,
+ password: string,
+ navigate: NavigateFunction
+) {
+ function success() {
+ notifications.show({
+ title: t`Password set`,
+ message: t`The password was set successfully. You can now login with your new password`,
+ color: 'green',
+ autoClose: false
+ });
+ navigate('/login');
+ }
+
+ function passwordError(values: any) {
+ notifications.show({
+ title: t`Reset failed`,
+ message: values?.errors.map((e: any) => e.message).join('\n'),
+ color: 'red'
+ });
+ }
+
+ // Set password with call to backend
+ api
+ .post(
+ apiUrl(ApiEndpoints.user_reset_set),
+ {
+ key: key,
+ password: password
+ },
+ { headers: { Authorization: '' } }
+ )
+ .then((val) => {
+ if (val.status === 200) {
+ success();
+ } else {
+ passwordError(val.data);
+ }
+ })
+ .catch((err) => {
+ if (err.response?.status === 400) {
+ passwordError(err.response.data);
+ } else if (err.response?.status === 401) {
+ success();
+ } else {
+ passwordError(err.response.data);
+ }
+ });
+}
+
+export function handleVerifyEmail(
+ key: string | undefined,
+ navigate: NavigateFunction
+) {
+ // Set password with call to backend
+ api
+ .post(apiUrl(ApiEndpoints.auth_email_verify), {
+ key: key
+ })
+ .then((val) => {
+ if (val.status === 200) {
+ navigate('/login');
+ }
+ });
+}
+
+export function handleChangePassword(
+ pwd1: string,
+ pwd2: string,
+ current: string,
+ navigate: NavigateFunction
+) {
+ const { clearUserState } = useUserState.getState();
+
+ function passwordError(values: any) {
+ let message: any =
+ values?.new_password ||
+ values?.new_password2 ||
+ values?.new_password1 ||
+ values?.current_password ||
+ values?.error ||
+ t`Password could not be changed`;
+
+ // If message is array
+ if (!Array.isArray(message)) {
+ message = [message];
+ }
+
+ message.forEach((msg: string) => {
+ notifications.show({
+ title: t`Error`,
+ message: msg,
+ color: 'red'
+ });
+ });
+ }
+
+ // check if passwords match
+ if (pwd1 !== pwd2) {
+ passwordError({ new_password2: t`The two password fields didn’t match` });
+ return;
+ }
+
+ // Set password with call to backend
+ api
+ .post(apiUrl(ApiEndpoints.auth_pwd_change), {
+ current_password: current,
+ new_password: pwd2
+ })
+ .then((val) => {
+ passwordError(val.data);
+ })
+ .catch((err) => {
+ if (err.status === 401) {
+ notifications.show({
+ title: t`Password Changed`,
+ message: t`The password was set successfully. You can now login with your new password`,
+ color: 'green',
+ autoClose: false
+ });
+ clearUserState();
+ clearCsrfCookie();
+ navigate('/login');
+ } else {
+ // compile errors
+ const errors: { [key: string]: string[] } = {};
+ for (const val of err.response.data.errors) {
+ if (!errors[val.param]) {
+ errors[val.param] = [];
+ }
+ errors[val.param].push(val.message);
+ }
+ passwordError(errors);
+ }
+ });
+}
diff --git a/src/frontend/src/pages/Auth/ChangePassword.tsx b/src/frontend/src/pages/Auth/ChangePassword.tsx
index 0c2d47a329..9a78c45a08 100644
--- a/src/frontend/src/pages/Auth/ChangePassword.tsx
+++ b/src/frontend/src/pages/Auth/ChangePassword.tsx
@@ -1,8 +1,6 @@
import { Trans, t } from '@lingui/macro';
import {
Button,
- Center,
- Container,
Divider,
Group,
Paper,
@@ -11,18 +9,11 @@ import {
Text
} from '@mantine/core';
import { useForm } from '@mantine/form';
-import { notifications } from '@mantine/notifications';
import { useNavigate } from 'react-router-dom';
-
-import { api } from '../../App';
-import SplashScreen from '../../components/SplashScreen';
import { StylishText } from '../../components/items/StylishText';
-import { ProtectedRoute } from '../../components/nav/Layout';
-import { LanguageContext } from '../../contexts/LanguageContext';
-import { ApiEndpoints } from '../../enums/ApiEndpoints';
-import { clearCsrfCookie } from '../../functions/auth';
-import { apiUrl } from '../../states/ApiState';
+import { handleChangePassword } from '../../functions/auth';
import { useUserState } from '../../states/UserState';
+import { Wrapper } from './Layout';
export default function Set_Password() {
const simpleForm = useForm({
@@ -36,123 +27,57 @@ export default function Set_Password() {
const user = useUserState();
const navigate = useNavigate();
- function passwordError(values: any) {
- let message: any =
- values?.new_password ||
- values?.new_password2 ||
- values?.new_password1 ||
- values?.current_password ||
- values?.error ||
- t`Password could not be changed`;
-
- // If message is array
- if (!Array.isArray(message)) {
- message = [message];
- }
-
- message.forEach((msg: string) => {
- notifications.show({
- title: t`Error`,
- message: msg,
- color: 'red'
- });
- });
- }
-
- function handleSet() {
- const { clearUserState } = useUserState.getState();
-
- // check if passwords match
- if (simpleForm.values.new_password1 !== simpleForm.values.new_password2) {
- passwordError({ new_password2: t`The two password fields didn’t match` });
- return;
- }
-
- // Set password with call to backend
- api
- .post(apiUrl(ApiEndpoints.auth_pwd_change), {
- current_password: simpleForm.values.current_password,
- new_password: simpleForm.values.new_password2
- })
- .then((val) => {
- passwordError(val.data);
- })
- .catch((err) => {
- if (err.status === 401) {
- notifications.show({
- title: t`Password Changed`,
- message: t`The password was set successfully. You can now login with your new password`,
- color: 'green',
- autoClose: false
- });
- clearUserState();
- clearCsrfCookie();
- navigate('/login');
- } else {
- // compile errors
- const errors: { [key: string]: string[] } = {};
- for (const val of err.response.data.errors) {
- if (!errors[val.param]) {
- errors[val.param] = [];
- }
- errors[val.param].push(val.message);
- }
- passwordError(errors);
- }
- });
- }
-
return (
-
-
-
-
-
-
-
- {t`Reset Password`}
-
- {user.username() && (
-
-
- {t`Username`}
- {user.username()}
-
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {user.username() && (
+
+
+ {t`User`}
+ {user.username()}
+
+
+ )}
+
+
+
+
+
+
+
+
);
}
diff --git a/src/frontend/src/pages/Auth/Layout.tsx b/src/frontend/src/pages/Auth/Layout.tsx
new file mode 100644
index 0000000000..05675750f0
--- /dev/null
+++ b/src/frontend/src/pages/Auth/Layout.tsx
@@ -0,0 +1,74 @@
+import { Trans } from '@lingui/macro';
+import {
+ Button,
+ Center,
+ Container,
+ Divider,
+ Group,
+ Loader,
+ Paper,
+ Stack
+} from '@mantine/core';
+import { Outlet, useNavigate } from 'react-router-dom';
+import SplashScreen from '../../components/SplashScreen';
+import { StylishText } from '../../components/items/StylishText';
+import { doLogout } from '../../functions/auth';
+
+export default function Layout() {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export function Wrapper({
+ children,
+ titleText,
+ logOff = false,
+ loader = false,
+ smallPadding = false
+}: Readonly<{
+ children?: React.ReactNode;
+ titleText: string;
+ logOff?: boolean;
+ loader?: boolean;
+ smallPadding?: boolean;
+}>) {
+ const navigate = useNavigate();
+
+ return (
+
+
+ {titleText}
+
+ {loader && (
+
+
+
+ )}
+ {children}
+ {logOff && (
+ <>
+
+
+ >
+ )}
+
+
+ );
+}
diff --git a/src/frontend/src/pages/Auth/Logged-In.tsx b/src/frontend/src/pages/Auth/LoggedIn.tsx
similarity index 50%
rename from src/frontend/src/pages/Auth/Logged-In.tsx
rename to src/frontend/src/pages/Auth/LoggedIn.tsx
index cff65b773f..d4feef1a10 100644
--- a/src/frontend/src/pages/Auth/Logged-In.tsx
+++ b/src/frontend/src/pages/Auth/LoggedIn.tsx
@@ -1,15 +1,14 @@
-import { Trans } from '@lingui/macro';
-import { Card, Container, Group, Loader, Stack, Text } from '@mantine/core';
+import { t } from '@lingui/macro';
import { useDebouncedCallback } from '@mantine/hooks';
import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { checkLoginState } from '../../functions/auth';
+import { Wrapper } from './Layout';
export default function Logged_In() {
const navigate = useNavigate();
const location = useLocation();
-
const checkLoginStateDebounced = useDebouncedCallback(checkLoginState, 300);
useEffect(() => {
@@ -17,19 +16,6 @@ export default function Logged_In() {
}, [navigate]);
return (
-
-
-
-
-
- Checking if you are already logged in
-
-
-
-
-
-
-
-
+
);
}
diff --git a/src/frontend/src/pages/Auth/Login.tsx b/src/frontend/src/pages/Auth/Login.tsx
index 67a032ab03..28e18697da 100644
--- a/src/frontend/src/pages/Auth/Login.tsx
+++ b/src/frontend/src/pages/Auth/Login.tsx
@@ -1,18 +1,12 @@
-import { t } from '@lingui/macro';
-import { Center, Container, Divider, Paper, Text } from '@mantine/core';
-import { useDisclosure, useToggle } from '@mantine/hooks';
+import { Trans, t } from '@lingui/macro';
+import { Anchor, Divider, Text } from '@mantine/core';
+import { useToggle } from '@mantine/hooks';
import { useEffect, useMemo } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { setApiDefaults } from '../../App';
-import SplashScreen from '../../components/SplashScreen';
import { AuthFormOptions } from '../../components/forms/AuthFormOptions';
-import {
- AuthenticationForm,
- ModeSelector,
- RegistrationForm
-} from '../../components/forms/AuthenticationForm';
+import { AuthenticationForm } from '../../components/forms/AuthenticationForm';
import { InstanceOptions } from '../../components/forms/InstanceOptions';
-import { StylishText } from '../../components/items/StylishText';
import { defaultHostKey } from '../../defaults/defaultHostList';
import {
checkLoginState,
@@ -21,6 +15,7 @@ import {
} from '../../functions/auth';
import { useServerApiState } from '../../states/ApiState';
import { useLocalState } from '../../states/LocalState';
+import { Wrapper } from './Layout';
export default function Login() {
const [hostKey, setHost, hostList] = useLocalState((state) => [
@@ -35,35 +30,29 @@ export default function Login() {
const hostname =
hostList[hostKey] === undefined ? t`No selection` : hostList[hostKey]?.name;
const [hostEdit, setHostEdit] = useToggle([false, true] as const);
- const [loginMode, setMode] = useDisclosure(true);
const navigate = useNavigate();
const location = useLocation();
const [searchParams] = useSearchParams();
-
- useEffect(() => {
- if (location.pathname === '/register') {
- setMode.close();
- } else {
- setMode.open();
- }
- }, [location]);
+ const [sso_registration, registration_enabled] = useServerApiState(
+ (state) => [state.sso_registration_enabled, state.registration_enabled]
+ );
+ const both_reg_enabled =
+ registration_enabled() || sso_registration() || false;
const LoginMessage = useMemo(() => {
const val = server.customize?.login_message;
- if (val) {
- return (
- <>
-
-
-
- dangerouslySetInnerHTML={{ __html: val }}
- />
-
- >
- );
- }
- return null;
+ if (val == undefined) return null;
+ return (
+ <>
+
+
+
+ dangerouslySetInnerHTML={{ __html: val }}
+ />
+
+ >
+ );
}, [server.customize]);
// Data manipulation functions
@@ -94,54 +83,37 @@ export default function Login() {
}
}, []);
- // Fetch server data on mount if no server data is present
- useEffect(() => {
- if (server.server === null) {
- fetchServerApiState();
- }
- }, [server]);
-
- // Main rendering block
return (
-
-
-
-
- {hostEdit ? (
-
- ) : (
- <>
-
-
- {loginMode ? t`Login` : t`Register`}
-
-
- {loginMode ? : }
- navigate(`/${newPage}`)}
- />
- {LoginMessage}
-
-
- >
+ <>
+ {hostEdit ? (
+
+ ) : (
+ <>
+
+
+ {both_reg_enabled === false && (
+
+ Don't have an account?{' '}
+ navigate('/register')}
+ >
+ Register
+
+
)}
-
-
-
-
+ {LoginMessage}
+
+
+ >
+ )}
+ >
);
}
diff --git a/src/frontend/src/pages/Auth/Logout.tsx b/src/frontend/src/pages/Auth/Logout.tsx
index 967564d738..e50f3f2bf8 100644
--- a/src/frontend/src/pages/Auth/Logout.tsx
+++ b/src/frontend/src/pages/Auth/Logout.tsx
@@ -1,9 +1,8 @@
-import { Trans } from '@lingui/macro';
-import { Card, Container, Group, Loader, Stack, Text } from '@mantine/core';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { doLogout } from '../../functions/auth';
+import { Wrapper } from './Layout';
/* Expose a route for explicit logout via URL */
export default function Logout() {
@@ -13,20 +12,5 @@ export default function Logout() {
doLogout(navigate);
}, []);
- return (
-
-
-
-
-
- Logging out
-
-
-
-
-
-
-
-
- );
+ return ;
}
diff --git a/src/frontend/src/pages/Auth/MFA.tsx b/src/frontend/src/pages/Auth/MFA.tsx
new file mode 100644
index 0000000000..a4f377e603
--- /dev/null
+++ b/src/frontend/src/pages/Auth/MFA.tsx
@@ -0,0 +1,35 @@
+import { Trans, t } from '@lingui/macro';
+import { Button, TextInput } from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { useState } from 'react';
+import { useLocation, useNavigate } from 'react-router-dom';
+import { handleMfaLogin } from '../../functions/auth';
+import { Wrapper } from './Layout';
+
+export default function Mfa() {
+ const simpleForm = useForm({ initialValues: { code: '' } });
+ const navigate = useNavigate();
+ const location = useLocation();
+ const [loginError, setLoginError] = useState(undefined);
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/frontend/src/pages/Auth/MFALogin.tsx b/src/frontend/src/pages/Auth/MFALogin.tsx
deleted file mode 100644
index 7bed8b29f1..0000000000
--- a/src/frontend/src/pages/Auth/MFALogin.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { Trans, t } from '@lingui/macro';
-import {
- Button,
- Center,
- Container,
- Paper,
- Stack,
- TextInput
-} from '@mantine/core';
-import { useForm } from '@mantine/form';
-import { useLocation, useNavigate } from 'react-router-dom';
-
-import { useState } from 'react';
-import SplashScreen from '../../components/SplashScreen';
-import { StylishText } from '../../components/items/StylishText';
-import { LanguageContext } from '../../contexts/LanguageContext';
-import { handleMfaLogin } from '../../functions/auth';
-
-export default function MFALogin() {
- const simpleForm = useForm({ initialValues: { code: '' } });
- const navigate = useNavigate();
- const location = useLocation();
- const [loginError, setLoginError] = useState(undefined);
-
- return (
-
-
-
-
-
-
- {t`Multi-Factor Login`}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/src/frontend/src/pages/Auth/MFASetup.tsx b/src/frontend/src/pages/Auth/MFASetup.tsx
index c4c7901a55..7564ef6cb0 100644
--- a/src/frontend/src/pages/Auth/MFASetup.tsx
+++ b/src/frontend/src/pages/Auth/MFASetup.tsx
@@ -1,12 +1,10 @@
import { Trans, t } from '@lingui/macro';
-import { Button, Center, Container, Stack, Title } from '@mantine/core';
-import { showNotification } from '@mantine/notifications';
+import { getTotpSecret, handleVerifyTotp } from '../../functions/auth';
+import { Wrapper } from './Layout';
+
+import { Button } from '@mantine/core';
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
-import { LanguageContext } from '../../contexts/LanguageContext';
-import { ApiEndpoints } from '../../enums/ApiEndpoints';
-import { authApi, doLogout, followRedirect } from '../../functions/auth';
-import { apiUrl } from '../../states/ApiState';
import { QrRegistrationForm } from '../Index/Settings/AccountSettings/QrRegistrationForm';
export default function MFASetup() {
@@ -16,61 +14,24 @@ export default function MFASetup() {
const [totpQr, setTotpQr] = useState<{ totp_url: string; secret: string }>();
const [value, setValue] = useState('');
- const registerTotp = async () => {
- await authApi(apiUrl(ApiEndpoints.auth_totp), undefined, 'get').catch(
- (err) => {
- if (err.status == 404 && err.response.data.meta.secret) {
- setTotpQr(err.response.data.meta);
- } else {
- const msg = err.response.data.errors[0].message;
- showNotification({
- title: t`Failed to set up MFA`,
- message: msg,
- color: 'red'
- });
- }
- }
- );
- };
-
useEffect(() => {
- if (!totpQr) {
- registerTotp();
- }
- }, [totpQr]);
+ getTotpSecret(setTotpQr);
+ }, []);
return (
-
-
-
-
-
- MFA Setup Required
-
-
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/src/frontend/src/pages/Auth/Register.tsx b/src/frontend/src/pages/Auth/Register.tsx
new file mode 100644
index 0000000000..f8a810354c
--- /dev/null
+++ b/src/frontend/src/pages/Auth/Register.tsx
@@ -0,0 +1,27 @@
+import { Trans, t } from '@lingui/macro';
+import { Anchor, Text } from '@mantine/core';
+import { useNavigate } from 'react-router-dom';
+import { RegistrationForm } from '../../components/forms/AuthenticationForm';
+import {} from '../../functions/auth';
+import { Wrapper } from './Layout';
+
+export default function Register() {
+ const navigate = useNavigate();
+
+ return (
+
+
+
+ navigate('/login')}
+ >
+ Go back to login
+
+
+
+ );
+}
diff --git a/src/frontend/src/pages/Auth/Reset.tsx b/src/frontend/src/pages/Auth/Reset.tsx
index 2529c7644e..95d3f480f3 100644
--- a/src/frontend/src/pages/Auth/Reset.tsx
+++ b/src/frontend/src/pages/Auth/Reset.tsx
@@ -1,48 +1,29 @@
import { Trans, t } from '@lingui/macro';
-import {
- Button,
- Center,
- Container,
- Stack,
- TextInput,
- Title
-} from '@mantine/core';
+import { Button, TextInput } from '@mantine/core';
import { useForm } from '@mantine/form';
import { useNavigate } from 'react-router-dom';
-
-import { LanguageContext } from '../../contexts/LanguageContext';
import { handleReset } from '../../functions/auth';
+import { Wrapper } from './Layout';
export default function Reset() {
const simpleForm = useForm({ initialValues: { email: '' } });
const navigate = useNavigate();
return (
-
-
-
-
-
- Reset password
-
-
-
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/src/frontend/src/pages/Auth/ResetPassword.tsx b/src/frontend/src/pages/Auth/ResetPassword.tsx
index c47bb96f42..845e346dfe 100644
--- a/src/frontend/src/pages/Auth/ResetPassword.tsx
+++ b/src/frontend/src/pages/Auth/ResetPassword.tsx
@@ -1,114 +1,47 @@
import { Trans, t } from '@lingui/macro';
-import {
- Button,
- Center,
- Container,
- PasswordInput,
- Stack,
- Title
-} from '@mantine/core';
+import { Button, PasswordInput } from '@mantine/core';
import { useForm } from '@mantine/form';
import { notifications } from '@mantine/notifications';
import { useEffect } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
-import { api } from '../../App';
-import { LanguageContext } from '../../contexts/LanguageContext';
-import { ApiEndpoints } from '../../enums/ApiEndpoints';
-import { apiUrl } from '../../states/ApiState';
+import { handlePasswordReset } from '../../functions/auth';
+import { Wrapper } from './Layout';
export default function ResetPassword() {
const simpleForm = useForm({ initialValues: { password: '' } });
const [searchParams] = useSearchParams();
const navigate = useNavigate();
-
const key = searchParams.get('key');
- function invalidKey() {
- notifications.show({
- title: t`Key invalid`,
- message: t`You need to provide a valid key to set a new password. Check your inbox for a reset link.`,
- color: 'red'
- });
- navigate('/login');
- }
-
- function success() {
- notifications.show({
- title: t`Password set`,
- message: t`The password was set successfully. You can now login with your new password`,
- color: 'green',
- autoClose: false
- });
- navigate('/login');
- }
-
- function passwordError(values: any) {
- notifications.show({
- title: t`Reset failed`,
- message: values?.errors.map((e: any) => e.message).join('\n'),
- color: 'red'
- });
- }
-
+ // make sure we have a key
useEffect(() => {
- // make sure we have a key
if (!key) {
- invalidKey();
+ notifications.show({
+ title: t`Key invalid`,
+ message: t`You need to provide a valid key to set a new password. Check your inbox for a reset link.`,
+ color: 'red',
+ autoClose: false
+ });
}
}, [key]);
- function handleSet() {
- // Set password with call to backend
- api
- .post(
- apiUrl(ApiEndpoints.user_reset_set),
- {
- key: key,
- password: simpleForm.values.password
- },
- { headers: { Authorization: '' } }
- )
- .then((val) => {
- if (val.status === 200) {
- success();
- } else {
- passwordError(val.data);
- }
- })
- .catch((err) => {
- if (err.response?.status === 400) {
- passwordError(err.response.data);
- } else if (err.response?.status === 401) {
- success();
- } else {
- passwordError(err.response.data);
- }
- });
- }
-
return (
-
-
-
-
-
- Set new password
-
-
-
-
-
-
-
-
-
+
+
+
+
);
}
diff --git a/src/frontend/src/pages/Auth/VerifyEmail.tsx b/src/frontend/src/pages/Auth/VerifyEmail.tsx
index c6e098252d..64fc040a69 100644
--- a/src/frontend/src/pages/Auth/VerifyEmail.tsx
+++ b/src/frontend/src/pages/Auth/VerifyEmail.tsx
@@ -1,61 +1,33 @@
import { Trans, t } from '@lingui/macro';
-import { Button, Center, Container, Stack, Title } from '@mantine/core';
+import { Button } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
-import { api } from '../../App';
-import { LanguageContext } from '../../contexts/LanguageContext';
-import { ApiEndpoints } from '../../enums/ApiEndpoints';
-import { apiUrl } from '../../states/ApiState';
+import { handleVerifyEmail } from '../../functions/auth';
+import { Wrapper } from './Layout';
export default function VerifyEmail() {
const { key } = useParams();
const navigate = useNavigate();
- function invalidKey() {
- notifications.show({
- title: t`Key invalid`,
- message: t`You need to provide a valid key.`,
- color: 'red'
- });
- navigate('/login');
- }
-
+ // make sure we have a key
useEffect(() => {
- // make sure we have a key
if (!key) {
- invalidKey();
+ notifications.show({
+ title: t`Key invalid`,
+ message: t`You need to provide a valid key.`,
+ color: 'red'
+ });
+ navigate('/login');
}
}, [key]);
- function handleSet() {
- // Set password with call to backend
- api
- .post(apiUrl(ApiEndpoints.auth_email_verify), {
- key: key
- })
- .then((val) => {
- if (val.status === 200) {
- navigate('/login');
- }
- });
- }
-
return (
-
-
-
-
-
- Verify Email
-
-
-
-
-
-
+
+
+
);
}
diff --git a/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx b/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx
index 1b5b32d4f1..ab48b75304 100644
--- a/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx
+++ b/src/frontend/src/pages/Index/Settings/AccountSettings/SecurityContent.tsx
@@ -107,7 +107,6 @@ function EmailSection() {
queryFn: () =>
authApi(apiUrl(ApiEndpoints.auth_email)).then((res) => res.data.data)
});
-
const emailAvailable = useMemo(() => {
return data == undefined || data.length == 0;
}, [data]);
diff --git a/src/frontend/src/router.tsx b/src/frontend/src/router.tsx
index 4fc9a2a6df..4ffb6ec6b8 100644
--- a/src/frontend/src/router.tsx
+++ b/src/frontend/src/router.tsx
@@ -7,6 +7,10 @@ import { Loadable } from './functions/loading';
export const LayoutComponent = Loadable(
lazy(() => import('./components/nav/Layout'))
);
+export const LoginLayoutComponent = Loadable(
+ lazy(() => import('./pages/Auth/Layout'))
+);
+
export const Home = Loadable(lazy(() => import('./pages/Index/Home')));
export const CompanyDetail = Loadable(
@@ -103,17 +107,19 @@ export const AdminCenter = Loadable(
export const NotFound = Loadable(
lazy(() => import('./components/errors/NotFound'))
);
+
+// Auth
export const Login = Loadable(lazy(() => import('./pages/Auth/Login')));
-export const MFALogin = Loadable(lazy(() => import('./pages/Auth/MFALogin')));
-export const MFASetup = Loadable(lazy(() => import('./pages/Auth/MFASetup')));
+export const LoggedIn = Loadable(lazy(() => import('./pages/Auth/LoggedIn')));
export const Logout = Loadable(lazy(() => import('./pages/Auth/Logout')));
-export const Logged_In = Loadable(lazy(() => import('./pages/Auth/Logged-In')));
-export const Reset = Loadable(lazy(() => import('./pages/Auth/Reset')));
+export const Register = Loadable(lazy(() => import('./pages/Auth/Register')));
+export const Mfa = Loadable(lazy(() => import('./pages/Auth/MFA')));
+export const MfaSetup = Loadable(lazy(() => import('./pages/Auth/MFASetup')));
export const ChangePassword = Loadable(
lazy(() => import('./pages/Auth/ChangePassword'))
);
-
+export const Reset = Loadable(lazy(() => import('./pages/Auth/Reset')));
export const ResetPassword = Loadable(
lazy(() => import('./pages/Auth/ResetPassword'))
);
@@ -173,16 +179,20 @@ export const routes = (
} />
- }>
+ }
+ errorElement={}
+ >
} />,
- } />,
- } />,
- } />,
+ } />
} />,
- } />
+ } />,
+ } />,
+ } />,
+ } />
} />
} />
- } />
} />
diff --git a/src/frontend/tests/pui_settings.spec.ts b/src/frontend/tests/pui_settings.spec.ts
index 9afe28f219..f47e5577ea 100644
--- a/src/frontend/tests/pui_settings.spec.ts
+++ b/src/frontend/tests/pui_settings.spec.ts
@@ -13,7 +13,7 @@ test('Settings - Language / Color', async ({ page }) => {
await page.getByRole('button', { name: 'Ally Access' }).click();
await page.getByRole('menuitem', { name: 'Logout' }).click();
await page.getByRole('button', { name: 'Send me an email' }).click();
- await page.getByRole('button').nth(3).click();
+ await page.getByLabel('Language toggle').click();
await page.getByLabel('Select language').first().click();
await page.getByRole('option', { name: 'German' }).click();
await page.waitForTimeout(200);