mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
refactoring: migrating to react-query to manage service-side state.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
NavbarGroup,
|
||||
NavbarDivider,
|
||||
@@ -11,36 +11,35 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { connect } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import { If, Icon, DashboardActionViewsList } from 'components';
|
||||
|
||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||
import { useCustomersListContext } from './CustomersListProvider';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const CustomerActionsBar = ({
|
||||
/**
|
||||
* Customers actions bar.
|
||||
*/
|
||||
function CustomerActionsBar({
|
||||
// #withCustomers
|
||||
customersViews,
|
||||
customersSelectedRows,
|
||||
|
||||
//#withCustomersActions
|
||||
addCustomersTableQueries,
|
||||
changeCustomerView,
|
||||
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #ownProps
|
||||
onFilterChanged,
|
||||
}) => {
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const { customersViews } = useCustomersListContext();
|
||||
|
||||
const onClickNewCustomer = useCallback(() => {
|
||||
history.push('/customers/new');
|
||||
@@ -52,7 +51,6 @@ const CustomerActionsBar = ({
|
||||
};
|
||||
|
||||
const handleTabChange = (viewId) => {
|
||||
changeCustomerView(viewId.id || -1);
|
||||
addCustomersTableQueries({
|
||||
custom_view_id: viewId.id || null,
|
||||
});
|
||||
@@ -111,19 +109,9 @@ const CustomerActionsBar = ({
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
resourceName: 'customers',
|
||||
});
|
||||
const withCustomersActionsBar = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withCustomersActionsBar,
|
||||
withCustomersActions,
|
||||
withResourceDetail(({ resourceFields }) => ({
|
||||
resourceFields,
|
||||
})),
|
||||
withCustomers(({ customersViews, customersSelectedRows }) => ({
|
||||
customersViews,
|
||||
withCustomers(({ customersSelectedRows }) => ({
|
||||
customersSelectedRows,
|
||||
})),
|
||||
withAlertActions,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, Position, Classes, ControlGroup } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
@@ -13,21 +13,23 @@ import {
|
||||
} from 'components';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import withCurrencies from 'containers/Currencies/withCurrencies';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
import {
|
||||
compose,
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
inputIntent,
|
||||
} from 'utils';
|
||||
|
||||
function CustomerFinancialPanel({
|
||||
// #withCurrencies
|
||||
currenciesList,
|
||||
/**
|
||||
* Customer financial panel.
|
||||
*/
|
||||
export default function CustomerFinancialPanel() {
|
||||
const {
|
||||
currencies,
|
||||
customerId
|
||||
} = useCustomerFormContext();
|
||||
|
||||
customerId,
|
||||
}) {
|
||||
return (
|
||||
<div className={'tab-panel--financial'}>
|
||||
<Row>
|
||||
@@ -103,7 +105,7 @@ function CustomerFinancialPanel({
|
||||
inline={true}
|
||||
>
|
||||
<CurrencySelectList
|
||||
currenciesList={currenciesList}
|
||||
currenciesList={currencies}
|
||||
selectedCurrencyCode={value}
|
||||
onCurrencySelected={(currency) => {
|
||||
form.setFieldValue('currency_code', currency.currency_code);
|
||||
@@ -118,7 +120,3 @@ function CustomerFinancialPanel({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCurrencies(({ currenciesList }) => ({ currenciesList })),
|
||||
)(CustomerFinancialPanel);
|
||||
|
||||
@@ -13,40 +13,38 @@ import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { saveInvoke } from 'utils';
|
||||
import { Icon } from 'components';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
/**
|
||||
* Customer floating actions bar.
|
||||
*/
|
||||
export default function CustomerFloatingActions({
|
||||
onSubmitClick,
|
||||
onCancelClick,
|
||||
isSubmitting,
|
||||
customerId,
|
||||
}) {
|
||||
const { resetForm, submitForm } = useFormikContext();
|
||||
export default function CustomerFloatingActions() {
|
||||
// Customer form context.
|
||||
const { customerId, setSubmitPayload } = useCustomerFormContext();
|
||||
|
||||
// Formik context.
|
||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||
|
||||
// Handle submit button click.
|
||||
const handleSubmitBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
noRedirect: false,
|
||||
});
|
||||
setSubmitPayload({ noRedirect: false });
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
saveInvoke(onCancelClick, event);
|
||||
|
||||
};
|
||||
|
||||
// handle clear button clicl.
|
||||
const handleClearBtnClick = (event) => {
|
||||
// saveInvoke(onClearClick, event);
|
||||
resetForm();
|
||||
};
|
||||
|
||||
// Handle submit & new button click.
|
||||
const handleSubmitAndNewClick = (event) => {
|
||||
submitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
noRedirect: true,
|
||||
});
|
||||
setSubmitPayload({ noRedirect: true });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -55,6 +53,7 @@ export default function CustomerFloatingActions({
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
loading={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitBtnClick}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { Formik, Form } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
@@ -16,14 +16,10 @@ import CustomersTabs from 'containers/Customers/CustomersTabs';
|
||||
import CustomerFloatingActions from './CustomerFloatingActions';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withCustomerDetail from 'containers/Customers/withCustomerDetail';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withCustomers from 'containers/Customers//withCustomers';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import useMedia from 'hooks/useMedia';
|
||||
|
||||
import { compose, transformToForm } from 'utils';
|
||||
import { useCustomerFormContext } from './CustomerFormProvider';
|
||||
|
||||
const defaultInitialValues = {
|
||||
customer_type: 'business',
|
||||
@@ -68,43 +64,20 @@ function CustomerForm({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
// #withCustomers
|
||||
customers,
|
||||
|
||||
// #withCustomerDetail
|
||||
customer,
|
||||
|
||||
// #withSettings
|
||||
baseCurrency,
|
||||
|
||||
// #withCustomersActions
|
||||
requestSubmitCustomer,
|
||||
requestEditCustomer,
|
||||
|
||||
// #withMediaActions
|
||||
requestSubmitMedia,
|
||||
requestDeleteMedia,
|
||||
|
||||
// #Props
|
||||
customerId,
|
||||
onFormSubmit,
|
||||
onCancelForm,
|
||||
}) {
|
||||
const isNewMode = !customerId;
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
const {
|
||||
customer,
|
||||
customerId,
|
||||
submitPayload,
|
||||
editCustomerMutate,
|
||||
createCustomerMutate,
|
||||
} = useCustomerFormContext();
|
||||
|
||||
const isNewMode = !customerId;
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const {
|
||||
setFiles,
|
||||
saveMedia,
|
||||
deletedFiles,
|
||||
setDeletedFiles,
|
||||
deleteMedia,
|
||||
} = useMedia({
|
||||
saveCallback: requestSubmitMedia,
|
||||
deleteCallback: requestDeleteMedia,
|
||||
});
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
customer_type: Yup.string()
|
||||
@@ -158,7 +131,7 @@ function CustomerForm({
|
||||
currency_code: baseCurrency,
|
||||
...transformToForm(customer, defaultInitialValues),
|
||||
}),
|
||||
[customer, defaultInitialValues],
|
||||
[customer, baseCurrency],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -196,53 +169,14 @@ function CustomerForm({
|
||||
};
|
||||
|
||||
if (customer && customer.id) {
|
||||
requestEditCustomer(customer.id, formValues)
|
||||
editCustomerMutate(customer.id, formValues)
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
} else {
|
||||
requestSubmitCustomer(formValues).then(onSuccess).catch(onError);
|
||||
createCustomerMutate(formValues).then(onSuccess).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
const initialAttachmentFiles = useMemo(() => {
|
||||
return customer && customer.media
|
||||
? customer.media.map((attach) => ({
|
||||
preview: attach.attachment_file,
|
||||
upload: true,
|
||||
metadata: { ...attach },
|
||||
}))
|
||||
: [];
|
||||
}, []);
|
||||
|
||||
const handleDropFiles = useCallback((_files) => {
|
||||
setFiles(_files.filter((file) => file.uploaded === false));
|
||||
}, []);
|
||||
|
||||
const handleDeleteFile = useCallback(
|
||||
(_deletedFiles) => {
|
||||
_deletedFiles.forEach((deletedFile) => {
|
||||
if (deletedFile.uploaded && deletedFile.metadata.id) {
|
||||
setDeletedFiles([...deletedFiles, deletedFile.metadata.id]);
|
||||
}
|
||||
});
|
||||
},
|
||||
[setDeletedFiles, deletedFiles],
|
||||
);
|
||||
|
||||
const handleCancelClick = useCallback(
|
||||
(event) => {
|
||||
history.goBack();
|
||||
},
|
||||
[history],
|
||||
);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(event, payload) => {
|
||||
setSubmitPayload({ ...payload });
|
||||
},
|
||||
[setSubmitPayload],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM, CLASSES.PAGE_FORM_CUSTOMER)}>
|
||||
<Formik
|
||||
@@ -250,42 +184,29 @@ function CustomerForm({
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
{({ isSubmitting }) => (
|
||||
<Form>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<CustomerFormPrimarySection />
|
||||
</div>
|
||||
<Form>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<CustomerFormPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={'page-form__after-priamry-section'}>
|
||||
<CustomerFormAfterPrimarySection />
|
||||
</div>
|
||||
<div className={'page-form__after-priamry-section'}>
|
||||
<CustomerFormAfterPrimarySection />
|
||||
</div>
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||
<CustomersTabs customer={customerId} />
|
||||
</div>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||
<CustomersTabs />
|
||||
</div>
|
||||
|
||||
<CustomerFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
customerId={customer}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
onCancelClick={handleCancelClick}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
<CustomerFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCustomerDetail,
|
||||
withCustomers(({ customers }) => ({
|
||||
customers,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
withDashboardActions,
|
||||
withCustomersActions,
|
||||
withMediaActions,
|
||||
)(CustomerForm);
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useParams, useHistory } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { DashboardCard } from 'components';
|
||||
import CustomerForm from 'containers/Customers/CustomerForm';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import CustomerForm from './CustomerForm';
|
||||
import { CustomerFormProvider } from './CustomerFormProvider';
|
||||
|
||||
import withCustomersActions from './withCustomersActions';
|
||||
import withCurrenciesActions from 'containers/Currencies/withCurrenciesActions';
|
||||
@@ -13,60 +12,19 @@ import { compose } from 'utils';
|
||||
|
||||
import 'style/pages/Customers/PageForm.scss';
|
||||
|
||||
function CustomerFormPage({
|
||||
// // #withDashboardActions
|
||||
// changePageTitle,
|
||||
|
||||
// formik,
|
||||
//#withCustomersActions
|
||||
requestFetchCustomers,
|
||||
requestFetchCustomer,
|
||||
|
||||
// #wihtCurrenciesActions
|
||||
requestFetchCurrencies,
|
||||
}) {
|
||||
function CustomerFormPage() {
|
||||
const { id } = useParams();
|
||||
const history = useHistory();
|
||||
|
||||
// Handle fetch customers data table
|
||||
const fetchCustomers = useQuery('customers-table', () =>
|
||||
requestFetchCustomers({}),
|
||||
);
|
||||
// Handle fetch customer details.
|
||||
const fetchCustomer = useQuery(
|
||||
['customer', id],
|
||||
(key, customerId) => requestFetchCustomer(customerId),
|
||||
{ enabled: id && id },
|
||||
);
|
||||
// Handle fetch Currencies data table
|
||||
const fetchCurrencies = useQuery('currencies', () =>
|
||||
requestFetchCurrencies(),
|
||||
);
|
||||
|
||||
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
fetchCustomer.isFetching ||
|
||||
fetchCustomers.isFetching ||
|
||||
fetchCurrencies.isFetching
|
||||
}
|
||||
name={'customer-form'}
|
||||
>
|
||||
<CustomerFormProvider customerId={id}>
|
||||
<DashboardCard page>
|
||||
<CustomerForm
|
||||
onFormSubmit={handleFormSubmit}
|
||||
customerId={id}
|
||||
onCancelForm={handleCancel}
|
||||
/>
|
||||
<CustomerForm />
|
||||
</DashboardCard>
|
||||
</DashboardInsider>
|
||||
</CustomerFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withCustomersActions, withCurrenciesActions)(CustomerFormPage);
|
||||
export default compose(
|
||||
withCustomersActions,
|
||||
withCurrenciesActions,
|
||||
)(CustomerFormPage);
|
||||
|
||||
65
client/src/containers/Customers/CustomerFormProvider.js
Normal file
65
client/src/containers/Customers/CustomerFormProvider.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import React, { useState, createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useCustomers,
|
||||
useCustomer,
|
||||
useCurrencies,
|
||||
useCreateCustomer,
|
||||
useEditCustomer,
|
||||
} from 'hooks/query';
|
||||
|
||||
const CustomerFormContext = createContext();
|
||||
|
||||
function CustomerFormProvider({ customerId, ...props }) {
|
||||
// Handle fetch customer details.
|
||||
const { data: customer, isFetching: isCustomerLoading } = useCustomer(
|
||||
customerId,
|
||||
{
|
||||
enabled: !!customerId,
|
||||
}
|
||||
);
|
||||
|
||||
// Handle fetch customers data table
|
||||
const {
|
||||
data: { customers },
|
||||
isFetching: isCustomersLoading,
|
||||
} = useCustomers();
|
||||
|
||||
// Handle fetch Currencies data table
|
||||
const { data: currencies, isFetching: isCurrenciesLoading } = useCurrencies();
|
||||
|
||||
// Form submit payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
const { mutateAsync: editCustomerMutate } = useEditCustomer();
|
||||
const { mutateAsync: createCustomerMutate } = useCreateCustomer();
|
||||
|
||||
const provider = {
|
||||
customerId,
|
||||
customer,
|
||||
customers,
|
||||
currencies,
|
||||
submitPayload,
|
||||
|
||||
isCustomerLoading,
|
||||
isCustomersLoading,
|
||||
isCurrenciesLoading,
|
||||
|
||||
setSubmitPayload,
|
||||
editCustomerMutate,
|
||||
createCustomerMutate
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isCustomerLoading || isCustomerLoading || isCurrenciesLoading}
|
||||
name={'customer-form'}
|
||||
>
|
||||
<CustomerFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useCustomerFormContext = () => React.useContext(CustomerFormContext);
|
||||
|
||||
export { CustomerFormProvider, useCustomerFormContext };
|
||||
@@ -8,12 +8,11 @@ import {
|
||||
Position,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { useIsValuePassed } from 'hooks';
|
||||
import { useIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import CustomersEmptyStatus from './CustomersEmptyStatus';
|
||||
import { DataTable, Icon, Money, Choose, LoadingIndicator } from 'components';
|
||||
import { DataTable, Icon, Money, Choose } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import withCustomers from './withCustomers';
|
||||
@@ -25,7 +24,19 @@ const AvatarCell = (row) => {
|
||||
return <span className="avatar">{firstLettersArgs(row.display_name)}</span>;
|
||||
};
|
||||
|
||||
const CustomerTable = ({
|
||||
const PhoneNumberAccessor = (row) => (
|
||||
<div>
|
||||
<div className={'work_phone'}>{row.work_phone}</div>
|
||||
<div className={'personal_phone'}>{row.personal_phone}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const BalanceAccessor = (row) => {
|
||||
return (<Money amount={row.closing_balance} currency={row.currency_code} />);
|
||||
};
|
||||
|
||||
|
||||
function CustomerTable({
|
||||
//#withCustomers
|
||||
customers,
|
||||
customersLoading,
|
||||
@@ -42,9 +53,8 @@ const CustomerTable = ({
|
||||
onDeleteCustomer,
|
||||
onFetchData,
|
||||
onSelectedRowsChange,
|
||||
}) => {
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const isLoadedBefore = useIsValuePassed(loading, false);
|
||||
|
||||
// Customers actions list.
|
||||
const renderContextMenu = useMemo(
|
||||
@@ -125,21 +135,14 @@ const CustomerTable = ({
|
||||
{
|
||||
id: 'phone_number',
|
||||
Header: formatMessage({ id: 'phone_number' }),
|
||||
accessor: (row) => (
|
||||
<div>
|
||||
<div className={'work_phone'}>{row.work_phone}</div>
|
||||
<div className={'personal_phone'}>{row.personal_phone}</div>
|
||||
</div>
|
||||
),
|
||||
accessor: PhoneNumberAccessor,
|
||||
className: 'phone_number',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'receivable_balance',
|
||||
Header: formatMessage({ id: 'receivable_balance' }),
|
||||
accessor: (r) => (
|
||||
<Money amount={r.closing_balance} currency={r.currency_code} />
|
||||
),
|
||||
accessor: BalanceAccessor,
|
||||
className: 'receivable_balance',
|
||||
width: 100,
|
||||
},
|
||||
@@ -193,35 +196,33 @@ const CustomerTable = ({
|
||||
].every((condition) => condition === true);
|
||||
return (
|
||||
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||
<LoadingIndicator loading={customersLoading && !isLoadedBefore}>
|
||||
<Choose>
|
||||
<Choose.When condition={showEmptyStatus}>
|
||||
<CustomersEmptyStatus />
|
||||
</Choose.When>
|
||||
<Choose>
|
||||
<Choose.When condition={showEmptyStatus}>
|
||||
<CustomersEmptyStatus />
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={customers}
|
||||
onFetchData={handleFetchData}
|
||||
selectionColumn={true}
|
||||
expandable={false}
|
||||
sticky={true}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
spinnerProps={{ size: 30 }}
|
||||
rowContextMenu={rowContextMenu}
|
||||
pagination={true}
|
||||
manualSortBy={true}
|
||||
pagesCount={customerPagination.pagesCount}
|
||||
autoResetSortBy={false}
|
||||
autoResetPage={false}
|
||||
initialPageSize={customersTableQuery.page_size}
|
||||
initialPageIndex={customersTableQuery.page - 1}
|
||||
/>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</LoadingIndicator>
|
||||
<Choose.Otherwise>
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={customers}
|
||||
onFetchData={handleFetchData}
|
||||
selectionColumn={true}
|
||||
expandable={false}
|
||||
sticky={true}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
spinnerProps={{ size: 30 }}
|
||||
rowContextMenu={rowContextMenu}
|
||||
pagination={true}
|
||||
manualSortBy={true}
|
||||
pagesCount={customerPagination.pagesCount}
|
||||
autoResetSortBy={false}
|
||||
autoResetPage={false}
|
||||
initialPageSize={customersTableQuery.page_size}
|
||||
initialPageIndex={customersTableQuery.page - 1}
|
||||
/>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
|
||||
|
||||
import CustomerActionsBar from 'containers/Customers/CustomerActionsBar';
|
||||
import CustomersAlerts from 'containers/Customers/CustomersAlerts';
|
||||
import CustomersViewPage from 'containers/Customers/CustomersViewPage';
|
||||
import { CustomersListProvider } from './CustomersListProvider';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withResourceActions from 'containers/Resources/withResourcesActions';
|
||||
import withViewsActions from 'containers/Views/withViewsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
@@ -25,59 +22,29 @@ function CustomersList({
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
|
||||
// #withResourceActions
|
||||
requestFetchResourceViews,
|
||||
|
||||
// #withCustomers
|
||||
customersTableQuery,
|
||||
|
||||
// #withCustomersActions
|
||||
requestFetchCustomers,
|
||||
addCustomersTableQueries,
|
||||
}) {
|
||||
const [tableLoading, setTableLoading] = useState(false);
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
changePageTitle(formatMessage({ id: 'customers_list' }));
|
||||
}, [changePageTitle, formatMessage]);
|
||||
|
||||
// Fetch customers resource views and fields.
|
||||
const fetchResourceViews = useQuery(
|
||||
['resource-views', 'customers'],
|
||||
(key, resourceName) => requestFetchResourceViews(resourceName),
|
||||
);
|
||||
|
||||
const fetchCustomers = useQuery(
|
||||
['customers-table', customersTableQuery],
|
||||
(key, query) => requestFetchCustomers({ ...query }),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableLoading && !fetchCustomers.isFetching) {
|
||||
setTableLoading(false);
|
||||
}
|
||||
}, [tableLoading, fetchCustomers.isFetching]);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={fetchResourceViews.isFetching}
|
||||
name={'customers-list'}
|
||||
>
|
||||
<CustomersListProvider query={customersTableQuery}>
|
||||
<CustomerActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<CustomersViewPage />
|
||||
</DashboardPageContent>
|
||||
<CustomersAlerts />
|
||||
</DashboardInsider>
|
||||
</CustomersListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withResourceActions,
|
||||
withCustomersActions,
|
||||
withDashboardActions,
|
||||
withViewsActions,
|
||||
withCustomers(({ customersTableQuery }) => ({ customersTableQuery })),
|
||||
)(CustomersList);
|
||||
|
||||
41
client/src/containers/Customers/CustomersListProvider.js
Normal file
41
client/src/containers/Customers/CustomersListProvider.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { createContext } from 'react';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useCustomers } from 'hooks/query';
|
||||
|
||||
const CustomersListContext = createContext();
|
||||
|
||||
function CustomersListProvider({ query, ...props }) {
|
||||
// Fetch customers resource views and fields.
|
||||
const {
|
||||
data: customersViews,
|
||||
isFetching: isCustomersViewsLoading,
|
||||
} = useResourceViews('customers');
|
||||
|
||||
// Fetches customers data with pagination meta.
|
||||
const {
|
||||
data: { customers, pagination },
|
||||
isFetching: isCustomersLoading,
|
||||
} = useCustomers(query);
|
||||
|
||||
const state = {
|
||||
customersViews,
|
||||
customers,
|
||||
pagination,
|
||||
|
||||
isCustomersViewsLoading,
|
||||
isCustomersLoading,
|
||||
|
||||
isEmptyStatus: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider loading={isCustomersViewsLoading} name={'customers-list'}>
|
||||
<CustomersListContext.Provider value={state} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useCustomersListContext = () => React.useContext(CustomersListContext);
|
||||
|
||||
export { CustomersListProvider, useCustomersListContext };
|
||||
@@ -6,7 +6,7 @@ import CustomerAttachmentTabs from './CustomerAttachmentTabs';
|
||||
import CustomerFinancialPanel from './CustomerFinancialPanel';
|
||||
import CustomerNotePanel from './CustomerNotePanel';
|
||||
|
||||
export default function CustomersTabs({ customer }) {
|
||||
export default function CustomersTabs() {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
return (
|
||||
@@ -20,7 +20,7 @@ export default function CustomersTabs({ customer }) {
|
||||
<Tab
|
||||
id={'financial'}
|
||||
title={formatMessage({ id: 'financial_details' })}
|
||||
panel={<CustomerFinancialPanel customerId={customer} />}
|
||||
panel={<CustomerFinancialPanel />}
|
||||
/>
|
||||
<Tab
|
||||
id={'address'}
|
||||
|
||||
@@ -46,9 +46,9 @@ function CustomersViewPage({
|
||||
>
|
||||
<CustomersViewsTabs />
|
||||
<CustomersTable
|
||||
onDeleteCustomer={handleDeleteCustomer}
|
||||
onEditCustomer={handleEditCustomer}
|
||||
onSelectedRowsChange={handleSelectedRowsChange}
|
||||
// onDeleteCustomer={handleDeleteCustomer}
|
||||
// onEditCustomer={handleEditCustomer}
|
||||
// onSelectedRowsChange={handleSelectedRowsChange}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
|
||||
@@ -1,43 +1,25 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { compose } from 'redux';
|
||||
import { useParams, withRouter } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withViewDetail from 'containers/Views/withViewDetails';
|
||||
import { pick } from 'lodash';
|
||||
import { useCustomersListContext } from './CustomersListProvider';
|
||||
|
||||
/**
|
||||
* Customers views tabs.
|
||||
*/
|
||||
function CustomersViewsTabs({
|
||||
// #withViewDetail
|
||||
viewId,
|
||||
viewItem,
|
||||
|
||||
// #withCustomers
|
||||
customersViews,
|
||||
|
||||
// #withCustomersActions
|
||||
addCustomersTableQueries,
|
||||
changeCustomerView,
|
||||
|
||||
// #withDashboardActions
|
||||
setTopbarEditView,
|
||||
changePageSubtitle,
|
||||
}) {
|
||||
const { custom_view_id: customViewId = null } = useParams();
|
||||
const { customersViews } = useCustomersListContext();
|
||||
|
||||
useEffect(() => {
|
||||
changePageSubtitle(customViewId && viewItem ? viewItem.name : '');
|
||||
setTopbarEditView(customViewId);
|
||||
}, [customViewId]);
|
||||
|
||||
const tabs = useMemo(() =>
|
||||
customersViews.map(
|
||||
(view) => ({
|
||||
@@ -45,14 +27,13 @@ function CustomersViewsTabs({
|
||||
}),
|
||||
[customersViews],
|
||||
),
|
||||
[customersViews]
|
||||
);
|
||||
|
||||
const handleTabsChange = (viewId) => {
|
||||
changeCustomerView(viewId || -1);
|
||||
addCustomersTableQueries({
|
||||
custom_view_id: viewId || null,
|
||||
});
|
||||
setTopbarEditView(viewId);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -69,19 +50,7 @@ function CustomersViewsTabs({
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
viewId: ownProps.match.params.custom_view_id,
|
||||
});
|
||||
|
||||
const withCustomersViewsTabs = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
withDashboardActions,
|
||||
withCustomersViewsTabs,
|
||||
withCustomersActions,
|
||||
withViewDetail(),
|
||||
withCustomers(({ customersViews }) => ({
|
||||
customersViews,
|
||||
})),
|
||||
)(CustomersViewsTabs);
|
||||
|
||||
Reference in New Issue
Block a user