feat: fix items list datatable.

This commit is contained in:
a.bouhuolia
2021-02-08 13:17:11 +02:00
parent adac2386bb
commit 304f0c9ae5
43 changed files with 777 additions and 835 deletions

View File

@@ -1,20 +1,18 @@
import React, { useState } from 'react';
import React 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 { handleDeleteErrors } from 'containers/Items/utils';
import { useDeleteItem } from 'hooks/query';
import {
useDeleteItem
} from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions';
import withItemsActions from 'containers/Items/withItemsActions';
import { compose } from 'utils';
@@ -30,15 +28,19 @@ function ItemDeleteAlert({
// #withAlertActions
closeAlert,
// #withItemsActions
addItemsTableQueries
}) {
const { mutateAsync: deleteItem, isLoading } = useDeleteItem();
const { formatMessage } = useIntl();
// handle cancel delete item alert.
// Handle cancel delete item alert.
const handleCancelItemDelete = () => {
closeAlert(name);
};
// Handle confirm delete item.
const handleConfirmDeleteItem = () => {
deleteItem(itemId)
.then(() => {
@@ -48,6 +50,8 @@ function ItemDeleteAlert({
}),
intent: Intent.SUCCESS,
});
// Reset to page number one.
addItemsTableQueries({ page: 1 });
})
.catch(({ errors }) => {
handleDeleteErrors(errors);
@@ -80,4 +84,5 @@ function ItemDeleteAlert({
export default compose(
withAlertStoreConnect(),
withAlertActions,
withItemsActions
)(ItemDeleteAlert);

View File

@@ -179,6 +179,7 @@ function InventoryAdjustmentDataTable({
autoResetSortBy={false}
autoResetPage={false}
isLoading={isLoading}
noResults={'There is no inventory adjustments transactions yet.'}
// pagesCount={inventoryAdjustmentsPagination.pagesCount}
// initialPageSize={inventoryAdjustmentsPagination.pageSize}
// initialPageIndex={inventoryAdjustmentsPagination.page - 1}

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { ItemFormProvider } from './ItemFormProvider';
@@ -12,8 +12,21 @@ import { compose } from 'utils';
/**
* Item form page.
*/
function ItemFormPage() {
function ItemFormPage({
// #withDashboardActions
setDashboardBackLink
}) {
const { id } = useParams();
useEffect(() => {
// Show the back link on dashboard topbar.
setDashboardBackLink(true);
return () => {
// Hide the back link on dashboard topbar.
setDashboardBackLink(false);
};
}, [setDashboardBackLink]);
return (
<ItemFormProvider itemId={id}>
@@ -24,4 +37,6 @@ function ItemFormPage() {
);
}
export default compose(withDashboardActions)(ItemFormPage);
export default compose(
withDashboardActions,
)(ItemFormPage);

View File

@@ -53,7 +53,7 @@ function ItemsActionsBar({
// Handle tab changing.
const handleTabChange = (viewId) => {
addItemsTableQueries({
custom_view_id: viewId.id || null,
customViewId: viewId.id || null,
});
};
@@ -87,7 +87,7 @@ function ItemsActionsBar({
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={`${formatMessage({ id: 'filters_applied' })}`}
text={`${formatMessage({ id: 'filter' })}`}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>

View File

@@ -18,6 +18,7 @@ import {
CostPriceCell,
ItemTypeAccessor,
ItemsActionsTableCell,
ItemsActionMenuList
} from './components';
// Items datatable.
@@ -118,11 +119,14 @@ function ItemsDataTable({
manualSortBy={true}
pagesCount={1}
autoResetSortBy={false}
autoResetPage={false}
autoResetPage={true}
manualPagination={true}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
initialPageSize={pagination.pageSize}
initialPageIndex={pagination.page}
pageSize={pagination.pageSize}
pageIndex={pagination.page - 1}
ContextMenu={ItemsActionMenuList}
{...tableProps}
/>
</Choose.Otherwise>

View File

@@ -1,13 +1,21 @@
import React, { useEffect, createContext } from 'react';
import { useIntl } from 'react-intl';
import { isEmpty } from 'lodash';
import { transformTableQueryToParams, isTableEmptyStatus } from 'utils';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import { useResourceViews, useResourceFields, useItems } from 'hooks/query';
import { useDashboardPageTitle } from 'hooks/state';
const ItemsContext = createContext();
function ItemsListProvider({ query, ...props }) {
/**
* Items list provider.
*/
function ItemsListProvider({
query,
...props
}) {
// Fetch accounts resource views and fields.
const { data: itemsViews, isFetching: isViewsLoading } = useResourceViews(
'items',
@@ -21,11 +29,16 @@ function ItemsListProvider({ query, ...props }) {
// Handle fetching the items table based on the given query.
const {
data: { items, pagination, filterMeta },
isFetching: isItemsLoading,
} = useItems(query);
isFetching: isItemsFetching,
isLoading: isItemsLoading,
} = useItems({
...transformTableQueryToParams(query)
}, { keepPreviousData: true });
// Detarmines the datatable empty status.
const isEmptyStatus = isEmpty(items) && !isItemsLoading && !filterMeta.view;
const isEmptyStatus = isTableEmptyStatus({
data: items, pagination, filterMeta,
}) && !isItemsFetching;
// Format message intl.
const { formatMessage } = useIntl();
@@ -42,15 +55,15 @@ function ItemsListProvider({ query, ...props }) {
itemsFields,
items,
pagination,
isViewsLoading,
isItemsLoading,
isEmptyStatus: false,
isItemsFetching: isItemsFetching,
isEmptyStatus,
};
return (
<DashboardInsider
loading={isFieldsLoading || isViewsLoading}
loading={isFieldsLoading}
name={'items-list'}
>
<ItemsContext.Provider value={state} {...props} />

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Switch, Route, useHistory } from 'react-router-dom';
import React, { useMemo } from 'react';
import { useHistory } from 'react-router-dom';
import ItemsViewsTabs from './ItemsViewsTabs';
import ItemsDataTable from './ItemsDataTable';
@@ -7,9 +7,39 @@ import ItemsDataTable from './ItemsDataTable';
import withItemsActions from 'containers/Items/withItemsActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withItems from 'containers/Items/withItems';
import { compose } from 'utils';
import { useItemsListContext } from './ItemsListProvider';
function transformPaginationToProps(pagination) {
const { page, pageSize, total } = pagination;
return {
initialPageIndex: Math.max(page - 1, 0),
initialPageSize: pageSize,
pagesCount: Math.ceil(total / pageSize),
};
}
function transformPaginationToQuery(query) {
const { pageSize, pageIndex, sortBy } = query;
return {
page_size: pageSize,
page: pageIndex + 1,
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
};
}
/**
* Items view page.
*/
function ItemsViewPage({
// #withAlertsActions.
openAlert,
@@ -19,10 +49,14 @@ function ItemsViewPage({
// #withItemsActions.
setSelectedRowsItems,
addItemsTableQueries
addItemsTableQueries,
itemsTableQuery,
}) {
const history = useHistory();
const { pagination, isItemsFetching } = useItemsListContext();
// Handle delete action Item.
const handleDeleteItem = ({ id }) => {
openAlert('item-delete', { itemId: id });
@@ -52,49 +86,49 @@ function ItemsViewPage({
// Handle item make adjustment.
const handleMakeAdjustment = ({ id }) => {
openDialog('inventory-adjustment', { itemId: id });
}
};
// Handle fetch data once the page index, size or sort by of the table change.
const handleFetchData = ({ pageIndex, pageSize, sortBy }) => {
const handlePaginationChange = ({ pageSize, page }) => {
addItemsTableQueries({
page_size: pageSize,
page: pageIndex,
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
// ...transformPaginationToQuery(query),
page,
pageSize,
});
};
const controlledState = (state) => ({
...state,
pageIndex: itemsTableQuery.page - 1,
});
return (
<Switch>
<Route
exact={true}
path={['/items/:custom_view_id/custom_view', '/items']}
>
<ItemsViewsTabs />
<ItemsDataTable
tableProps={{
payload: {
onDeleteItem: handleDeleteItem,
onEditItem: handleEditItem,
onInactivateItem: handleInactiveItem,
onActivateItem: handleActivateItem,
onMakeAdjustment: handleMakeAdjustment
},
onFetchData: handleFetchData
}}
onSelectedRowsChange={handleSelectedRowsChange}
/>
</Route>
</Switch>
<>
<ItemsViewsTabs />
<ItemsDataTable
tableProps={{
payload: {
onDeleteItem: handleDeleteItem,
onEditItem: handleEditItem,
onInactivateItem: handleInactiveItem,
onActivateItem: handleActivateItem,
onMakeAdjustment: handleMakeAdjustment,
},
...transformPaginationToProps(pagination),
onPaginationChange: handlePaginationChange,
progressBarLoading: isItemsFetching,
// useControlledState: controlledState
// progressBarLoading: true
}}
onSelectedRowsChange={handleSelectedRowsChange}
/>
</>
);
}
export default compose(
withAlertsActions,
withItemsActions,
withDialogActions
withDialogActions,
withItems(({ itemsTableQuery }) => ({ itemsTableQuery })),
)(ItemsViewPage);

View File

@@ -1,11 +1,13 @@
import React from 'react';
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
import { useParams } from 'react-router-dom';
import { compose } from 'utils';
import { DashboardViewsTabs } from 'components';
import { pick } from 'lodash';
import { withRouter } from 'react-router-dom';
import withItemsActions from 'containers/Items/withItemsActions';
import withItems from 'containers/Items/withItems';
import { useItemsListContext } from './ItemsListProvider';
/**
@@ -14,17 +16,22 @@ import { useItemsListContext } from './ItemsListProvider';
function ItemsViewsTabs({
// #withItemsActions
addItemsTableQueries,
// #withItems
itemsTableQuery
}) {
const { custom_view_id: customViewId = null } = useParams();
const { itemsViews } = useItemsListContext();
// Mapped items views.
const tabs = itemsViews.map((view) => ({
...pick(view, ['name', 'id']),
}));
// Handles the active tab change.
const handleTabChange = (viewId) => {
addItemsTableQueries({
custom_view_id: viewId || null,
page: 1,
customViewId: viewId || null,
});
};
@@ -32,7 +39,7 @@ function ItemsViewsTabs({
<Navbar className="navbar--dashboard-views">
<NavbarGroup align={Alignment.LEFT}>
<DashboardViewsTabs
initialViewId={customViewId}
currentViewId={itemsTableQuery.customViewId}
resourceName={'items'}
tabs={tabs}
onChange={handleTabChange}
@@ -42,7 +49,8 @@ function ItemsViewsTabs({
);
}
export default compose(
withRouter,
withItems(({ itemsTableQuery }) => ({ itemsTableQuery })),
withItemsActions,
)(ItemsViewsTabs);

View File

@@ -71,7 +71,7 @@ export const ItemTypeAccessor = (row) => {
) : null;
};
export const ItemsActionMenuList = ({
export function ItemsActionMenuList({
row: { original },
payload: {
onEditItem,
@@ -80,9 +80,9 @@ export const ItemsActionMenuList = ({
onMakeAdjustment,
onDeleteItem,
},
}) => {
}) {
const { formatMessage } = useIntl();
return (
<Menu>
<MenuItem
@@ -112,6 +112,7 @@ export const ItemsActionMenuList = ({
<If condition={original.type === 'inventory'}>
<MenuItem
text={formatMessage({ id: 'make_adjustment' })}
icon={<Icon icon={'swap-vert'} iconSize={16} />}
onClick={safeCallback(onMakeAdjustment, original)}
/>
</If>

View File

@@ -1,30 +1,15 @@
import {connect} from 'react-redux';
import {
getResourceViews,
} from 'store/customViews/customViews.selectors'
import {
getItemsCurrentPageFactory,
getItemsPaginationMetaFactory,
getItemsTableQueryFactory,
getItemsCurrentViewIdFactory
} from 'store/items/items.selectors';
export default (mapState) => {
const getItemsCurrentPage = getItemsCurrentPageFactory();
const getItemsPaginationMeta = getItemsPaginationMetaFactory();
const getItemsTableQuery = getItemsTableQueryFactory();
const getItemsCurrentViewId = getItemsCurrentViewIdFactory();
const mapStateToProps = (state, props) => {
const mapped = {
itemsViews: getResourceViews(state, props, 'items'),
itemsCurrentPage: getItemsCurrentPage(state, props),
itemsBulkSelected: state.items.bulkActions,
itemsTableLoading: state.items.loading,
itemsSelectedRows: state.items.selectedRows,
itemsTableQuery: getItemsTableQuery(state, props),
itemsPagination: getItemsPaginationMeta(state, props),
itemsCurrentViewId: getItemsCurrentViewId(state, props),
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -1,35 +1,7 @@
import { connect } from 'react-redux';
import {
fetchItems,
fetchItem,
deleteItem,
submitItem,
editItem,
deleteBulkItems,
activateItem,
inactiveItem,
} from 'store/items/items.actions';
import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({
requestFetchItems: (query) => dispatch(fetchItems({ query })),
requestFetchItem: (id) => dispatch(fetchItem({ id })),
requestDeleteItem: (id) => dispatch(deleteItem({ id })),
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,
itemId: id,
}),
removeBulkActionItem: (id) =>
dispatch({
type: t.ITEM_BULK_ACTION_REMOVE,
itemId: id,
}),
setItemsTableQuery: (key, value) =>
dispatch({
type: t.ITEMS_TABLE_QUERY_SET,
@@ -41,12 +13,6 @@ export const mapDispatchToProps = (dispatch) => ({
type: t.ITEMS_TABLE_QUERIES_ADD,
payload: { queries },
}),
changeItemsCurrentView: (id) =>
dispatch({
type: t.ITEMS_SET_CURRENT_VIEW,
currentViewId: parseInt(id, 10),
}),
setSelectedRowsItems: (selectedRows) =>
dispatch({
type: t.ITEM_SELECTED_ROWS_SET,

View File

@@ -68,7 +68,7 @@ export default function ItemsCategoryTable({
sticky={true}
selectionColumn={true}
TableLoadingRenderer={TableSkeletonRows}
{...tableProps}
noResults={'There is no items categories in table yet.'}
/>
</div>
);