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 +}