mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Registration (#6309)
* optimize login layout * move auth/reg up * [PUI] Registration Fixes #6282 * [PUI] Registration Fixes #6282 * fix type * style: cleaned imports
This commit is contained in:
parent
2fbb8c757f
commit
d64fbfc254
@ -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 (
|
||||
<Paper radius="md" p="xl" withBorder>
|
||||
<Text size="lg" weight={500}>
|
||||
<Trans>Welcome, log in below</Trans>
|
||||
</Text>
|
||||
<form onSubmit={classicForm.onSubmit(() => {})}>
|
||||
{classicLoginMode ? (
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Username`}
|
||||
placeholder={t`Your username`}
|
||||
{...classicForm.getInputProps('username')}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password`}
|
||||
placeholder={t`Your password`}
|
||||
{...classicForm.getInputProps('password')}
|
||||
/>
|
||||
<Group position="apart" mt="0">
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => navigate('/reset-password')}
|
||||
>
|
||||
<Trans>Reset password</Trans>
|
||||
</Anchor>
|
||||
</Group>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Email`}
|
||||
description={t`We will send you a link to login - if you are registered`}
|
||||
placeholder="email@example.org"
|
||||
{...simpleForm.getInputProps('email')}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
<form onSubmit={classicForm.onSubmit(() => {})}>
|
||||
{classicLoginMode ? (
|
||||
<Stack spacing={0}>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Username`}
|
||||
placeholder={t`Your username`}
|
||||
{...classicForm.getInputProps('username')}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password`}
|
||||
placeholder={t`Your password`}
|
||||
{...classicForm.getInputProps('password')}
|
||||
/>
|
||||
<Group position="apart" mt="0">
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => navigate('/reset-password')}
|
||||
>
|
||||
<Trans>Reset password</Trans>
|
||||
</Anchor>
|
||||
</Group>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Email`}
|
||||
description={t`We will send you a link to login - if you are registered`}
|
||||
placeholder="email@example.org"
|
||||
{...simpleForm.getInputProps('email')}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Group position="apart" mt="xl">
|
||||
<Group position="apart" mt="xl">
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => setMode.toggle()}
|
||||
>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Send me an email</Trans>
|
||||
) : (
|
||||
<Trans>Use username and password</Trans>
|
||||
)}
|
||||
</Anchor>
|
||||
<Button type="submit" disabled={isLoggingIn} onClick={handleLogin}>
|
||||
{isLoggingIn ? (
|
||||
<Loader size="sm" />
|
||||
) : (
|
||||
<>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Log In</Trans>
|
||||
) : (
|
||||
<Trans>Send Email</Trans>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function RegistrationForm() {
|
||||
const registrationForm = useForm({
|
||||
initialValues: { username: '', email: '', password1: '', password2: '' }
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
const [isRegistering, setIsRegistering] = useState<boolean>(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: <IconCheck size="1rem" />
|
||||
});
|
||||
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 (
|
||||
<form onSubmit={registrationForm.onSubmit(() => {})}>
|
||||
<Stack spacing={0}>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Username`}
|
||||
placeholder={t`Your username`}
|
||||
{...registrationForm.getInputProps('username')}
|
||||
/>
|
||||
<TextInput
|
||||
required
|
||||
label={t`Email`}
|
||||
description={t`This will be used for a confirmation`}
|
||||
placeholder="email@example.org"
|
||||
{...registrationForm.getInputProps('email')}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password`}
|
||||
placeholder={t`Your password`}
|
||||
{...registrationForm.getInputProps('password1')}
|
||||
/>
|
||||
<PasswordInput
|
||||
required
|
||||
label={t`Password repeat`}
|
||||
placeholder={t`Repeat password`}
|
||||
{...registrationForm.getInputProps('password2')}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Group position="apart" mt="xl">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isRegistering}
|
||||
onClick={handleRegistration}
|
||||
fullWidth
|
||||
>
|
||||
<Trans>Register</Trans>
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function ModeSelector({
|
||||
loginMode,
|
||||
setMode
|
||||
}: {
|
||||
loginMode: boolean;
|
||||
setMode: any;
|
||||
}) {
|
||||
return (
|
||||
<Text ta="center" size={'xs'} mt={'md'}>
|
||||
{loginMode ? (
|
||||
<>
|
||||
<Trans>Don't have an account?</Trans>{' '}
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
@ -130,27 +260,20 @@ export function AuthenticationForm() {
|
||||
size="xs"
|
||||
onClick={() => setMode.toggle()}
|
||||
>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Send me an email</Trans>
|
||||
) : (
|
||||
<Trans>I will use username and password</Trans>
|
||||
)}
|
||||
<Trans>Register</Trans>
|
||||
</Anchor>
|
||||
<Button type="submit" disabled={isLoggingIn} onClick={handleLogin}>
|
||||
{isLoggingIn ? (
|
||||
<Loader size="sm" />
|
||||
) : (
|
||||
<>
|
||||
{classicLoginMode ? (
|
||||
<Trans>Log In</Trans>
|
||||
) : (
|
||||
<Trans>Send Email</Trans>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Paper>
|
||||
</>
|
||||
) : (
|
||||
<Anchor
|
||||
component="button"
|
||||
type="button"
|
||||
color="dimmed"
|
||||
size="xs"
|
||||
onClick={() => setMode.toggle()}
|
||||
>
|
||||
<Trans>Go back to login</Trans>
|
||||
</Anchor>
|
||||
)}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
@ -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',
|
||||
|
@ -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() {
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<AuthenticationForm />
|
||||
<Paper radius="md" p="xl" withBorder>
|
||||
<Text size="lg" weight={500}>
|
||||
<Trans>Welcome, log in below</Trans>
|
||||
</Text>
|
||||
{loginMode ? <AuthenticationForm /> : <RegistrationForm />}
|
||||
<ModeSelector loginMode={loginMode} setMode={setMode} />
|
||||
</Paper>
|
||||
<AuthFormOptions hostname={hostname} toggleHostEdit={setHostEdit} />
|
||||
</>
|
||||
)}
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user