diff --git a/client/app/assets/less/ant.less b/client/app/assets/less/ant.less
index e95084039..574d420fb 100644
--- a/client/app/assets/less/ant.less
+++ b/client/app/assets/less/ant.less
@@ -31,6 +31,7 @@
@import "~antd/lib/badge/style/index";
@import "~antd/lib/card/style/index";
@import "~antd/lib/spin/style/index";
+@import "~antd/lib/skeleton/style/index";
@import "~antd/lib/tabs/style/index";
@import "~antd/lib/notification/style/index";
@import "~antd/lib/collapse/style/index";
diff --git a/client/app/assets/less/inc/table.less b/client/app/assets/less/inc/table.less
index 7a43a6f9e..9b562f1f0 100755
--- a/client/app/assets/less/inc/table.less
+++ b/client/app/assets/less/inc/table.less
@@ -1,149 +1,153 @@
.table {
- margin-bottom: 0;
-
- th.sortable-column {
- cursor: pointer;
+ margin-bottom: 0;
+
+ th.sortable-column {
+ cursor: pointer;
+ }
+
+ &:not(.table-striped) > thead > tr > th {
+ background-color: #fafafa;
+ }
+
+ [class*="bg-"] {
+ & > tr > th {
+ color: #fff;
+ border-bottom: 0;
+ background: transparent !important;
}
-
- &:not(.table-striped) > thead > tr > th {
- background-color: #FAFAFA;
+
+ & + tbody > tr:first-child > td {
+ border-top: 0;
}
-
- [class*="bg-"] {
- & > tr > th {
- color: #fff;
- border-bottom: 0;
- background: transparent !important;
- }
-
- & + tbody > tr:first-child > td {
- border-top: 0;
- }
- }
-
- & > thead > tr > th {
- vertical-align: middle;
- font-weight: 500;
- color: #333;
- border-width: 1px;
- text-transform: uppercase;
- padding: 15px 10px;
- }
-
- & > thead > tr,
- & > tbody > tr,
- & > tfoot > tr {
-
- & > th, & > td {
-
- &:first-child {
- padding-left: 30px;
- }
-
- &:last-child {
- padding-right: 30px;
- }
-
- }
- }
-
- tbody > tr:last-child > td {
- padding-bottom: 20px;
+ }
+
+ & > thead > tr > th {
+ vertical-align: middle;
+ font-weight: 500;
+ color: #333;
+ border-width: 1px;
+ text-transform: uppercase;
+ padding: 15px 10px;
+ }
+
+ & > thead > tr,
+ & > tbody > tr,
+ & > tfoot > tr {
+ & > th,
+ & > td {
+ &:first-child {
+ padding-left: 30px;
+ }
+
+ &:last-child {
+ padding-right: 30px;
+ }
}
+ }
+
+ tbody > tr:last-child > td {
+ padding-bottom: 20px;
+ }
}
.table-bordered {
- border: 0;
-
- & > tbody > tr {
- & > td, & > th {
- border-bottom: 0;
- border-left: 0;
-
- &:last-child {
- border-right: 0;
- }
- }
+ border: 0;
+
+ & > tbody > tr {
+ & > td,
+ & > th {
+ border-bottom: 0;
+ border-left: 0;
+
+ &:last-child {
+ border-right: 0;
+ }
}
-
- & > thead > tr > th {
- border-left: 0;
-
- &:last-child {
- border-right: 0;
- }
+ }
+
+ & > thead > tr > th {
+ border-left: 0;
+
+ &:last-child {
+ border-right: 0;
}
+ }
}
.table-vmiddle {
- td {
- vertical-align: middle !important;
- }
+ td {
+ vertical-align: middle !important;
+ }
}
.table-responsive {
- border: 0;
+ border: 0;
}
-.tile .table {
-
- & > thead:not([class*="bg-"]) > tr > th {
- border-top: 1px solid @table-border-color;
-
- }
+.tile .table {
+ & > thead:not([class*="bg-"]) > tr > th {
+ border-top: 1px solid @table-border-color;
+ }
}
.table-hover > tbody > tr:hover {
- background-color: #f4f4f4;
+ background-color: #f4f4f4;
}
.table-data {
- tbody > tr > td {
- padding-top: 5px !important;
- }
-
- .btn-favourite, .btn-archive {
- font-size: 15px;
- }
+ thead > tr > th {
+ white-space: nowrap;
+ }
+
+ tbody > tr > td {
+ padding-top: 5px !important;
+ }
+
+ .btn-favourite,
+ .btn-archive {
+ font-size: 15px;
+ }
}
.table-main-title {
- font-weight: 500;
- line-height: 1.7 !important;
+ font-weight: 500;
+ line-height: 1.7 !important;
}
.btn-favourite {
- color: #d4d4d4;
- transition: all .25s ease-in-out;
-
- &:hover, &:focus {
- color: @yellow-darker;
- cursor: pointer;
- }
-
- .fa-star {
- color: @yellow-darker;
- }
+ color: #d4d4d4;
+ transition: all 0.25s ease-in-out;
+
+ &:hover,
+ &:focus {
+ color: @yellow-darker;
+ cursor: pointer;
+ }
+
+ .fa-star {
+ color: @yellow-darker;
+ }
}
.btn-archive {
- color: #d4d4d4;
- transition: all .25s ease-in-out;
-
- &:hover, &:focus {
- color: @gray-light;
- }
-
- .fa-archive {
- color: @gray-light;
- }
+ color: #d4d4d4;
+ transition: all 0.25s ease-in-out;
+
+ &:hover,
+ &:focus {
+ color: @gray-light;
+ }
+
+ .fa-archive {
+ color: @gray-light;
+ }
}
.table > thead > tr > th {
- text-transform: none;
+ text-transform: none;
}
.table-data .label-tag {
- display: inline-block;
- max-width: 135px;
- }
\ No newline at end of file
+ display: inline-block;
+ max-width: 135px;
+}
diff --git a/client/app/components/items-list/ItemsList.jsx b/client/app/components/items-list/ItemsList.jsx
index 1d2de3f00..19ba7b1fb 100644
--- a/client/app/components/items-list/ItemsList.jsx
+++ b/client/app/components/items-list/ItemsList.jsx
@@ -110,9 +110,9 @@ export function wrap(WrappedComponent, createItemsSource, createStateStorage) {
isLoaded,
isEmpty: !isLoaded || totalCount === 0,
- totalItemsCount: isLoaded ? totalCount : 0,
+ totalItemsCount: totalCount,
pageSizeOptions: clientConfig.pageSizeOptions,
- pageItems: isLoaded ? pageItems : [],
+ pageItems: pageItems || [],
};
}
diff --git a/client/app/components/items-list/classes/ItemsSource.js b/client/app/components/items-list/classes/ItemsSource.js
index bc18ca567..e877507a7 100644
--- a/client/app/components/items-list/classes/ItemsSource.js
+++ b/client/app/components/items-list/classes/ItemsSource.js
@@ -41,18 +41,24 @@ export class ItemsSource {
extend(customParams, params);
},
};
- return this._beforeUpdate().then(() =>
- this._fetcher
+ return this._beforeUpdate().then(() => {
+ const fetchToken = Math.random()
+ .toString(36)
+ .substr(2);
+ this._currentFetchToken = fetchToken;
+ return this._fetcher
.fetch(changes, state, context)
.then(({ results, count, allResults }) => {
- this._pageItems = results;
- this._allItems = allResults || null;
- this._paginator.setTotalCount(count);
- this._params = { ...this._params, ...customParams };
- return this._afterUpdate();
+ if (this._currentFetchToken === fetchToken) {
+ this._pageItems = results;
+ this._allItems = allResults || null;
+ this._paginator.setTotalCount(count);
+ this._params = { ...this._params, ...customParams };
+ return this._afterUpdate();
+ }
})
- .catch(error => this.handleError(error))
- );
+ .catch(error => this.handleError(error));
+ });
}
constructor({ getRequest, doRequest, processResults, isPlainList = false, ...defaultState }) {
diff --git a/client/app/components/items-list/components/ItemsTable.jsx b/client/app/components/items-list/components/ItemsTable.jsx
index ac95a7d65..c5ade7d9e 100644
--- a/client/app/components/items-list/components/ItemsTable.jsx
+++ b/client/app/components/items-list/components/ItemsTable.jsx
@@ -1,8 +1,9 @@
-import { isFunction, map, filter, extend, omit, identity } from "lodash";
+import { isFunction, map, filter, extend, omit, identity, range, isEmpty } from "lodash";
import React from "react";
import PropTypes from "prop-types";
import classNames from "classnames";
import Table from "antd/lib/table";
+import Skeleton from "antd/lib/skeleton";
import FavoritesControl from "@/components/FavoritesControl";
import TimeAgo from "@/components/TimeAgo";
import { durationHumanize, formatDate, formatDateTime } from "@/lib/utils";
@@ -151,8 +152,10 @@ export default class ItemsTable extends React.Component {
}
render() {
- const columns = this.prepareColumns();
- const rows = map(this.props.items, (item, index) => ({ key: "row" + index, item }));
+ const tableDataProps = {
+ columns: this.prepareColumns(),
+ dataSource: map(this.props.items, (item, index) => ({ key: "row" + index, item })),
+ };
// Bind events only if `onRowClick` specified
const onTableRow = isFunction(this.props.onRowClick)
@@ -164,17 +167,27 @@ export default class ItemsTable extends React.Component {
: null;
const { showHeader } = this.props;
+ if (this.props.loading) {
+ if (isEmpty(tableDataProps.dataSource)) {
+ tableDataProps.columns = tableDataProps.columns.map(column => ({
+ ...column,
+ sorter: false,
+ render: () =>