From bd09ab1144612369705a44477435618a3ca919c8 Mon Sep 17 00:00:00 2001
From: elforjani3
Date: Wed, 16 Dec 2020 18:54:01 +0200
Subject: [PATCH] feat: activate & inactive item.
---
client/src/containers/Items/ItemsDataTable.js | 50 ++++++-
client/src/containers/Items/ItemsList.js | 138 ++++++++++++++----
.../src/containers/Items/withItemsActions.js | 4 +
client/src/lang/en/index.js | 20 ++-
client/src/store/items/items.actions.js | 8 +
client/src/style/pages/items.scss | 74 +++++-----
6 files changed, 218 insertions(+), 76 deletions(-)
diff --git a/client/src/containers/Items/ItemsDataTable.js b/client/src/containers/Items/ItemsDataTable.js
index 948d99979..305a21b2c 100644
--- a/client/src/containers/Items/ItemsDataTable.js
+++ b/client/src/containers/Items/ItemsDataTable.js
@@ -12,7 +12,14 @@ import {
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
-import { Icon, DataTable, Money, LoadingIndicator, Choose } from 'components';
+import {
+ Icon,
+ DataTable,
+ Money,
+ LoadingIndicator,
+ Choose,
+ If,
+} from 'components';
import ItemsEmptyStatus from './ItemsEmptyStatus';
import { useIsValuePassed } from 'hooks';
import { CLASSES } from 'common/classes';
@@ -36,6 +43,8 @@ function ItemsDataTable({
// props
onEditItem,
onDeleteItem,
+ onInactiveItem,
+ onActivateItem,
onSelectedRowsChange,
}) {
const { formatMessage } = useIntl();
@@ -83,6 +92,20 @@ function ItemsDataTable({
text={formatMessage({ id: 'edit_item' })}
onClick={handleEditItem(item)}
/>
+
+ }
+ onClick={() => onInactiveItem(item)}
+ />
+
+
+ }
+ onClick={() => onActivateItem(item)}
+ />
+
}
@@ -91,7 +114,13 @@ function ItemsDataTable({
/>
),
- [handleEditItem, handleDeleteItem, formatMessage],
+ [
+ handleEditItem,
+ handleDeleteItem,
+ onInactiveItem,
+ onActivateItem,
+ formatMessage,
+ ],
);
const handleRowContextMenu = useCallback(
@@ -148,11 +177,11 @@ function ItemsDataTable({
{
Header: formatMessage({ id: 'cost_price' }),
accessor: (row) =>
- !isBlank(row.sell_price) ? (
-
- ) : (
- ''
- ),
+ !isBlank(row.sell_price) ? (
+
+ ) : (
+ ''
+ ),
className: 'cost-price',
width: 150,
},
@@ -196,6 +225,12 @@ function ItemsDataTable({
[onSelectedRowsChange],
);
+ const rowClassNames = (row) => {
+ return {
+ inactive: !row.original.active,
+ };
+ };
+
const showEmptyStatus = [
itemsCurrentPage.length === 0,
itemsCurrentViewId === -1,
@@ -221,6 +256,7 @@ function ItemsDataTable({
rowContextMenu={handleRowContextMenu}
expandable={false}
sticky={true}
+ rowClassNames={rowClassNames}
pagination={true}
pagesCount={itemsPagination.pagesCount}
autoResetSortBy={false}
diff --git a/client/src/containers/Items/ItemsList.js b/client/src/containers/Items/ItemsList.js
index 606501507..25f41f142 100644
--- a/client/src/containers/Items/ItemsList.js
+++ b/client/src/containers/Items/ItemsList.js
@@ -1,7 +1,7 @@
import React, { useEffect, useCallback, useState, useMemo } from 'react';
import { Route, Switch, useHistory } from 'react-router-dom';
import { Intent, Alert } from '@blueprintjs/core';
-import { useQuery } from 'react-query';
+import { useQuery, queryCache } from 'react-query';
import {
FormattedMessage as T,
FormattedHTMLMessage,
@@ -38,10 +38,14 @@ function ItemsList({
// #withItemsActions
requestDeleteItem,
requestFetchItems,
+ requestInactiveItem,
+ requestActivateItem,
addItemsTableQueries,
requestDeleteBulkItems,
}) {
const [deleteItem, setDeleteItem] = useState(false);
+ const [inactiveItem, setInactiveItem] = useState(false);
+ const [activateItem, setActivateItem] = useState(false);
const [selectedRows, setSelectedRows] = useState([]);
const [bulkDelete, setBulkDelete] = useState(false);
@@ -65,9 +69,8 @@ function ItemsList({
);
// Handle fetching the items table based on the given query.
- const fetchItems = useQuery(
- ['items-table', itemsTableQuery],
- (key, _query) => requestFetchItems({ ..._query }),
+ const fetchItems = useQuery(['items-table', itemsTableQuery], (key, _query) =>
+ requestFetchItems({ ..._query }),
);
// Handle click delete item.
@@ -92,33 +95,36 @@ function ItemsList({
// handle confirm delete item.
const handleConfirmDeleteItem = useCallback(() => {
- requestDeleteItem(deleteItem.id).then(() => {
- AppToaster.show({
- message: formatMessage({
- id: 'the_item_has_been_successfully_deleted',
- }),
- intent: Intent.SUCCESS,
- });
- setDeleteItem(false);
- }).catch(({ errors }) => {
- if (errors.find(error => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')) {
+ requestDeleteItem(deleteItem.id)
+ .then(() => {
AppToaster.show({
message: formatMessage({
- id: 'the_item_has_associated_transactions',
+ id: 'the_item_has_been_successfully_deleted',
}),
- intent: Intent.DANGER,
+ intent: Intent.SUCCESS,
});
- }
- setDeleteItem(false);
- });
+ setDeleteItem(false);
+ })
+ .catch(({ errors }) => {
+ if (
+ errors.find(
+ (error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
+ )
+ ) {
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_item_has_associated_transactions',
+ }),
+ intent: Intent.DANGER,
+ });
+ }
+ setDeleteItem(false);
+ });
}, [requestDeleteItem, deleteItem, formatMessage]);
- const handleFetchData = useCallback(
- ({ pageIndex, pageSize, sortBy }) => {
-
- },
- [addItemsTableQueries],
- );
+ const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {}, [
+ addItemsTableQueries,
+ ]);
// Handle filter change to re-fetch the items.
const handleFilterChanged = useCallback(
@@ -174,6 +180,62 @@ function ItemsList({
setBulkDelete(false);
}, []);
+ // Handle cancel/confirm item inactive.
+ const handleInactiveItem = useCallback((item) => {
+ setInactiveItem(item);
+ }, []);
+
+ // Handle cancel inactive item alert.
+ const handleCancelInactiveItem = useCallback(() => {
+ setInactiveItem(false);
+ }, []);
+
+ // Handle confirm item Inactive.
+ const handleConfirmItemInactive = useCallback(() => {
+ requestInactiveItem(inactiveItem.id)
+ .then(() => {
+ setInactiveItem(false);
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_item_has_been_successfully_inactivated',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('items-table');
+ })
+ .catch((error) => {
+ setInactiveItem(false);
+ });
+ }, [inactiveItem, requestInactiveItem, formatMessage]);
+
+ // Handle activate item click.
+ const handleActivateItem = useCallback((item) => {
+ setActivateItem(item);
+ });
+
+ // Handle activate item alert cancel.
+ const handleCancelActivateItem = useCallback(() => {
+ setActivateItem(false);
+ });
+
+ // Handle activate item confirm.
+ const handleConfirmItemActivate = useCallback(() => {
+ requestActivateItem(activateItem.id)
+ .then(() => {
+ setActivateItem(false);
+ AppToaster.show({
+ message: formatMessage({
+ id: 'the_item_has_been_successfully_activated',
+ }),
+ intent: Intent.SUCCESS,
+ });
+ queryCache.invalidateQueries('items-table');
+ })
+ .catch((error) => {
+ setActivateItem(false);
+ });
+ }, [activateItem, requestActivateItem, formatMessage]);
+
return (
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={inactiveItem}
+ onCancel={handleCancelInactiveItem}
+ onConfirm={handleConfirmItemInactive}
+ >
+
+
+
+
+ }
+ confirmButtonText={}
+ intent={Intent.WARNING}
+ isOpen={activateItem}
+ onCancel={handleCancelActivateItem}
+ onConfirm={handleConfirmItemActivate}
+ >
+
+
+
+
diff --git a/client/src/containers/Items/withItemsActions.js b/client/src/containers/Items/withItemsActions.js
index f117728c4..4458aace4 100644
--- a/client/src/containers/Items/withItemsActions.js
+++ b/client/src/containers/Items/withItemsActions.js
@@ -6,6 +6,8 @@ import {
submitItem,
editItem,
deleteBulkItems,
+ activateItem,
+ inactiveItem,
} from 'store/items/items.actions';
import t from 'store/types';
@@ -16,6 +18,8 @@ export const mapDispatchToProps = (dispatch) => ({
requestDeleteBulkItems: (ids) => dispatch(deleteBulkItems({ ids })),
requestSubmitItem: (form) => dispatch(submitItem({ form })),
requestEditItem: (id, form) => dispatch(editItem({ id, form })),
+ requestInactiveItem: (id) => dispatch(inactiveItem({ id })),
+ requestActivateItem: (id) => dispatch(activateItem({ id })),
addBulkActionItem: (id) =>
dispatch({
type: t.ITEM_BULK_ACTION_ADD,
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index 173ef21b5..7e5a08a84 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -918,9 +918,17 @@ export default {
'Are you sure you want to reject this estimate?',
mark_as_approved: 'Mark as approved',
mark_as_rejected: 'Mark as rejected',
- delivered:'Delivered',
- rejected:'Rejected',
- approved:'Approved'
-
-
-};
+ delivered: 'Delivered',
+ rejected: 'Rejected',
+ approved: 'Approved',
+ the_item_has_been_successfully_inactivated:
+ 'The item has been successfully inactivated.',
+ the_item_has_been_successfully_activated:
+ 'The item has been successfully activated.',
+ are_sure_to_inactive_this_item:
+ 'Are you sure you want to inactive this item? You will be able to activate it later',
+ are_sure_to_activate_this_item:
+ 'Are you sure you want to activate this item? You will be able to inactivate it later',
+ inactivate_item: 'Inactivate Item',
+ activate_item: 'Activate Item',
+ };
diff --git a/client/src/store/items/items.actions.js b/client/src/store/items/items.actions.js
index d836990aa..f57fc3698 100644
--- a/client/src/store/items/items.actions.js
+++ b/client/src/store/items/items.actions.js
@@ -105,3 +105,11 @@ export const deleteBulkItems = ({ ids }) => {
});
});
};
+
+export const activateItem = ({ id }) => {
+ return (dispatch) => ApiService.post(`items/${id}/activate`);
+};
+
+export const inactiveItem = ({ id }) => {
+ return (dispatch) => ApiService.post(`items/${id}/inactivate`);
+};
diff --git a/client/src/style/pages/items.scss b/client/src/style/pages/items.scss
index 1a5480885..383b524a0 100644
--- a/client/src/style/pages/items.scss
+++ b/client/src/style/pages/items.scss
@@ -1,12 +1,11 @@
-
-.page-form--item{
+.page-form--item {
$self: '.page-form';
padding: 20px;
- #{$self}__header{
+ #{$self}__header {
padding: 0;
}
- #{$self}__primary-section{
+ #{$self}__primary-section {
overflow: hidden;
padding-top: 5px;
margin-bottom: 20px;
@@ -15,89 +14,88 @@
max-width: 1000px;
}
- #{$self}__body{
- .bp3-form-group{
+ #{$self}__body {
+ .bp3-form-group {
max-width: 500px;
margin-bottom: 14px;
-
- &.bp3-inline{
-
- .bp3-label{
+
+ &.bp3-inline {
+ .bp3-label {
min-width: 140px;
}
}
- .bp3-form-content{
+ .bp3-form-content {
width: 100%;
}
}
-
- h3{
+
+ h3 {
font-weight: 500;
font-size: 14px;
margin-bottom: 1.4rem;
}
-
- .bp3-control{
-
- h3{
+
+ .bp3-control {
+ h3 {
display: inline;
margin-bottom: 0;
}
}
-
+
.form-group--sellable,
- .form-group--purchasable{
+ .form-group--purchasable {
margin-bottom: 1rem;
}
}
- #{$self}__section{
+ #{$self}__section {
max-width: 850px;
margin-bottom: 1rem;
- .bp3-form-group{
+ .bp3-form-group {
max-width: 400px;
}
- &--selling-cost{
+ &--selling-cost {
border-bottom: 1px solid #eaeaea;
margin-bottom: 1.25rem;
padding-bottom: 0.25rem;
}
}
- #{$self}__floating-actions{
+ #{$self}__floating-actions {
margin-left: -40px;
margin-right: -40px;
- .form-group--active{
+ .form-group--active {
display: inline-block;
margin: 0;
margin-left: 40px;
}
}
- .bp3-tooltip-indicator{
+ .bp3-tooltip-indicator {
border-bottom: 1px dashed #d0d0d0;
}
}
-
-
-.dashboard__insider--items-list{
-
-
- .bigcapital-datatable{
-
- .table{
- .tbody{
- .item_type.td{
-
- .bp3-tag{
+.dashboard__insider--items-list {
+ .bigcapital-datatable {
+ .table {
+ .tbody {
+ .item_type.td {
+ .bp3-tag {
font-size: 13px;
}
}
+ .tr.inactive .td {
+ color: #646b82;
+
+ &.normal .#{$ns}-icon {
+ color: #9eaab6;
+ }
+ }
}
}
}
-}
\ No newline at end of file
+}