mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
Merge branch 'feature/react-query' of https://github.com/abouolia/Bigcapital into feature/react-query
This commit is contained in:
@@ -4,9 +4,8 @@ import BodyClassName from 'react-body-classname';
|
||||
import { TransitionGroup, CSSTransition } from 'react-transition-group';
|
||||
import authenticationRoutes from 'routes/authentication';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import { compose } from 'utils';
|
||||
import Icon from 'components/Icon';
|
||||
import { useIsAuthenticated } from 'hooks/state';
|
||||
|
||||
import 'style/pages/Authentication/Auth.scss'
|
||||
|
||||
@@ -14,14 +13,15 @@ function PageFade(props) {
|
||||
return <CSSTransition {...props} classNames="authTransition" timeout={500} />;
|
||||
}
|
||||
|
||||
function AuthenticationWrapper({ isAuthorized = false, ...rest }) {
|
||||
export default function AuthenticationWrapper({ ...rest }) {
|
||||
const to = { pathname: '/homepage' };
|
||||
const location = useLocation();
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
const locationKey = location.pathname;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isAuthorized ? (
|
||||
{isAuthenticated ? (
|
||||
<Redirect to={to} />
|
||||
) : (
|
||||
<BodyClassName className={'authentication'}>
|
||||
@@ -61,7 +61,3 @@ function AuthenticationWrapper({ isAuthorized = false, ...rest }) {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(({ isAuthorized }) => ({ isAuthorized })),
|
||||
)(AuthenticationWrapper);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Menu,
|
||||
@@ -10,48 +10,46 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
import { firstLettersArgs } from 'utils';
|
||||
import { useAuthActions, useAuthUser } from 'hooks/state';
|
||||
|
||||
import { compose, firstLettersArgs } from 'utils';
|
||||
|
||||
function DashboardTopbarUser({ requestLogout, user }) {
|
||||
export default function DashboardTopbarUser() {
|
||||
const history = useHistory();
|
||||
const { setLogout } = useAuthActions();
|
||||
const user = useAuthUser();
|
||||
|
||||
const onClickLogout = useCallback(() => {
|
||||
requestLogout();
|
||||
}, []);
|
||||
|
||||
const userAvatarDropMenu = useMemo(
|
||||
() => (
|
||||
<Menu className={'menu--logged-user-dropdown'}>
|
||||
<MenuItem
|
||||
multiline={true}
|
||||
className={'menu-item--profile'}
|
||||
text={
|
||||
<div>
|
||||
<div class="person">
|
||||
{user.first_name} {user.last_name}
|
||||
</div>
|
||||
<div class="org">
|
||||
<T id="organization_id" />: {user.tenant_id}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={<T id={'preferences'} />}
|
||||
onClick={() => history.push('/preferences')}
|
||||
/>
|
||||
<MenuItem text={<T id={'logout'} />} onClick={onClickLogout} />
|
||||
</Menu>
|
||||
),
|
||||
[onClickLogout],
|
||||
);
|
||||
const onClickLogout = () => {
|
||||
setLogout();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover content={userAvatarDropMenu} position={Position.BOTTOM}>
|
||||
<Popover
|
||||
content={
|
||||
<Menu className={'menu--logged-user-dropdown'}>
|
||||
<MenuItem
|
||||
multiline={true}
|
||||
className={'menu-item--profile'}
|
||||
text={
|
||||
<div>
|
||||
<div class="person">
|
||||
{user.first_name} {user.last_name}
|
||||
</div>
|
||||
<div class="org">
|
||||
<T id="organization_id" />: {user.tenant_id}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={<T id={'preferences'} />}
|
||||
onClick={() => history.push('/preferences')}
|
||||
/>
|
||||
<MenuItem text={<T id={'logout'} />} onClick={onClickLogout} />
|
||||
</Menu>
|
||||
}
|
||||
position={Position.BOTTOM}
|
||||
>
|
||||
<Button>
|
||||
<div className="user-text">
|
||||
{firstLettersArgs(user.first_name, user.last_name)}
|
||||
@@ -60,8 +58,3 @@ function DashboardTopbarUser({ requestLogout, user }) {
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(({ user }) => ({ user })),
|
||||
withAuthenticationActions,
|
||||
)(DashboardTopbarUser);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import BodyClassName from 'react-body-classname';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import { compose } from 'utils';
|
||||
import { useIsAuthenticated } from 'hooks/state';
|
||||
|
||||
export default function PrivateRoute({ component: Component, ...rest }) {
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
|
||||
function PrivateRoute({ component: Component, isAuthorized = false, ...rest }) {
|
||||
return (
|
||||
<BodyClassName className={''}>
|
||||
{isAuthorized ? (
|
||||
{isAuthenticated ? (
|
||||
<Component />
|
||||
) : (
|
||||
<Redirect
|
||||
@@ -20,7 +20,3 @@ function PrivateRoute({ component: Component, isAuthorized = false, ...rest }) {
|
||||
</BodyClassName>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthentication(({ isAuthorized }) => ({ isAuthorized })),
|
||||
)(PrivateRoute);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
|
||||
function AuthCopyright() {
|
||||
export default function AuthCopyright() {
|
||||
return (
|
||||
<div class="auth-copyright">
|
||||
<div class="auth-copyright__text">
|
||||
@@ -11,6 +11,4 @@ function AuthCopyright() {
|
||||
<Icon width={122} height={22} icon={'bigcapital'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthCopyright;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import AuthCopyright from './AuthCopyright';
|
||||
|
||||
/**
|
||||
* Authentication insider page.
|
||||
*/
|
||||
export default function AuthInsider({
|
||||
logo = true,
|
||||
copyright = true,
|
||||
|
||||
@@ -1,276 +1,20 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Position,
|
||||
Spinner,
|
||||
} from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
import useAsync from 'hooks/async';
|
||||
import InviteAcceptForm from './InviteAcceptForm';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import { InviteAcceptProvider } from './InviteAcceptProvider';
|
||||
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function Invite({ requestInviteAccept, requestInviteMetaByToken }) {
|
||||
const { formatMessage } = useIntl();
|
||||
/**
|
||||
* Authentication invite page.
|
||||
*/
|
||||
export default function Invite() {
|
||||
const { token } = useParams();
|
||||
const history = useHistory();
|
||||
const [shown, setShown] = useState(false);
|
||||
const passwordRevealer = useCallback(() => {
|
||||
setShown(!shown);
|
||||
}, [shown]);
|
||||
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
first_name: Yup.string().required().label(formatMessage({id:'first_name_'})),
|
||||
last_name: Yup.string().required().label(formatMessage({id:'last_name_'})),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({id:'phone_number'})),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required().label(formatMessage({id:'password'}))
|
||||
});
|
||||
|
||||
const inviteMeta = useAsync(() => {
|
||||
return requestInviteMetaByToken(token);
|
||||
});
|
||||
|
||||
const inviteErrors = inviteMeta.error || [];
|
||||
const inviteValue = {
|
||||
organization_name: '',
|
||||
invited_email: '',
|
||||
...(inviteMeta.value ? inviteMeta.value.data.data : {}),
|
||||
};
|
||||
|
||||
if (inviteErrors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: 'An unexpected error occurred',
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
phone_number: '',
|
||||
password: '',
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const {
|
||||
touched,
|
||||
errors,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
requestInviteAccept(values, token)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: `Congrats! Your account has been created and invited to
|
||||
<strong>${inviteValue.organization_name}</strong> organization successfully.`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'an_unexpected_error_occurred' }),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
if (errors.find((e) => e.type === 'PHONE_MUMNER.ALREADY.EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: 'This phone number is used in another account.',
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const passwordRevealerTmp = useMemo(
|
||||
() => (
|
||||
<span class='password-revealer' onClick={() => passwordRevealer()}>
|
||||
<If condition={shown}>
|
||||
<>
|
||||
<Icon icon='eye-slash' />{' '}
|
||||
<span class='text'>
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<>
|
||||
<Icon icon='eye' />{' '}
|
||||
<span class='text'>
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
</span>
|
||||
),
|
||||
[shown, passwordRevealer]
|
||||
);
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<div className={'invite-form'}>
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3>
|
||||
<T id={'welcome_to_bigcapital'} />
|
||||
</h3>
|
||||
<p>
|
||||
<T id={'enter_your_personal_information'} />
|
||||
<b>{inviteValue.organization_name}</b> <T id={'organization'}/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'First Name'} />}
|
||||
className={'form-group--first_name'}
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'first_name'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('first_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'Last Name'} />}
|
||||
className={'form-group--last_name'}
|
||||
intent={errors.last_name && touched.last_name && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'last_name'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.last_name && touched.last_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('last_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'Phone Number'} />}
|
||||
className={'form-group--phone_number'}
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'phone_number'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('phone_number')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={passwordRevealerTmp}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={shown ? 'text' : 'password'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'invite-form__statement-section'}>
|
||||
<p>
|
||||
<T id={'You email address is'} />{' '}
|
||||
<b>{inviteValue.invited_email},</b> <br />
|
||||
<T id={'you_will_use_this_address_to_sign_in_to_bigcapital'} />
|
||||
</p>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
type='submit'
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'create_account'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{inviteMeta.pending && (
|
||||
<div class='authentication-page__loading-overlay'>
|
||||
<Spinner size={40} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<InviteAcceptProvider token={token}>
|
||||
<InviteAcceptForm />
|
||||
</InviteAcceptProvider>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(Invite);
|
||||
|
||||
100
client/src/containers/Authentication/InviteAcceptForm.js
Normal file
100
client/src/containers/Authentication/InviteAcceptForm.js
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from 'react';
|
||||
import { Intent, Position } from '@blueprintjs/core';
|
||||
import { Formik } from 'formik';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { useInviteAcceptContext } from './InviteAcceptProvider';
|
||||
import { AppToaster } from 'components';
|
||||
import { InviteAcceptSchema } from './utils';
|
||||
import InviteAcceptFormContent from './InviteAcceptFormContent';
|
||||
|
||||
export default function InviteAcceptForm() {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
// Invite accept context.
|
||||
const {
|
||||
inviteAcceptMutate,
|
||||
inviteMeta,
|
||||
token,
|
||||
} = useInviteAcceptContext();
|
||||
|
||||
// Invite value.
|
||||
const inviteValue = {
|
||||
organization_name: '',
|
||||
invited_email: '',
|
||||
...(!isEmpty(inviteMeta)
|
||||
? {
|
||||
invited_email: inviteMeta.email,
|
||||
organization_name: inviteMeta.organizationName,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
// Handle form submitting.
|
||||
const handleSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
inviteAcceptMutate([values, token])
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: `Congrats! Your account has been created and invited to
|
||||
<strong>${inviteValue.organization_name}</strong> organization successfully.`,
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'an_unexpected_error_occurred' }),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
if (errors.find((e) => e.type === 'PHONE_MUMNER.ALREADY.EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: 'This phone number is used in another account.',
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: 'An unexpected error occurred',
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
setSubmitting(false);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'invite-form'}>
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3>
|
||||
<T id={'welcome_to_bigcapital'} />
|
||||
</h3>
|
||||
<p>
|
||||
<T id={'enter_your_personal_information'} />{' '}
|
||||
<b>{inviteValue.organization_name}</b> <T id={'organization'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Formik
|
||||
validationSchema={InviteAcceptSchema}
|
||||
initialValues={inviteValue}
|
||||
onSubmit={handleSubmit}
|
||||
component={InviteAcceptFormContent}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
122
client/src/containers/Authentication/InviteAcceptFormContent.js
Normal file
122
client/src/containers/Authentication/InviteAcceptFormContent.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField, useFormikContext } from 'formik';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { inputIntent } from 'utils';
|
||||
import { Col, Row } from 'components';
|
||||
import { useInviteAcceptContext } from './InviteAcceptProvider';
|
||||
import { PasswordRevealer } from './components';
|
||||
/**
|
||||
* Invite user form.
|
||||
*/
|
||||
export default function InviteUserFormContent() {
|
||||
// Invite accept context.
|
||||
const { inviteMeta } = useInviteAcceptContext();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<FastField name={'first_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'First Name'} />}
|
||||
className={'form-group--first_name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'first_name'} />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FastField name={'last_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'Last Name'} />}
|
||||
className={'form-group--last_name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'last_name'} />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FastField name={'phone_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'Phone Number'} />}
|
||||
className={'form-group--phone_number'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'phone_number'} />}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={<PasswordRevealer />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'invite-form__statement-section'}>
|
||||
<p>
|
||||
<T id={'You email address is'} /> <b>{inviteMeta.email},</b> <br />
|
||||
<T id={'you_will_use_this_address_to_sign_in_to_bigcapital'} />
|
||||
</p>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'create_account'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
56
client/src/containers/Authentication/InviteAcceptProvider.js
Normal file
56
client/src/containers/Authentication/InviteAcceptProvider.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { useInviteMetaByToken, useAuthInviteAccept } from 'hooks/query';
|
||||
import { InviteAcceptLoading } from './components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
const InviteAcceptContext = createContext();
|
||||
|
||||
/**
|
||||
* Invite accept provider.
|
||||
*/
|
||||
function InviteAcceptProvider({ token, ...props }) {
|
||||
// Invite meta by token.
|
||||
const {
|
||||
data: inviteMeta,
|
||||
error: inviteMetaError,
|
||||
isError: isInviteMetaError,
|
||||
isFetching: isInviteMetaLoading,
|
||||
} = useInviteMetaByToken(token, { retry: false });
|
||||
|
||||
// Invite accept mutate.
|
||||
const { mutateAsync: inviteAcceptMutate } = useAuthInviteAccept({
|
||||
retry: false,
|
||||
});
|
||||
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inviteMetaError) { history.push('/auth/login'); }
|
||||
}, [history, inviteMetaError]);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
token,
|
||||
inviteMeta,
|
||||
inviteMetaError,
|
||||
isInviteMetaError,
|
||||
isInviteMetaLoading,
|
||||
inviteAcceptMutate
|
||||
};
|
||||
|
||||
if (inviteMetaError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<InviteAcceptLoading isLoading={isInviteMetaLoading}>
|
||||
{ isInviteMetaError }
|
||||
<InviteAcceptContext.Provider value={provider} {...props} />
|
||||
</InviteAcceptLoading>
|
||||
);
|
||||
}
|
||||
|
||||
const useInviteAcceptContext = () => useContext(InviteAcceptContext);
|
||||
|
||||
export { InviteAcceptProvider, useInviteAcceptContext };
|
||||
@@ -1,132 +1,38 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Checkbox,
|
||||
} from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import Toaster from 'components/AppToaster';
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import { useAuthLogin } from 'hooks/query';
|
||||
|
||||
import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
import LoginForm from './LoginForm';
|
||||
import { LoginSchema, transformLoginErrorsToToasts } from './utils';
|
||||
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const ERRORS_TYPES = {
|
||||
INVALID_DETAILS: 'INVALID_DETAILS',
|
||||
USER_INACTIVE: 'USER_INACTIVE',
|
||||
LOGIN_TO_MANY_ATTEMPTS: 'LOGIN_TO_MANY_ATTEMPTS',
|
||||
};
|
||||
function Login({ requestLogin }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
const [shown, setShown] = useState(false);
|
||||
const passwordRevealer = () => {
|
||||
setShown(!shown);
|
||||
};
|
||||
|
||||
// Validation schema.
|
||||
const loginValidationSchema = Yup.object().shape({
|
||||
crediential: Yup.string()
|
||||
.required()
|
||||
.email()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
password: Yup.string()
|
||||
.required()
|
||||
.min(4)
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
// Formik validation schema and submit handler.
|
||||
const {
|
||||
touched,
|
||||
errors,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
initialValues: {
|
||||
crediential: '',
|
||||
password: '',
|
||||
},
|
||||
validationSchema: loginValidationSchema,
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
requestLogin({
|
||||
crediential: values.crediential,
|
||||
password: values.password,
|
||||
/**
|
||||
* Login page.
|
||||
*/
|
||||
export default function Login() {
|
||||
const { mutateAsync: loginMutate } = useAuthLogin();
|
||||
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
loginMutate({
|
||||
crediential: values.crediential,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
setSubmitting(false);
|
||||
})
|
||||
.then(() => {
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
const toastBuilders = [];
|
||||
if (errors.find((e) => e.type === ERRORS_TYPES.INVALID_DETAILS)) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'email_and_password_entered_did_not_match',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === ERRORS_TYPES.USER_INACTIVE)) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'the_user_has_been_suspended_from_admin',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (
|
||||
errors.find((e) => e.type === ERRORS_TYPES.LOGIN_TO_MANY_ATTEMPTS)
|
||||
) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'your_account_has_been_locked',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
toastBuilders.forEach((builder) => {
|
||||
Toaster.show(builder);
|
||||
});
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
const toastBuilders = transformLoginErrorsToToasts(errors);
|
||||
|
||||
const passwordRevealerTmp = useMemo(
|
||||
() => (
|
||||
<span class="password-revealer" onClick={() => passwordRevealer()}>
|
||||
<If condition={shown}>
|
||||
<>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<>
|
||||
<Icon icon="eye" />{' '}
|
||||
<span class="text">
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
</span>
|
||||
),
|
||||
[shown, passwordRevealer],
|
||||
);
|
||||
toastBuilders.forEach((builder) => {
|
||||
Toaster.show(builder);
|
||||
});
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
@@ -142,59 +48,15 @@ function Login({ requestLogin }) {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'authentication-page__form'}>
|
||||
<FormGroup
|
||||
label={<T id={'email_or_phone_number'} />}
|
||||
intent={errors.crediential && touched.crediential && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'crediential'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--crediential'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.crediential && touched.crediential && Intent.DANGER
|
||||
}
|
||||
large={true}
|
||||
{...getFieldProps('crediential')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={passwordRevealerTmp}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
large={true}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
type={shown ? 'text' : 'password'}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'login-form__checkbox-section'}>
|
||||
<Checkbox large={true} className={'checkbox--remember-me'}>
|
||||
<T id={'keep_me_logged_in'} />
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
fill={true}
|
||||
lang={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'log_in'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<Formik
|
||||
initialValues={{
|
||||
crediential: '',
|
||||
password: '',
|
||||
}}
|
||||
validationSchema={LoginSchema}
|
||||
onSubmit={handleSubmit}
|
||||
component={LoginForm}
|
||||
/>
|
||||
|
||||
<div class="authentication-page__footer-links">
|
||||
<Link to={'/auth/send_reset_password'}>
|
||||
@@ -204,6 +66,4 @@ function Login({ requestLogin }) {
|
||||
</div>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(Login);
|
||||
}
|
||||
77
client/src/containers/Authentication/LoginForm.js
Normal file
77
client/src/containers/Authentication/LoginForm.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Checkbox,
|
||||
} from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { inputIntent } from 'utils';
|
||||
import { PasswordRevealer } from './components';
|
||||
|
||||
/**
|
||||
* Login form.
|
||||
*/
|
||||
export default function LoginForm({
|
||||
isSubmitting
|
||||
}) {
|
||||
return (
|
||||
<Form className={'authentication-page__form'}>
|
||||
<FastField name={'crediential'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'email_or_phone_number'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'crediential'} />}
|
||||
className={'form-group--crediential'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
large={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={<PasswordRevealer />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
large={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'login-form__checkbox-section'}>
|
||||
<Checkbox large={true} className={'checkbox--remember-me'}>
|
||||
<T id={'keep_me_logged_in'} />
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
fill={true}
|
||||
lang={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'log_in'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -1,56 +1,28 @@
|
||||
import React, { useMemo, useState, useCallback } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik } from 'formik';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Spinner,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import { useAuthLogin, useAuthRegister } from '../../hooks/query/authentication';
|
||||
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import Icon from 'components/Icon';
|
||||
import { If } from 'components';
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
import RegisterForm from './RegisterForm';
|
||||
import { RegisterSchema, transformRegisterErrorsToForm } from './utils';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
/**
|
||||
* Register form.
|
||||
*/
|
||||
export default function RegisterUserForm() {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
const [shown, setShown] = useState(false);
|
||||
const passwordRevealer = useCallback(() => {
|
||||
setShown(!shown);
|
||||
}, [shown]);
|
||||
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
first_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'first_name_' })),
|
||||
last_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'last_name_' })),
|
||||
email: Yup.string()
|
||||
.email()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'phone_number_' })),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
const { mutateAsync: authLoginMutate } = useAuthLogin();
|
||||
const { mutateAsync: authRegisterMutate } = useAuthRegister();
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
first_name: '',
|
||||
@@ -58,85 +30,37 @@ function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
email: '',
|
||||
phone_number: '',
|
||||
password: '',
|
||||
country: 'LY',
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
country: 'LY',
|
||||
},
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
requestRegister(values)
|
||||
.then((response) => {
|
||||
requestLogin({
|
||||
crediential: values.email,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
history.push('/register/subscription');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'something_wentwrong' }),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
});
|
||||
const handleSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
authRegisterMutate(values)
|
||||
.then((response) => {
|
||||
authLoginMutate({
|
||||
crediential: values.email,
|
||||
password: values.password,
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.some((e) => e.type === 'PHONE_NUMBER_EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: formatMessage({
|
||||
id: 'the_phone_number_already_used_in_another_account',
|
||||
}),
|
||||
.then(() => {
|
||||
history.push('/register/subscription');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({ id: 'something_wentwrong' }),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
}
|
||||
if (errors.some((e) => e.type === 'EMAIL.EXISTS')) {
|
||||
setErrors({
|
||||
email: formatMessage({
|
||||
id: 'the_email_already_used_in_another_account',
|
||||
}),
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const passwordRevealerTmp = useMemo(
|
||||
() => (
|
||||
<span class="password-revealer" onClick={() => passwordRevealer()}>
|
||||
<If condition={shown}>
|
||||
<>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<>
|
||||
<Icon icon="eye" />{' '}
|
||||
<span class="text">
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</>
|
||||
</If>
|
||||
</span>
|
||||
),
|
||||
[shown, passwordRevealer],
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
const formErrors = transformRegisterErrorsToForm(errors);
|
||||
|
||||
setErrors(formErrors);
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<div className={'register-form'}>
|
||||
@@ -146,136 +70,17 @@ function RegisterUserForm({ requestRegister, requestLogin }) {
|
||||
</h3>
|
||||
<T id={'you_have_a_bigcapital_account'} />
|
||||
<Link to="/auth/login">
|
||||
{' '}
|
||||
<T id={'login'} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'authentication-page__form'}>
|
||||
<Row className={'name-section'}>
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'first_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--first-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.first_name && touched.first_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('first_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
intent={errors.last_name && touched.last_name && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'last_name'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--last-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.last_name && touched.last_name && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('last_name')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'phone_number'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--phone-number'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.phone_number && touched.phone_number && Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('phone_number')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'email'} />}
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'email'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--email'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.email && touched.email && Intent.DANGER}
|
||||
{...getFieldProps('email')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={passwordRevealerTmp}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={shown ? 'text' : 'password'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'register-form__agreement-section'}>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
className={'btn-register'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'register'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<If condition={isSubmitting}>
|
||||
<div class="authentication-page__loading-overlay">
|
||||
<Spinner size={50} />
|
||||
</div>
|
||||
</If>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={RegisterSchema}
|
||||
onSubmit={handleSubmit}
|
||||
component={RegisterForm}
|
||||
/>
|
||||
</div>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(RegisterUserForm);
|
||||
}
|
||||
141
client/src/containers/Authentication/RegisterForm.js
Normal file
141
client/src/containers/Authentication/RegisterForm.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Spinner
|
||||
} from '@blueprintjs/core';
|
||||
import { ErrorMessage, FastField, Form } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Row, Col, If } from 'components';
|
||||
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Register form.
|
||||
*/
|
||||
export default function RegisterForm({
|
||||
isSubmitting,
|
||||
}) {
|
||||
return (
|
||||
<Form className={'authentication-page__form'}>
|
||||
<Row className={'name-section'}>
|
||||
<Col md={6}>
|
||||
<FastField name={'first_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'first_name'} />}
|
||||
className={'form-group--first-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FastField name={'last_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'last_name'} />}
|
||||
className={'form-group--last-name'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FastField name={'phone_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'phone_number'} />}
|
||||
className={'form-group--phone-number'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FastField name={'email'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'email'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'email'} />}
|
||||
className={'form-group--email'}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
// labelInfo={passwordRevealerTmp}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'register-form__agreement-section'}>
|
||||
<p>
|
||||
<T id={'signing_in_or_creating'} /> <br />
|
||||
<Link>
|
||||
<T id={'terms_conditions'} />
|
||||
</Link>{' '}
|
||||
<T id={'and'} />
|
||||
<Link>
|
||||
{' '}
|
||||
<T id={'privacy_statement'} />
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
className={'btn-register'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'register'} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<If condition={isSubmitting}>
|
||||
<div class="authentication-page__loading-overlay">
|
||||
<Spinner size={50} />
|
||||
</div>
|
||||
</If>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -1,40 +1,31 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
|
||||
import { Formik } from 'formik';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { Link, useParams, useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
|
||||
import { useAuthResetPassword } from 'hooks/query';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function ResetPassword({ requestResetPassword }) {
|
||||
import ResetPasswordForm from './ResetPasswordForm';
|
||||
import { ResetPasswordSchema } from './utils';
|
||||
/**
|
||||
* Reset password page.
|
||||
*/
|
||||
export default function ResetPassword() {
|
||||
const { formatMessage } = useIntl();
|
||||
const { token } = useParams();
|
||||
const history = useHistory();
|
||||
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
confirm_password: Yup.string()
|
||||
.oneOf([Yup.ref('password'), null])
|
||||
.required()
|
||||
.label(formatMessage({ id: 'confirm_password' })),
|
||||
});
|
||||
// Authentication reset password.
|
||||
const { mutateAsync: authResetPasswordMutate } = useAuthResetPassword();
|
||||
|
||||
// Initial values of the form.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
password: '',
|
||||
@@ -43,42 +34,30 @@ function ResetPassword({ requestResetPassword }) {
|
||||
[],
|
||||
);
|
||||
|
||||
const {
|
||||
touched,
|
||||
errors,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
requestResetPassword(values, token)
|
||||
.then((response) => {
|
||||
// Handle the form submitting.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
authResetPasswordMutate([token, values])
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage('password_successfully_updated'),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
if (errors.find((e) => e.type === 'TOKEN_INVALID')) {
|
||||
AppToaster.show({
|
||||
message: formatMessage('password_successfully_updated'),
|
||||
message: formatMessage({ id: 'an_unexpected_error_occurred' }),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.find((e) => e.type === 'TOKEN_INVALID')) {
|
||||
AppToaster.show({
|
||||
message: formatMessage('an_unexpected_error_occurred'),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
@@ -93,66 +72,13 @@ function ResetPassword({ requestResetPassword }) {
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<FormGroup
|
||||
label={<T id={'new_password'} />}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--password'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={'password'}
|
||||
intent={errors.password && touched.password && Intent.DANGER}
|
||||
{...getFieldProps('password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup
|
||||
label={<T id={'new_password'} />}
|
||||
labelInfo={'(again):'}
|
||||
intent={
|
||||
errors.confirm_password &&
|
||||
touched.confirm_password &&
|
||||
Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name={'confirm_password'}
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
className={'form-group--confirm-password'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={'password'}
|
||||
intent={
|
||||
errors.confirm_password &&
|
||||
touched.confirm_password &&
|
||||
Intent.DANGER
|
||||
}
|
||||
{...getFieldProps('confirm_password')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
fill={true}
|
||||
className={'btn-new'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'submit'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={ResetPasswordSchema}
|
||||
onSubmit={handleSubmit}
|
||||
component={ResetPasswordForm}
|
||||
/>
|
||||
</div>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(ResetPassword);
|
||||
}
|
||||
63
client/src/containers/Authentication/ResetPasswordForm.js
Normal file
63
client/src/containers/Authentication/ResetPasswordForm.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Reset password form.
|
||||
*/
|
||||
export default function ResetPasswordForm({ isSubmitting }) {
|
||||
return (
|
||||
<Form>
|
||||
<FastField name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'new_password'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
className={'form-group--password'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={'password'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'confirm_password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'new_password'} />}
|
||||
labelInfo={'(again):'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'confirm_password'} />}
|
||||
className={'form-group--confirm-password'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={'password'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
fill={true}
|
||||
className={'btn-new'}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'submit'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -1,75 +1,60 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Formik } from 'formik';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useAuthSendResetPassword } from 'hooks/query';
|
||||
import Toaster from 'components/AppToaster';
|
||||
import SendResetPasswordForm from './SendResetPasswordForm';
|
||||
import { SendResetPasswordSchema, transformSendResetPassErrorsToToasts } from './utils';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import ErrorMessage from 'components/ErrorMessage';
|
||||
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
|
||||
import withAuthenticationActions from './withAuthenticationActions';
|
||||
|
||||
function SendResetPassword({ requestSendResetPassword }) {
|
||||
/**
|
||||
* Send reset password page.
|
||||
*/
|
||||
export default function SendResetPassword({ requestSendResetPassword }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
// Validation schema.
|
||||
const ValidationSchema = Yup.object().shape({
|
||||
crediential: Yup.string()
|
||||
.required()
|
||||
.email().label(formatMessage({ id: 'email' })),
|
||||
});
|
||||
const { mutateAsync: sendResetPasswordMutate } = useAuthSendResetPassword();
|
||||
|
||||
// Initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
crediential: '',
|
||||
}),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
// Formik validation
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
handleSubmit,
|
||||
getFieldProps,
|
||||
isSubmitting,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema: ValidationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: (values, { setSubmitting }) => {
|
||||
requestSendResetPassword(values.crediential)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({id:'check_your_email_for_a_link_to_reset'}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch((errors) => {
|
||||
if (errors.find((e) => e.type === 'EMAIL.NOT.REGISTERED')) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({id:'we_couldn_t_find_your_account_with_that_email'}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
setSubmitting(false);
|
||||
// Handle form submitting.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
sendResetPasswordMutate({ email: values.crediential })
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'check_your_email_for_a_link_to_reset',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
},
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
const toastBuilders = transformSendResetPassErrorsToToasts(errors);
|
||||
|
||||
toastBuilders.forEach((builder) => {
|
||||
Toaster.show(builder);
|
||||
});
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<div className='reset-form'>
|
||||
<div className="reset-form">
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3>
|
||||
<T id={'you_can_t_login'} />
|
||||
@@ -79,38 +64,14 @@ function SendResetPassword({ requestSendResetPassword }) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className={'send-reset-password'}>
|
||||
<FormGroup
|
||||
label={'Email or Phone Number'}
|
||||
intent={errors.crediential && touched.crediential && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'crediential'} {...{ errors, touched }} />
|
||||
}
|
||||
className={'form-group--crediential'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.crediential && touched.crediential && Intent.DANGER
|
||||
}
|
||||
large={true}
|
||||
{...getFieldProps('crediential')}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'send_reset_password_mail'} />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class='authentication-page__footer-links'>
|
||||
<Link to='/auth/login'>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmit}
|
||||
validationSchema={SendResetPasswordSchema}
|
||||
component={SendResetPasswordForm}
|
||||
/>
|
||||
<div class="authentication-page__footer-links">
|
||||
<Link to="/auth/login">
|
||||
<T id={'return_to_log_in'} />
|
||||
</Link>
|
||||
</div>
|
||||
@@ -118,5 +79,3 @@ function SendResetPassword({ requestSendResetPassword }) {
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAuthenticationActions)(SendResetPassword);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Send reset password form.
|
||||
*/
|
||||
export default function SendResetPasswordForm({
|
||||
isSubmitting
|
||||
}) {
|
||||
return (
|
||||
<Form className={'send-reset-password'}>
|
||||
<FastField name={'crediential'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={'Email or Phone Number'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'crediential'} />}
|
||||
className={'form-group--crediential'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
large={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'send_reset_password_mail'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
49
client/src/containers/Authentication/components.js
Normal file
49
client/src/containers/Authentication/components.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import ContentLoader from 'react-content-loader';
|
||||
import { If, Icon } from 'components';
|
||||
|
||||
export function PasswordRevealer({ shown, onClick }) {
|
||||
return (
|
||||
<span class="password-revealer" onClick={onClick}>
|
||||
<If condition={shown}>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
<T id={'hide'} />
|
||||
</span>
|
||||
</If>
|
||||
<If condition={!shown}>
|
||||
<Icon icon="eye" />{' '}
|
||||
<span class="text">
|
||||
<T id={'show'} />
|
||||
</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invite accept loading space.
|
||||
*/
|
||||
export function InviteAcceptLoading({ isLoading, children, ...props }) {
|
||||
return isLoading ? (
|
||||
<ContentLoader
|
||||
speed={2}
|
||||
width={400}
|
||||
height={280}
|
||||
viewBox="0 0 400 280"
|
||||
backgroundColor="#f3f3f3"
|
||||
foregroundColor="#e6e6e6"
|
||||
{...props}
|
||||
>
|
||||
<rect x="0" y="80" rx="2" ry="2" width="200" height="20" />
|
||||
<rect x="0" y="0" rx="2" ry="2" width="250" height="30" />
|
||||
<rect x="0" y="38" rx="2" ry="2" width="300" height="15" />
|
||||
<rect x="0" y="175" rx="2" ry="2" width="200" height="20" />
|
||||
<rect x="1" y="205" rx="2" ry="2" width="385" height="38" />
|
||||
<rect x="0" y="110" rx="2" ry="2" width="385" height="38" />
|
||||
</ContentLoader>
|
||||
) : (
|
||||
children
|
||||
);
|
||||
}
|
||||
145
client/src/containers/Authentication/utils.js
Normal file
145
client/src/containers/Authentication/utils.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import * as Yup from 'yup';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { formatMessage } from 'services/intl';
|
||||
|
||||
export const LOGIN_ERRORS = {
|
||||
INVALID_DETAILS: 'INVALID_DETAILS',
|
||||
USER_INACTIVE: 'USER_INACTIVE',
|
||||
LOGIN_TO_MANY_ATTEMPTS: 'LOGIN_TO_MANY_ATTEMPTS',
|
||||
};
|
||||
|
||||
const REGISTER_ERRORS = {
|
||||
PHONE_NUMBER_EXISTS: 'PHONE_NUMBER_EXISTS',
|
||||
EMAIL_EXISTS: 'EMAIL.EXISTS',
|
||||
};
|
||||
|
||||
|
||||
export const LoginSchema = Yup.object().shape({
|
||||
crediential: Yup.string()
|
||||
.required()
|
||||
.email()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
password: Yup.string()
|
||||
.required()
|
||||
.min(4)
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
export const RegisterSchema = Yup.object().shape({
|
||||
first_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'first_name_' })),
|
||||
last_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'last_name_' })),
|
||||
email: Yup.string()
|
||||
.email()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'phone_number_' })),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
export const ResetPasswordSchema = Yup.object().shape({
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
confirm_password: Yup.string()
|
||||
.oneOf([Yup.ref('password'), null])
|
||||
.required()
|
||||
.label(formatMessage({ id: 'confirm_password' })),
|
||||
});
|
||||
|
||||
// Validation schema.
|
||||
export const SendResetPasswordSchema = Yup.object().shape({
|
||||
crediential: Yup.string()
|
||||
.required()
|
||||
.email()
|
||||
.label(formatMessage({ id: 'email' })),
|
||||
});
|
||||
|
||||
export const InviteAcceptSchema = Yup.object().shape({
|
||||
first_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'first_name_' })),
|
||||
last_name: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'last_name_' })),
|
||||
phone_number: Yup.string()
|
||||
.matches()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'phone_number' })),
|
||||
password: Yup.string()
|
||||
.min(4)
|
||||
.required()
|
||||
.label(formatMessage({ id: 'password' })),
|
||||
});
|
||||
|
||||
export const transformSendResetPassErrorsToToasts = (errors) => {
|
||||
const toastBuilders = [];
|
||||
|
||||
if (errors.find((e) => e.type === 'EMAIL.NOT.REGISTERED')) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'we_couldn_t_find_your_account_with_that_email',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
return toastBuilders;
|
||||
}
|
||||
|
||||
export const transformLoginErrorsToToasts = (errors) => {
|
||||
const toastBuilders = [];
|
||||
|
||||
if (errors.find((e) => e.type === LOGIN_ERRORS.INVALID_DETAILS)) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'email_and_password_entered_did_not_match',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === LOGIN_ERRORS.USER_INACTIVE)) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'the_user_has_been_suspended_from_admin',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (
|
||||
errors.find((e) => e.type === LOGIN_ERRORS.LOGIN_TO_MANY_ATTEMPTS)
|
||||
) {
|
||||
toastBuilders.push({
|
||||
message: formatMessage({
|
||||
id: 'your_account_has_been_locked',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
return toastBuilders;
|
||||
}
|
||||
|
||||
export const transformRegisterErrorsToForm = (errors) => {
|
||||
const formErrors = {};
|
||||
|
||||
if (errors.some((e) => e.type === REGISTER_ERRORS.PHONE_NUMBER_EXISTS)) {
|
||||
formErrors.phone_number = formatMessage({
|
||||
id: 'the_phone_number_already_used_in_another_account',
|
||||
});
|
||||
}
|
||||
if (errors.some((e) => e.type === REGISTER_ERRORS.EMAIL_EXISTS)) {
|
||||
formErrors.email = formatMessage({
|
||||
id: 'the_email_already_used_in_another_account',
|
||||
});
|
||||
}
|
||||
return formErrors;
|
||||
}
|
||||
@@ -1,22 +1,13 @@
|
||||
import {
|
||||
login,
|
||||
resetPassword,
|
||||
sendResetPassword,
|
||||
inviteAccept,
|
||||
register,
|
||||
inviteMetaByToken,
|
||||
} from 'store/authentication/authentication.actions';
|
||||
import { connect } from 'react-redux';
|
||||
import t from 'store/types';
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
requestLogin: (form) => dispatch(login({ form })),
|
||||
requestLogout: () => dispatch({ type: t.LOGOUT }),
|
||||
requestRegister: (form) => dispatch(register({ form })),
|
||||
requestSendResetPassword: (email) => dispatch(sendResetPassword({ email })),
|
||||
requestResetPassword: (form, token) => dispatch(resetPassword({ form, token })),
|
||||
requestInviteAccept: (form, token) => dispatch(inviteAccept({ form, token })),
|
||||
requestInviteMetaByToken: (token) => dispatch(inviteMetaByToken({ token })),
|
||||
// requestLogin: (form) => dispatch(login({ form })),
|
||||
// requestLogout: () => dispatch({ type: t.LOGOUT }),
|
||||
// requestRegister: (form) => dispatch(register({ form })),
|
||||
// requestSendResetPassword: (email) => dispatch(sendResetPassword({ email })),
|
||||
// requestResetPassword: (form, token) => dispatch(resetPassword({ form, token })),
|
||||
// requestInviteAccept: (form, token) => dispatch(inviteAccept({ form, token })),
|
||||
// requestInviteMetaByToken: (token) => dispatch(inviteMetaByToken({ token })),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { Row, Col, ErrorMessage } from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { ErrorMessage } from 'components';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { Icon, For } from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import withAuthenticationActions from 'containers/Authentication/withAuthenticationActions';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
|
||||
import footerLinks from 'config/footerLinks';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { useAuthActions, useAuthOrganizationId } from 'hooks/state';
|
||||
|
||||
function FooterLinkItem({ title, link }) {
|
||||
return (
|
||||
@@ -21,16 +16,13 @@ function FooterLinkItem({ title, link }) {
|
||||
/**
|
||||
* Wizard setup left section.
|
||||
*/
|
||||
function SetupLeftSection({
|
||||
// #withAuthenticationActions
|
||||
requestLogout,
|
||||
export default function SetupLeftSection() {
|
||||
const { setLogout } = useAuthActions();
|
||||
const organizationId = useAuthOrganizationId();
|
||||
|
||||
// #withAuthentication
|
||||
currentOrganizationId
|
||||
}) {
|
||||
const onClickLogout = useCallback(() => {
|
||||
requestLogout();
|
||||
}, [requestLogout]);
|
||||
const onClickLogout = () => {
|
||||
setLogout();
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={'setup-page__left-section'}>
|
||||
@@ -50,7 +42,7 @@ function SetupLeftSection({
|
||||
|
||||
<div className={'content__organization'}>
|
||||
<span class="organization-id">
|
||||
Oragnization ID: <span class="id">{ currentOrganizationId }</span>,
|
||||
Oragnization ID: <span class="id">{ organizationId }</span>,
|
||||
</span>
|
||||
<br />
|
||||
<span class="signout">
|
||||
@@ -70,9 +62,4 @@ function SetupLeftSection({
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAuthenticationActions,
|
||||
withAuthentication(({ currentOrganizationId }) => ({ currentOrganizationId })),
|
||||
)(SetupLeftSection);
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export default function BillingPlansForm() {
|
||||
return (
|
||||
<div class="billing-plans">
|
||||
<BillingPlansInput
|
||||
title={<T id={'sele ct_a_plan'} values={{ order: 1 }} />}
|
||||
title={<T id={'select_a_plan'} values={{ order: 1 }} />}
|
||||
description={<T id={'please_enter_your_preferred_payment_method'} />}
|
||||
/>
|
||||
<BillingPeriodsInput
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
// Transform the account.
|
||||
const transformAccount = (response) => {
|
||||
@@ -11,9 +11,11 @@ const transformAccount = (response) => {
|
||||
* Retrieve accounts list.
|
||||
*/
|
||||
export function useAccounts(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['ACCOUNTS', query],
|
||||
() => ApiService.get('accounts', { params: query }),
|
||||
() => apiRequest.get('accounts', { params: query }),
|
||||
{
|
||||
select: (response) => {
|
||||
return response.data.accounts;
|
||||
@@ -32,9 +34,11 @@ export function useAccounts(query, props) {
|
||||
* @param {number} id -
|
||||
*/
|
||||
export function useAccount(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['ACCOUNT', id],
|
||||
() => ApiService.get(`accounts/${id}`).then(transformAccount),
|
||||
() => apiRequest.get(`accounts/${id}`).then(transformAccount),
|
||||
props,
|
||||
);
|
||||
return {
|
||||
@@ -47,9 +51,11 @@ export function useAccount(id, props) {
|
||||
* Retrieve accounts types list.
|
||||
*/
|
||||
export function useAccountsTypes(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['ACCOUNTS_TYPES'],
|
||||
() => ApiService.get('account_types'),
|
||||
() => apiRequest.get('account_types'),
|
||||
{
|
||||
select: (res) => res.data.account_types,
|
||||
...props,
|
||||
@@ -66,8 +72,9 @@ export function useAccountsTypes(props) {
|
||||
*/
|
||||
export function useCreateAccount(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('accounts', values), {
|
||||
return useMutation((values) => apiRequest.post('accounts', values), {
|
||||
onSuccess: () => {
|
||||
client.invalidateQueries('ACCOUNTS');
|
||||
},
|
||||
@@ -80,9 +87,10 @@ export function useCreateAccount(props) {
|
||||
*/
|
||||
export function useEditAccount(props) {
|
||||
const query = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id,values]) => ApiService.post(`accounts/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`accounts/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
query.invalidateQueries('ACCOUNTS');
|
||||
@@ -97,8 +105,9 @@ export function useEditAccount(props) {
|
||||
*/
|
||||
export function useDeleteAccount(props) {
|
||||
const query = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`accounts/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`accounts/${id}`), {
|
||||
onSuccess: () => {
|
||||
query.invalidateQueries('ACCOUNTS');
|
||||
},
|
||||
@@ -111,8 +120,9 @@ export function useDeleteAccount(props) {
|
||||
*/
|
||||
export function useActivateAccount(props) {
|
||||
const query = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`accounts/${id}/activate`), {
|
||||
return useMutation((id) => apiRequest.post(`accounts/${id}/activate`), {
|
||||
onSuccess: () => {
|
||||
query.invalidateQueries('ACCOUNTS');
|
||||
},
|
||||
@@ -125,8 +135,9 @@ export function useActivateAccount(props) {
|
||||
*/
|
||||
export function useInactivateAccount(props) {
|
||||
const query = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`accounts/${id}/inactivate`), {
|
||||
return useMutation((id) => apiRequest.post(`accounts/${id}/inactivate`), {
|
||||
onSuccess: () => {
|
||||
query.invalidateQueries('ACCOUNTS');
|
||||
},
|
||||
|
||||
63
client/src/hooks/query/authentication.js
Normal file
63
client/src/hooks/query/authentication.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { useAuthActions } from '../state';
|
||||
|
||||
/**
|
||||
* Authentication login.
|
||||
*/
|
||||
export const useAuthLogin = (props) => {
|
||||
const { setLogin } = useAuthActions();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useMutation(
|
||||
(values) => apiRequest.post('auth/login', values),
|
||||
{
|
||||
select: (res) => res.data,
|
||||
...props
|
||||
}
|
||||
);
|
||||
const { isSuccess, data: response } = states;
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess) { setLogin(response.data); }
|
||||
}, [isSuccess, response, setLogin]);
|
||||
|
||||
return states;
|
||||
};
|
||||
|
||||
/**
|
||||
* Authentication register.
|
||||
*/
|
||||
export const useAuthRegister = (props) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => apiRequest.post('auth/register', values),
|
||||
props,
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Authentication send reset password.
|
||||
*/
|
||||
export const useAuthSendResetPassword = (props) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(email) => apiRequest.post('auth/send_reset_password', email),
|
||||
props
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication reset password.
|
||||
*/
|
||||
export const useAuthResetPassword = (props) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([token, values]) => apiRequest.post(`auth/reset/${token}`, values),
|
||||
props,
|
||||
)
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { useQueryClient, useQuery, useMutation } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Creates a new sale invoice.
|
||||
*/
|
||||
export function useCreateBill(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('purchases/bills', values), {
|
||||
return useMutation((values) => apiRequest.post('purchases/bills', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('BILLS');
|
||||
queryClient.invalidateQueries('BILL');
|
||||
@@ -23,9 +24,10 @@ export function useCreateBill(props) {
|
||||
*/
|
||||
export function useEditBill(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`purchases/bills/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`purchases/bills/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('BILLS');
|
||||
@@ -41,8 +43,9 @@ export function useEditBill(props) {
|
||||
*/
|
||||
export function useDeleteBill(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`purchases/bills/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`purchases/bills/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('BILLS');
|
||||
queryClient.invalidateQueries('BILL');
|
||||
@@ -55,10 +58,12 @@ export function useDeleteBill(props) {
|
||||
* Retrieve sale invoices list with pagination meta.
|
||||
*/
|
||||
export function useBills(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['BILLS', query],
|
||||
() =>
|
||||
ApiService.get('purchases/bills', { params: query }),
|
||||
apiRequest.get('purchases/bills', { params: query }),
|
||||
{
|
||||
select: (response) => ({
|
||||
bills: response.data.bills,
|
||||
@@ -88,9 +93,11 @@ export function useBills(query, props) {
|
||||
* @param {number} id - Bill id.
|
||||
*/
|
||||
export function useBill(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['BILL', id],
|
||||
() => ApiService.get(`/purchases/bills/${id}`),
|
||||
() => apiRequest.get(`/purchases/bills/${id}`),
|
||||
{
|
||||
select: (res) => res.data.bill,
|
||||
...props,
|
||||
@@ -108,9 +115,10 @@ export function useBill(id, props) {
|
||||
*/
|
||||
export function useOpenBill(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`purchases/bills/${id}/open`),
|
||||
(id) => apiRequest.delete(`purchases/bills/${id}/open`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('BILLS');
|
||||
@@ -125,10 +133,12 @@ export function useOpenBill(props) {
|
||||
* @param {number} vendorId -
|
||||
*/
|
||||
export function useDueBills(vendorId, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['BILLS_DUE', vendorId],
|
||||
() =>
|
||||
ApiService.get(`purchases/bills/due`, {
|
||||
apiRequest.get(`purchases/bills/due`, {
|
||||
params: { vendor_id: vendorId },
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { useMutation, useQueryClient, useQuery } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Create a new currency.
|
||||
*/
|
||||
export function useCreateCurrency(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('currencies', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CURRENCIES');
|
||||
},
|
||||
...props,
|
||||
}
|
||||
);
|
||||
return useMutation((values) => apiRequest.post('currencies', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CURRENCIES');
|
||||
},
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -24,15 +22,17 @@ export function useCreateCurrency(props) {
|
||||
*/
|
||||
export function useEditCurrency(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(([currencyCode, values]) =>
|
||||
ApiService.post(`currencies/${currencyCode}`, values),
|
||||
return useMutation(
|
||||
([currencyCode, values]) =>
|
||||
apiRequest.post(`currencies/${currencyCode}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CURRENCIES');
|
||||
},
|
||||
...props,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,15 +41,16 @@ export function useEditCurrency(props) {
|
||||
*/
|
||||
export function useDeleteCurrency(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((currencyCode) =>
|
||||
ApiService.delete(`currencies/${currencyCode}`),
|
||||
return useMutation(
|
||||
(currencyCode) => apiRequest.delete(`currencies/${currencyCode}`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CURRENCIES');
|
||||
},
|
||||
...props
|
||||
}
|
||||
...props,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,14 +58,16 @@ export function useDeleteCurrency(props) {
|
||||
* Retrieve the currencies list.
|
||||
*/
|
||||
export function useCurrencies(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['CURRENCIES'],
|
||||
() => ApiService.get('currencies').then(res => res.data.currencies),
|
||||
() => apiRequest.get('currencies').then((res) => res.data.currencies),
|
||||
props,
|
||||
);
|
||||
|
||||
return {
|
||||
...states,
|
||||
data: defaultTo(states.data, []),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
const defaultPagination = {
|
||||
pageSize: 12,
|
||||
@@ -13,9 +13,11 @@ const defaultPagination = {
|
||||
* Retrieve customers list with pagination meta.
|
||||
*/
|
||||
export function useCustomers(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['CUSTOMERS', query],
|
||||
() => ApiService.get(`customers`, { params: query }),
|
||||
() => apiRequest.get(`customers`, { params: query }),
|
||||
{
|
||||
select: (response) => ({
|
||||
customers: response.data.customers,
|
||||
@@ -42,9 +44,10 @@ export function useCustomers(query, props) {
|
||||
*/
|
||||
export function useEditCustomer(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`customers/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`customers/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CUSTOMERS');
|
||||
@@ -60,9 +63,10 @@ export function useEditCustomer(props) {
|
||||
*/
|
||||
export function useDeleteCustomer(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`customers/${id}`),
|
||||
(id) => apiRequest.delete(`customers/${id}`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CUSTOMERS');
|
||||
@@ -78,9 +82,10 @@ export function useDeleteCustomer(props) {
|
||||
*/
|
||||
export function useCreateCustomer(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('customers', values),
|
||||
(values) => apiRequest.post('customers', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('CUSTOMERS');
|
||||
@@ -94,9 +99,12 @@ export function useCreateCustomer(props) {
|
||||
* Retrieve the customer details.
|
||||
*/
|
||||
export function useCustomer(id, props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['CUSTOMER', id],
|
||||
() => ApiService.get(`customers/${id}`),
|
||||
() => apiRequest.get(`customers/${id}`),
|
||||
{
|
||||
select: (res) => res.data.customer,
|
||||
...props
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQueryClient, useQuery, useMutation } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { transformPagination } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -8,8 +8,9 @@ import { transformPagination } from 'utils';
|
||||
*/
|
||||
export function useCreateEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('sales/estimates', values), {
|
||||
return useMutation((values) => apiRequest.post('sales/estimates', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
},
|
||||
@@ -22,9 +23,10 @@ export function useCreateEstimate(props) {
|
||||
*/
|
||||
export function useEditEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`sales/estimates/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`sales/estimates/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
@@ -38,9 +40,11 @@ export function useEditEstimate(props) {
|
||||
* Retrieve sale estimate details.
|
||||
*/
|
||||
export function useEstimate(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_ESTIMATE', id],
|
||||
() => ApiService.get(`sales/estimates/${id}`),
|
||||
() => apiRequest.get(`sales/estimates/${id}`),
|
||||
{
|
||||
select: (res) => res.data.estimate,
|
||||
...props,
|
||||
@@ -57,9 +61,11 @@ export function useEstimate(id, props) {
|
||||
* Retrieve sale invoices list with pagination meta.
|
||||
*/
|
||||
export function useEstimates(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_ESTIMATES', query],
|
||||
() => ApiService.get('sales/estimates', { params: query }),
|
||||
() => apiRequest.get('sales/estimates', { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
estimates: res.data.sales_estimates,
|
||||
@@ -89,8 +95,9 @@ export function useEstimates(query, props) {
|
||||
*/
|
||||
export function useDeleteEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`sales/estimates/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`sales/estimates/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
},
|
||||
@@ -103,9 +110,10 @@ export function useDeleteEstimate(props) {
|
||||
*/
|
||||
export function useDeliverEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.post(`sales/estimates/${id}/deliver`),
|
||||
(id) => apiRequest.post(`sales/estimates/${id}/deliver`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
@@ -120,9 +128,10 @@ export function useDeliverEstimate(props) {
|
||||
*/
|
||||
export function useApproveEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.post(`sales/estimates/${id}/approve`),
|
||||
(id) => apiRequest.post(`sales/estimates/${id}/approve`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
@@ -137,9 +146,10 @@ export function useApproveEstimate(props) {
|
||||
*/
|
||||
export function useRejectEstimate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.post(`sales/estimates/${id}/reject`),
|
||||
(id) => apiRequest.post(`sales/estimates/${id}/reject`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_ESTIMATES');
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Creates a new exchange rate.
|
||||
*/
|
||||
export function useCreateExchangeRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('exchange_rates', values), {
|
||||
return useMutation((values) => apiRequest.post('exchange_rates', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXCHANGES_RATES');
|
||||
},
|
||||
@@ -21,9 +22,10 @@ export function useCreateExchangeRate(props) {
|
||||
*/
|
||||
export function useEdiExchangeRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`exchange_rates/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`exchange_rates/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXCHANGES_RATES');
|
||||
@@ -38,8 +40,9 @@ export function useEdiExchangeRate(props) {
|
||||
*/
|
||||
export function useDeleteExchangeRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`exchange_rates/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`exchange_rates/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXCHANGES_RATES');
|
||||
},
|
||||
@@ -59,10 +62,12 @@ const transformExchangesRates = (response) => {
|
||||
* Retrieve the exchange rate list.
|
||||
*/
|
||||
export function useExchangeRates(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['EXCHANGES_RATES', query],
|
||||
() =>
|
||||
ApiService.get('exchange_rates', { params: { query } }).then(
|
||||
apiRequest.get('exchange_rates', { params: { query } }).then(
|
||||
transformExchangesRates,
|
||||
),
|
||||
props,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { transformPagination } from 'utils';
|
||||
|
||||
const defaultPagination = {
|
||||
@@ -13,9 +13,11 @@ const defaultPagination = {
|
||||
* Retrieve the expenses list.
|
||||
*/
|
||||
export function useExpenses(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['EXPENSES', query],
|
||||
() => ApiService.get(`expenses`, { params: { ...query } }),
|
||||
() => apiRequest.get(`expenses`, { params: { ...query } }),
|
||||
{
|
||||
select: (response) => ({
|
||||
expenses: response.data.expenses,
|
||||
@@ -41,10 +43,16 @@ export function useExpenses(query, props) {
|
||||
* @param {number} id - Expense id.
|
||||
*/
|
||||
export function useExpense(id, props) {
|
||||
const states = useQuery(['EXPENSE', id], () => ApiService.get(`expenses/${id}`), {
|
||||
select: (res) => res.data.expense,
|
||||
...props,
|
||||
});
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['EXPENSE', id],
|
||||
() => apiRequest.get(`expenses/${id}`),
|
||||
{
|
||||
select: (res) => res.data.expense,
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
...states,
|
||||
@@ -56,9 +64,10 @@ export function useExpense(id, props) {
|
||||
* Deletes the given expense.
|
||||
*/
|
||||
export function useDeleteExpense(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`expenses/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`expenses/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXPENSES');
|
||||
queryClient.invalidateQueries('EXPENSE');
|
||||
@@ -72,9 +81,10 @@ export function useDeleteExpense(props) {
|
||||
*/
|
||||
export function useEditExpense(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`expenses/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`expenses/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXPENSES');
|
||||
@@ -90,8 +100,9 @@ export function useEditExpense(props) {
|
||||
*/
|
||||
export function useCreateExpense(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('expenses', values), {
|
||||
return useMutation((values) => apiRequest.post('expenses', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXPENSES');
|
||||
queryClient.invalidateQueries('EXPENSE');
|
||||
@@ -105,12 +116,13 @@ export function useCreateExpense(props) {
|
||||
*/
|
||||
export function usePublishExpense(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`expenses/${id}/publish`), {
|
||||
return useMutation((id) => apiRequest.post(`expenses/${id}/publish`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('EXPENSES');
|
||||
queryClient.invalidateQueries('EXPENSE');
|
||||
},
|
||||
...props,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useQuery } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import {
|
||||
trialBalanceSheetReducer,
|
||||
balanceSheetRowsReducer,
|
||||
@@ -8,15 +7,18 @@ import {
|
||||
generalLedgerTableRowsReducer,
|
||||
journalTableRowsReducer,
|
||||
} from 'containers/FinancialStatements/reducers';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Retrieve balance sheet.
|
||||
*/
|
||||
export function useBalanceSheet(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['FINANCIAL-REPORT', 'BALANCE-SHEET', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/balance_sheet', {
|
||||
apiRequest.get('/financial_statements/balance_sheet', {
|
||||
params: query,
|
||||
}),
|
||||
{
|
||||
@@ -43,10 +45,12 @@ export function useBalanceSheet(query, props) {
|
||||
* Retrieve trial balance sheet.
|
||||
*/
|
||||
export function useTrialBalanceSheet(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['FINANCIAL-REPORT', 'TRIAL-BALANCE-SHEET', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/trial_balance_sheet', {
|
||||
apiRequest.get('/financial_statements/trial_balance_sheet', {
|
||||
params: query,
|
||||
}),
|
||||
{
|
||||
@@ -72,10 +76,12 @@ export function useTrialBalanceSheet(query, props) {
|
||||
* Retrieve profit/loss (P&L) sheet.
|
||||
*/
|
||||
export function useProfitLossSheet(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['FINANCIAL-REPORT', 'PROFIT-LOSS-SHEET', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/profit_loss_sheet', {
|
||||
apiRequest.get('/financial_statements/profit_loss_sheet', {
|
||||
params: query,
|
||||
}),
|
||||
{
|
||||
@@ -101,10 +107,12 @@ export function useProfitLossSheet(query, props) {
|
||||
* Retrieve general ledger (GL) sheet.
|
||||
*/
|
||||
export function useGeneralLedgerSheet(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['FINANCIAL-REPORT', 'GENERAL-LEDGER', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/general_ledger', {
|
||||
apiRequest.get('/financial_statements/general_ledger', {
|
||||
params: query,
|
||||
}),
|
||||
{
|
||||
@@ -130,10 +138,12 @@ export function useGeneralLedgerSheet(query, props) {
|
||||
* Retrieve journal sheet.
|
||||
*/
|
||||
export function useJournalSheet(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['FINANCIAL-REPORT', 'JOURNAL', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/journal', { params: query }),
|
||||
apiRequest.get('/financial_statements/journal', { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
tableRows: journalTableRowsReducer(res.data.data),
|
||||
@@ -157,10 +167,12 @@ export function useJournalSheet(query, props) {
|
||||
* Retrieve AR aging summary report.
|
||||
*/
|
||||
export function useARAgingSummaryReport(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['FINANCIAL-REPORT', 'AR-AGING-SUMMARY', query],
|
||||
() =>
|
||||
ApiService.get('/financial_statements/receivable_aging_summary', {
|
||||
apiRequest.get('/financial_statements/receivable_aging_summary', {
|
||||
params: query,
|
||||
}),
|
||||
props,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
export * from './authentication';
|
||||
export * from './accounts';
|
||||
export * from './views';
|
||||
export * from './items';
|
||||
@@ -18,4 +18,5 @@ export * from './paymentReceives';
|
||||
export * from './paymentMades';
|
||||
export * from './settings';
|
||||
export * from './users';
|
||||
export * from './exchangeRates';
|
||||
export * from './invite';
|
||||
export * from './exchangeRates';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
const invalidateQueries = (queryClient) => {
|
||||
queryClient.invalidateQueries('INVENTORY_ADJUSTMENTS');
|
||||
@@ -15,9 +15,10 @@ const invalidateQueries = (queryClient) => {
|
||||
*/
|
||||
export function useCreateInventoryAdjustment(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('inventory_adjustments/quick', values),
|
||||
(values) => apiRequest.post('inventory_adjustments/quick', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
invalidateQueries(queryClient)
|
||||
@@ -32,9 +33,10 @@ export function useCreateInventoryAdjustment(props) {
|
||||
*/
|
||||
export function useDeleteInventoryAdjustment(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`inventory_adjustments/${id}`),
|
||||
(id) => apiRequest.delete(`inventory_adjustments/${id}`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
invalidateQueries(queryClient)
|
||||
@@ -55,9 +57,11 @@ const inventoryAdjustmentsTransformer = (response) => {
|
||||
* Retrieve inventory adjustment list with pagination meta.
|
||||
*/
|
||||
export function useInventoryAdjustments(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['INVENTORY_ADJUSTMENTS', query],
|
||||
() => ApiService.get('inventory_adjustments', { params: query })
|
||||
() => apiRequest.get('inventory_adjustments', { params: query })
|
||||
.then(inventoryAdjustmentsTransformer),
|
||||
props,
|
||||
);
|
||||
|
||||
31
client/src/hooks/query/invite.js
Normal file
31
client/src/hooks/query/invite.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { useMutation, useQuery } from 'react-query';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Authentication invite accept.
|
||||
*/
|
||||
export const useAuthInviteAccept = (props) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([values, token]) => apiRequest.post(`invite/accept/${token}`, values),
|
||||
props,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the invite meta by the given token.
|
||||
* @param {string} token - Token.
|
||||
*/
|
||||
export const useInviteMetaByToken = (token, props) => {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['INVITE_META', token],
|
||||
() => apiRequest.get(`invite/invited/${token}`),
|
||||
{
|
||||
select: (res) => res.data,
|
||||
...props
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
import { useQueryClient, useQuery, useMutation } from 'react-query';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Creates a new sale invoice.
|
||||
*/
|
||||
export function useCreateInvoice(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('sales/invoices', values), {
|
||||
return useMutation((values) => apiRequest.post('sales/invoices', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_INVOICES');
|
||||
},
|
||||
@@ -22,9 +23,10 @@ export function useCreateInvoice(props) {
|
||||
*/
|
||||
export function useEditInvoice(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`sales/invoices/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`sales/invoices/${id}`, values),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
queryClient.invalidateQueries('SALE_INVOICES');
|
||||
@@ -40,8 +42,9 @@ export function useEditInvoice(props) {
|
||||
*/
|
||||
export function useDeleteInvoice(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`sales/invoices/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`sales/invoices/${id}`), {
|
||||
onSuccess: (res, id) => {
|
||||
queryClient.invalidateQueries('SALE_INVOICES');
|
||||
queryClient.invalidateQueries(['SALE_INVOICE', id]);
|
||||
@@ -54,9 +57,11 @@ export function useDeleteInvoice(props) {
|
||||
* Retrieve sale invoices list with pagination meta.
|
||||
*/
|
||||
export function useInvoices(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_INVOICES', query],
|
||||
() => ApiService.get('sales/invoices', { params: query }),
|
||||
() => apiRequest.get('sales/invoices', { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
invoices: res.data.sales_invoices,
|
||||
@@ -86,8 +91,9 @@ export function useInvoices(query, props) {
|
||||
*/
|
||||
export function useDeliverInvoice(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`sales/invoices/${id}/deliver`), {
|
||||
return useMutation((id) => apiRequest.post(`sales/invoices/${id}/deliver`), {
|
||||
onSuccess: (res, id) => {
|
||||
queryClient.invalidateQueries('SALE_INVOICES');
|
||||
queryClient.invalidateQueries(['SALE_INVOICE', id]);
|
||||
@@ -100,9 +106,11 @@ export function useDeliverInvoice(props) {
|
||||
* Retrieve the sale invoice details.
|
||||
*/
|
||||
export function useInvoice(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_INVOICE', id],
|
||||
() => ApiService.get(`sales/invoices/${id}`),
|
||||
() => apiRequest.get(`sales/invoices/${id}`),
|
||||
{
|
||||
select: (res) => res.data.sale_invoice,
|
||||
...props,
|
||||
@@ -120,10 +128,12 @@ export function useInvoice(id, props) {
|
||||
* @param {number} customerId - Customer id.
|
||||
*/
|
||||
export function useDueInvoices(customerId, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_INVOICE_DUE', customerId],
|
||||
() =>
|
||||
ApiService.get(`sales/invoices/payable`, {
|
||||
apiRequest.get(`sales/invoices/payable`, {
|
||||
params: { customer_id: customerId },
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination, transformResponse } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
const defaultPagination = {
|
||||
pageSize: 12,
|
||||
@@ -14,8 +14,9 @@ const defaultPagination = {
|
||||
*/
|
||||
export function useCreateItem(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('items', values), {
|
||||
return useMutation((values) => apiRequest.post('items', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
queryClient.invalidateQueries('ITEMS_CATEGORIES');
|
||||
@@ -29,8 +30,9 @@ export function useCreateItem(props) {
|
||||
*/
|
||||
export function useEditItem(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(([id, values]) => ApiService.post(`items/${id}`, values), {
|
||||
return useMutation(([id, values]) => apiRequest.post(`items/${id}`, values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
queryClient.invalidateQueries('ITEM');
|
||||
@@ -45,8 +47,9 @@ export function useEditItem(props) {
|
||||
*/
|
||||
export function useDeleteItem(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`items/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`items/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
queryClient.invalidateQueries('ITEM');
|
||||
@@ -71,10 +74,12 @@ const transformItemsResponse = (response) => {
|
||||
* Retrieves items list.
|
||||
*/
|
||||
export function useItems(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const result = useQuery(
|
||||
['ITEMS', query],
|
||||
() =>
|
||||
ApiService.get(`items`, { params: query }).then(transformItemsResponse),
|
||||
apiRequest.get(`items`, { params: query }).then(transformItemsResponse),
|
||||
props,
|
||||
);
|
||||
|
||||
@@ -93,9 +98,11 @@ export function useItems(query, props) {
|
||||
* @param {number} id - Item id.
|
||||
*/
|
||||
export function useItem(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['ITEM', id],
|
||||
() => ApiService.get(`items/${id}`).then((response) => response.data.item),
|
||||
() => apiRequest.get(`items/${id}`).then((response) => response.data.item),
|
||||
props,
|
||||
);
|
||||
}
|
||||
@@ -105,8 +112,9 @@ export function useItem(id, props) {
|
||||
*/
|
||||
export function useActivateItem(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`items/${id}/activate`), {
|
||||
return useMutation((id) => apiRequest.post(`items/${id}/activate`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
queryClient.invalidateQueries('ITEM');
|
||||
@@ -120,8 +128,9 @@ export function useActivateItem(props) {
|
||||
*/
|
||||
export function useInactivateItem(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`items/${id}/inactivate`), {
|
||||
return useMutation((id) => apiRequest.post(`items/${id}/inactivate`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
queryClient.invalidateQueries('ITEM');
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Creates a new item category.
|
||||
*/
|
||||
export function useCreateItemCategory(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('item_categories', values), {
|
||||
return useMutation((values) => apiRequest.post('item_categories', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS_CATEGORIES');
|
||||
},
|
||||
@@ -21,9 +22,10 @@ export function useCreateItemCategory(props) {
|
||||
*/
|
||||
export function useEditItemCategory(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`item_categories/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`item_categories/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS_CATEGORIES');
|
||||
@@ -39,8 +41,9 @@ export function useEditItemCategory(props) {
|
||||
*/
|
||||
export function useDeleteItemCategory(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`item_categories/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`item_categories/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('ITEMS_CATEGORIES');
|
||||
queryClient.invalidateQueries('ITEMS');
|
||||
@@ -61,10 +64,12 @@ const transformItemsCategories = (response) => {
|
||||
* Retrieve the items categories.
|
||||
*/
|
||||
export function useItemsCategories(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['ITEMS_CATEGORIES', query],
|
||||
() =>
|
||||
ApiService.get(`item_categories`, { params: query }).then(
|
||||
apiRequest.get(`item_categories`, { params: query }).then(
|
||||
transformItemsCategories,
|
||||
),
|
||||
props,
|
||||
@@ -84,10 +89,12 @@ export function useItemsCategories(query, props) {
|
||||
* @param {number} id - Item category.
|
||||
*/
|
||||
export function useItemCategory(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['ITEMS_CATEGORY', id],
|
||||
() =>
|
||||
ApiService.get(`item_categories/${id}`).then((res) => res.data.category),
|
||||
apiRequest.get(`item_categories/${id}`).then((res) => res.data.category),
|
||||
props,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Creates a new manual journal.
|
||||
*/
|
||||
export function useCreateJournal(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('manual-journals', values),
|
||||
(values) => apiRequest.post('manual-journals', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('JOURNALS');
|
||||
@@ -25,9 +26,10 @@ export function useCreateJournal(props) {
|
||||
*/
|
||||
export function useEditJournal(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`manual-journals/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`manual-journals/${id}`, values),
|
||||
{
|
||||
onSuccess: (res, [id]) => {
|
||||
queryClient.invalidateQueries('JOURNALS');
|
||||
@@ -43,9 +45,10 @@ export function useEditJournal(props) {
|
||||
*/
|
||||
export function useDeleteJournal(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`manual-journals/${id}`),
|
||||
(id) => apiRequest.delete(`manual-journals/${id}`),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
queryClient.invalidateQueries('JOURNALS');
|
||||
@@ -61,9 +64,10 @@ export function useDeleteJournal(props) {
|
||||
*/
|
||||
export function usePublishJournal(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.post(`manual-journals/${id}/publish`),
|
||||
(id) => apiRequest.post(`manual-journals/${id}/publish`),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
queryClient.invalidateQueries('JOURNALS');
|
||||
@@ -78,9 +82,11 @@ export function usePublishJournal(props) {
|
||||
* Retrieve the manual journals with pagination meta.
|
||||
*/
|
||||
export function useJournals(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['JOURNALS', query],
|
||||
() => ApiService.get('manual-journals', { params: query }),
|
||||
() => apiRequest.get('manual-journals', { params: query }),
|
||||
{
|
||||
select: (response) => ({
|
||||
manualJournals: response.data.manual_journals,
|
||||
@@ -105,9 +111,11 @@ export function useJournals(query, props) {
|
||||
* Retrieve the manual journal details.
|
||||
*/
|
||||
export function useJournal(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['JOURNAL', id],
|
||||
() => ApiService.get(`manual-journals/${id}`),
|
||||
() => apiRequest.get(`manual-journals/${id}`),
|
||||
{
|
||||
select: (res) => res.data.manual_journal,
|
||||
...props,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Retrieve payment mades list.
|
||||
*/
|
||||
export function usePaymentMades(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['PAYMENT_MADES', query],
|
||||
() => ApiService.get('purchases/bill_payments', { params: query }),
|
||||
() => apiRequest.get('purchases/bill_payments', { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
paymentMades: res.data.bill_payments,
|
||||
@@ -35,9 +37,10 @@ export function usePaymentMades(query, props) {
|
||||
*/
|
||||
export function useCreatePaymentMade(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('purchases/bill_payments', values),
|
||||
(values) => apiRequest.post('purchases/bill_payments', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
client.invalidateQueries('PAYMENT_MADES');
|
||||
@@ -52,9 +55,10 @@ export function useCreatePaymentMade(props) {
|
||||
*/
|
||||
export function useEditPaymentMade(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`purchases/bill_payments/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`purchases/bill_payments/${id}`, values),
|
||||
{
|
||||
onSuccess: (res, [id, values]) => {
|
||||
client.invalidateQueries('PAYMENT_MADES');
|
||||
@@ -70,9 +74,10 @@ export function useEditPaymentMade(props) {
|
||||
*/
|
||||
export function useDeletePaymentMade(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`purchases/bill_payments/${id}`),
|
||||
(id) => apiRequest.delete(`purchases/bill_payments/${id}`),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
client.invalidateQueries('PAYMENT_MADES');
|
||||
@@ -87,9 +92,11 @@ export function useDeletePaymentMade(props) {
|
||||
* Retrieve specific payment made.
|
||||
*/
|
||||
export function usePaymentMade(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['PAYMENT_MADE', id],
|
||||
() => ApiService.get(`purchases/bill_payments/${id}`),
|
||||
() => apiRequest.get(`purchases/bill_payments/${id}`),
|
||||
{
|
||||
select: res => ({
|
||||
paymentMade: res.data.bill_payment,
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { transformPagination, saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Retrieve accounts list.
|
||||
*/
|
||||
export function usePaymentReceives(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['PAYMENT_RECEIVES', query],
|
||||
() => ApiService.get('sales/payment_receives', { params: query }),
|
||||
() => apiRequest.get('sales/payment_receives', { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
paymentReceives: res.data.payment_receives,
|
||||
@@ -39,9 +41,10 @@ export function usePaymentReceives(query, props) {
|
||||
*/
|
||||
export function useCreatePaymentReceive(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('sales/payment_receives', values),
|
||||
(values) => apiRequest.post('sales/payment_receives', values),
|
||||
{
|
||||
onSuccess: (data, values) => {
|
||||
client.invalidateQueries('PAYMENT_RECEIVES');
|
||||
@@ -61,9 +64,10 @@ export function useCreatePaymentReceive(props) {
|
||||
*/
|
||||
export function useEditPaymentReceive(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`sales/payment_receives/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`sales/payment_receives/${id}`, values),
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
client.invalidateQueries('PAYMENT_RECEIVES');
|
||||
@@ -83,9 +87,10 @@ export function useEditPaymentReceive(props) {
|
||||
*/
|
||||
export function useDeletePaymentReceive(props) {
|
||||
const client = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`sales/payment_receives/${id}`),
|
||||
(id) => apiRequest.delete(`sales/payment_receives/${id}`),
|
||||
{
|
||||
onSuccess: (data, [id]) => {
|
||||
client.invalidateQueries('PAYMENT_RECEIVES');
|
||||
@@ -105,9 +110,11 @@ export function useDeletePaymentReceive(props) {
|
||||
* @param {number} id - Payment receive.
|
||||
*/
|
||||
export function usePaymentReceive(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['PAYMENT_RECEIVE', id],
|
||||
() => ApiService.get(`sales/payment_receives/${id}`),
|
||||
() => apiRequest.get(`sales/payment_receives/${id}`),
|
||||
{
|
||||
select: (res) => ({
|
||||
paymentReceive: res.data.payment_receive,
|
||||
@@ -132,9 +139,11 @@ export function usePaymentReceive(id, props) {
|
||||
* @param {number} id - Payment receive id.
|
||||
*/
|
||||
export function usePaymentReceiveEditPage(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['PAYMENT_RECEIVE_EDIT_PAGE', id],
|
||||
() => ApiService.get(`sales/payment_receives/${id}/edit-page`),
|
||||
() => apiRequest.get(`sales/payment_receives/${id}/edit-page`),
|
||||
{
|
||||
select: (res) => ({
|
||||
paymentReceive: res.data.payment_receive,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQueryClient, useQuery, useMutation } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
import { transformPagination } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -8,8 +8,9 @@ import { transformPagination } from 'utils';
|
||||
*/
|
||||
export function useCreateReceipt(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => ApiService.post('sales/receipts', values), {
|
||||
return useMutation((values) => apiRequest.post('sales/receipts', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_RECEIPTS');
|
||||
},
|
||||
@@ -22,9 +23,10 @@ export function useCreateReceipt(props) {
|
||||
*/
|
||||
export function useEditReceipt(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`sales/receipts/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`sales/receipts/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_RECEIPTS');
|
||||
@@ -39,8 +41,9 @@ export function useEditReceipt(props) {
|
||||
*/
|
||||
export function useDeleteReceipt(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`sales/receipts/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`sales/receipts/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_RECEIPTS');
|
||||
},
|
||||
@@ -53,8 +56,9 @@ export function useDeleteReceipt(props) {
|
||||
*/
|
||||
export function useCloseReceipt(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.post(`sales/receipts/${id}/close`), {
|
||||
return useMutation((id) => apiRequest.post(`sales/receipts/${id}/close`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SALE_RECEIPTS');
|
||||
},
|
||||
@@ -66,9 +70,11 @@ export function useCloseReceipt(props) {
|
||||
* Retrieve sale invoices list with pagination meta.
|
||||
*/
|
||||
export function useReceipts(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_RECEIPTS', query],
|
||||
() => ApiService.get('sales/receipts', { params: query }),
|
||||
() => apiRequest.get('sales/receipts', { params: query }),
|
||||
{
|
||||
select: (response) => ({
|
||||
receipts: response.data.sale_receipts,
|
||||
@@ -97,9 +103,11 @@ export function useReceipts(query, props) {
|
||||
* Retrieve sale invoices list with pagination meta.
|
||||
*/
|
||||
export function useReceipt(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['SALE_RECEIPT', id],
|
||||
() => ApiService.get(`sales/receipts/${id}`),
|
||||
() => apiRequest.get(`sales/receipts/${id}`),
|
||||
{
|
||||
select: (res) => res.data.sale_receipt,
|
||||
...props,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
import t from 'store/types';
|
||||
|
||||
/**
|
||||
@@ -8,13 +8,14 @@ import t from 'store/types';
|
||||
*/
|
||||
export function useSettings(query, props) {
|
||||
const dispatch = useDispatch();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const settings = useQuery(
|
||||
['SETTINGS', query],
|
||||
async () => {
|
||||
const {
|
||||
data: { settings },
|
||||
} = await ApiService.get('settings', { params: query });
|
||||
} = await apiRequest.get('settings', { params: query });
|
||||
|
||||
return settings;
|
||||
},
|
||||
@@ -35,8 +36,9 @@ export function useSettings(query, props) {
|
||||
*/
|
||||
export function useSaveSettings(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((settings) => ApiService.post('settings', settings), {
|
||||
return useMutation((settings) => apiRequest.post('settings', settings), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('SETTINGS');
|
||||
},
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { useMutation, useQueryClient, useQuery } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
|
||||
/**
|
||||
* Create a new invite user.
|
||||
*/
|
||||
export function useCreateInviteUser(props) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation((values) => ApiService.post('invite/send', values), {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((values) => apiRequest.post('invite/send', values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('USERS');
|
||||
},
|
||||
@@ -21,8 +24,9 @@ export function useCreateInviteUser(props) {
|
||||
*/
|
||||
export function useEditUser(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(([id, values]) => ApiService.post(`users/${id}`, values), {
|
||||
return useMutation(([id, values]) => apiRequest.post(`users/${id}`, values), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('USERS');
|
||||
},
|
||||
@@ -35,8 +39,9 @@ export function useEditUser(props) {
|
||||
*/
|
||||
export function useDeleteUser(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => ApiService.delete(`users/${id}`), {
|
||||
return useMutation((id) => apiRequest.delete(`users/${id}`), {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('USERS');
|
||||
queryClient.invalidateQueries('USER');
|
||||
@@ -49,9 +54,11 @@ export function useDeleteUser(props) {
|
||||
* Retrieves users list.
|
||||
*/
|
||||
export function useUsers(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const result = useQuery(
|
||||
['USERS'],
|
||||
() => ApiService.get(`USERS`).then((response) => response.data.users),
|
||||
() => apiRequest.get(`USERS`).then((response) => response.data.users),
|
||||
props,
|
||||
);
|
||||
|
||||
@@ -65,9 +72,11 @@ export function useUsers(props) {
|
||||
* Retrieve details of the given user.
|
||||
*/
|
||||
export function useUser(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['USER', id],
|
||||
() => ApiService.get(`users/${id}`).then((response) => response.data.item),
|
||||
() => apiRequest.get(`users/${id}`).then((response) => response.data.item),
|
||||
props,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from 'services/ApiService';
|
||||
import { transformPagination } from 'utils';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
/**
|
||||
* Retrieve vendors list.
|
||||
*/
|
||||
export function useVendors(query, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['VENDORS', query],
|
||||
() => ApiService.get(`vendors`, { params: query }),
|
||||
() => apiRequest.get(`vendors`, { params: query }),
|
||||
{
|
||||
select: (res) => ({
|
||||
vendors: res.data.vendors,
|
||||
@@ -35,9 +37,10 @@ export function useVendors(query, props) {
|
||||
*/
|
||||
export function useEditVendor(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
([id, values]) => ApiService.post(`vendors/${id}`, values),
|
||||
([id, values]) => apiRequest.post(`vendors/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('VENDORS');
|
||||
@@ -53,9 +56,10 @@ export function useEditVendor(props) {
|
||||
*/
|
||||
export function useDeleteVendor(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id) => ApiService.delete(`vendors/${id}`),
|
||||
(id) => apiRequest.delete(`vendors/${id}`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('VENDORS');
|
||||
@@ -70,9 +74,10 @@ export function useDeleteVendor(props) {
|
||||
*/
|
||||
export function useCreateVendor(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => ApiService.post('vendors', values),
|
||||
(values) => apiRequest.post('vendors', values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries('VENDORS');
|
||||
@@ -86,9 +91,11 @@ export function useCreateVendor(props) {
|
||||
* Retrieve vendor details.
|
||||
*/
|
||||
export function useVendor(id, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['VENDOR', id],
|
||||
() => ApiService.get(`vendors/${id}`),
|
||||
() => apiRequest.get(`vendors/${id}`),
|
||||
{
|
||||
select: (res) => res.data.vendor,
|
||||
...props
|
||||
|
||||
@@ -1,26 +1,13 @@
|
||||
import { useQuery } from 'react-query';
|
||||
import { defaultTo } from 'lodash';
|
||||
import ApiService from "services/ApiService";
|
||||
// export function useSaveView(values) {
|
||||
// return ApiService.post('views', form);
|
||||
// }
|
||||
|
||||
// export function useEditView(values, id) {
|
||||
// return ApiService.post(`views/${id}`, values);
|
||||
// }
|
||||
|
||||
// export function useDeleteView(id) {
|
||||
// return ApiService.delete(`views/${id}`);
|
||||
// }
|
||||
|
||||
// export function useView(id) {
|
||||
// return useQuery(['VIEW', id], () => ApiService.get(`views/${id}`)
|
||||
// }
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
export function useResourceViews(resourceSlug) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['RESOURCE_VIEW', resourceSlug],
|
||||
() => ApiService.get(`views/resource/${resourceSlug}`)
|
||||
() => apiRequest.get(`views/resource/${resourceSlug}`)
|
||||
.then((response) => response.data.views),
|
||||
);
|
||||
|
||||
@@ -32,9 +19,11 @@ export function useResourceViews(resourceSlug) {
|
||||
|
||||
|
||||
export function useResourceColumns(resourceSlug) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useQuery(
|
||||
['RESOURCE_COLUMNS', resourceSlug],
|
||||
() => ApiService.get(`resources/${resourceSlug}/columns`),
|
||||
() => apiRequest.get(`resources/${resourceSlug}/columns`),
|
||||
{
|
||||
initialData: [],
|
||||
},
|
||||
@@ -42,9 +31,11 @@ export function useResourceColumns(resourceSlug) {
|
||||
}
|
||||
|
||||
export function useResourceFields(resourceSlug, props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
const states = useQuery(
|
||||
['RESOURCE_FIELDS', resourceSlug],
|
||||
() => ApiService.get(`resources/${resourceSlug}/fields`)
|
||||
() => apiRequest.get(`resources/${resourceSlug}/fields`)
|
||||
.then((res) => res.data.resource_fields),
|
||||
props
|
||||
);
|
||||
|
||||
41
client/src/hooks/state/authentication.js
Normal file
41
client/src/hooks/state/authentication.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useCallback } from 'react';
|
||||
import { isAuthenticated } from 'store/authentication/authentication.reducer';
|
||||
import { setLogin, setLogout } from 'store/authentication/authentication.actions';
|
||||
|
||||
export const useAuthActions = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return {
|
||||
setLogin: useCallback((login) => dispatch(setLogin(login)), [dispatch]),
|
||||
setLogout: useCallback(() => dispatch(setLogout()), [dispatch]),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve whether the user is authenticated.
|
||||
*/
|
||||
export const useIsAuthenticated = () => {
|
||||
return useSelector(isAuthenticated);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the authentication token.
|
||||
*/
|
||||
export const useAuthToken = () => {
|
||||
return useSelector((state) => state.authentication.token);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the authentication user.
|
||||
*/
|
||||
export const useAuthUser = () => {
|
||||
return useSelector((state) => state.authentication.user);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the authenticated organization id.
|
||||
*/
|
||||
export const useAuthOrganizationId = () => {
|
||||
return useSelector((state) => state.authentication.organization);
|
||||
};
|
||||
17
client/src/hooks/state/globalErrors.js
Normal file
17
client/src/hooks/state/globalErrors.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { setGlobalErrors } from 'store/globalErrors/globalErrors.actions';
|
||||
|
||||
export const useSetGlobalErrors = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return useCallback((errors) => {
|
||||
dispatch(setGlobalErrors(errors));
|
||||
}, [dispatch]);
|
||||
};
|
||||
|
||||
export const useGlobalErrors = () => {
|
||||
const globalErrors = useSelector(state => state.globalErrors.data);
|
||||
|
||||
return { globalErrors };
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
export * from './dashboard';
|
||||
export * from './dashboard';
|
||||
export * from './authentication';
|
||||
export * from './globalErrors';
|
||||
85
client/src/hooks/useRequest.js
Normal file
85
client/src/hooks/useRequest.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import axios from 'axios';
|
||||
import {
|
||||
useAuthActions,
|
||||
useAuthOrganizationId,
|
||||
useSetGlobalErrors,
|
||||
useAuthToken,
|
||||
} from './state';
|
||||
|
||||
export default function useApiRequest() {
|
||||
const setGlobalErrors = useSetGlobalErrors();
|
||||
const { setLogout } = useAuthActions();
|
||||
|
||||
// Authentication token.
|
||||
const token = useAuthToken();
|
||||
|
||||
// Authentication organization id.
|
||||
const organizationId = useAuthOrganizationId();
|
||||
|
||||
const http = React.useMemo(() => {
|
||||
axios.create();
|
||||
|
||||
// Request interceptors.
|
||||
axios.interceptors.request.use(
|
||||
(request) => {
|
||||
const locale = 'en';
|
||||
|
||||
if (token) {
|
||||
request.headers.common['x-access-token'] = token;
|
||||
}
|
||||
if (organizationId) {
|
||||
request.headers.common['organization-id'] = organizationId;
|
||||
}
|
||||
if (locale) {
|
||||
request.headers.common['Accept-Language'] = locale;
|
||||
}
|
||||
return request;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// Response interceptors.
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
const { status } = error.response;
|
||||
|
||||
if (status >= 500) {
|
||||
setGlobalErrors({ something_wrong: true });
|
||||
}
|
||||
if (status === 401) {
|
||||
setGlobalErrors({ session_expired: true });
|
||||
setLogout();
|
||||
}
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
return axios;
|
||||
}, [token, organizationId, setGlobalErrors, setLogout]);
|
||||
|
||||
return {
|
||||
get(resource, params) {
|
||||
return http.get(`/api/${resource}`, params);
|
||||
},
|
||||
|
||||
post(resource, params, config) {
|
||||
return http.post(`/api/${resource}`, params, config);
|
||||
},
|
||||
|
||||
update(resource, slug, params) {
|
||||
return http.put(`/api/${resource}/${slug}`, params);
|
||||
},
|
||||
|
||||
put(resource, params) {
|
||||
return http.put(`/api/${resource}`, params);
|
||||
},
|
||||
|
||||
delete(resource, params) {
|
||||
return http.delete(`/api/${resource}`, params);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -20,16 +20,16 @@ export default [
|
||||
pageTitle: 'Accounts Chart',
|
||||
},
|
||||
// Custom views.
|
||||
{
|
||||
path: `/custom_views/:resource_slug/new`,
|
||||
component: lazy(() => import('containers/Views/ViewFormPage')),
|
||||
breadcrumb: 'New',
|
||||
},
|
||||
{
|
||||
path: `/custom_views/:view_id/edit`,
|
||||
component: lazy(() => import('containers/Views/ViewFormPage')),
|
||||
breadcrumb: 'Edit',
|
||||
},
|
||||
// {
|
||||
// path: `/custom_views/:resource_slug/new`,
|
||||
// component: lazy(() => import('containers/Views/ViewFormPage')),
|
||||
// breadcrumb: 'New',
|
||||
// },
|
||||
// {
|
||||
// path: `/custom_views/:view_id/edit`,
|
||||
// component: lazy(() => import('containers/Views/ViewFormPage')),
|
||||
// breadcrumb: 'Edit',
|
||||
// },
|
||||
// Accounting.
|
||||
{
|
||||
path: `/make-journal-entry`,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { store } from 'store/createStore';
|
||||
import { logout } from 'store/authentication/authentication.actions';
|
||||
import { setGlobalErrors } from 'store/globalErrors/globalErrors.actions';
|
||||
const http = axios.create();
|
||||
|
||||
|
||||
@@ -27,13 +25,13 @@ http.interceptors.request.use((request) => {
|
||||
http.interceptors.response.use((response) => response, (error) => {
|
||||
const { status } = error.response;
|
||||
|
||||
if (status >= 500) {
|
||||
store.dispatch(setGlobalErrors({ something_wrong: true }));
|
||||
}
|
||||
if (status === 401) {
|
||||
store.dispatch(setGlobalErrors({ session_expired: true }));
|
||||
store.dispatch(logout());
|
||||
}
|
||||
// if (status >= 500) {
|
||||
// store.dispatch(setGlobalErrors({ something_wrong: true }));
|
||||
// }
|
||||
// if (status === 401) {
|
||||
// // store.dispatch(setGlobalErrors({ session_expired: true }));
|
||||
// // store.dispatch(logout());
|
||||
// }
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,106 +1,14 @@
|
||||
import ApiService from 'services/ApiService';
|
||||
import t from 'store/types';
|
||||
|
||||
export function login({ form }) {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve, reject) => {
|
||||
ApiService.post('auth/login', form)
|
||||
.then((response) => {
|
||||
const { data } = response;
|
||||
export const setLogin = ({ user, token, tenant }) => ({
|
||||
type: t.LOGIN_SUCCESS,
|
||||
payload: {
|
||||
user,
|
||||
token,
|
||||
tenant,
|
||||
},
|
||||
});
|
||||
|
||||
if (data.token && data.user) {
|
||||
dispatch({
|
||||
type: t.LOGIN_SUCCESS,
|
||||
payload: {
|
||||
user: data.user,
|
||||
token: data.token,
|
||||
tenant: data.tenant,
|
||||
},
|
||||
});
|
||||
}
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
const { response } = error;
|
||||
const { data } = response;
|
||||
const { errors = [] } = data;
|
||||
|
||||
reject(errors);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const logout = () => {
|
||||
return (dispatch) =>
|
||||
dispatch({
|
||||
type: t.LOGOUT,
|
||||
});
|
||||
};
|
||||
|
||||
export const register = ({ form }) => {
|
||||
return (dispatch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ApiService.post('auth/register', form)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error.response.data.errors || []);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const resetPassword = ({ form, token }) => {
|
||||
return (dispatch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ApiService.post(`auth/reset/${token}`, form)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error.response.data.errors || []);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const sendResetPassword = (email) => {
|
||||
return (dispatch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ApiService.post('auth/send_reset_password', email)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error.response.data.errors || []);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const inviteAccept = ({ form, token }) => {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve, reject) => {
|
||||
ApiService.post(`invite/accept/${token}`, { ...form })
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error.response.data.errors || []);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const inviteMetaByToken = ({ token }) => {
|
||||
return (dispatch) =>
|
||||
new Promise((resolve, reject) => {
|
||||
ApiService.get(`invite/invited/${token}`)
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error.response.data.errors || []);
|
||||
});
|
||||
});
|
||||
};
|
||||
export const setLogout = () => ({
|
||||
type: t.LOGOUT,
|
||||
});
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
|
||||
|
||||
export const setGlobalErrors = (errors) => {
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: 'GLOBAL_ERRORS_SET',
|
||||
payload: {
|
||||
errors,
|
||||
},
|
||||
});
|
||||
}
|
||||
return {
|
||||
type: 'GLOBAL_ERRORS_SET',
|
||||
payload: {
|
||||
errors,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -211,7 +211,7 @@ body.authentication {
|
||||
margin-top: -10px;
|
||||
|
||||
p {
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ $sidebar-menu-item-color: rgb(255, 255, 255);
|
||||
$sidebar-menu-item-color-active: rgb(255, 255, 255);
|
||||
$sidebar-popover-submenu-bg: rgb(1, 20, 62);
|
||||
$sidebar-menu-label-color: rgba(255, 255, 255, 0.45);
|
||||
$sidebar-submenu-item-color: rgba(255, 255, 255, 0.7);
|
||||
$sidebar-submenu-item-color: rgba(255, 255, 255, 0.85);
|
||||
$sidebar-submenu-item-hover-color: rgb(255, 255, 255);
|
||||
$sidebar-logo-opacity: 0.5;
|
||||
$sidebar-submenu-item-bg-color: rgba(255, 255, 255, 0.2);
|
||||
|
||||
@@ -2,7 +2,6 @@ import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import Currency from 'js-money/lib/currency';
|
||||
import PProgress from 'p-progress';
|
||||
import accounting from 'accounting';
|
||||
import deepMapKeys from 'deep-map-keys';
|
||||
import { createSelectorCreator, defaultMemoize } from 'reselect';
|
||||
@@ -207,20 +206,20 @@ export const saveFilesInAsync = (files, actionCb, extraTasks) => {
|
||||
files.forEach((file) => {
|
||||
const formData = new FormData();
|
||||
formData.append('attachment', file.file);
|
||||
const oper = new PProgress((resolve, reject, progress) => {
|
||||
actionCb(formData, file, (requestProgress) => {
|
||||
progress(requestProgress);
|
||||
})
|
||||
.then((data) => {
|
||||
resolve(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
opers.push(oper);
|
||||
// const oper = new PProgress((resolve, reject, progress) => {
|
||||
// actionCb(formData, file, (requestProgress) => {
|
||||
// progress(requestProgress);
|
||||
// })
|
||||
// .then((data) => {
|
||||
// resolve(data);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// reject(error);
|
||||
// });
|
||||
// });
|
||||
// opers.push(oper);
|
||||
});
|
||||
return PProgress.all(opers);
|
||||
// return PProgress.all(opers);
|
||||
};
|
||||
|
||||
export const firstLettersArgs = (...args) => {
|
||||
|
||||
Reference in New Issue
Block a user