[WebsiteList] add preloader, move to components
parent
2d591a7906
commit
6cb85959b5
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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]));
|
||||
};
|
||||
}
|
||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue