diff --git a/client/src/containers/Alerts/Customers/CustomerBulkDeleteAlert.js b/client/src/containers/Alerts/Customers/CustomerBulkDeleteAlert.js
new file mode 100644
index 000000000..253f04016
--- /dev/null
+++ b/client/src/containers/Alerts/Customers/CustomerBulkDeleteAlert.js
@@ -0,0 +1,81 @@
+import React, { useCallback, useState } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { Intent, Alert } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+import { transformErrors } from 'containers/Customers/utils';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withCustomersActions from 'containers/Customers/withCustomersActions';
+
+import { compose } from 'utils';
+
+/**
+ * Customer bulk delete alert.
+ */
+function CustomerBulkDeleteAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { customersIds },
+ // #withCustomersActions
+ requestDeleteBulkCustomers,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+
+ // handle cancel delete alert.
+ const handleCancelDeleteAlert = () => {
+ closeAlert(name);
+ };
+
+ console.log(customersIds, 'EE');
+
+ // Handle confirm customers bulk delete.
+ const handleConfirmBulkDelete = useCallback(() => {
+ setLoading(true);
+ requestDeleteBulkCustomers(customersIds)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_customers_has_been_deleted_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ })
+ .catch((errors) => {
+ transformErrors(errors);
+ })
+ .finally(() => {
+ setLoading(false);
+ closeAlert(name);
+ });
+ }, [requestDeleteBulkCustomers, customersIds, formatMessage]);
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelDeleteAlert}
+ onConfirm={handleConfirmBulkDelete}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withCustomersActions,
+)(CustomerBulkDeleteAlert);
diff --git a/client/src/containers/Alerts/Customers/CustomerDeleteAlert.js b/client/src/containers/Alerts/Customers/CustomerDeleteAlert.js
new file mode 100644
index 000000000..c90da2124
--- /dev/null
+++ b/client/src/containers/Alerts/Customers/CustomerDeleteAlert.js
@@ -0,0 +1,87 @@
+import React, { useCallback, useState } from 'react';
+import {
+ FormattedMessage as T,
+ FormattedHTMLMessage,
+ useIntl,
+} from 'react-intl';
+import { Intent, Alert } from '@blueprintjs/core';
+import { queryCache } from 'react-query';
+import { AppToaster } from 'components';
+import { transformErrors } from 'containers/Customers/utils';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withCustomersActions from 'containers/Customers/withCustomersActions';
+
+import { compose } from 'utils';
+
+/**
+ * Customer delete alert.
+ */
+function CustomerDeleteAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { customerId },
+ // #withCustomersActions
+ requestDeleteCustomer,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+
+ // handle cancel delete alert.
+ const handleCancelDeleteAlert = () => {
+ closeAlert(name);
+ };
+
+ // handle confirm delete customer.
+ const handleConfirmDeleteCustomer = useCallback(() => {
+ setLoading(true);
+ requestDeleteCustomer(customerId)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_customer_has_been_deleted_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('customers-table');
+ })
+ .catch((errors) => {
+ transformErrors(errors);
+ })
+ .finally(() => {
+ setLoading(false);
+ closeAlert(name);
+ });
+ }, [requestDeleteCustomer, customerId, formatMessage]);
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancelDeleteAlert}
+ onConfirm={handleConfirmDeleteCustomer}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withCustomersActions,
+)(CustomerDeleteAlert);
diff --git a/client/src/containers/Alerts/Items/ItemBulkDeleteAlert.js b/client/src/containers/Alerts/Items/ItemBulkDeleteAlert.js
index 354fd43e4..7da176ab3 100644
--- a/client/src/containers/Alerts/Items/ItemBulkDeleteAlert.js
+++ b/client/src/containers/Alerts/Items/ItemBulkDeleteAlert.js
@@ -27,6 +27,7 @@ function ItemBulkDeleteAlert({
}) {
const { formatMessage } = useIntl();
const [isLoading, setLoading] = useState(false);
+
// handle cancel item bulk delete alert.
const handleCancelBulkDelete = () => {
closeAlert(name);
@@ -49,7 +50,6 @@ function ItemBulkDeleteAlert({
closeAlert(name);
});
};
-
return (
}
@@ -61,6 +61,7 @@ function ItemBulkDeleteAlert({
isOpen={isOpen}
onCancel={handleCancelBulkDelete}
onConfirm={handleConfirmBulkDelete}
+ loading={isLoading}
>
diff --git a/client/src/containers/Customers/CustomerActionsBar.js b/client/src/containers/Customers/CustomerActionsBar.js
index aaf60526c..678b5ff10 100644
--- a/client/src/containers/Customers/CustomerActionsBar.js
+++ b/client/src/containers/Customers/CustomerActionsBar.js
@@ -14,33 +14,31 @@ import classNames from 'classnames';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';
-import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
-import FilterDropdown from 'components/FilterDropdown';
-import { If, DashboardActionViewsList } from 'components';
+import { If, Icon, DashboardActionViewsList } from 'components';
import withResourceDetail from 'containers/Resources/withResourceDetails';
import withCustomers from 'containers/Customers/withCustomers';
import withCustomersActions from 'containers/Customers/withCustomersActions';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
import { compose } from 'utils';
const CustomerActionsBar = ({
- // #withResourceDetail
- resourceFields,
-
// #withCustomers
customersViews,
+ customersSelectedRows,
//#withCustomersActions
addCustomersTableQueries,
changeCustomerView,
+ // #withAlertActions
+ openAlert,
+
// #ownProps
- selectedRows = [],
onFilterChanged,
- onBulkDelete,
}) => {
- const [filterCount, setFilterCount] = useState(0);
const history = useHistory();
const { formatMessage } = useIntl();
@@ -48,14 +46,10 @@ const CustomerActionsBar = ({
history.push('/customers/new');
}, [history]);
-
- const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
- selectedRows,
- ]);
-
- const handleBulkDelete = useCallback(() => {
- onBulkDelete && onBulkDelete(selectedRows.map((r) => r.id));
- }, [onBulkDelete, selectedRows]);
+ // Handle Customers bulk delete button click.,
+ const handleBulkDelete = () => {
+ openAlert('customers-bulk-delete', { customersIds: customersSelectedRows });
+ };
const handleTabChange = (viewId) => {
changeCustomerView(viewId.id || -1);
@@ -88,18 +82,12 @@ const CustomerActionsBar = ({
>
- ) : (
- `${filterCount} ${formatMessage({ id: 'filters_applied' })}`
- )
- }
+ text={`${formatMessage({ id: 'filters_applied' })}`}
icon={}
/>
-
+
}
@@ -134,7 +122,9 @@ export default compose(
withResourceDetail(({ resourceFields }) => ({
resourceFields,
})),
- withCustomers(({ customersViews }) => ({
+ withCustomers(({ customersViews, customersSelectedRows }) => ({
customersViews,
+ customersSelectedRows,
})),
+ withAlertActions,
)(CustomerActionsBar);
diff --git a/client/src/containers/Customers/CustomerTable.js b/client/src/containers/Customers/CustomerTable.js
index 87158ad16..0698f3330 100644
--- a/client/src/containers/Customers/CustomerTable.js
+++ b/client/src/containers/Customers/CustomerTable.js
@@ -204,7 +204,6 @@ const CustomerTable = ({
noInitialFetch={true}
columns={columns}
data={customers}
- // loading={customersLoading}
onFetchData={handleFetchData}
selectionColumn={true}
expandable={false}
diff --git a/client/src/containers/Customers/CustomersAlerts.js b/client/src/containers/Customers/CustomersAlerts.js
new file mode 100644
index 000000000..8215e6588
--- /dev/null
+++ b/client/src/containers/Customers/CustomersAlerts.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import CustomerDeleteAlert from 'containers/Alerts/Customers/CustomerDeleteAlert';
+import CustomerBulkDeleteAlert from 'containers/Alerts/Customers/CustomerBulkDeleteAlert';
+
+/**
+ * Customers alert.
+ */
+export default function ItemsAlerts() {
+ return (
+
+
+
+
+ );
+}
diff --git a/client/src/containers/Customers/CustomersList.js b/client/src/containers/Customers/CustomersList.js
index 18844c6e9..7aac45a7d 100644
--- a/client/src/containers/Customers/CustomersList.js
+++ b/client/src/containers/Customers/CustomersList.js
@@ -1,21 +1,13 @@
-import React, { useEffect, useCallback, useState, useMemo } from 'react';
-import { Route, Switch, useHistory } from 'react-router-dom';
-import { Intent, Alert } from '@blueprintjs/core';
+import React, { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
-import {
- FormattedMessage as T,
- FormattedHTMLMessage,
- useIntl,
-} from 'react-intl';
+import { FormattedMessage as T, useIntl } from 'react-intl';
-import AppToaster from 'components/AppToaster';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
-import CustomersTable from 'containers/Customers/CustomerTable';
import CustomerActionsBar from 'containers/Customers/CustomerActionsBar';
-import CustomersViewsTabs from 'containers/Customers/CustomersViewsTabs';
-
+import CustomersAlerts from 'containers/Customers/CustomersAlerts';
+import CustomersViewPage from 'containers/Customers/CustomersViewPage';
import withCustomers from 'containers/Customers/withCustomers';
import withCustomersActions from 'containers/Customers/withCustomersActions';
import withResourceActions from 'containers/Resources/withResourcesActions';
@@ -35,26 +27,21 @@ function CustomersList({
// #withResourceActions
requestFetchResourceViews,
- requestFetchResourceFields,
// #withCustomers
customersTableQuery,
+ // #withAlertsActions.
+ openAlert,
+
// #withCustomersActions
requestFetchCustomers,
- requestDeleteCustomer,
- requestDeleteBulkCustomers,
+
addCustomersTableQueries,
}) {
- const [deleteCustomer, setDeleteCustomer] = useState(false);
- const [selectedRows, setSelectedRows] = useState([]);
const [tableLoading, setTableLoading] = useState(false);
- const [bulkDelete, setBulkDelete] = useState(false);
-
const { formatMessage } = useIntl();
- const history = useHistory();
-
useEffect(() => {
changePageTitle(formatMessage({ id: 'customers_list' }));
}, [changePageTitle, formatMessage]);
@@ -70,178 +57,23 @@ function CustomersList({
(key, query) => requestFetchCustomers({ ...query }),
);
- const handleEditCustomer = useCallback(
- (customer) => {
- history.push(`/customers/${customer.id}/edit`);
- },
- [history],
- );
-
- // Handle click delete customer.
- const handleDeleteCustomer = useCallback(
- (customer) => {
- setDeleteCustomer(customer);
- },
- [setDeleteCustomer],
- );
-
- // Handle cancel delete the customer.
- const handleCancelDeleteCustomer = useCallback(() => {
- setDeleteCustomer(false);
- }, [setDeleteCustomer]);
-
- const transformErrors = (errors) => {
- if (errors.some((e) => e.type === 'CUSTOMER.HAS.SALES_INVOICES')) {
- AppToaster.show({
- message: formatMessage({
- id: 'customer_has_sales_invoices',
- }),
- intent: Intent.DANGER,
- });
- }
- };
-
- // handle confirm delete customer.
- const handleConfirmDeleteCustomer = useCallback(() => {
- requestDeleteCustomer(deleteCustomer.id)
- .then(() => {
- setDeleteCustomer(false);
- AppToaster.show({
- message: formatMessage({
- id: 'the_customer_has_been_deleted_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- })
- .catch((errors) => {
- setDeleteCustomer(false);
- transformErrors(errors);
- });
- }, [requestDeleteCustomer, deleteCustomer, formatMessage]);
-
- // Handle selected rows change.
- const handleSelectedRowsChange = useCallback(
- (customer) => {
- setSelectedRows(customer);
- },
- [setSelectedRows],
- );
-
useEffect(() => {
if (tableLoading && !fetchCustomers.isFetching) {
setTableLoading(false);
}
}, [tableLoading, fetchCustomers.isFetching]);
- // Calculates the data table selected rows count.
- const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
- selectedRows,
- ]);
-
- // Handle Customers bulk delete button click.,
- const handleBulkDelete = useCallback(
- (customersIds) => {
- setBulkDelete(customersIds);
- },
- [setBulkDelete],
- );
-
- // Handle cancel cusomters bulk delete.
- const handleCancelBulkDelete = useCallback(() => {
- setBulkDelete(false);
- }, []);
-
- const transformApiErrors = (errors) => {
- if (
- errors.find(
- (error) => error.type === 'SOME.CUSTOMERS.HAVE.SALES_INVOICES',
- )
- ) {
- AppToaster.show({
- message: formatMessage({
- id: 'some_customers_have_sales_invoices',
- }),
- intent: Intent.DANGER,
- });
- }
- };
- // Handle confirm customers bulk delete.
- const handleConfirmBulkDelete = useCallback(() => {
- requestDeleteBulkCustomers(bulkDelete)
- .then(() => {
- setBulkDelete(false);
- AppToaster.show({
- message: formatMessage({
- id: 'the_customers_has_been_deleted_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- })
- .catch((errors) => {
- transformApiErrors(errors);
- setBulkDelete(false);
- });
- }, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
-
return (
-
+
-
-
-
-
-
-
-
- }
- confirmButtonText={}
- icon="trash"
- intent={Intent.DANGER}
- isOpen={deleteCustomer}
- onCancel={handleCancelDeleteCustomer}
- onConfirm={handleConfirmDeleteCustomer}
- >
-
-
-
-
-
- }
- confirmButtonText={`${formatMessage({
- id: 'delete',
- })} (${selectedRowsCount})`}
- icon="trash"
- intent={Intent.DANGER}
- isOpen={bulkDelete}
- onCancel={handleCancelBulkDelete}
- onConfirm={handleConfirmBulkDelete}
- >
-
-
-
-
+
+
);
}
diff --git a/client/src/containers/Customers/CustomersViewPage.js b/client/src/containers/Customers/CustomersViewPage.js
new file mode 100644
index 000000000..2d093fbbc
--- /dev/null
+++ b/client/src/containers/Customers/CustomersViewPage.js
@@ -0,0 +1,61 @@
+import React, { useCallback } from 'react';
+import { Route, Switch, useHistory } from 'react-router-dom';
+
+import CustomersViewsTabs from 'containers/Customers/CustomersViewsTabs';
+import CustomersTable from 'containers/Customers/CustomerTable';
+
+import withCustomersActions from 'containers/Customers/withCustomersActions';
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import { compose } from 'utils';
+
+function CustomersViewPage({
+ // #withAlertsActions.
+ openAlert,
+
+ // #withCustomersActions
+ setSelectedRowsCustomers,
+}) {
+ const history = useHistory();
+
+ // Handle click delete customer.
+ const handleDeleteCustomer = useCallback(
+ ({ id }) => {
+ openAlert('customer-delete', { customerId: id });
+ },
+ [openAlert],
+ );
+
+ // Handle select customer rows.
+ const handleSelectedRowsChange = (selectedRows) => {
+ const selectedRowsIds = selectedRows.map((r) => r.id);
+ setSelectedRowsCustomers(selectedRowsIds);
+ };
+
+ const handleEditCustomer = useCallback(
+ (customer) => {
+ history.push(`/customers/${customer.id}/edit`);
+ },
+ [history],
+ );
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertsActions,
+ withCustomersActions,
+)(CustomersViewPage);
diff --git a/client/src/containers/Customers/utils.js b/client/src/containers/Customers/utils.js
new file mode 100644
index 000000000..e766e18a5
--- /dev/null
+++ b/client/src/containers/Customers/utils.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import { Intent } from '@blueprintjs/core';
+import { AppToaster } from 'components';
+import { formatMessage } from 'services/intl';
+
+export const transformErrors = (errors) => {
+ if (errors.some((e) => e.type === 'CUSTOMER.HAS.SALES_INVOICES')) {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'customer_has_sales_invoices',
+ }),
+ intent: Intent.DANGER,
+ });
+ }
+ if (
+ errors.find((error) => error.type === 'SOME.CUSTOMERS.HAVE.SALES_INVOICES')
+ ) {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'some_customers_have_sales_invoices',
+ }),
+ intent: Intent.DANGER,
+ });
+ }
+};
diff --git a/client/src/containers/Customers/withCustomers.js b/client/src/containers/Customers/withCustomers.js
index 5dce50e19..b6a538951 100644
--- a/client/src/containers/Customers/withCustomers.js
+++ b/client/src/containers/Customers/withCustomers.js
@@ -15,7 +15,6 @@ export default (mapState) => {
const mapStateToProps = (state, props) => {
const query = getCustomerTableQuery(state, props);
-
const mapped = {
customers: getCustomersList(state, props, query),
customersViews: getResourceViews(state, props, 'customers'),
@@ -24,7 +23,7 @@ export default (mapState) => {
customersLoading: state.customers.loading,
customersItems: state.customers.items,
customersCurrentViewId: getCustomersCurrentViewId(state, props),
- // customerErrors: state.customers.errors,
+ customersSelectedRows: state.customers.selectedRows,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
diff --git a/client/src/containers/Customers/withCustomersActions.js b/client/src/containers/Customers/withCustomersActions.js
index a00c52240..9622b907e 100644
--- a/client/src/containers/Customers/withCustomersActions.js
+++ b/client/src/containers/Customers/withCustomersActions.js
@@ -27,6 +27,11 @@ export const mapDispatchToProps = (dispatch) => ({
currentViewId: parseInt(id, 10),
});
},
+ setSelectedRowsCustomers: (selectedRows) =>
+ dispatch({
+ type: t.CUSTOMER_SELECTED_ROWS_SET,
+ payload: { selectedRows },
+ }),
});
export default connect(null, mapDispatchToProps);
diff --git a/client/src/store/customers/customers.reducer.js b/client/src/store/customers/customers.reducer.js
index e127d789c..0a52e15e0 100644
--- a/client/src/store/customers/customers.reducer.js
+++ b/client/src/store/customers/customers.reducer.js
@@ -10,7 +10,7 @@ const initialState = {
views: {},
loading: false,
currentViewId: -1,
-
+ selectedRows: [],
// Responsible for data fetch query based on this query.
tableQuery: {
page_size: 12,
@@ -49,7 +49,6 @@ export default createReducer(initialState, {
state.views[viewId] = {
...view,
pages: {
-
...(state.views?.[viewId]?.pages || {}),
[paginationMeta.page]: {
ids: customers.map((i) => i.id),
@@ -85,11 +84,14 @@ export default createReducer(initialState, {
});
state.items = items;
},
-
+ [t.CUSTOMER_SELECTED_ROWS_SET]: (state, action) => {
+ const { selectedRows } = action.payload;
+ state.selectedRows = selectedRows;
+ },
...viewPaginationSetReducer(t.CUSTOMERS_PAGINATION_SET),
...createTableQueryReducers('CUSTOMERS'),
});
export const getCustomerById = (state, id) => {
return state.customers.items[id];
-};
+};
\ No newline at end of file
diff --git a/client/src/store/customers/customers.type.js b/client/src/store/customers/customers.type.js
index c43ef801a..a38ed454e 100644
--- a/client/src/store/customers/customers.type.js
+++ b/client/src/store/customers/customers.type.js
@@ -8,4 +8,5 @@ export default {
CUSTOMERS_BULK_DELETE: 'CUSTOMERS_BULK_DELETE',
CUSTOMERS_PAGINATION_SET: 'CUSTOMERS_PAGINATION_SET',
CUSTOMERS_SET_CURRENT_VIEW: 'CUSTOMERS_SET_CURRENT_VIEW',
+ CUSTOMER_SELECTED_ROWS_SET: 'CUSTOMER_SELECTED_ROWS_SET',
};