mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
Fix : Customers
This commit is contained in:
@@ -2,22 +2,25 @@ import React, { useCallback } from 'react';
|
|||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
|
||||||
import CustomerForm from 'containers/Customers/CustomerForm';
|
import CustomerForm from 'containers/Customers/CustomerForm';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|
||||||
import withCustomersActions from './withCustomersActions';
|
import withCustomersActions from './withCustomersActions';
|
||||||
|
import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
function Customer({
|
function Customer({
|
||||||
// #withDashboardActions
|
// // #withDashboardActions
|
||||||
changePageTitle,
|
// changePageTitle,
|
||||||
|
|
||||||
formik,
|
// formik,
|
||||||
//#withCustomersActions
|
//#withCustomersActions
|
||||||
requestFetchCustomers,
|
requestFetchCustomers,
|
||||||
requestFetchCustomer,
|
requestFetchCustomer,
|
||||||
|
|
||||||
|
// #wihtCurrenciesActions
|
||||||
|
requestFetchCurrencies,
|
||||||
}) {
|
}) {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -32,7 +35,11 @@ function Customer({
|
|||||||
(key, customerId) => requestFetchCustomer(customerId),
|
(key, customerId) => requestFetchCustomer(customerId),
|
||||||
{ enabled: id && id },
|
{ enabled: id && id },
|
||||||
);
|
);
|
||||||
|
// Handle fetch Currencies data table
|
||||||
|
const fetchCurrencies = useQuery('currencies', () =>
|
||||||
|
requestFetchCurrencies(),
|
||||||
|
);
|
||||||
|
|
||||||
const handleFormSubmit = useCallback(
|
const handleFormSubmit = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
payload.redirect && history.push('/customers');
|
payload.redirect && history.push('/customers');
|
||||||
@@ -46,7 +53,11 @@ function Customer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
loading={fetchCustomer.isFetching || fetchCustomers.isFetching}
|
loading={
|
||||||
|
fetchCustomer.isFetching ||
|
||||||
|
fetchCustomers.isFetching ||
|
||||||
|
fetchCurrencies.isFetching
|
||||||
|
}
|
||||||
name={'customer-form'}
|
name={'customer-form'}
|
||||||
>
|
>
|
||||||
<CustomerForm
|
<CustomerForm
|
||||||
@@ -58,4 +69,4 @@ function Customer({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(withDashboardActions, withCustomersActions)(Customer);
|
export default compose(withCustomersActions, withCurrenciesActions)(Customer);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const CustomerBillingAddress = ({
|
|||||||
<h4>
|
<h4>
|
||||||
<T id={'billing_address'} />
|
<T id={'billing_address'} />
|
||||||
</h4>
|
</h4>
|
||||||
|
{/*------------ Billing Address country -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
intent={
|
intent={
|
||||||
@@ -44,7 +44,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_country')}
|
{...getFieldProps('billing_address_country')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address 1 -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'address_line_1'} />}
|
label={<T id={'address_line_1'} />}
|
||||||
className={'form-group--address_line_1'}
|
className={'form-group--address_line_1'}
|
||||||
@@ -67,7 +67,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_1')}
|
{...getFieldProps('billing_address_1')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address 2 -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'address_line_2'} />}
|
label={<T id={'address_line_2'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -90,7 +90,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_2')}
|
{...getFieldProps('billing_address_2')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address city -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'city_town'} />}
|
label={<T id={'city_town'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -116,7 +116,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_city')}
|
{...getFieldProps('billing_address_city')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address state -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'state'} />}
|
label={<T id={'state'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -142,47 +142,47 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_state')}
|
{...getFieldProps('billing_address_state')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address postcode -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'zip_code'} />}
|
label={<T id={'zip_code'} />}
|
||||||
intent={
|
intent={
|
||||||
errors.billing_address_zipcode &&
|
errors.billing_address_postcode &&
|
||||||
touched.billing_address_zipcode &&
|
touched.billing_address_postcode &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name="billing_address_zipcode"
|
name="billing_address_postcode"
|
||||||
{...{ errors, touched }}
|
{...{ errors, touched }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={
|
intent={
|
||||||
errors.billing_address_zipcode &&
|
errors.billing_address_postcode &&
|
||||||
touched.billing_address_zipcode &&
|
touched.billing_address_postcode &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
{...getFieldProps('billing_address_zipcode')}
|
{...getFieldProps('billing_address_postcode')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Billing Address phone -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'phone'} />}
|
label={<T id={'phone'} />}
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
errors.billing_address_phone && touched.billing_address_phone && Intent.DANGER
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage name="shipping_phone" {...{ errors, touched }} />
|
<ErrorMessage name="billing_address_phone" {...{ errors, touched }} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
errors.billing_address_phone && touched.billing_address_phone && Intent.DANGER
|
||||||
}
|
}
|
||||||
{...getFieldProps('shipping_phone')}
|
{...getFieldProps('billing_address_phone')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -191,7 +191,7 @@ const CustomerBillingAddress = ({
|
|||||||
<h4>
|
<h4>
|
||||||
<T id={'shipping_address'} />
|
<T id={'shipping_address'} />
|
||||||
</h4>
|
</h4>
|
||||||
|
{/*------------ Shipping Address country -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'country'} />}
|
label={<T id={'country'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -217,7 +217,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('shipping_address_country')}
|
{...getFieldProps('shipping_address_country')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address 1 -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'address_line_1'} />}
|
label={<T id={'address_line_1'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -240,7 +240,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_1')}
|
{...getFieldProps('billing_address_1')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address 2 -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'address_line_2'} />}
|
label={<T id={'address_line_2'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -263,7 +263,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('billing_address_2')}
|
{...getFieldProps('billing_address_2')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address city -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'city_town'} />}
|
label={<T id={'city_town'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -289,7 +289,7 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('shipping_address_city')}
|
{...getFieldProps('shipping_address_city')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address state -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'state'} />}
|
label={<T id={'state'} />}
|
||||||
className={'form-group--journal-number'}
|
className={'form-group--journal-number'}
|
||||||
@@ -315,47 +315,47 @@ const CustomerBillingAddress = ({
|
|||||||
{...getFieldProps('shipping_address_state')}
|
{...getFieldProps('shipping_address_state')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address postcode -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'zip_code'} />}
|
label={<T id={'zip_code'} />}
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_address_zipcode &&
|
errors.shipping_address_postcode &&
|
||||||
touched.shipping_address_zipcode &&
|
touched.shipping_address_postcode &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name="shipping_address_zipcode"
|
name="shipping_address_postcode"
|
||||||
{...{ errors, touched }}
|
{...{ errors, touched }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_address_zipcode &&
|
errors.shipping_address_postcode &&
|
||||||
touched.shipping_address_zipcode &&
|
touched.shipping_address_postcode &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
{...getFieldProps('shipping_address_zipcode')}
|
{...getFieldProps('shipping_address_postcode')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Shipping Address phone -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'phone'} />}
|
label={<T id={'phone'} />}
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
errors.shipping_address_phone && touched.shipping_address_phone && Intent.DANGER
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage name="shipping_phone" {...{ errors, touched }} />
|
<ErrorMessage name="shipping_address_phone" {...{ errors, touched }} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={
|
intent={
|
||||||
errors.shipping_phone && touched.shipping_phone && Intent.DANGER
|
errors.shipping_address_phone && touched.shipping_address_phone && Intent.DANGER
|
||||||
}
|
}
|
||||||
{...getFieldProps('shipping_phone')}
|
{...getFieldProps('shipping_address_phone')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, Intent, Position, Classes } from '@blueprintjs/core';
|
import { FormGroup, Intent, Position, Classes } from '@blueprintjs/core';
|
||||||
@@ -6,52 +6,86 @@ import { DateInput } from '@blueprintjs/datetime';
|
|||||||
import {
|
import {
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
MoneyInputGroup,
|
MoneyInputGroup,
|
||||||
CurrenciesSelectList,
|
CurrencySelectList,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { momentFormatter, tansformDateValue } from 'utils';
|
|
||||||
|
|
||||||
export default function CustomerFinancialPanel({
|
import withCurrencies from 'containers/Currencies/withCurrencies';
|
||||||
|
|
||||||
|
import { compose, momentFormatter, tansformDateValue } from 'utils';
|
||||||
|
|
||||||
|
function CustomerFinancialPanel({
|
||||||
setFieldValue,
|
setFieldValue,
|
||||||
errors,
|
errors,
|
||||||
touched,
|
touched,
|
||||||
values,
|
values,
|
||||||
|
|
||||||
|
// #withCurrencies
|
||||||
|
currenciesList,
|
||||||
|
|
||||||
|
customerId,
|
||||||
}) {
|
}) {
|
||||||
|
const [selectedItems, setSelectedItems] = useState();
|
||||||
|
|
||||||
const handleDateChange = useCallback(
|
const handleDateChange = useCallback(
|
||||||
(date) => {
|
(date) => {
|
||||||
const formatted = moment(date).format('YYYY-MM-DD');
|
const formatted = moment(date).format('YYYY-MM-DD');
|
||||||
setFieldValue('payment_date', formatted);
|
setFieldValue('opening_balance_at', formatted);
|
||||||
},
|
},
|
||||||
[setFieldValue],
|
[setFieldValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleMoneyInputChange = useCallback(
|
||||||
|
(e, value) => {
|
||||||
|
setFieldValue('opening_balance', value);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onItemsSelect = useCallback(
|
||||||
|
(filedName) => {
|
||||||
|
return (filed) => {
|
||||||
|
setSelectedItems({
|
||||||
|
...selectedItems,
|
||||||
|
[filedName]: filed,
|
||||||
|
});
|
||||||
|
setFieldValue(filedName, filed.currency_code);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue, selectedItems],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className={'tab-panel--financial'}>
|
<div className={'tab-panel--financial'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
|
{/*------------ Opening balance at -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'opening_balance_at'} />}
|
label={<T id={'opening_balance_at'} />}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
intent={
|
intent={
|
||||||
errors.opening_balance_date &&
|
errors.opening_balance_at &&
|
||||||
touched.opening_balance_date &&
|
touched.opening_balance_at &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
<ErrorMessage
|
||||||
|
name="opening_balance_at"
|
||||||
|
{...{ errors, touched }}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DateInput
|
<DateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
value={tansformDateValue(values.payment_date)}
|
value={tansformDateValue(values.opening_balance_at)}
|
||||||
onChange={handleDateChange}
|
onChange={handleDateChange}
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
disabled={customerId}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Opening balance -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'opening_balance'} />}
|
label={<T id={'opening_balance'} />}
|
||||||
className={classNames('form-group--opening-balance', Classes.FILL)}
|
className={classNames('form-group--opening-balance', Classes.FILL)}
|
||||||
@@ -63,12 +97,14 @@ export default function CustomerFinancialPanel({
|
|||||||
<MoneyInputGroup
|
<MoneyInputGroup
|
||||||
value={values.opening_balance}
|
value={values.opening_balance}
|
||||||
prefix={'$'}
|
prefix={'$'}
|
||||||
|
onChange={handleMoneyInputChange}
|
||||||
inputGroupProps={{
|
inputGroupProps={{
|
||||||
fill: true,
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
disabled={customerId}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
{/*------------ Currency -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'currency'} />}
|
label={<T id={'currency'} />}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
@@ -78,10 +114,20 @@ export default function CustomerFinancialPanel({
|
|||||||
)}
|
)}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
<CurrenciesSelectList />
|
{/* <CurrenciesSelectList /> */}
|
||||||
|
<CurrencySelectList
|
||||||
|
currenciesList={currenciesList}
|
||||||
|
selectedCurrencyCode={values.currency_code}
|
||||||
|
onCurrencySelected={onItemsSelect('currency_code')}
|
||||||
|
disabled={customerId}
|
||||||
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCurrencies(({ currenciesList }) => ({ currenciesList })),
|
||||||
|
)(CustomerFinancialPanel);
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ export default function CustomerFloatingActions({
|
|||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||||
<Button
|
<Button
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onSubmitClick({ publish: true, redirect: true });
|
onSubmitClick({ publish: true, redirect: true });
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
|
import moment from 'moment';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
@@ -24,6 +25,9 @@ import useMedia from 'hooks/useMedia';
|
|||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer form.
|
||||||
|
*/
|
||||||
function CustomerForm({
|
function CustomerForm({
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
@@ -36,7 +40,6 @@ function CustomerForm({
|
|||||||
|
|
||||||
// #withCustomersActions
|
// #withCustomersActions
|
||||||
requestSubmitCustomer,
|
requestSubmitCustomer,
|
||||||
requestFetchCustomers,
|
|
||||||
requestEditCustomer,
|
requestEditCustomer,
|
||||||
|
|
||||||
// #withMediaActions
|
// #withMediaActions
|
||||||
@@ -67,6 +70,7 @@ function CustomerForm({
|
|||||||
.required()
|
.required()
|
||||||
.trim()
|
.trim()
|
||||||
.label(formatMessage({ id: 'customer_type_' })),
|
.label(formatMessage({ id: 'customer_type_' })),
|
||||||
|
salutation: Yup.string().trim(),
|
||||||
first_name: Yup.string().trim(),
|
first_name: Yup.string().trim(),
|
||||||
last_name: Yup.string().trim(),
|
last_name: Yup.string().trim(),
|
||||||
company_name: Yup.string().trim(),
|
company_name: Yup.string().trim(),
|
||||||
@@ -74,48 +78,71 @@ function CustomerForm({
|
|||||||
.trim()
|
.trim()
|
||||||
.required()
|
.required()
|
||||||
.label(formatMessage({ id: 'display_name_' })),
|
.label(formatMessage({ id: 'display_name_' })),
|
||||||
|
|
||||||
email: Yup.string().email(),
|
email: Yup.string().email(),
|
||||||
|
|
||||||
work_phone: Yup.number(),
|
work_phone: Yup.number(),
|
||||||
|
personal_phone: Yup.number(),
|
||||||
|
website: Yup.string().url(),
|
||||||
|
|
||||||
active: Yup.boolean(),
|
active: Yup.boolean(),
|
||||||
|
note: Yup.string().trim(),
|
||||||
|
|
||||||
billing_address_city: Yup.string().trim(),
|
|
||||||
billing_address_country: Yup.string().trim(),
|
billing_address_country: Yup.string().trim(),
|
||||||
billing_address_email: Yup.string().email(),
|
billing_address_1: Yup.string().trim(),
|
||||||
billing_address_zipcode: Yup.number().nullable(),
|
billing_address_2: Yup.string().trim(),
|
||||||
billing_address_phone: Yup.number(),
|
billing_address_city: Yup.string().trim(),
|
||||||
billing_address_state: Yup.string().trim(),
|
billing_address_state: Yup.string().trim(),
|
||||||
|
billing_address_postcode: Yup.number().nullable(),
|
||||||
|
billing_address_phone: Yup.number(),
|
||||||
|
|
||||||
shipping_address_city: Yup.string().trim(),
|
|
||||||
shipping_address_country: Yup.string().trim(),
|
shipping_address_country: Yup.string().trim(),
|
||||||
shipping_address_email: Yup.string().email(),
|
shipping_address_1: Yup.string().trim(),
|
||||||
shipping_address_zipcode: Yup.number().nullable(),
|
shipping_address_2: Yup.string().trim(),
|
||||||
shipping_address_phone: Yup.number(),
|
shipping_address_city: Yup.string().trim(),
|
||||||
shipping_address_state: Yup.string().trim(),
|
shipping_address_state: Yup.string().trim(),
|
||||||
|
shipping_address_postcode: Yup.number().nullable(),
|
||||||
|
shipping_address_phone: Yup.number(),
|
||||||
|
|
||||||
|
opening_balance: Yup.number(),
|
||||||
|
currency_code: Yup.string(),
|
||||||
|
opening_balance_at: Yup.date(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultInitialValues = useMemo(
|
const defaultInitialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
customer_type: 'business',
|
customer_type: 'business',
|
||||||
|
salutation: '',
|
||||||
first_name: '',
|
first_name: '',
|
||||||
last_name: '',
|
last_name: '',
|
||||||
company_name: '',
|
company_name: '',
|
||||||
display_name: '',
|
display_name: '',
|
||||||
|
|
||||||
email: '',
|
email: '',
|
||||||
work_phone: '',
|
work_phone: '',
|
||||||
|
personal_phone: '',
|
||||||
|
website: '',
|
||||||
|
note: '',
|
||||||
active: true,
|
active: true,
|
||||||
|
|
||||||
billing_address_city: '',
|
|
||||||
billing_address_country: '',
|
billing_address_country: '',
|
||||||
billing_address_zipcode: null,
|
billing_address_1: '',
|
||||||
billing_address_phone: '',
|
billing_address_2: '',
|
||||||
|
billing_address_city: '',
|
||||||
billing_address_state: '',
|
billing_address_state: '',
|
||||||
|
billing_address_postcode: null,
|
||||||
|
billing_address_phone: '',
|
||||||
|
|
||||||
shipping_address_city: '',
|
|
||||||
shipping_address_country: '',
|
shipping_address_country: '',
|
||||||
shipping_address_zipcode: null,
|
shipping_address_1: '',
|
||||||
shipping_address_phone: '',
|
shipping_address_2: '',
|
||||||
|
shipping_address_city: '',
|
||||||
shipping_address_state: '',
|
shipping_address_state: '',
|
||||||
|
shipping_address_postcode: null,
|
||||||
|
shipping_address_phone: '',
|
||||||
|
|
||||||
|
opening_balance: '',
|
||||||
|
currency_code: '',
|
||||||
|
opening_balance_at: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
}),
|
}),
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -147,7 +174,10 @@ function CustomerForm({
|
|||||||
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
||||||
}, [changePageTitle, customer, formatMessage]);
|
}, [changePageTitle, customer, formatMessage]);
|
||||||
|
|
||||||
const handleFormSubmit = (values, { setSubmitting, resetForm, setErrors }) => {
|
const handleFormSubmit = (
|
||||||
|
values,
|
||||||
|
{ setSubmitting, resetForm, setErrors },
|
||||||
|
) => {
|
||||||
const formValues = { ...values, status: payload.publish };
|
const formValues = { ...values, status: payload.publish };
|
||||||
if (customer && customer.id) {
|
if (customer && customer.id) {
|
||||||
requestEditCustomer(customer.id, formValues)
|
requestEditCustomer(customer.id, formValues)
|
||||||
@@ -189,7 +219,8 @@ function CustomerForm({
|
|||||||
errors,
|
errors,
|
||||||
values,
|
values,
|
||||||
touched,
|
touched,
|
||||||
handleSubmit
|
isSubmitting,
|
||||||
|
handleSubmit,
|
||||||
} = useFormik({
|
} = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
validationSchema: validationSchema,
|
validationSchema: validationSchema,
|
||||||
@@ -199,7 +230,6 @@ function CustomerForm({
|
|||||||
onSubmit: handleFormSubmit,
|
onSubmit: handleFormSubmit,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return customer && customer.media
|
return customer && customer.media
|
||||||
? customer.media.map((attach) => ({
|
? customer.media.map((attach) => ({
|
||||||
@@ -209,6 +239,7 @@ function CustomerForm({
|
|||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDropFiles = useCallback((_files) => {
|
const handleDropFiles = useCallback((_files) => {
|
||||||
setFiles(_files.filter((file) => file.uploaded === false));
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -269,16 +300,19 @@ function CustomerForm({
|
|||||||
getFieldProps={getFieldProps}
|
getFieldProps={getFieldProps}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
values={values}
|
values={values}
|
||||||
touched={touched} />
|
touched={touched}
|
||||||
|
customerId={customer}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CustomerFloatingActions
|
|
||||||
onSubmitClick={handleSubmitClick}
|
|
||||||
customer={customer}
|
|
||||||
onCancelClick={handleCancelClick}
|
|
||||||
customerId={null}
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<CustomerFloatingActions
|
||||||
|
isSubmitting={isSubmitting}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
// customer={customer}
|
||||||
|
onCancelClick={handleCancelClick}
|
||||||
|
customerId={customer}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,12 +31,8 @@ export default function CustomerFormAfterPrimarySection({
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{/*------------ Customer email -----------*/}
|
{/*------------ Phone number -----------*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
intent={errors.work_phone && touched.work_phone && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name={'work_phone'} {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
className={'form-group--phone-number'}
|
className={'form-group--phone-number'}
|
||||||
label={<T id={'phone_number'} />}
|
label={<T id={'phone_number'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
@@ -49,8 +45,8 @@ export default function CustomerFormAfterPrimarySection({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<InputGroup
|
<InputGroup
|
||||||
intent={errors.work_phone && touched.work_phone && Intent.DANGER}
|
intent={errors.personal_phone && touched.personal_phone && Intent.DANGER}
|
||||||
{...getFieldProps('work_phone')}
|
{...getFieldProps('personal_phone')}
|
||||||
placeholder={'Mobile'}
|
placeholder={'Mobile'}
|
||||||
/>
|
/>
|
||||||
</ControlGroup>
|
</ControlGroup>
|
||||||
|
|||||||
@@ -32,15 +32,20 @@ export default function CustomerFormPrimarySection({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Handle salutation field select.
|
// Handle salutation field select.
|
||||||
const handleSalutationSelect = useCallback((salutation) => {
|
const handleSalutationSelect = useCallback(
|
||||||
setFieldValue('salutation', salutation.label);
|
(salutation) => {
|
||||||
}, [setFieldValue]);
|
setFieldValue('salutation', salutation.label);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle display name field select.
|
// Handle display name field select.
|
||||||
const handleDisplayNameSelect = useCallback((displayName) => {
|
const handleDisplayNameSelect = useCallback(
|
||||||
setFieldValue('display_name', displayName.label);
|
(displayName) => {
|
||||||
}, [setFieldValue]);
|
setFieldValue('display_name', displayName.label);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className={'customer-form__primary-section-content'}>
|
<div className={'customer-form__primary-section-content'}>
|
||||||
{/**-----------Customer type. -----------*/}
|
{/**-----------Customer type. -----------*/}
|
||||||
@@ -58,6 +63,7 @@ export default function CustomerFormPrimarySection({
|
|||||||
<ControlGroup>
|
<ControlGroup>
|
||||||
<SalutationList
|
<SalutationList
|
||||||
onItemSelect={handleSalutationSelect}
|
onItemSelect={handleSalutationSelect}
|
||||||
|
selectedItem={values.salutation}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
CLASSES.FORM_GROUP_LIST_SELECT,
|
CLASSES.FORM_GROUP_LIST_SELECT,
|
||||||
@@ -119,6 +125,7 @@ export default function CustomerFormPrimarySection({
|
|||||||
company={values.company_name}
|
company={values.company_name}
|
||||||
salutation={values.salutation}
|
salutation={values.salutation}
|
||||||
onItemSelect={handleDisplayNameSelect}
|
onItemSelect={handleDisplayNameSelect}
|
||||||
|
// selectedItem={values.display_name}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|||||||
@@ -10,14 +10,12 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import DataTable from 'components/DataTable';
|
|
||||||
import Icon from 'components/Icon';
|
|
||||||
import { Money } from 'components';
|
|
||||||
import { useUpdateEffect } from 'hooks';
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
import LoadingIndicator from 'components/LoadingIndicator';
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
import withCustomers from './withCustomers';
|
import { DataTable, Icon, Money } from 'components';
|
||||||
|
|
||||||
|
import withCustomers from './withCustomers';
|
||||||
import { compose, firstLettersArgs, saveInvoke } from 'utils';
|
import { compose, firstLettersArgs, saveInvoke } from 'utils';
|
||||||
|
|
||||||
const AvatarCell = (row) => {
|
const AvatarCell = (row) => {
|
||||||
@@ -25,13 +23,13 @@ const AvatarCell = (row) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const CustomerTable = ({
|
const CustomerTable = ({
|
||||||
loading,
|
|
||||||
|
|
||||||
//#withCustomers
|
//#withCustomers
|
||||||
customers,
|
customers,
|
||||||
customersLoading,
|
customersLoading,
|
||||||
|
customerPagination,
|
||||||
|
|
||||||
//#props
|
//#OwnProps
|
||||||
|
loading,
|
||||||
onEditCustomer,
|
onEditCustomer,
|
||||||
onDeleteCustomer,
|
onDeleteCustomer,
|
||||||
onFetchData,
|
onFetchData,
|
||||||
@@ -80,18 +78,21 @@ const CustomerTable = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Renders actions table cell.
|
// Renders actions table cell.
|
||||||
const renderActionsCell = useMemo(() => ({ cell }) => (
|
const renderActionsCell = useMemo(
|
||||||
<Popover
|
() => ({ cell }) => (
|
||||||
content={renderContextMenu({
|
<Popover
|
||||||
customer: cell.row.original,
|
content={renderContextMenu({
|
||||||
onEditCustomer,
|
customer: cell.row.original,
|
||||||
onDeleteCustomer,
|
onEditCustomer,
|
||||||
})}
|
onDeleteCustomer,
|
||||||
position={Position.RIGHT_BOTTOM}
|
})}
|
||||||
>
|
position={Position.RIGHT_BOTTOM}
|
||||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
>
|
||||||
</Popover>
|
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||||
), [onDeleteCustomer, onEditCustomer, renderContextMenu]);
|
</Popover>
|
||||||
|
),
|
||||||
|
[onDeleteCustomer, onEditCustomer, renderContextMenu],
|
||||||
|
);
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -186,14 +187,19 @@ const CustomerTable = ({
|
|||||||
loading={customersLoading && !initialMount}
|
loading={customersLoading && !initialMount}
|
||||||
spinnerProps={{ size: 30 }}
|
spinnerProps={{ size: 30 }}
|
||||||
rowContextMenu={rowContextMenu}
|
rowContextMenu={rowContextMenu}
|
||||||
|
pagination={true}
|
||||||
|
pagesCount={customerPagination.pagesCount}
|
||||||
|
initialPageSize={customerPagination.pageSize}
|
||||||
|
initialPageIndex={customerPagination.page - 1}
|
||||||
/>
|
/>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withCustomers(({ customers, customersLoading }) => ({
|
withCustomers(({ customers, customersLoading, customerPagination }) => ({
|
||||||
customers,
|
customers,
|
||||||
customersLoading,
|
customersLoading,
|
||||||
|
customerPagination,
|
||||||
})),
|
})),
|
||||||
)(CustomerTable);
|
)(CustomerTable);
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import CustomersTable from 'containers/Customers/CustomerTable';
|
|||||||
import CustomerActionsBar from 'containers/Customers/CustomerActionsBar';
|
import CustomerActionsBar from 'containers/Customers/CustomerActionsBar';
|
||||||
import CustomersViewsTabs from 'containers/Customers/CustomersViewsTabs';
|
import CustomersViewsTabs from 'containers/Customers/CustomersViewsTabs';
|
||||||
|
|
||||||
|
import withCustomers from 'containers/Customers/withCustomers';
|
||||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||||
|
import withViewsActions from 'containers/Views/withViewsActions';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
@@ -30,7 +32,10 @@ function CustomersList({
|
|||||||
requestFetchResourceViews,
|
requestFetchResourceViews,
|
||||||
requestFetchResourceFields,
|
requestFetchResourceFields,
|
||||||
|
|
||||||
//#withCustomersActions
|
// #withCustomers
|
||||||
|
customersTableQuery,
|
||||||
|
|
||||||
|
// #withCustomersActions
|
||||||
requestFetchCustomers,
|
requestFetchCustomers,
|
||||||
requestDeleteCustomer,
|
requestDeleteCustomer,
|
||||||
requestDeleteBulkCustomers,
|
requestDeleteBulkCustomers,
|
||||||
@@ -57,9 +62,10 @@ function CustomersList({
|
|||||||
// ]);
|
// ]);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
const fetchCustomers = useQuery('customers-table', () => {
|
const fetchCustomers = useQuery(
|
||||||
requestFetchCustomers({});
|
['customers-table', customersTableQuery],
|
||||||
});
|
() => requestFetchCustomers(),
|
||||||
|
);
|
||||||
|
|
||||||
const handleEditCustomer = useCallback(
|
const handleEditCustomer = useCallback(
|
||||||
(cusomter) => {
|
(cusomter) => {
|
||||||
@@ -67,6 +73,7 @@ function CustomersList({
|
|||||||
},
|
},
|
||||||
[history],
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle click delete customer.
|
// Handle click delete customer.
|
||||||
const handleDeleteCustomer = useCallback(
|
const handleDeleteCustomer = useCallback(
|
||||||
(customer) => {
|
(customer) => {
|
||||||
@@ -169,26 +176,29 @@ function CustomersList({
|
|||||||
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
|
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider name={'customers-list'}>
|
||||||
loading={fetchCustomers.isFetching}
|
|
||||||
name={'customers-list'}
|
|
||||||
>
|
|
||||||
<CustomerActionsBar
|
<CustomerActionsBar
|
||||||
selectedRows={selectedRows}
|
selectedRows={selectedRows}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
onBulkDelete={handleBulkDelete}
|
onBulkDelete={handleBulkDelete}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CustomersViewsTabs />
|
|
||||||
|
|
||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<CustomersTable
|
<Switch>
|
||||||
loading={tableLoading}
|
<Route
|
||||||
onDeleteCustomer={handleDeleteCustomer}
|
exact={true}
|
||||||
onEditCustomer={handleEditCustomer}
|
path={['/customers/:custom_view_id/custom_view', '/customers']}
|
||||||
onfetchData={handleFetchData}
|
>
|
||||||
onSelectedRowsChange={handleSelectedRowsChange}
|
<CustomersViewsTabs />
|
||||||
/>
|
<CustomersTable
|
||||||
|
loading={fetchCustomers.isFetching}
|
||||||
|
onDeleteCustomer={handleDeleteCustomer}
|
||||||
|
onEditCustomer={handleEditCustomer}
|
||||||
|
onfetchData={handleFetchData}
|
||||||
|
onSelectedRowsChange={handleSelectedRowsChange}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
|
||||||
<Alert
|
<Alert
|
||||||
cancelButtonText={<T id={'cancel'} />}
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
@@ -230,6 +240,7 @@ function CustomersList({
|
|||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withResourceActions,
|
withResourceActions,
|
||||||
withDashboardActions,
|
|
||||||
withCustomersActions,
|
withCustomersActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withCustomers(({ customersTableQuery }) => ({ customersTableQuery })),
|
||||||
)(CustomersList);
|
)(CustomersList);
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ export default function CustomersTabs({
|
|||||||
errors,
|
errors,
|
||||||
values,
|
values,
|
||||||
touched,
|
touched,
|
||||||
|
customerId,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const [customer] = useState(customerId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Tabs
|
<Tabs
|
||||||
@@ -32,6 +34,7 @@ export default function CustomersTabs({
|
|||||||
errors={errors}
|
errors={errors}
|
||||||
setFieldValue={setFieldValue}
|
setFieldValue={setFieldValue}
|
||||||
touched={touched}
|
touched={touched}
|
||||||
|
customerId={customer}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { getCustomersItems, getCustomersListFactory } from 'store/customers/customers.selectors';
|
|
||||||
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
import { getResourceViews } from 'store/customViews/customViews.selectors';
|
||||||
|
import {
|
||||||
|
getCustomerCurrentPageFactory,
|
||||||
|
getCustomerPaginationMetaFactory,
|
||||||
|
getCustomerTableQueryFactory,
|
||||||
|
} from 'store/customers/customers.selectors';
|
||||||
|
|
||||||
export default (mapState) => {
|
export default (mapState) => {
|
||||||
const getCustomersList = getCustomersListFactory();
|
const getCustomersList = getCustomerCurrentPageFactory();
|
||||||
|
const getCustomerPaginationMeta = getCustomerPaginationMetaFactory();
|
||||||
|
const getCustomerTableQuery = getCustomerTableQueryFactory();
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
const mapStateToProps = (state, props) => {
|
||||||
|
const query = getCustomerTableQuery(state, props);
|
||||||
|
|
||||||
const mapped = {
|
const mapped = {
|
||||||
|
customers: getCustomersList(state, props, query),
|
||||||
customersViews: getResourceViews(state, props, 'customers'),
|
customersViews: getResourceViews(state, props, 'customers'),
|
||||||
customersItems: getCustomersList(state, props),
|
customersTableQuery: query,
|
||||||
customers: getCustomersItems(state, state.customers.currentViewId),
|
customerPagination: getCustomerPaginationMeta(state, props, query),
|
||||||
customersLoading: state.customers.loading,
|
customersLoading: state.customers.loading,
|
||||||
customerErrors: state.customers.errors,
|
customersItems: state.customers.items,
|
||||||
|
// customerErrors: state.customers.errors,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { resolve } from 'p-progress';
|
|
||||||
import ApiService from 'services/ApiService';
|
import ApiService from 'services/ApiService';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
export const submitCustomer = ({ form }) => {
|
export const submitCustomer = ({ form }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
|
|
||||||
ApiService.post('customers', form)
|
ApiService.post('customers', form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response);
|
resolve(response);
|
||||||
@@ -25,10 +20,6 @@ export const submitCustomer = ({ form }) => {
|
|||||||
export const editCustomer = ({ form, id }) => {
|
export const editCustomer = ({ form, id }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
|
||||||
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
|
||||||
});
|
|
||||||
|
|
||||||
ApiService.post(`customers/${id}`, form)
|
ApiService.post(`customers/${id}`, form)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
resolve(response);
|
resolve(response);
|
||||||
@@ -45,7 +36,8 @@ export const editCustomer = ({ form, id }) => {
|
|||||||
export const fetchCustomers = ({ query }) => {
|
export const fetchCustomers = ({ query }) => {
|
||||||
return (dispatch, getState) =>
|
return (dispatch, getState) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const pageQuery = getState().items.tableQuery;
|
const pageQuery = getState().customers.tableQuery;
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.CUSTOMERS_TABLE_LOADING,
|
type: t.CUSTOMERS_TABLE_LOADING,
|
||||||
payload: { loading: true },
|
payload: { loading: true },
|
||||||
@@ -53,14 +45,25 @@ export const fetchCustomers = ({ query }) => {
|
|||||||
ApiService.get(`customers`, { params: { ...pageQuery, ...query } })
|
ApiService.get(`customers`, { params: { ...pageQuery, ...query } })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.CUSTOMERS_ITEMS_SET,
|
type: t.CUSTOMERS_PAGE_SET,
|
||||||
customers: response.data.customers,
|
payload: {
|
||||||
|
customers: response.data.customers,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
paginationMeta: response.data.pagination,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.CUSTOMERS_PAGE_SET,
|
type: t.CUSTOMERS_ITEMS_SET,
|
||||||
customers: response.data.customers,
|
payload: {
|
||||||
customViewId: response.data.customers?.viewMeta?.customViewId || -1,
|
customers: response.data.customers,
|
||||||
paginationMeta: response.data.pagination,
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.CUSTOMERS_PAGINATION_SET,
|
||||||
|
payload: {
|
||||||
|
pagination: response.data.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.CUSTOMERS_TABLE_LOADING,
|
type: t.CUSTOMERS_TABLE_LOADING,
|
||||||
@@ -83,7 +86,7 @@ export const fetchCustomer = ({ id }) => {
|
|||||||
type: t.CUSTOMER_SET,
|
type: t.CUSTOMER_SET,
|
||||||
payload: {
|
payload: {
|
||||||
id,
|
id,
|
||||||
customer: response.data.contact,
|
customer: response.data.customer,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resolve(response);
|
resolve(response);
|
||||||
@@ -99,7 +102,10 @@ export const deleteCustomer = ({ id }) => {
|
|||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
ApiService.delete(`customers/${id}`)
|
ApiService.delete(`customers/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({ type: t.CUSTOMER_DELETE, id });
|
dispatch({
|
||||||
|
type: t.CUSTOMER_DELETE,
|
||||||
|
payload: { id },
|
||||||
|
});
|
||||||
resolve(response);
|
resolve(response);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
import { snakeCase } from 'lodash';
|
||||||
import { createReducer } from '@reduxjs/toolkit';
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
import { createTableQueryReducers } from 'store/queryReducers';
|
import { createTableQueryReducers } from 'store/queryReducers';
|
||||||
|
|
||||||
@@ -7,14 +8,26 @@ const initialState = {
|
|||||||
views: {},
|
views: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
currentViewId: -1,
|
currentViewId: -1,
|
||||||
|
tableQuery: {
|
||||||
|
page_size: 5,
|
||||||
|
page: 1,
|
||||||
|
},
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const customersReducer = createReducer(initialState, {
|
const customersReducer = createReducer(initialState, {
|
||||||
|
[t.CUSTOMER_SET]: (state, action) => {
|
||||||
|
const { id, customer } = action.payload;
|
||||||
|
const _customers = state.items[id] || {};
|
||||||
|
state.items[id] = { ..._customers, ...customer };
|
||||||
|
},
|
||||||
|
|
||||||
[t.CUSTOMERS_ITEMS_SET]: (state, action) => {
|
[t.CUSTOMERS_ITEMS_SET]: (state, action) => {
|
||||||
|
const { customers } = action.payload;
|
||||||
|
|
||||||
const _customers = {};
|
const _customers = {};
|
||||||
|
|
||||||
action.customers.forEach((customer) => {
|
customers.forEach((customer) => {
|
||||||
_customers[customer.id] = customer;
|
_customers[customer.id] = customer;
|
||||||
});
|
});
|
||||||
state.items = {
|
state.items = {
|
||||||
@@ -22,24 +35,59 @@ const customersReducer = createReducer(initialState, {
|
|||||||
..._customers,
|
..._customers,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
[t.CUSTOMERS_PAGE_SET]: (state, action) => {
|
[t.CUSTOMERS_PAGE_SET]: (state, action) => {
|
||||||
const viewId = action.customViewId || -1;
|
const { customViewId, customers, paginationMeta } = action.payload;
|
||||||
|
|
||||||
|
const viewId = customViewId || -1;
|
||||||
const view = state.views[viewId] || {};
|
const view = state.views[viewId] || {};
|
||||||
|
|
||||||
state.views[viewId] = {
|
state.views[viewId] = {
|
||||||
...view,
|
...view,
|
||||||
ids: action.customers.map((i) => i.id),
|
pages: {
|
||||||
|
...(state.views?.[viewId]?.pages || {}),
|
||||||
|
[paginationMeta.page]: {
|
||||||
|
ids: customers.map((i) => i.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
[t.CUSTOMER_DELETE]: (state, action) => {
|
[t.CUSTOMER_DELETE]: (state, action) => {
|
||||||
if (typeof state.items[action.id] !== 'undefined') {
|
const { id } = action.payload;
|
||||||
|
|
||||||
|
if (typeof state.items[id] !== 'undefined') {
|
||||||
delete state.items[action.id];
|
delete state.items[action.id];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
[t.CUSTOMERS_TABLE_LOADING]: (state, action) => {
|
[t.CUSTOMERS_TABLE_LOADING]: (state, action) => {
|
||||||
const { loading } = action.payload;
|
const { loading } = action.payload;
|
||||||
state.loading = !!loading;
|
state.loading = loading;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[t.CUSTOMERS_PAGINATION_SET]: (state, action) => {
|
||||||
|
const { pagination, customViewId } = action.payload;
|
||||||
|
|
||||||
|
const mapped = {
|
||||||
|
pageSize: parseInt(pagination.page_size, 10),
|
||||||
|
page: parseInt(pagination.page, 10),
|
||||||
|
total: parseInt(pagination.total, 10),
|
||||||
|
};
|
||||||
|
const paginationMeta = {
|
||||||
|
...mapped,
|
||||||
|
pagesCount: Math.ceil(mapped.total / mapped.pageSize),
|
||||||
|
pageIndex: Math.max(mapped.page - 1, 0),
|
||||||
|
};
|
||||||
|
state.views = {
|
||||||
|
...state.views,
|
||||||
|
[customViewId]: {
|
||||||
|
...(state.views?.[customViewId] || {}),
|
||||||
|
paginationMeta,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
[t.CUSTOMERS_BULK_DELETE]: (state, action) => {
|
[t.CUSTOMERS_BULK_DELETE]: (state, action) => {
|
||||||
const { ids } = action.payload;
|
const { ids } = action.payload;
|
||||||
const items = { ...state.items };
|
const items = { ...state.items };
|
||||||
@@ -51,10 +99,6 @@ const customersReducer = createReducer(initialState, {
|
|||||||
});
|
});
|
||||||
state.items = items;
|
state.items = items;
|
||||||
},
|
},
|
||||||
[t.CUSTOMER_SET]: (state, action) => {
|
|
||||||
const { id, customer } = action.payload;
|
|
||||||
state.items[id] = { ...customer };
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createTableQueryReducers('customers', customersReducer);
|
export default createTableQueryReducers('customers', customersReducer);
|
||||||
|
|||||||
@@ -1,26 +1,54 @@
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { pickItemsFromIds } from 'store/selectors';
|
import { pickItemsFromIds, paginationLocationQuery } from 'store/selectors';
|
||||||
|
|
||||||
const customersViewsSelector = state => state.customers.views;
|
const customerTableQuery = (state) => state.customers.tableQuery;
|
||||||
const customersItemsSelector = state => state.customers.items;
|
|
||||||
const customersCurrentViewSelector = state => state.customers.currentViewId;
|
|
||||||
|
|
||||||
export const getCustomersItems = createSelector(
|
const customersByIdSelector = (state, props) => {
|
||||||
customersViewsSelector,
|
return state.customers.items[props.customerId];
|
||||||
customersItemsSelector,
|
};
|
||||||
customersCurrentViewSelector,
|
|
||||||
(customersViews, customersItems, currentViewId) => {
|
|
||||||
const customersView = customersViews[currentViewId || -1];
|
|
||||||
|
|
||||||
return (typeof customersView === 'object')
|
const customersPaginationSelector = (state, props) => {
|
||||||
? pickItemsFromIds(customersItems, customersView.ids) || []
|
const viewId = state.customers.currentViewId;
|
||||||
: [];
|
return state.customers.views?.[viewId];
|
||||||
},
|
};
|
||||||
);
|
|
||||||
|
const customerPageSelector = (state, props, query) => {
|
||||||
|
const viewId = state.customers.currentViewId;
|
||||||
|
return state.customers.views?.[viewId]?.pages?.[query.page];
|
||||||
|
};
|
||||||
|
|
||||||
|
const customersItemsSelector = (state) => state.customers.items;
|
||||||
|
|
||||||
|
export const getCustomerTableQueryFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
paginationLocationQuery,
|
||||||
|
customerTableQuery,
|
||||||
|
(locationQuery, tableQuery) => {
|
||||||
|
return {
|
||||||
|
...locationQuery,
|
||||||
|
...tableQuery,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getCustomerCurrentPageFactory = () =>
|
||||||
|
createSelector(
|
||||||
|
customerPageSelector,
|
||||||
|
customersItemsSelector,
|
||||||
|
(customerPage, customersItems) => {
|
||||||
|
return typeof customerPage === 'object'
|
||||||
|
? pickItemsFromIds(customersItems, customerPage.ids) || []
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getCustomersByIdFactory = () =>
|
||||||
|
createSelector(customersByIdSelector, (customer) => {
|
||||||
|
return customer;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getCustomerPaginationMetaFactory = () =>
|
||||||
|
createSelector(customersPaginationSelector, (customerPage) => {
|
||||||
|
return customerPage?.paginationMeta || {};
|
||||||
|
});
|
||||||
|
|
||||||
export const getCustomersListFactory = () => createSelector(
|
|
||||||
customersItemsSelector,
|
|
||||||
(customersItems) => {
|
|
||||||
return Object.values(customersItems);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -4,6 +4,7 @@ export default {
|
|||||||
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
||||||
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
||||||
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
||||||
CUSTOMER_DELETE:'CUSTOMER_DELETE',
|
CUSTOMER_DELETE: 'CUSTOMER_DELETE',
|
||||||
CUSTOMERS_BULK_DELETE:'CUSTOMERS_BULK_DELETE'
|
CUSTOMERS_BULK_DELETE: 'CUSTOMERS_BULK_DELETE',
|
||||||
|
CUSTOMERS_PAGINATION_SET: 'CUSTOMERS_PAGINATION_SET',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user