diff --git a/client/src/common/classes.js b/client/src/common/classes.js
index 213774aed..22d3c1b2f 100644
--- a/client/src/common/classes.js
+++ b/client/src/common/classes.js
@@ -29,6 +29,9 @@ const CLASSES = {
CLOUD_SPINNER: 'cloud-spinner',
IS_LOADING: 'is-loading',
+
+ DATATABLE_EMPTY_STATE: 'datatable-empty-state',
+ DATATABLE_EMPTY_STATE_TITLE: 'datatable-empty-state__title',
...Classes,
};
diff --git a/client/src/components/DataTable.js b/client/src/components/DataTable.js
index 84ae9520a..6db7f3536 100644
--- a/client/src/components/DataTable.js
+++ b/client/src/components/DataTable.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef, useCallback } from 'react';
+import React, { useEffect, useRef, useCallback, useMemo } from 'react';
import {
useTable,
useExpanded,
@@ -7,20 +7,18 @@ import {
useResizeColumns,
useSortBy,
useFlexLayout,
+ useAsyncDebounce,
} from 'react-table';
-import {
- Checkbox,
- Spinner,
- ContextMenu,
-} from '@blueprintjs/core';
+import { Checkbox, Spinner, ContextMenu } from '@blueprintjs/core';
import classnames from 'classnames';
import { FixedSizeList } from 'react-window';
import { useSticky } from 'react-table-sticky';
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
-import { ConditionalWrapper } from 'utils';
import { useUpdateEffect } from 'hooks';
-import { If, Pagination } from 'components';
+import { If, Pagination, Choose } from 'components';
+
+import { ConditionalWrapper, saveInvoke } from 'utils';
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
@@ -31,10 +29,13 @@ const IndeterminateCheckbox = React.forwardRef(
export default function DataTable({
columns,
data,
+
loading,
onFetchData,
+
onSelectedRowsChange,
manualSortBy = false,
+ manualPagination = true,
selectionColumn = false,
expandSubRows = true,
className,
@@ -53,11 +54,24 @@ export default function DataTable({
pagination = false,
pagesCount: controlledPageCount,
- initialPageIndex,
- initialPageSize,
+
+ // Pagination props.
+ initialPageIndex = 0,
+ initialPageSize = 10,
rowContextMenu,
expandColumnSpace = 1.5,
+
+ updateDebounceTime = 200,
+
+ // Read this document to know why! https://bit.ly/2Uw9SEc
+ autoResetPage = true,
+ autoResetExpanded = true,
+ autoResetGroupBy = true,
+ autoResetSelectedRows = true,
+ autoResetSortBy = true,
+ autoResetFilters = true,
+ autoResetRowState = true,
}) {
const {
getTableProps,
@@ -85,18 +99,25 @@ export default function DataTable({
} = useTable(
{
columns,
- data: data,
+ data,
initialState: {
pageIndex: initialPageIndex,
pageSize: initialPageSize,
- expanded,
- }, // Pass our hoisted table state
- manualPagination: true,
+ },
+ manualPagination,
pageCount: controlledPageCount,
getSubRows: (row) => row.children,
manualSortBy,
expandSubRows,
payload,
+
+ autoResetPage,
+ autoResetExpanded,
+ autoResetGroupBy,
+ autoResetSelectedRows,
+ autoResetSortBy,
+ autoResetFilters,
+ autoResetRowState,
},
useSortBy,
useExpanded,
@@ -145,18 +166,23 @@ export default function DataTable({
);
const isInitialMount = useRef(noInitialFetch);
-
+ const onFetchDataDebounced = useAsyncDebounce(
+ (...args) => {
+ saveInvoke(onFetchData, ...args);
+ },
+ updateDebounceTime,
+ );
// When these table states change, fetch new data!
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
} else {
- onFetchData && onFetchData({ pageIndex, pageSize, sortBy });
+ onFetchDataDebounced({ pageIndex, pageSize, sortBy });
}
- }, [pageIndex, pageSize, manualSortBy ? sortBy : null, onFetchData]);
+ }, [pageIndex, pageSize, sortBy, onFetchDataDebounced]);
useUpdateEffect(() => {
- onSelectedRowsChange && onSelectedRowsChange(selectedFlatRows);
+ saveInvoke(onSelectedRowsChange, selectedFlatRows);
}, [selectedRowIds, onSelectedRowsChange]);
// Renders table cell.
@@ -177,40 +203,50 @@ export default function DataTable({
{
// Use the row.canExpand and row.getToggleRowExpandedProps prop getter
// to build the toggle for expanding a row
- row.canExpand && expandable && index === expandToggleColumn && (
-
-
-
- )
}
+
+
+
+
+
{cell.render('Cell')}
),
- [expandable, expandToggleColumn],
+ [expandable, expandToggleColumn, expandColumnSpace],
);
- const handleRowContextMenu = (cell, row) => (e) => {
- if (typeof rowContextMenu === 'function') {
- e.preventDefault();
- const tr = e.currentTarget.closest('.tr');
- tr.classList.add('is-context-menu-active');
+ // Handle rendering row context menu.
+ const handleRowContextMenu = useMemo(
+ () => (cell, row) => (e) => {
+ if (typeof rowContextMenu === 'function') {
+ e.preventDefault();
+ const tr = e.currentTarget.closest('.tr');
+ tr.classList.add('is-context-menu-active');
- const DropdownEl = rowContextMenu(cell, row);
+ const DropdownEl = rowContextMenu(cell, row);
- ContextMenu.show(DropdownEl, { left: e.clientX, top: e.clientY }, () => {
- tr.classList.remove('is-context-menu-active');
- });
- }
- };
+ ContextMenu.show(
+ DropdownEl,
+ { left: e.clientX, top: e.clientY },
+ () => {
+ tr.classList.remove('is-context-menu-active');
+ },
+ );
+ }
+ },
+ [rowContextMenu],
+ );
// Renders table row.
const RenderRow = useCallback(
@@ -221,9 +257,13 @@ export default function DataTable({
return (
@@ -243,7 +283,7 @@ export default function DataTable({
);
},
- [prepareRow, rowClassNames, expandable, RenderCell, expandToggleColumn],
+ [prepareRow, rowClassNames, RenderCell, handleRowContextMenu],
);
// Renders virtualize circle table rows.
@@ -254,7 +294,7 @@ export default function DataTable({
},
[RenderRow, rows],
);
-
+ // Renders page with multi-rows.
const RenderPage = useCallback(
({ style, index } = {}) => {
return page.map((row, index) => RenderRow({ row }));
@@ -284,6 +324,21 @@ export default function DataTable({
RenderPage,
]);
+ const handlePageChange = useCallback(
+ (currentPage) => {
+ gotoPage(currentPage - 1);
+ },
+ [gotoPage],
+ );
+
+ const handlePageSizeChange = useCallback(
+ (pageSize, currentPage) => {
+ gotoPage(0);
+ setPageSize(pageSize);
+ },
+ [gotoPage, setPageSize],
+ );
+
return (
- {expandable && index + 1 === expandToggleColumn && (
+
- )}
+
{column.render('Header')}
- {column.isSorted && (
+
- )}
+
{column.canResize && (
@@ -358,37 +417,36 @@ export default function DataTable({
-
{RenderTBody()}
+
+
+
+
+
+
-
-
-
+
+ {RenderTBody()}
+
+
+
+
+
+
-
-
-
-
-
-
-
+
{
- gotoPage(currentPage - 1);
- }}
- onPageSizeChange={(pageSize, currentPage) => {
- gotoPage(currentPage - 1);
- setPageSize(pageSize);
- }}
+ onPageChange={handlePageChange}
+ onPageSizeChange={handlePageSizeChange}
/>
diff --git a/client/src/containers/Accounting/ManualJournalsDataTable.js b/client/src/containers/Accounting/ManualJournalsDataTable.js
index 52e5523ca..484a4d545 100644
--- a/client/src/containers/Accounting/ManualJournalsDataTable.js
+++ b/client/src/containers/Accounting/ManualJournalsDataTable.js
@@ -15,13 +15,22 @@ import { withRouter, useParams } from 'react-router-dom';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
-import { DataTable, If, Money, Choose, Icon } from 'components';
-import { compose } from 'utils';
+import {
+ DataTable,
+ If,
+ Money,
+ Choose,
+ Icon,
+ LoadingIndicator,
+} from 'components';
+import { useIsValuePassed } from 'hooks';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withManualJournals from 'containers/Accounting/withManualJournals';
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
+import { compose, saveInvoke } from 'utils';
+
/**
* Status column accessor.
*/
@@ -68,44 +77,36 @@ function ManualJournalsDataTable({
manualJournalsPagination,
manualJournalsTableQuery,
- onFetchData,
+ // #withManualJournalsActions
+ addManualJournalsTableQueries,
+
+ // #ownProps
onEditJournal,
onDeleteJournal,
onPublishJournal,
onSelectedRowsChange,
}) {
- const [isMounted, setIsMounted] = useState(false);
const { custom_view_id: customViewId } = useParams();
-
const { formatMessage } = useIntl();
-
- useEffect(() => {
- setIsMounted(false);
- }, [customViewId]);
-
- useEffect(() => {
- if (!manualJournalsLoading) {
- setIsMounted(true);
- }
- }, [manualJournalsLoading, setIsMounted]);
+ const isLoadedBefore = useIsValuePassed(manualJournalsLoading, false);
const handlePublishJournal = useCallback(
(journal) => () => {
- onPublishJournal && onPublishJournal(journal);
+ saveInvoke(onPublishJournal, journal);
},
[onPublishJournal],
);
const handleEditJournal = useCallback(
(journal) => () => {
- onEditJournal && onEditJournal(journal);
+ saveInvoke(onEditJournal, journal);
},
[onEditJournal],
);
const handleDeleteJournal = useCallback(
(journal) => () => {
- onDeleteJournal && onDeleteJournal(journal);
+ saveInvoke(onDeleteJournal, journal);
},
[onDeleteJournal],
);
@@ -115,7 +116,8 @@ function ManualJournalsDataTable({