Skip to content

Commit d3428d4

Browse files
committed
Implement table loading
1 parent effa2ef commit d3428d4

File tree

8 files changed

+180
-92
lines changed

8 files changed

+180
-92
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"dependencies": {
1818
"@material-ui/core": "4.9.11",
1919
"@material-ui/icons": "4.9.1",
20+
"@material-ui/lab": "^4.0.0-alpha.51",
2021
"@rehooks/component-size": "^1.0.3",
2122
"@types/lodash": "^4.14.149",
2223
"@types/uuid": "^3.4.6",

src/Administration/Users/UsersList/UsersList.js

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,63 @@ import React, { useEffect, useState } from 'react'
44
import Grid from '@material-ui/core/Grid'
55
import {
66
makeStyles,
7+
Paper,
78
TableContainer,
89
Table,
910
TableCell,
1011
TableRow,
1112
TableHead,
12-
Paper,
13+
TableBody,
1314
TableFooter,
1415
} from '@material-ui/core'
1516

17+
import { Alert, AlertTitle } from '@material-ui/lab'
18+
1619
import api from '@/_api'
1720

18-
import BasePageContainer from '../../../_common/BasePageContainer'
19-
import BasePageToolbar from '../../../_common/BasePageToolbar'
20-
import { BaseTablePagination } from '../../../_common/BaseTable'
21+
import BasePageContainer from '@/_common/BasePageContainer'
22+
import BasePageToolbar from '@/_common/BasePageToolbar'
23+
import { BaseTablePagination } from '@/_common/BaseTable'
2124

22-
import UsersListTableBody from './UsersListTableBody'
25+
import UsersListTableItems from './UsersListTableItems'
2326

2427
const UsersList = ({ match }) => {
2528
const classes = useStyles()
2629

30+
const [status, setStatus] = React.useState('idle')
31+
const [statusMessage, setStatusMessage] = React.useState('')
2732
const [page, setPage] = React.useState(0)
2833
const [usersData, setUsersData] = useState({ users: [], count: 0 })
2934
const [rowsPerPage, setRowsPerPage] = React.useState(10)
3035

36+
// Request users
3137
useEffect(() => {
3238
async function fetchUsers() {
33-
const userDataRes = await api.users.getList({
34-
limit: rowsPerPage,
35-
offset: page * rowsPerPage,
36-
})
37-
38-
setUsersData(userDataRes)
39+
setStatus('loading')
40+
41+
try {
42+
const userDataRes = await api.users.getList({
43+
limit: rowsPerPage,
44+
offset: page * rowsPerPage,
45+
})
46+
47+
// Make some artificial delay
48+
await new Promise(resolve => {
49+
setTimeout(() => resolve(true), 1000)
50+
})
51+
52+
setStatus('idle')
53+
setUsersData(userDataRes)
54+
} catch (err) {
55+
console.log('error', err.message)
56+
57+
setStatus('error')
58+
setStatusMessage(err.message)
59+
}
3960
}
4061

4162
fetchUsers()
42-
}, [page, rowsPerPage])
63+
}, [page, rowsPerPage, usersData.count])
4364

4465
const handleChangePage = (event, newPage) => {
4566
setPage(newPage)
@@ -51,38 +72,54 @@ const UsersList = ({ match }) => {
5172
}
5273

5374
const { users, count } = usersData
75+
const rowsExpected = count ? Math.max(count - rowsPerPage * page, 0) : rowsPerPage
5476

5577
return (
5678
<BasePageContainer>
5779
<BasePageToolbar title={'Users Adminstration'}></BasePageToolbar>
5880
<Grid container spacing={3}>
5981
<Grid item xs={12}>
60-
<TableContainer component={Paper}>
61-
<Table className={classes.table} aria-label="custom pagination table">
62-
<TableHead>
63-
<TableRow>
64-
<TableCell>Avatar</TableCell>
65-
<TableCell>First Name</TableCell>
66-
<TableCell>Last Name</TableCell>
67-
<TableCell>Username</TableCell>
68-
<TableCell>Email</TableCell>
69-
<TableCell>Actions</TableCell>
70-
</TableRow>
71-
</TableHead>
72-
<UsersListTableBody users={users} count={count} rowsPerPage={rowsPerPage} />
73-
<TableFooter>
74-
<TableRow>
75-
<BaseTablePagination
76-
page={page}
82+
{status === 'error' && (
83+
<Alert severity="error">
84+
<AlertTitle>Error</AlertTitle>
85+
{statusMessage}
86+
</Alert>
87+
)}
88+
89+
{status !== 'error' && (
90+
<TableContainer component={Paper}>
91+
<Table className={classes.table} aria-label="custom pagination table">
92+
<TableHead>
93+
<TableRow>
94+
<TableCell>Avatar</TableCell>
95+
<TableCell>First Name</TableCell>
96+
<TableCell>Last Name</TableCell>
97+
<TableCell>Username</TableCell>
98+
<TableCell>Email</TableCell>
99+
<TableCell>Actions</TableCell>
100+
</TableRow>
101+
</TableHead>
102+
<TableBody>
103+
<UsersListTableItems
104+
users={status === 'loading' ? [] : users}
77105
rowsPerPage={rowsPerPage}
78-
count={count}
79-
onChangePage={handleChangePage}
80-
onChangeRowsPerPage={handleChangeRowsPerPage}
106+
rowsExpected={rowsExpected}
81107
/>
82-
</TableRow>
83-
</TableFooter>
84-
</Table>
85-
</TableContainer>
108+
</TableBody>
109+
<TableFooter>
110+
<TableRow>
111+
<BaseTablePagination
112+
page={page}
113+
rowsPerPage={rowsPerPage}
114+
count={count}
115+
onChangePage={handleChangePage}
116+
onChangeRowsPerPage={handleChangeRowsPerPage}
117+
/>
118+
</TableRow>
119+
</TableFooter>
120+
</Table>
121+
</TableContainer>
122+
)}
86123
</Grid>
87124
</Grid>
88125
</BasePageContainer>

src/Administration/Users/UsersList/UsersListTableBody.js

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
import { makeStyles, TableBody, TableCell, TableRow, Avatar } from '@material-ui/core'
5+
import { Skeleton } from '@material-ui/lab'
6+
7+
import { Edit as EditIcon, Delete as DeleteIcon } from '@material-ui/icons/'
8+
9+
const UsersListTableItems = ({ users, rowsPerPage = 10, rowsExpected = 10 }) => {
10+
// const classes = useStyles()
11+
12+
// Count how many empty rows needs to be filled
13+
const usersVisible = users.length || rowsExpected
14+
const usersArrayExpected = Array.from({ length: usersVisible }).map(
15+
(item, index) => index,
16+
)
17+
const emptyRows = rowsPerPage - usersVisible
18+
19+
return (
20+
<>
21+
{!users.length &&
22+
usersArrayExpected.map(item => (
23+
<TableRow key={item}>
24+
<TableCell>
25+
<Skeleton variant="circle" width={40} height={40} />
26+
</TableCell>
27+
<TableCell>
28+
<Skeleton variant="text" />
29+
</TableCell>
30+
<TableCell>
31+
<Skeleton variant="text" />
32+
</TableCell>
33+
<TableCell>
34+
<Skeleton variant="text" />
35+
</TableCell>
36+
<TableCell>
37+
<Skeleton variant="text" />
38+
</TableCell>
39+
<TableCell>
40+
<Skeleton variant="text" />
41+
</TableCell>
42+
</TableRow>
43+
))}
44+
{users.map(row => (
45+
<TableRow key={row.id}>
46+
<TableCell>
47+
<Avatar alt={row.firstName} src={row.avatarUrl} />
48+
</TableCell>
49+
<TableCell component="th" scope="row">
50+
{row.firstName}
51+
</TableCell>
52+
<TableCell>{row.lastName}</TableCell>
53+
<TableCell>{row.username}</TableCell>
54+
<TableCell>{row.email}</TableCell>
55+
<TableCell>
56+
<EditIcon />
57+
<DeleteIcon />
58+
</TableCell>
59+
</TableRow>
60+
))}
61+
{emptyRows > 0 && (
62+
<TableRow style={{ height: 53 * emptyRows }}>
63+
<TableCell colSpan={6} />
64+
</TableRow>
65+
)}
66+
</>
67+
)
68+
}
69+
70+
UsersListTableItems.propTypes = {}
71+
72+
const useStyles = makeStyles(theme => ({
73+
root: {
74+
flexShrink: 0,
75+
marginLeft: theme.spacing(2.5),
76+
},
77+
}))
78+
79+
export default UsersListTableItems

src/_api/_mocks/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import organizationsMocks from './organizationsMocks'
77
const init = (instance: AxiosInstance) => {
88
const mockAdapter = new MockAdapter(instance, { delayResponse: 200 })
99

10-
usersMocks.init(mockAdapter)
11-
organizationsMocks.init(mockAdapter)
10+
usersMocks.init(mockAdapter, instance)
11+
organizationsMocks.init(mockAdapter, instance)
1212

1313
return mockAdapter
1414
}

src/_api/_mocks/organizationsMocks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { AxiosInstance } from 'axios'
12
import MockAdapter from 'axios-mock-adapter'
23
import organizationsData from '../_data/organizationsData'
34

45
export default {
5-
init(mock: MockAdapter) {
6+
init(mock: MockAdapter, instance: AxiosInstance) {
67
mock.onGet('/organizations').reply(200, {
78
organizations: {
89
...organizationsData.list,

src/_api/_mocks/usersMocks.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
import { AxiosInstance } from 'axios'
12
import MockAdapter from 'axios-mock-adapter'
23
import usersData from '../_data/usersData'
34

45
export default {
5-
init(mock: MockAdapter) {
6+
init(mock: MockAdapter, instance: AxiosInstance) {
67
mock.onGet('/users/profile').reply(200, {
78
...usersData.current,
89
})
910

1011
mock.onGet('/users').reply(config => {
11-
const { limit = 10, offset = 0 } = config.params
12+
const { limit = 10, offset = 0, response } = config.params
13+
14+
if (response) {
15+
return [
16+
response.status || 403,
17+
{
18+
message: response.message || 'Something went wrong...',
19+
},
20+
]
21+
}
1222

1323
return [
1424
200,

yarn.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,17 @@
14201420
dependencies:
14211421
"@babel/runtime" "^7.4.4"
14221422

1423+
"@material-ui/lab@^4.0.0-alpha.51":
1424+
version "4.0.0-alpha.51"
1425+
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.51.tgz#c50c429a374a1d7e31591677137c000918985b89"
1426+
integrity sha512-X/qv/sZQGhXhKDn83L94gNahGDQj2Rd6r7/9tPpQbSn2A1LAt1+jlTiWD1HUgDXZEPqTsJMajOjWSEmTL7/q7w==
1427+
dependencies:
1428+
"@babel/runtime" "^7.4.4"
1429+
"@material-ui/utils" "^4.9.6"
1430+
clsx "^1.0.4"
1431+
prop-types "^15.7.2"
1432+
react-is "^16.8.0"
1433+
14231434
"@material-ui/react-transition-group@^4.2.0":
14241435
version "4.2.0"
14251436
resolved "https://registry.yarnpkg.com/@material-ui/react-transition-group/-/react-transition-group-4.2.0.tgz#afec833bbcc79f05a9b4d4828b3e07965cc7e321"

0 commit comments

Comments
 (0)