mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 07:40:32 +00:00
feat(webapp): sign-up restrictions
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React, { createContext } from 'react';
|
||||||
|
import { useAuthMetadata } from '@/hooks/query';
|
||||||
|
import { Spinner } from '@blueprintjs/core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const AuthMetaBootContext = createContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boots the authentication page metadata.
|
||||||
|
*/
|
||||||
|
function AuthMetaBootProvider({ ...props }) {
|
||||||
|
const { isLoading: isAuthMetaLoading, data: authMeta } = useAuthMetadata();
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
isAuthMetaLoading,
|
||||||
|
signupDisabled: authMeta?.meta?.signup_disabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isAuthMetaLoading) {
|
||||||
|
return (
|
||||||
|
<SpinnerRoot>
|
||||||
|
<Spinner size={30} value={null} />
|
||||||
|
</SpinnerRoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <AuthMetaBootContext.Provider value={state} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useAuthMetaBoot = () => React.useContext(AuthMetaBootContext);
|
||||||
|
|
||||||
|
export { AuthMetaBootContext, AuthMetaBootProvider, useAuthMetaBoot };
|
||||||
|
|
||||||
|
const SpinnerRoot = styled.div`
|
||||||
|
margin-top: 5rem;
|
||||||
|
`;
|
||||||
@@ -10,12 +10,11 @@ import { Icon, FormattedMessage as T } from '@/components';
|
|||||||
import { useIsAuthenticated } from '@/hooks/state';
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
|
|
||||||
import '@/style/pages/Authentication/Auth.scss';
|
import '@/style/pages/Authentication/Auth.scss';
|
||||||
|
import { AuthMetaBootProvider } from './AuthMetaBoot';
|
||||||
|
|
||||||
export function Authentication() {
|
export function Authentication() {
|
||||||
const to = { pathname: '/' };
|
const to = { pathname: '/' };
|
||||||
const location = useLocation();
|
|
||||||
const isAuthenticated = useIsAuthenticated();
|
const isAuthenticated = useIsAuthenticated();
|
||||||
const locationKey = location.pathname;
|
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
return <Redirect to={to} />;
|
return <Redirect to={to} />;
|
||||||
@@ -28,30 +27,41 @@ export function Authentication() {
|
|||||||
<Icon icon="bigcapital" height={37} width={214} />
|
<Icon icon="bigcapital" height={37} width={214} />
|
||||||
</AuthLogo>
|
</AuthLogo>
|
||||||
|
|
||||||
<TransitionGroup>
|
<AuthMetaBootProvider>
|
||||||
<CSSTransition
|
<AuthenticationRoutes />
|
||||||
timeout={500}
|
</AuthMetaBootProvider>
|
||||||
key={locationKey}
|
|
||||||
classNames="authTransition"
|
|
||||||
>
|
|
||||||
<Switch>
|
|
||||||
{authenticationRoutes.map((route, index) => (
|
|
||||||
<Route
|
|
||||||
key={index}
|
|
||||||
path={route.path}
|
|
||||||
exact={route.exact}
|
|
||||||
component={route.component}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Switch>
|
|
||||||
</CSSTransition>
|
|
||||||
</TransitionGroup>
|
|
||||||
</AuthInsider>
|
</AuthInsider>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
</BodyClassName>
|
</BodyClassName>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AuthenticationRoutes() {
|
||||||
|
const location = useLocation();
|
||||||
|
const locationKey = location.pathname;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TransitionGroup>
|
||||||
|
<CSSTransition
|
||||||
|
timeout={500}
|
||||||
|
key={locationKey}
|
||||||
|
classNames="authTransition"
|
||||||
|
>
|
||||||
|
<Switch>
|
||||||
|
{authenticationRoutes.map((route, index) => (
|
||||||
|
<Route
|
||||||
|
key={index}
|
||||||
|
path={route.path}
|
||||||
|
exact={route.exact}
|
||||||
|
component={route.component}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Switch>
|
||||||
|
</CSSTransition>
|
||||||
|
</TransitionGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const AuthPage = styled.div``;
|
const AuthPage = styled.div``;
|
||||||
const AuthInsider = styled.div`
|
const AuthInsider = styled.div`
|
||||||
width: 384px;
|
width: 384px;
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ import {
|
|||||||
AuthFooterLink,
|
AuthFooterLink,
|
||||||
AuthInsiderCard,
|
AuthInsiderCard,
|
||||||
} from './_components';
|
} from './_components';
|
||||||
|
import { useAuthMetaBoot } from './AuthMetaBoot';
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
crediential: '',
|
crediential: '',
|
||||||
password: '',
|
password: '',
|
||||||
keepLoggedIn: false
|
keepLoggedIn: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,12 +65,15 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function LoginFooterLinks() {
|
function LoginFooterLinks() {
|
||||||
|
const { signupDisabled } = useAuthMetaBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthFooterLinks>
|
<AuthFooterLinks>
|
||||||
<AuthFooterLink>
|
{!signupDisabled && (
|
||||||
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
<AuthFooterLink>
|
||||||
</AuthFooterLink>
|
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
||||||
|
</AuthFooterLink>
|
||||||
|
)}
|
||||||
<AuthFooterLink>
|
<AuthFooterLink>
|
||||||
<Link to={'/auth/send_reset_password'}>
|
<Link to={'/auth/send_reset_password'}>
|
||||||
<T id={'forget_my_password'} />
|
<T id={'forget_my_password'} />
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import AuthInsider from '@/containers/Authentication/AuthInsider';
|
|||||||
import { useAuthLogin, useAuthRegister } from '@/hooks/query/authentication';
|
import { useAuthLogin, useAuthRegister } from '@/hooks/query/authentication';
|
||||||
|
|
||||||
import RegisterForm from './RegisterForm';
|
import RegisterForm from './RegisterForm';
|
||||||
import { RegisterSchema, transformRegisterErrorsToForm } from './utils';
|
import { RegisterSchema, transformRegisterErrorsToForm, transformRegisterToastMessages } from './utils';
|
||||||
import {
|
import {
|
||||||
AuthFooterLinks,
|
AuthFooterLinks,
|
||||||
AuthFooterLink,
|
AuthFooterLink,
|
||||||
@@ -57,7 +57,11 @@ export default function RegisterUserForm() {
|
|||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
const formErrors = transformRegisterErrorsToForm(errors);
|
const formErrors = transformRegisterErrorsToForm(errors);
|
||||||
|
const toastMessages = transformRegisterToastMessages(errors);
|
||||||
|
|
||||||
|
toastMessages.forEach((toastMessage) => {
|
||||||
|
AppToaster.show(toastMessage);
|
||||||
|
});
|
||||||
setErrors(formErrors);
|
setErrors(formErrors);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
} from './_components';
|
} from './_components';
|
||||||
import ResetPasswordForm from './ResetPasswordForm';
|
import ResetPasswordForm from './ResetPasswordForm';
|
||||||
import { ResetPasswordSchema } from './utils';
|
import { ResetPasswordSchema } from './utils';
|
||||||
|
import { useAuthMetaBoot } from './AuthMetaBoot';
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
password: '',
|
password: '',
|
||||||
@@ -79,12 +80,15 @@ export default function ResetPassword() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ResetPasswordFooterLinks() {
|
function ResetPasswordFooterLinks() {
|
||||||
|
const { signupDisabled } = useAuthMetaBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthFooterLinks>
|
<AuthFooterLinks>
|
||||||
<AuthFooterLink>
|
{!signupDisabled && (
|
||||||
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
<AuthFooterLink>
|
||||||
</AuthFooterLink>
|
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
||||||
|
</AuthFooterLink>
|
||||||
|
)}
|
||||||
<AuthFooterLink>
|
<AuthFooterLink>
|
||||||
Return to <Link to={'/auth/login'}>Sign In</Link>
|
Return to <Link to={'/auth/login'}>Sign In</Link>
|
||||||
</AuthFooterLink>
|
</AuthFooterLink>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
transformSendResetPassErrorsToToasts,
|
transformSendResetPassErrorsToToasts,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import AuthInsider from '@/containers/Authentication/AuthInsider';
|
import AuthInsider from '@/containers/Authentication/AuthInsider';
|
||||||
|
import { useAuthMetaBoot } from './AuthMetaBoot';
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
crediential: '',
|
crediential: '',
|
||||||
@@ -27,7 +28,7 @@ const initialValues = {
|
|||||||
/**
|
/**
|
||||||
* Send reset password page.
|
* Send reset password page.
|
||||||
*/
|
*/
|
||||||
export default function SendResetPassword({ requestSendResetPassword }) {
|
export default function SendResetPassword() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { mutateAsync: sendResetPasswordMutate } = useAuthSendResetPassword();
|
const { mutateAsync: sendResetPasswordMutate } = useAuthSendResetPassword();
|
||||||
|
|
||||||
@@ -75,12 +76,15 @@ export default function SendResetPassword({ requestSendResetPassword }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SendResetPasswordFooterLinks() {
|
function SendResetPasswordFooterLinks() {
|
||||||
|
const { signupDisabled } = useAuthMetaBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthFooterLinks>
|
<AuthFooterLinks>
|
||||||
<AuthFooterLink>
|
{!signupDisabled && (
|
||||||
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
<AuthFooterLink>
|
||||||
</AuthFooterLink>
|
Don't have an account? <Link to={'/auth/register'}>Sign up</Link>
|
||||||
|
</AuthFooterLink>
|
||||||
|
)}
|
||||||
<AuthFooterLink>
|
<AuthFooterLink>
|
||||||
Return to <Link to={'/auth/login'}>Sign In</Link>
|
Return to <Link to={'/auth/login'}>Sign In</Link>
|
||||||
</AuthFooterLink>
|
</AuthFooterLink>
|
||||||
|
|||||||
@@ -94,3 +94,29 @@ export const transformRegisterErrorsToForm = (errors) => {
|
|||||||
}
|
}
|
||||||
return formErrors;
|
return formErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const transformRegisterToastMessages = (errors) => {
|
||||||
|
const toastErrors = [];
|
||||||
|
|
||||||
|
if (errors.some((e) => e.type === 'SIGNUP_NOT_ALLOWED_EMAIL_DOMAIN')) {
|
||||||
|
toastErrors.push({
|
||||||
|
message:
|
||||||
|
'The sign-up is restricted, the given email domain is not allowed to sign-up.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
errors.some((e) => e.type === 'SIGNUP_NOT_ALLOWED_EMAIL_ADDRESS')
|
||||||
|
) {
|
||||||
|
toastErrors.push({
|
||||||
|
message:
|
||||||
|
'The sign-up is restricted, the given email address is not allowed to sign-up.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
} else if (errors.find((e) => e.type === 'SIGNUP_RESTRICTED')) {
|
||||||
|
toastErrors.push({
|
||||||
|
message: 'Sign-up is disabled, and no new accounts can be created.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return toastErrors;
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import { setCookie } from '../../utils';
|
import { setCookie } from '../../utils';
|
||||||
|
import { useRequestQuery } from '../useQueryRequest';
|
||||||
|
import t from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the response data to cookies.
|
* Saves the response data to cookies.
|
||||||
@@ -70,3 +72,21 @@ export const useAuthResetPassword = (props) => {
|
|||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the authentication page metadata.
|
||||||
|
*/
|
||||||
|
export const useAuthMetadata = (props) => {
|
||||||
|
return useRequestQuery(
|
||||||
|
[t.AUTH_METADATA_PAGE,],
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
url: `auth/meta`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
select: (res) => res.data,
|
||||||
|
defaultData: {},
|
||||||
|
...props,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
const Authentication = {
|
||||||
|
AUTH_METADATA_PAGE: 'AUTH_META_PAGE'
|
||||||
|
}
|
||||||
|
|
||||||
const ACCOUNTS = {
|
const ACCOUNTS = {
|
||||||
ACCOUNT: 'ACCOUNT',
|
ACCOUNT: 'ACCOUNT',
|
||||||
ACCOUNT_TRANSACTION: 'ACCOUNT_TRANSACTION',
|
ACCOUNT_TRANSACTION: 'ACCOUNT_TRANSACTION',
|
||||||
@@ -217,6 +221,7 @@ const DASHBOARD = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
...Authentication,
|
||||||
...ACCOUNTS,
|
...ACCOUNTS,
|
||||||
...BILLS,
|
...BILLS,
|
||||||
...VENDORS,
|
...VENDORS,
|
||||||
|
|||||||
Reference in New Issue
Block a user