mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: sync the isVerified state of authed user
This commit is contained in:
@@ -35,7 +35,7 @@ export default class SystemUser extends SystemModel {
|
|||||||
* Virtual attributes.
|
* Virtual attributes.
|
||||||
*/
|
*/
|
||||||
static get virtualAttributes() {
|
static get virtualAttributes() {
|
||||||
return ['fullName', 'isDeleted', 'isInviteAccepted'];
|
return ['fullName', 'isDeleted', 'isInviteAccepted', 'isVerified'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'moment/locale/ar-ly';
|
|||||||
import 'moment/locale/es-us';
|
import 'moment/locale/es-us';
|
||||||
|
|
||||||
import AppIntlLoader from './AppIntlLoader';
|
import AppIntlLoader from './AppIntlLoader';
|
||||||
import PrivateRoute from '@/components/Guards/PrivateRoute';
|
import { EnsureAuthenticated } from '@/components/Guards/EnsureAuthenticated';
|
||||||
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
|
||||||
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
|
||||||
import { Authentication } from '@/containers/Authentication/Authentication';
|
import { Authentication } from '@/containers/Authentication/Authentication';
|
||||||
@@ -20,6 +20,9 @@ import { queryConfig } from '../hooks/query/base';
|
|||||||
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
|
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
|
||||||
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
|
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
|
||||||
|
|
||||||
|
const EmailConfirmation = LazyLoader({
|
||||||
|
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
||||||
|
});
|
||||||
const RegisterVerify = LazyLoader({
|
const RegisterVerify = LazyLoader({
|
||||||
loader: () => import('@/containers/Authentication/RegisterVerify'),
|
loader: () => import('@/containers/Authentication/RegisterVerify'),
|
||||||
});
|
});
|
||||||
@@ -33,24 +36,28 @@ function AppInsider({ history }) {
|
|||||||
<DashboardThemeProvider>
|
<DashboardThemeProvider>
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route path={'/auth/register/verify'}>
|
||||||
|
<EnsureAuthenticated>
|
||||||
|
<RegisterVerify />
|
||||||
|
</EnsureAuthenticated>
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path={'/auth/email_confirmation'}>
|
||||||
|
<EmailConfirmation />
|
||||||
|
</Route>
|
||||||
|
|
||||||
<Route path={'/auth'}>
|
<Route path={'/auth'}>
|
||||||
<EnsureAuthNotAuthenticated>
|
<EnsureAuthNotAuthenticated>
|
||||||
<Authentication />
|
<Authentication />
|
||||||
</EnsureAuthNotAuthenticated>
|
</EnsureAuthNotAuthenticated>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={'/register/verify'}>
|
|
||||||
<PrivateRoute>
|
|
||||||
<RegisterVerify />
|
|
||||||
</PrivateRoute>
|
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path={'/'}>
|
<Route path={'/'}>
|
||||||
<PrivateRoute>
|
<EnsureAuthenticated>
|
||||||
<EnsureUserEmailVerified>
|
<EnsureUserEmailVerified>
|
||||||
<DashboardPrivatePages />
|
<DashboardPrivatePages />
|
||||||
</EnsureUserEmailVerified>
|
</EnsureUserEmailVerified>
|
||||||
</PrivateRoute>
|
</EnsureAuthenticated>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
useAuthenticatedAccount,
|
useAuthenticatedAccount,
|
||||||
useCurrentOrganization,
|
useCurrentOrganization,
|
||||||
@@ -116,6 +116,14 @@ export function useApplicationBoot() {
|
|||||||
isBooted.current = true;
|
isBooted.current = true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// Reset the loading states once the hook unmount.
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
isAuthUserLoading && !isBooted.current && stopLoading();
|
||||||
|
isOrgLoading && !isBooted.current && stopLoading();
|
||||||
|
},
|
||||||
|
[isAuthUserLoading, isOrgLoading, stopLoading],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading: isOrgLoading || isAuthUserLoading,
|
isLoading: isOrgLoading || isAuthUserLoading,
|
||||||
|
|||||||
@@ -3,12 +3,20 @@ import React from 'react';
|
|||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
|
|
||||||
interface PrivateRouteProps {
|
interface EnsureAuthNotAuthenticatedProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
redirectTo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function EnsureAuthNotAuthenticated({ children }: PrivateRouteProps) {
|
export function EnsureAuthNotAuthenticated({
|
||||||
|
children,
|
||||||
|
redirectTo = '/',
|
||||||
|
}: EnsureAuthNotAuthenticatedProps) {
|
||||||
const isAuthenticated = useIsAuthenticated();
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
return !isAuthenticated ? children : <Redirect to={{ pathname: '/' }} />;
|
return !isAuthenticated ? (
|
||||||
|
<>{children}</>
|
||||||
|
) : (
|
||||||
|
<Redirect to={{ pathname: redirectTo }} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import BodyClassName from 'react-body-classname';
|
|
||||||
import { Redirect } from 'react-router-dom';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { useIsAuthenticated } from '@/hooks/state';
|
import { useIsAuthenticated } from '@/hooks/state';
|
||||||
|
|
||||||
interface PrivateRouteProps {
|
interface EnsureAuthenticatedProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
redirectTo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PrivateRoute({ children }: PrivateRouteProps) {
|
export function EnsureAuthenticated({
|
||||||
|
children,
|
||||||
|
redirectTo = '/auth/login',
|
||||||
|
}: EnsureAuthenticatedProps) {
|
||||||
const isAuthenticated = useIsAuthenticated();
|
const isAuthenticated = useIsAuthenticated();
|
||||||
|
|
||||||
return isAuthenticated ? (
|
return isAuthenticated ? (
|
||||||
children
|
<>{children}</>
|
||||||
) : (
|
) : (
|
||||||
<Redirect to={{ pathname: '/auth/login' }} />
|
<Redirect to={{ pathname: redirectTo }} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { useAuthUserVerified } from '@/hooks/state';
|
|||||||
|
|
||||||
interface EnsureUserEmailVerifiedProps {
|
interface EnsureUserEmailVerifiedProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
redirectTo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,11 +13,12 @@ interface EnsureUserEmailVerifiedProps {
|
|||||||
*/
|
*/
|
||||||
export function EnsureUserEmailVerified({
|
export function EnsureUserEmailVerified({
|
||||||
children,
|
children,
|
||||||
|
redirectTo = '/auth/register/verify',
|
||||||
}: EnsureUserEmailVerifiedProps) {
|
}: EnsureUserEmailVerifiedProps) {
|
||||||
const isAuthVerified = useAuthUserVerified();
|
const isAuthVerified = useAuthUserVerified();
|
||||||
|
|
||||||
if (!isAuthVerified) {
|
if (!isAuthVerified) {
|
||||||
return <Redirect to={{ pathname: '/register/verify' }} />;
|
return <Redirect to={{ pathname: redirectTo }} />;
|
||||||
}
|
}
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
|
||||||
import { Route, Switch, useLocation } from 'react-router-dom';
|
import { Route, Switch, useLocation } from 'react-router-dom';
|
||||||
import BodyClassName from 'react-body-classname';
|
import BodyClassName from 'react-body-classname';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|||||||
@@ -1,27 +1,46 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useLocation, useHistory } from 'react-router-dom';
|
||||||
import { useAuthSignUpVerify } from '@/hooks/query';
|
import { useAuthSignUpVerify } from '@/hooks/query';
|
||||||
|
import { AppToaster } from '@/components';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
function useQuery() {
|
||||||
|
const { search } = useLocation();
|
||||||
|
return useMemo(() => new URLSearchParams(search), [search]);
|
||||||
|
}
|
||||||
|
|
||||||
export default function EmailConfirmation() {
|
export default function EmailConfirmation() {
|
||||||
const { mutateAsync: authSignupVerify } = useAuthSignUpVerify();
|
const { mutateAsync: authSignupVerify } = useAuthSignUpVerify();
|
||||||
const params = useParams();
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const query = useQuery();
|
||||||
|
|
||||||
const token = params.token;
|
const token = query.get('token');
|
||||||
const email = params.email;
|
const email = query.get('email');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!token || !email) {
|
if (!token || !email) {
|
||||||
history.push('register/email_confirmation');
|
history.push('/auth/login');
|
||||||
}
|
}
|
||||||
}, [history, token, email]);
|
}, [history, token, email]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
authSignupVerify(token, email)
|
authSignupVerify({ token, email })
|
||||||
.then(() => {})
|
.then(() => {
|
||||||
.catch((error) => {});
|
AppToaster.show({
|
||||||
}, [token, email, authSignupVerify]);
|
message: 'Your email has been verified, Congrats!',
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
history.push('/');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
history.push('/');
|
||||||
|
});
|
||||||
|
}, [token, email, authSignupVerify, history]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import { AppToaster, Stack } from '@/components';
|
|||||||
import { useAuthActions } from '@/hooks/state';
|
import { useAuthActions } from '@/hooks/state';
|
||||||
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
import { useAuthSignUpVerifyResendMail } from '@/hooks/query';
|
||||||
import { AuthContainer } from './AuthContainer';
|
import { AuthContainer } from './AuthContainer';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function RegisterVerify() {
|
export default function RegisterVerify() {
|
||||||
const history = useHistory();
|
|
||||||
const { setLogout } = useAuthActions();
|
const { setLogout } = useAuthActions();
|
||||||
const { mutateAsync: resendSignUpVerifyMail, isLoading } =
|
const { mutateAsync: resendSignUpVerifyMail, isLoading } =
|
||||||
useAuthSignUpVerifyResendMail();
|
useAuthSignUpVerifyResendMail();
|
||||||
@@ -30,8 +28,6 @@ export default function RegisterVerify() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle logout link click.
|
|
||||||
const handleSignOutBtnClick = () => {
|
const handleSignOutBtnClick = () => {
|
||||||
setLogout();
|
setLogout();
|
||||||
};
|
};
|
||||||
@@ -60,11 +56,11 @@ export default function RegisterVerify() {
|
|||||||
<Button
|
<Button
|
||||||
large
|
large
|
||||||
fill
|
fill
|
||||||
intent={Intent.DANGER}
|
|
||||||
minimal
|
minimal
|
||||||
|
intent={Intent.DANGER}
|
||||||
onClick={handleSignOutBtnClick}
|
onClick={handleSignOutBtnClick}
|
||||||
>
|
>
|
||||||
Signout
|
Not my email
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</AuthInsiderCard>
|
</AuthInsiderCard>
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export const useAuthResetPassword = (props) => {
|
|||||||
*/
|
*/
|
||||||
export const useAuthMetadata = (props) => {
|
export const useAuthMetadata = (props) => {
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
[t.AUTH_METADATA_PAGE,],
|
[t.AUTH_METADATA_PAGE],
|
||||||
{
|
{
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: `auth/meta`,
|
url: `auth/meta`,
|
||||||
@@ -88,12 +88,11 @@ export const useAuthMetadata = (props) => {
|
|||||||
defaultData: {},
|
defaultData: {},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const useAuthSignUpVerifyResendMail = (props) => {
|
export const useAuthSignUpVerifyResendMail = (props) => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
@@ -104,16 +103,20 @@ export const useAuthSignUpVerifyResendMail = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface AuthSignUpVerifyValues {
|
||||||
|
token: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const useAuthSignUpVerify = (props) => {
|
export const useAuthSignUpVerify = (props) => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
(token: string, email: string) => apiRequest.post('auth/register/verify'),
|
(values: AuthSignUpVerifyValues) =>
|
||||||
|
apiRequest.post('auth/register/verify', values),
|
||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useQueryTenant, useRequestQuery } from '../useQueryRequest';
|
|||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
import { useSetFeatureDashboardMeta } from '../state/feature';
|
import { useSetFeatureDashboardMeta } from '../state/feature';
|
||||||
import t from './types';
|
import t from './types';
|
||||||
|
import { useSetAuthEmailConfirmed } from '../state';
|
||||||
|
|
||||||
// Common invalidate queries.
|
// Common invalidate queries.
|
||||||
const commonInvalidateQueries = (queryClient) => {
|
const commonInvalidateQueries = (queryClient) => {
|
||||||
@@ -130,6 +131,8 @@ export function useUser(id, props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useAuthenticatedAccount(props) {
|
export function useAuthenticatedAccount(props) {
|
||||||
|
const setEmailConfirmed = useSetAuthEmailConfirmed();
|
||||||
|
|
||||||
return useRequestQuery(
|
return useRequestQuery(
|
||||||
['AuthenticatedAccount'],
|
['AuthenticatedAccount'],
|
||||||
{
|
{
|
||||||
@@ -139,6 +142,9 @@ export function useAuthenticatedAccount(props) {
|
|||||||
{
|
{
|
||||||
select: (response) => response.data.data,
|
select: (response) => response.data.data,
|
||||||
defaultData: {},
|
defaultData: {},
|
||||||
|
onSuccess: (data) => {
|
||||||
|
setEmailConfirmed(data.is_verified);
|
||||||
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -166,4 +172,3 @@ export const useDashboardMeta = (props) => {
|
|||||||
}, [state.isSuccess, state.data, setFeatureDashboardMeta]);
|
}, [state.isSuccess, state.data, setFeatureDashboardMeta]);
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { isAuthenticated } from '@/store/authentication/authentication.reducer';
|
import { isAuthenticated } from '@/store/authentication/authentication.reducer';
|
||||||
import { setLogin } from '@/store/authentication/authentication.actions';
|
import {
|
||||||
|
setEmailConfirmed,
|
||||||
|
setLogin,
|
||||||
|
} from '@/store/authentication/authentication.actions';
|
||||||
import { useQueryClient } from 'react-query';
|
import { useQueryClient } from 'react-query';
|
||||||
import { removeCookie } from '@/utils';
|
import { removeCookie } from '@/utils';
|
||||||
|
|
||||||
@@ -66,8 +69,20 @@ export const useAuthOrganizationId = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieves the user's email verification status.
|
||||||
*/
|
*/
|
||||||
export const useAuthUserVerified = () => {
|
export const useAuthUserVerified = () => {
|
||||||
return useSelector(() => false);
|
return useSelector((state) => state.authentication.verified);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user's email verification status.
|
||||||
|
*/
|
||||||
|
export const useSetAuthEmailConfirmed = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
return useCallback(
|
||||||
|
(verified?: boolean = true) => dispatch(setEmailConfirmed(verified)),
|
||||||
|
[dispatch],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,4 +3,8 @@ import t from '@/store/types';
|
|||||||
|
|
||||||
export const setLogin = () => ({ type: t.LOGIN_SUCCESS });
|
export const setLogin = () => ({ type: t.LOGIN_SUCCESS });
|
||||||
export const setLogout = () => ({ type: t.LOGOUT });
|
export const setLogout = () => ({ type: t.LOGOUT });
|
||||||
export const setStoreReset = () => ({ type: t.RESET });
|
export const setStoreReset = () => ({ type: t.RESET });
|
||||||
|
export const setEmailConfirmed = (verified?: boolean) => ({
|
||||||
|
type: t.SET_EMAIL_VERIFIED,
|
||||||
|
action: { verified },
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { createReducer } from '@reduxjs/toolkit';
|
import { PayloadAction, createReducer } from '@reduxjs/toolkit';
|
||||||
import { persistReducer } from 'redux-persist';
|
import { persistReducer } from 'redux-persist';
|
||||||
import purgeStoredState from 'redux-persist/es/purgeStoredState';
|
import purgeStoredState from 'redux-persist/es/purgeStoredState';
|
||||||
import storage from 'redux-persist/lib/storage';
|
import storage from 'redux-persist/lib/storage';
|
||||||
|
import { isUndefined } from 'lodash';
|
||||||
import { getCookie } from '@/utils';
|
import { getCookie } from '@/utils';
|
||||||
import t from '@/store/types';
|
import t from '@/store/types';
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ const initialState = {
|
|||||||
tenantId: getCookie('tenant_id'),
|
tenantId: getCookie('tenant_id'),
|
||||||
userId: getCookie('authenticated_user_id'),
|
userId: getCookie('authenticated_user_id'),
|
||||||
locale: getCookie('locale'),
|
locale: getCookie('locale'),
|
||||||
|
verified: true, // Let's be optimistic and assume the user's email is confirmed.
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +34,15 @@ const reducerInstance = createReducer(initialState, {
|
|||||||
state.errors = [];
|
state.errors = [];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[t.SET_EMAIL_VERIFIED]: (
|
||||||
|
state,
|
||||||
|
payload: PayloadAction<{ verified?: boolean }>,
|
||||||
|
) => {
|
||||||
|
state.verified = !isUndefined(payload.action.verified)
|
||||||
|
? payload.action.verified
|
||||||
|
: true;
|
||||||
|
},
|
||||||
|
|
||||||
[t.RESET]: (state) => {
|
[t.RESET]: (state) => {
|
||||||
purgeStoredState(CONFIG);
|
purgeStoredState(CONFIG);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ export default {
|
|||||||
LOGOUT: 'LOGOUT',
|
LOGOUT: 'LOGOUT',
|
||||||
LOGIN_CLEAR_ERRORS: 'LOGIN_CLEAR_ERRORS',
|
LOGIN_CLEAR_ERRORS: 'LOGIN_CLEAR_ERRORS',
|
||||||
RESET: 'RESET',
|
RESET: 'RESET',
|
||||||
|
SET_EMAIL_VERIFIED: 'SET_EMAIL_VERIFIED'
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user