From d64fbfc254cfb5de572bde0e2096be6ff751d788 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Mon, 22 Jan 2024 10:39:06 +0000 Subject: [PATCH] [PUI] Registration (#6309) * optimize login layout * move auth/reg up * [PUI] Registration Fixes #6282 * [PUI] Registration Fixes #6282 * fix type * style: cleaned imports --- .../components/forms/AuthenticationForm.tsx | 253 +++++++++++++----- src/frontend/src/enums/ApiEndpoints.tsx | 1 + src/frontend/src/pages/Auth/Login.tsx | 21 +- src/frontend/src/states/ApiState.tsx | 2 + 4 files changed, 207 insertions(+), 70 deletions(-) diff --git a/src/frontend/src/components/forms/AuthenticationForm.tsx b/src/frontend/src/components/forms/AuthenticationForm.tsx index b0ff229480..b0f6b5f0e5 100644 --- a/src/frontend/src/components/forms/AuthenticationForm.tsx +++ b/src/frontend/src/components/forms/AuthenticationForm.tsx @@ -4,7 +4,6 @@ import { Button, Group, Loader, - Paper, PasswordInput, Stack, Text, @@ -17,7 +16,10 @@ import { IconCheck } from '@tabler/icons-react'; import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { api } from '../../App'; +import { ApiPaths } from '../../enums/ApiEndpoints'; import { doClassicLogin, doSimpleLogin } from '../../functions/auth'; +import { apiUrl } from '../../states/ApiState'; export function AuthenticationForm() { const classicForm = useForm({ @@ -79,50 +81,178 @@ export function AuthenticationForm() { } return ( - - - Welcome, log in below - -
{})}> - {classicLoginMode ? ( - - - - - navigate('/reset-password')} - > - Reset password - - - - ) : ( - - - - )} + {})}> + {classicLoginMode ? ( + + + + + navigate('/reset-password')} + > + Reset password + + + + ) : ( + + + + )} - + + setMode.toggle()} + > + {classicLoginMode ? ( + Send me an email + ) : ( + Use username and password + )} + + + + + ); +} + +export function RegistrationForm() { + const registrationForm = useForm({ + initialValues: { username: '', email: '', password1: '', password2: '' } + }); + const navigate = useNavigate(); + const [isRegistering, setIsRegistering] = useState(false); + + function handleRegistration() { + setIsRegistering(true); + api + .post(apiUrl(ApiPaths.user_register), registrationForm.values, { + headers: { Authorization: '' } + }) + .then((ret) => { + if (ret?.status === 204) { + setIsRegistering(false); + notifications.show({ + title: t`Registration successful`, + message: t`Please confirm your email address to complete the registration`, + color: 'green', + icon: + }); + navigate('/home'); + } + }) + .catch((err) => { + if (err.response.status === 400) { + setIsRegistering(false); + for (const [key, value] of Object.entries(err.response.data)) { + registrationForm.setFieldError(key, value as string); + } + let err_msg = ''; + if (err.response?.data?.non_field_errors) { + err_msg = err.response.data.non_field_errors; + } + notifications.show({ + title: t`Input error`, + message: t`Check your input and try again. ` + err_msg, + color: 'red', + autoClose: 30000 + }); + } + }); + } + + return ( +
{})}> + + + + + + + + + + +
+ ); +} + +export function ModeSelector({ + loginMode, + setMode +}: { + loginMode: boolean; + setMode: any; +}) { + return ( + + {loginMode ? ( + <> + Don't have an account?{' '} setMode.toggle()} > - {classicLoginMode ? ( - Send me an email - ) : ( - I will use username and password - )} + Register - -
- -
+ + ) : ( + setMode.toggle()} + > + Go back to login + + )} + ); } diff --git a/src/frontend/src/enums/ApiEndpoints.tsx b/src/frontend/src/enums/ApiEndpoints.tsx index d5f676c73a..24388bec07 100644 --- a/src/frontend/src/enums/ApiEndpoints.tsx +++ b/src/frontend/src/enums/ApiEndpoints.tsx @@ -20,6 +20,7 @@ export enum ApiPaths { user_email_primary = 'api-user-email-primary', user_email_remove = 'api-user-email-remove', user_logout = 'api-user-logout', + user_register = 'api-user-register', user_list = 'api-user-list', group_list = 'api-group-list', diff --git a/src/frontend/src/pages/Auth/Login.tsx b/src/frontend/src/pages/Auth/Login.tsx index 00abb1f5b0..16de1b7509 100644 --- a/src/frontend/src/pages/Auth/Login.tsx +++ b/src/frontend/src/pages/Auth/Login.tsx @@ -1,12 +1,16 @@ -import { t } from '@lingui/macro'; -import { Center, Container } from '@mantine/core'; -import { useToggle } from '@mantine/hooks'; +import { Trans, t } from '@lingui/macro'; +import { Center, Container, Paper, Text } from '@mantine/core'; +import { useDisclosure, useToggle } from '@mantine/hooks'; import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { setApiDefaults } from '../../App'; import { AuthFormOptions } from '../../components/forms/AuthFormOptions'; -import { AuthenticationForm } from '../../components/forms/AuthenticationForm'; +import { + AuthenticationForm, + ModeSelector, + RegistrationForm +} from '../../components/forms/AuthenticationForm'; import { InstanceOptions } from '../../components/forms/InstanceOptions'; import { defaultHostKey } from '../../defaults/defaultHostList'; import { checkLoginState } from '../../functions/auth'; @@ -26,6 +30,7 @@ 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(); // Data manipulation functions @@ -63,7 +68,13 @@ export default function Login() { /> ) : ( <> - + + + Welcome, log in below + + {loginMode ? : } + + )} diff --git a/src/frontend/src/states/ApiState.tsx b/src/frontend/src/states/ApiState.tsx index 473e84bb6d..e4a17fd4c1 100644 --- a/src/frontend/src/states/ApiState.tsx +++ b/src/frontend/src/states/ApiState.tsx @@ -97,6 +97,8 @@ export function apiEndpoint(path: ApiPaths): string { return 'auth/emails/:id/primary/'; case ApiPaths.user_logout: return 'auth/logout/'; + case ApiPaths.user_register: + return 'auth/registration/'; case ApiPaths.currency_list: return 'currency/exchange/'; case ApiPaths.currency_refresh: