diff --git a/client/src/containers/Alerts/Estimates/EstimateApproveAlert.js b/client/src/containers/Alerts/Estimates/EstimateApproveAlert.js
new file mode 100644
index 000000000..33c819adf
--- /dev/null
+++ b/client/src/containers/Alerts/Estimates/EstimateApproveAlert.js
@@ -0,0 +1,78 @@
+import React, { useCallback, useState } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { Intent, Alert } from '@blueprintjs/core';
+import { queryCache } from 'react-query';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
+
+import { compose } from 'utils';
+
+/**
+ * Estimate Approve alert.
+ */
+function EstimateApproveAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { estimateId },
+
+ // #withEstimateActions
+ requestApproveEstimate,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+
+ // handle cancel approve alert.
+ const handleCancelApproveEstimate = () => {
+ closeAlert(name);
+ };
+ // Handle confirm estimate approve.
+ const handleConfirmEstimateApprove = useCallback(() => {
+ setLoading(true);
+ requestApproveEstimate(estimateId)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_estimate_has_been_approved_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('estimates-table');
+ })
+ .catch((error) => {})
+ .finally(() => {
+ setLoading(false);
+ closeAlert(name);
+ });
+ }, [estimateId, requestApproveEstimate, formatMessage]);
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ loading={isLoading}
+ onCancel={handleCancelApproveEstimate}
+ onConfirm={handleConfirmEstimateApprove}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withEstimateActions,
+)(EstimateApproveAlert);
diff --git a/client/src/containers/Alerts/Estimates/EstimateDeleteAlert.js b/client/src/containers/Alerts/Estimates/EstimateDeleteAlert.js
new file mode 100644
index 000000000..0a1b55cf2
--- /dev/null
+++ b/client/src/containers/Alerts/Estimates/EstimateDeleteAlert.js
@@ -0,0 +1,85 @@
+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 withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
+
+import { compose } from 'utils';
+
+/**
+ * Estimate delete alert.
+ */
+function EstimateDeleteAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { estimateId },
+
+ // #withEstimateActions
+ requestDeleteEstimate,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+
+ // handle cancel delete alert.
+ const handleCancelEstimateDelete = () => {
+ closeAlert(name);
+ };
+
+ // handle confirm delete estimate
+ const handleConfirmEstimateDelete = useCallback(() => {
+ setLoading(true);
+ requestDeleteEstimate(estimateId)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_estimate_has_been_deleted_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('estimates-table');
+ })
+ .catch(({ errors }) => {})
+ .finally(() => {
+ setLoading(false);
+ closeAlert(name);
+ });
+ }, [requestDeleteEstimate, formatMessage, estimateId]);
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ loading={isLoading}
+ onCancel={handleCancelEstimateDelete}
+ onConfirm={handleConfirmEstimateDelete}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withEstimateActions,
+)(EstimateDeleteAlert);
diff --git a/client/src/containers/Alerts/Estimates/EstimateDeliveredAlert.js b/client/src/containers/Alerts/Estimates/EstimateDeliveredAlert.js
new file mode 100644
index 000000000..6c78ff76d
--- /dev/null
+++ b/client/src/containers/Alerts/Estimates/EstimateDeliveredAlert.js
@@ -0,0 +1,78 @@
+import React, { useCallback, useState } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { Intent, Alert } from '@blueprintjs/core';
+import { queryCache } from 'react-query';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
+
+import { compose } from 'utils';
+
+/**
+ * Estimate delivered alert.
+ */
+function EstimateDeliveredAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { estimateId },
+
+ // #withEstimateActions
+ requestDeliveredEstimate,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+
+ // Handle cancel delivered estimate alert.
+ const handleCancelDeliveredEstimate = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm estimate delivered.
+ const handleConfirmEstimateDelivered = useCallback(() => {
+ setLoading(true);
+ requestDeliveredEstimate(estimateId)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_estimate_has_been_delivered_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('estimates-table');
+ })
+ .catch((error) => {})
+ .finally(() => {
+ closeAlert(name);
+ setLoading(false);
+ });
+ }, [estimateId, requestDeliveredEstimate, formatMessage]);
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelDeliveredEstimate}
+ onConfirm={handleConfirmEstimateDelivered}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withEstimateActions,
+)(EstimateDeliveredAlert);
diff --git a/client/src/containers/Alerts/Estimates/EstimateRejectAlert.js b/client/src/containers/Alerts/Estimates/EstimateRejectAlert.js
new file mode 100644
index 000000000..58a069752
--- /dev/null
+++ b/client/src/containers/Alerts/Estimates/EstimateRejectAlert.js
@@ -0,0 +1,77 @@
+import React, { useCallback, useState } from 'react';
+import { FormattedMessage as T, useIntl } from 'react-intl';
+import { Intent, Alert } from '@blueprintjs/core';
+import { queryCache } from 'react-query';
+import { AppToaster } from 'components';
+
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
+
+import { compose } from 'utils';
+
+/**
+ * Estimate reject delete alerts.
+ */
+function EstimateRejectAlert({
+ name,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { estimateId },
+
+ // #withEstimateActions
+ requestRejectEstimate,
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ const { formatMessage } = useIntl();
+ const [isLoading, setLoading] = useState(false);
+ // Handle cancel reject estimate alert.
+ const handleCancelRejectEstimate = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm estimate reject.
+ const handleConfirmEstimateReject = useCallback(() => {
+ setLoading(true);
+ requestRejectEstimate(estimateId)
+ .then(() => {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_estimate_has_been_rejected_successfully',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('estimates-table');
+ })
+ .catch((error) => {})
+ .finally(() => {
+ setLoading(false);
+ closeAlert(name);
+ });
+ }, [estimateId, requestRejectEstimate, formatMessage]);
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={isOpen}
+ onCancel={handleCancelRejectEstimate}
+ onConfirm={handleConfirmEstimateReject}
+ loading={isLoading}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+ withEstimateActions,
+)(EstimateRejectAlert);
diff --git a/client/src/containers/Sales/Estimate/EstimateFormPage.js b/client/src/containers/Sales/Estimate/EstimateFormPage.js
index 9dbb3ed63..0ff455427 100644
--- a/client/src/containers/Sales/Estimate/EstimateFormPage.js
+++ b/client/src/containers/Sales/Estimate/EstimateFormPage.js
@@ -23,7 +23,7 @@ function EstimateFormPage({
requestFetchItems,
// #withEstimateActions
- requsetFetchEstimate,
+ requestFetchEstimate,
// #withSettingsActions
requestFetchOptions,
@@ -52,7 +52,7 @@ function EstimateFormPage({
const fetchEstimate = useQuery(
['estimate', id],
- (key, _id) => requsetFetchEstimate(_id),
+ (key, _id) => requestFetchEstimate(_id),
{ enabled: !!id },
);
diff --git a/client/src/containers/Sales/Estimate/EstimatesAlerts.js b/client/src/containers/Sales/Estimate/EstimatesAlerts.js
new file mode 100644
index 000000000..ec27875f9
--- /dev/null
+++ b/client/src/containers/Sales/Estimate/EstimatesAlerts.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import EstimateDeleteAlert from 'containers/Alerts/Estimates/EstimateDeleteAlert';
+import EstimateDeliveredAlert from 'containers/Alerts/Estimates/EstimateDeliveredAlert';
+import EstimateApproveAlert from 'containers/Alerts/Estimates/EstimateApproveAlert';
+import EstimateRejectAlert from 'containers/Alerts/Estimates/EstimateRejectAlert';
+
+/**
+ * Estimates alert.
+ */
+export default function EstimatesAlerts() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/containers/Sales/Estimate/EstimatesList.js b/client/src/containers/Sales/Estimate/EstimatesList.js
index fbda147c1..dfaed428f 100644
--- a/client/src/containers/Sales/Estimate/EstimatesList.js
+++ b/client/src/containers/Sales/Estimate/EstimatesList.js
@@ -8,6 +8,7 @@ import { FormattedMessage as T, useIntl } from 'react-intl';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
+import EstimatesAlerts from './EstimatesAlerts';
import EstimatesDataTable from './EstimatesDataTable';
import EstimateActionsBar from './EstimateActionsBar';
import EstimateViewTabs from './EstimateViewTabs';
@@ -15,8 +16,9 @@ import EstimateViewTabs from './EstimateViewTabs';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withResourceActions from 'containers/Resources/withResourcesActions';
import withEstimates from './withEstimates';
-import withEstimateActions from './withEstimateActions';
+import withEstimateActions from 'containers/Sales/Estimate/withEstimateActions';
import withViewsActions from 'containers/Views/withViewsActions';
+import withAlertsActions from 'containers/Alert/withAlertActions';
import { compose } from 'utils';
@@ -32,9 +34,11 @@ function EstimatesList({
estimatesTableQuery,
estimateViews,
+ // #withAlertsActions.
+ openAlert,
+
//#withEistimateActions
requestFetchEstimatesTable,
- requestDeleteEstimate,
requestDeliverdEstimate,
requestApproveEstimate,
requestRejectEstimate,
@@ -42,7 +46,6 @@ function EstimatesList({
}) {
const history = useHistory();
const { formatMessage } = useIntl();
- const [deleteEstimate, setDeleteEstimate] = useState(false);
const [deliverEstimate, setDeliverEstimate] = useState(false);
const [approveEstimate, setApproveEstimate] = useState(false);
const [rejectEstimate, setRejectEstimate] = useState(false);
@@ -70,111 +73,35 @@ function EstimatesList({
// handle delete estimate click
const handleDeleteEstimate = useCallback(
- (estimate) => {
- setDeleteEstimate(estimate);
+ ({ id }) => {
+ openAlert('estimate-delete', { estimateId: id });
},
- [setDeleteEstimate],
+ [openAlert],
);
- // handle cancel estimate
- const handleCancelEstimateDelete = useCallback(() => {
- setDeleteEstimate(false);
- }, [setDeleteEstimate]);
-
- // handle confirm delete estimate
- const handleConfirmEstimateDelete = useCallback(() => {
- requestDeleteEstimate(deleteEstimate.id).then(() => {
- AppToaster.show({
- message: formatMessage({
- id: 'the_estimate_has_been_deleted_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- setDeleteEstimate(false);
- });
- }, [deleteEstimate, requestDeleteEstimate, formatMessage]);
-
// Handle cancel/confirm estimate deliver.
- const handleDeliverEstimate = useCallback((estimate) => {
- setDeliverEstimate(estimate);
- }, []);
-
- // Handle cancel deliver estimate alert.
- const handleCancelDeliverEstimate = useCallback(() => {
- setDeliverEstimate(false);
- }, []);
-
- // Handle confirm estimate deliver.
- const handleConfirmEstimateDeliver = useCallback(() => {
- requestDeliverdEstimate(deliverEstimate.id)
- .then(() => {
- setDeliverEstimate(false);
- AppToaster.show({
- message: formatMessage({
- id: 'the_estimate_has_been_delivered_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- queryCache.invalidateQueries('estimates-table');
- })
- .catch((error) => {
- setDeliverEstimate(false);
- });
- }, [deliverEstimate, requestDeliverdEstimate, formatMessage]);
+ const handleDeliverEstimate = useCallback(
+ ({ id }) => {
+ openAlert('estimate-deliver', { estimateId: id });
+ },
+ [openAlert],
+ );
// Handle cancel/confirm estimate approve.
- const handleApproveEstimate = useCallback((estimate) => {
- setApproveEstimate(estimate);
- }, []);
-
- // Handle cancel approve estimate alert.
- const handleCancelApproveEstimate = useCallback(() => {
- setApproveEstimate(false);
- }, []);
-
- // Handle confirm estimate approve.
- const handleConfirmEstimateApprove = useCallback(() => {
- requestApproveEstimate(approveEstimate.id)
- .then(() => {
- setApproveEstimate(false);
- AppToaster.show({
- message: formatMessage({
- id: 'the_estimate_has_been_approved_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- queryCache.invalidateQueries('estimates-table');
- })
- .catch((error) => {
- setApproveEstimate(false);
- });
- }, [approveEstimate, requestApproveEstimate, formatMessage]);
+ const handleApproveEstimate = useCallback(
+ ({ id }) => {
+ openAlert('estimate-Approve', { estimateId: id });
+ },
+ [openAlert],
+ );
// Handle cancel/confirm estimate reject.
- const handleRejectEstimate = useCallback((estimate) => {
- setRejectEstimate(estimate);
- }, []);
-
- // Handle cancel reject estimate alert.
- const handleCancelRejectEstimate = useCallback(() => {
- setRejectEstimate(false);
- }, []);
-
- // Handle confirm estimate reject.
- const handleConfirmEstimateReject = useCallback(() => {
- requestRejectEstimate(rejectEstimate.id)
- .then(() => {
- setRejectEstimate(false);
- AppToaster.show({
- message: formatMessage({
- id: 'the_estimate_has_been_rejected_successfully',
- }),
- intent: Intent.SUCCESS,
- });
- queryCache.invalidateQueries('estimates-table');
- })
- .catch((error) => {});
- }, [rejectEstimate, requestRejectEstimate, formatMessage]);
+ const handleRejectEstimate = useCallback(
+ ({ id }) => {
+ openAlert('estimate-reject', { estimateId: id });
+ },
+ [openAlert],
+ );
// Handle filter change to re-fetch data-table.
const handleFilterChanged = useCallback(() => {}, []);
@@ -224,56 +151,7 @@ function EstimatesList({
/>
-
- }
- confirmButtonText={}
- icon={'trash'}
- intent={Intent.DANGER}
- isOpen={deleteEstimate}
- onCancel={handleCancelEstimateDelete}
- onConfirm={handleConfirmEstimateDelete}
- >
-
-
-
-
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={deliverEstimate}
- onCancel={handleCancelDeliverEstimate}
- onConfirm={handleConfirmEstimateDeliver}
- >
-
-
-
-
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={approveEstimate}
- onCancel={handleCancelApproveEstimate}
- onConfirm={handleConfirmEstimateApprove}
- >
-
-
-
-
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={rejectEstimate}
- onCancel={handleCancelRejectEstimate}
- onConfirm={handleConfirmEstimateReject}
- >
-
-
-
-
+
);
@@ -288,4 +166,5 @@ export default compose(
estimatesTableQuery,
estimateViews,
})),
+ withAlertsActions,
)(EstimatesList);
diff --git a/client/src/containers/Sales/Estimate/withEstimateActions.js b/client/src/containers/Sales/Estimate/withEstimateActions.js
index 5c36a8fd2..cf54edf9c 100644
--- a/client/src/containers/Sales/Estimate/withEstimateActions.js
+++ b/client/src/containers/Sales/Estimate/withEstimateActions.js
@@ -7,18 +7,18 @@ import {
fetchEstimatesTable,
deliverEstimate,
approveEstimate,
- rejectEstimate
+ rejectEstimate,
} from 'store/Estimate/estimates.actions';
import t from 'store/types';
-const mapDipatchToProps = (dispatch) => ({
+const mapDispatchToProps = (dispatch) => ({
requestSubmitEstimate: (form) => dispatch(submitEstimate({ form })),
- requsetFetchEstimate: (id) => dispatch(fetchEstimate({ id })),
+ requestFetchEstimate: (id) => dispatch(fetchEstimate({ id })),
requestEditEstimate: (id, form) => dispatch(editEstimate(id, form)),
requestFetchEstimatesTable: (query = {}) =>
dispatch(fetchEstimatesTable({ query: { ...query } })),
requestDeleteEstimate: (id) => dispatch(deleteEstimate({ id })),
- requestDeliverdEstimate: (id) => dispatch(deliverEstimate({ id })),
+ requestDeliveredEstimate: (id) => dispatch(deliverEstimate({ id })),
requestApproveEstimate: (id) => dispatch(approveEstimate({ id })),
requestRejectEstimate: (id) => dispatch(rejectEstimate({ id })),
@@ -38,6 +38,11 @@ const mapDipatchToProps = (dispatch) => ({
type: t.ESTIMATE_NUMBER_CHANGED,
payload: { isChanged },
}),
+ setSelectedRowsEstimates: (selectedRows) =>
+ dispatch({
+ type: t.ESTIMATES_SELECTED_ROWS_SET,
+ payload: { selectedRows },
+ }),
});
-export default connect(null, mapDipatchToProps);
+export default connect(null, mapDispatchToProps);
diff --git a/client/src/containers/Sales/Estimate/withEstimates.js b/client/src/containers/Sales/Estimate/withEstimates.js
index 36be9d88c..ee812c25b 100644
--- a/client/src/containers/Sales/Estimate/withEstimates.js
+++ b/client/src/containers/Sales/Estimate/withEstimates.js
@@ -22,7 +22,8 @@ export default (mapState) => {
estimateViews: getResourceViews(state, props, 'sale_estimate'),
estimateItems: state.salesEstimates.items,
-
+ estimateSelectedRows: state.salesEstimates.selectedRows,
+
estimatesTableQuery: query,
estimatesPageination: getEstimatesPaginationMeta(state, props, query),
estimatesLoading: state.salesEstimates.loading,
diff --git a/client/src/store/Estimate/estimates.reducer.js b/client/src/store/Estimate/estimates.reducer.js
index 410b140e4..0ea564c6a 100644
--- a/client/src/store/Estimate/estimates.reducer.js
+++ b/client/src/store/Estimate/estimates.reducer.js
@@ -15,6 +15,7 @@ const initialState = {
page: 1,
},
currentViewId: -1,
+ selectedRows: [],
};
const defaultEstimate = {
@@ -101,6 +102,10 @@ export default createReducer(initialState, {
},
};
},
+ [t.ESTIMATES_SELECTED_ROWS_SET]: (state, action) => {
+ const { selectedRows } = action.payload;
+ state.selectedRows = selectedRows;
+ },
...journalNumberChangedReducer(t.ESTIMATE_NUMBER_CHANGED),
...createTableQueryReducers('ESTIMATES'),
diff --git a/client/src/store/Estimate/estimates.types.js b/client/src/store/Estimate/estimates.types.js
index 5a8801081..5d958d24d 100644
--- a/client/src/store/Estimate/estimates.types.js
+++ b/client/src/store/Estimate/estimates.types.js
@@ -10,4 +10,5 @@ export default {
ESTIMATES_PAGE_SET: 'ESTIMATES_PAGE_SET',
ESTIMATES_ITEMS_SET: 'ESTIMATES_ITEMS_SET',
ESTIMATE_NUMBER_CHANGED: 'ESTIMATE_NUMBER_CHANGED',
+ ESTIMATES_SELECTED_ROWS_SET: 'ESTIMATES_SELECTED_ROWS_SET',
};