[WebsiteList] add preloader, move to components

pull/381/head
Sergey Nikiforov 2020-11-24 15:21:44 +03:00
parent 2d591a7906
commit 6cb85959b5
7 changed files with 210 additions and 95 deletions

View File

@ -0,0 +1,61 @@
import React from 'react';
export default function Pagination({
gotoPage,
canPreviousPage,
previousPage,
canNextPage,
nextPage,
pageCount,
pageIndex,
pageOptions,
pageSize,
setPageSize,
}) {
return (
<div>
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{'<<'}
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>{' '}
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<span>
| Go to page:{' '}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: '100px' }}
/>
</span>{' '}
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[25, 50, 100].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
);
}

View File

@ -0,0 +1,41 @@
import React from 'react';
export default function TableNew({
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
}) {
return (
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr key={headerGroup} {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th key={column} {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row);
return (
<tr key={row} {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td key={cell} {...cell.getCellProps()}>
{cell.render('Cell', { ...row.original })}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
}

View File

@ -1,7 +1,10 @@
import React, { useLayoutEffect, useMemo, useState } from 'react';
import React, { useLayoutEffect, useMemo, useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { FormattedMessage } from 'react-intl';
import Link from 'components/common/Link';
import DateFilter from 'components/common/DateFilter';
import TableNew from 'components/common/TableNew';
import Pagination from 'components/common/Pagination';
import Page from 'components/layout/Page';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import useFetch from 'hooks/useFetch';
@ -12,13 +15,18 @@ import { get } from 'lib/web';
import { TOKEN_HEADER } from 'lib/constants';
import { useRouter } from 'next/router';
import { useTable, usePagination } from 'react-table';
import { setWebsitesData } from 'redux/actions/websites';
import find from 'lodash.find';
import Loader from 'react-loader-spinner';
import styles from './WebsiteList.module.css';
export default function WebsiteList({ userId }) {
const [stats, setStats] = useState([]);
const dispatch = useDispatch();
const fetchedData = useFetch('/api/websites', { params: { user_id: userId } });
const { basePath } = useRouter();
const shareToken = useShareToken();
const websites = useSelector(state => state.websites);
const [dateRange, setDateRange] = useDateRange();
const { startDate, endDate, value } = dateRange;
@ -27,17 +35,6 @@ export default function WebsiteList({ userId }) {
return fetchedData.data.map(site => site.website_id);
}, [fetchedData.data]);
const tableData = useMemo(() => {
if (!fetchedData.data || !stats.length) return [];
const _data = [];
fetchedData.data.forEach(i => {
const stat = find(stats, { id: i.website_id }) || {};
_data.push({ ...i, ...stat.data });
});
return _data;
}, [fetchedData.data, stats.length]);
const getStats = async () => {
const websitesData = [];
for (let id of websitesIds) {
@ -64,6 +61,18 @@ export default function WebsiteList({ userId }) {
getStats();
}, [fetchedData.data, stats.length]);
useEffect(() => {
if (!fetchedData.data || !stats.length) return [];
const _data = [];
fetchedData.data.forEach(i => {
const stat = find(stats, { id: i.website_id }) || {};
_data.push({ ...i, ...stat.data });
});
dispatch(setWebsitesData(_data));
}, [fetchedData.data, stats.length]);
const tableColumns = useMemo(
() => [
{
@ -123,95 +132,43 @@ export default function WebsiteList({ userId }) {
} = useTable(
{
columns: tableColumns,
data: tableData,
initialState: { pageIndex: 0, pageSize: 10 },
data: websites,
initialState: { pageIndex: 0, pageSize: 50 },
},
usePagination,
);
return (
<Page>
<DateFilter value={value} startDate={startDate} endDate={endDate} onChange={setDateRange} />
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr key={headerGroup} {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th key={column} {...column.getHeaderProps()}>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map(row => {
prepareRow(row);
return (
<tr key={row} {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td key={cell} {...cell.getCellProps()}>
{cell.render('Cell', { ...row.original })}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{'<<'}
</button>{' '}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{'<'}
</button>{' '}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{'>'}
</button>{' '}
<button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
{'>>'}
</button>{' '}
<span>
Page{' '}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{' '}
</span>
<span>
| Go to page:{' '}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={e => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: '100px' }}
/>
</span>{' '}
<select
value={pageSize}
onChange={e => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
<Page className={styles.root}>
<div className={styles.range}>
<b className={styles.rangeText}>Range:</b>
<DateFilter value={value} startDate={startDate} endDate={endDate} onChange={setDateRange} />
</div>
{/* <Table columns={columns} rows={data} empty={empty} /> */}
{/* {data.map(({ website_id, name, domain }) => (
<div key={website_id} className={styles.website}>
<WebsiteChart websiteId={website_id} title={name} domain={domain} showLink />
<TableNew
getTableProps={getTableProps}
getTableBodyProps={getTableBodyProps}
headerGroups={headerGroups}
prepareRow={prepareRow}
page={page}
/>
{stats.length === 0 && (
<div className={styles.loader}>
<Loader type="ThreeDots" color="#ccc" height={80} width={80} />
</div>
))} */}
{fetchedData.data?.length === 0 && (
)}
<Pagination
gotoPage={gotoPage}
canPreviousPage={canPreviousPage}
previousPage={previousPage}
canNextPage={canNextPage}
nextPage={nextPage}
pageCount={pageCount}
pageIndex={pageIndex}
pageOptions={pageOptions}
pageSize={pageSize}
setPageSize={setPageSize}
/>
{fetchedData.status === 200 && fetchedData.data.length === 0 && stats.length === 0 && (
<EmptyPlaceholder
msg={
<FormattedMessage

View File

@ -9,3 +9,26 @@
border-bottom: 0;
margin-bottom: 0;
}
.root {
padding-top: 8px;
padding-bottom: 8px;
}
.range {
display: flex;
align-items: center;
flex-direction: row;
justify-content: flex-end;
}
.rangeText {
margin-right: 8px;
}
.loader {
display: flex;
flex: 1;
justify-content: center;
align-items: center;
}

View File

@ -81,6 +81,7 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-intl": "^5.8.8",
"react-loader-spinner": "^3.1.14",
"react-redux": "^7.2.2",
"react-simple-maps": "^2.3.0",
"react-spring": "^8.0.27",

View File

@ -2,7 +2,7 @@ import { createSlice } from '@reduxjs/toolkit';
const websites = createSlice({
name: 'websites',
initialState: {},
initialState: [],
reducers: {
updateWebsites(state, action) {
state = action.payload;
@ -27,3 +27,9 @@ export function setDateRange(websiteId, dateRange) {
);
};
}
export function setWebsitesData(data) {
return dispatch => {
return dispatch(updateWebsites([...data]));
};
}

View File

@ -2151,6 +2151,14 @@ babel-plugin-transform-react-remove-prop-types@0.4.24:
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a"
integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
bail@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776"
@ -2918,6 +2926,11 @@ core-js-compat@^3.6.2:
browserslist "^4.8.5"
semver "7.0.0"
core-js@^2.4.0:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -7486,6 +7499,14 @@ react-is@16.13.1, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-loader-spinner@^3.1.14:
version "3.1.14"
resolved "https://registry.yarnpkg.com/react-loader-spinner/-/react-loader-spinner-3.1.14.tgz#2a201f07379c492766175609903c0f2ced57c720"
integrity sha512-7V+upnW+RVA/O94LIB/EQLK2uaz/TpZBHG5uNXlOXgvxvALxlxVYeEDmus5Oex2C58fiwrsRvSyu/4VRmLbZ9Q==
dependencies:
babel-runtime "^6.26.0"
prop-types "^15.7.2"
react-redux@^7.2.2:
version "7.2.2"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.2.tgz#03862e803a30b6b9ef8582dadcc810947f74b736"
@ -7670,6 +7691,11 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.1.tgz#cad92ad8e6b591773485fbe05a485caf4f457e6f"
integrity sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"