refactoring: migrating to react-query to manage service-side state.

This commit is contained in:
a.bouhuolia
2021-02-07 08:10:21 +02:00
parent e093be0663
commit adac2386bb
284 changed files with 8255 additions and 6610 deletions

View File

@@ -1,170 +1,39 @@
import React, { useCallback, useMemo } from 'react';
import {
Button,
Popover,
Menu,
MenuItem,
MenuDivider,
Position,
Intent,
Tag,
} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import React, { useMemo } from 'react';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { DataTable, Choose } from 'components';
import {
Icon,
DataTable,
Money,
LoadingIndicator,
Choose,
If,
} from 'components';
import ItemsEmptyStatus from './ItemsEmptyStatus';
import { useIsValuePassed } from 'hooks';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import { CLASSES } from 'common/classes';
import withItems from 'containers/Items/withItems';
import withItemsActions from 'containers/Items/withItemsActions';
import withSettings from 'containers/Settings/withSettings';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose, saveInvoke, isBlank, defaultToTransform } from 'utils';
import { useItemsListContext } from './ItemsListProvider';
import { compose } from 'utils';
import {
QuantityOnHandCell,
SellPriceCell,
CostPriceCell,
ItemTypeAccessor,
ItemsActionsTableCell,
} from './components';
// Items datatable.
function ItemsDataTable({
// #withItems
itemsTableLoading,
itemsCurrentPage,
itemsTableQuery,
itemsCurrentViewId,
itemsPagination,
// #withDialogActions
openDialog,
// #withItemsActions
addItemsTableQueries,
// #withSettings
baseCurrency,
// props
onEditItem,
onDeleteItem,
onInactiveItem,
onActivateItem,
onSelectedRowsChange,
itemsViewLoading,
// #ownProps
tableProps
}) {
const { formatMessage } = useIntl();
const isLoadedBefore = useIsValuePassed(itemsTableLoading, false);
const handleFetchData = useCallback(
({ pageIndex, pageSize, sortBy }) => {
addItemsTableQueries({
page_size: pageSize,
page: pageIndex + 1,
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
});
},
[addItemsTableQueries],
);
const handleMakeAdjustment = useCallback(
(item) => () => {
openDialog('inventory-adjustment-form', {
action: 'make_adjustment',
itemId: item.id,
});
},
[openDialog],
);
const handleEditItem = useCallback(
(item) => () => {
onEditItem && onEditItem(item);
},
[onEditItem],
);
const handleDeleteItem = useCallback(
(item) => () => {
onDeleteItem(item);
},
[onDeleteItem],
);
const actionMenuList = useCallback(
(item) => (
<Menu>
<MenuItem
icon={<Icon icon="reader-18" />}
text={formatMessage({ id: 'view_details' })}
/>
<MenuDivider />
<MenuItem
icon={<Icon icon="pen-18" />}
text={formatMessage({ id: 'edit_item' })}
onClick={handleEditItem(item)}
/>
<If condition={item.active}>
<MenuItem
text={formatMessage({ id: 'inactivate_item' })}
icon={<Icon icon="pause-16" iconSize={16} />}
onClick={() => onInactiveItem(item)}
/>
</If>
<If condition={!item.active}>
<MenuItem
text={formatMessage({ id: 'activate_item' })}
icon={<Icon icon="play-16" iconSize={16} />}
onClick={() => onActivateItem(item)}
/>
</If>
<If condition={item.type === 'inventory'}>
<MenuItem
text={formatMessage({ id: 'make_adjustment' })}
onClick={handleMakeAdjustment(item)}
/>
</If>
<MenuItem
text={formatMessage({ id: 'delete_item' })}
icon={<Icon icon="trash-16" iconSize={16} />}
onClick={handleDeleteItem(item)}
intent={Intent.DANGER}
/>
</Menu>
),
[
handleEditItem,
handleDeleteItem,
onInactiveItem,
onActivateItem,
formatMessage,
],
);
const quantityonHandCell = ({ value: quantity }) => {
return quantity <= 0 ? (
<span className={'quantity_on_hand'}>{quantity}</span>
) : (
<span>{quantity}</span>
);
};
const handleRowContextMenu = useCallback(
(cell) => {
return actionMenuList(cell.row.original);
},
[actionMenuList],
);
const {
items,
pagination,
isItemsLoading,
isEmptyStatus,
} = useItemsListContext();
// Datatable columns.
const columns = useMemo(
() => [
{
@@ -181,14 +50,7 @@ function ItemsDataTable({
},
{
Header: formatMessage({ id: 'item_type' }),
accessor: (row) =>
row.type ? (
<Tag minimal={true} round={true} intent={Intent.NONE}>
{formatMessage({ id: row.type })}
</Tag>
) : (
''
),
accessor: ItemTypeAccessor,
className: 'item_type',
width: 120,
},
@@ -200,127 +62,77 @@ function ItemsDataTable({
},
{
Header: formatMessage({ id: 'sell_price' }),
accessor: (row) =>
!isBlank(row.sell_price) ? (
<Money amount={row.sell_price} currency={baseCurrency} />
) : (
''
),
Cell: SellPriceCell,
accessor: 'sell_price',
className: 'sell-price',
width: 150,
},
{
Header: formatMessage({ id: 'cost_price' }),
accessor: (row) =>
!isBlank(row.cost_price) ? (
<Money amount={row.cost_price} currency={baseCurrency} />
) : (
''
),
Cell: CostPriceCell,
accessor: 'cost_price',
className: 'cost-price',
width: 150,
},
{
Header: formatMessage({ id: 'quantity_on_hand' }),
accessor: 'quantity_on_hand',
Cell: quantityonHandCell,
Cell: QuantityOnHandCell,
width: 140,
},
{
id: 'actions',
Cell: ({ cell }) => (
<Popover
content={actionMenuList(cell.row.original)}
position={Position.RIGHT_BOTTOM}
>
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
</Popover>
),
className: 'actions',
width: 50,
Cell: ItemsActionsTableCell,
width: 60,
skeletonWidthMin: 100,
},
],
[actionMenuList, formatMessage],
[formatMessage],
);
// Handle selected row change.
const handleSelectedRowsChange = useCallback(
(selectedRows) => {
saveInvoke(
onSelectedRowsChange,
selectedRows.map((s) => s.original),
);
},
[onSelectedRowsChange],
);
const rowClassNames = (row) => {
return {
inactive: !row.original.active,
};
};
const showEmptyStatus = [
itemsCurrentPage.length === 0,
itemsCurrentViewId === -1,
].every((condition) => condition === true);
// Table row class names.
const rowClassNames = (row) => ({
inactive: !row.original.active,
});
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<LoadingIndicator
loading={(itemsTableLoading && !isLoadedBefore) || itemsViewLoading}
>
<Choose>
<Choose.When condition={showEmptyStatus}>
<ItemsEmptyStatus />
</Choose.When>
<Choose>
<Choose.When condition={isEmptyStatus}>
<ItemsEmptyStatus />
</Choose.When>
<Choose.Otherwise>
<DataTable
columns={columns}
data={itemsCurrentPage}
onFetchData={handleFetchData}
noInitialFetch={true}
selectionColumn={true}
spinnerProps={{ size: 30 }}
onSelectedRowsChange={handleSelectedRowsChange}
rowContextMenu={handleRowContextMenu}
expandable={false}
sticky={true}
rowClassNames={rowClassNames}
pagination={true}
pagesCount={itemsPagination.pagesCount}
autoResetSortBy={false}
autoResetPage={false}
initialPageSize={itemsTableQuery.page_size}
initialPageIndex={itemsTableQuery.page - 1}
/>
</Choose.Otherwise>
</Choose>
</LoadingIndicator>
<Choose.Otherwise>
<DataTable
columns={columns}
data={items}
loading={isItemsLoading}
headerLoading={isItemsLoading}
noInitialFetch={true}
selectionColumn={true}
spinnerProps={{ size: 30 }}
expandable={false}
sticky={true}
rowClassNames={rowClassNames}
pagination={true}
manualSortBy={true}
pagesCount={1}
autoResetSortBy={false}
autoResetPage={false}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
initialPageSize={pagination.pageSize}
initialPageIndex={pagination.page}
{...tableProps}
/>
</Choose.Otherwise>
</Choose>
</div>
);
}
export default compose(
withItems(
({
itemsCurrentPage,
itemsTableLoading,
itemsTableQuery,
itemsCurrentViewId,
itemsPagination,
}) => ({
itemsCurrentPage,
itemsTableLoading,
itemsTableQuery,
itemsCurrentViewId,
itemsPagination,
}),
),
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withItemsActions,
withDialogActions,
)(ItemsDataTable);