Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 2da5627e

Přidáno uživatelem Václav Honzík před více než 2 roky(ů)

re #9130 Catalog table component template mui doc

Zobrazit rozdíly:

frontend/package.json
6 6
  "dependencies": {
7 7
    "@emotion/react": "^11.8.2",
8 8
    "@emotion/styled": "^11.8.1",
9
    "@mui/icons-material": "^5.5.1",
9 10
    "@mui/material": "^5.5.2",
10 11
    "@testing-library/jest-dom": "^5.14.1",
11 12
    "@testing-library/react": "^12.0.0",
frontend/src/api/axios.ts
1
import axios from 'axios'
2
import Config from '../config/Config'
3

  
4
export default axios.create({
5
    baseURL: Config.baseUrl,
6

  
7
})
8

  
frontend/src/config/Config.ts
1
const Config = {
2
    baseUrl: process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080'
3
}
4

  
5
export default Config
frontend/src/features/Auth/AuthService.ts
1
import axios from "../../api/axios"
2
import { UserDto } from "../../swagger/data-contracts"
3
import ApiCallError from "../../utils/ApiCallError"
4

  
5
export const getAccessToken = () => localStorage.getItem('accessToken') as string | null
6
export const getRefreshToken = () => localStorage.getItem('refreshToken') as string | null
7

  
8
export const setAccessToken = (accessToken: string) => localStorage.setItem('accessToken', accessToken)
9
export const setRefreshToken = (refreshToken: string) => localStorage.setItem('refreshToken', refreshToken)
10

  
11

  
12
export const sendRegisterRequest = (userDto: UserDto) => {
13
    
14
}
15

  
16
export const sendLoginRequest = async (username: string, password: string, setLoggedInState: (loggedIn: boolean) => void) => {
17
    try {
18
        const { data } = await axios.post('/login', {username, password})
19

  
20
        if (!data) {
21
            throw new ApiCallError("An authentication error has occurred. Please try again later")
22
        }
23

  
24
        // TODO - set state as logged in
25
        const { accessToken, refreshToken } = data
26
        setAccessToken(accessToken)
27
        setRefreshToken(refreshToken)
28
        setLoggedInState(true)
29
    }
30
    catch (err) {
31

  
32
    }
33
}
frontend/src/features/Auth/Register.tsx
1

  
2
export default {}
frontend/src/features/Catalog/Catalog.tsx
1
import {
2
    Box,
3
    Button,
4
    Container,
5
    Grid,
6
    Paper,
7
    Stack,
8
    TextField,
9
} from '@mui/material'
1 10
import { useEffect, useState } from 'react'
2 11
import { CatalogDto } from '../../swagger/data-contracts'
12
import CatalogTable from './CatalogTable'
3 13

  
4 14
const Catalog = () => {
5
    // List of all catalog items
6
    const [catalogItems, setCatalogItems] = useState<CatalogDto[]>([])
7

  
8
    // Whether the request has been processed
9
    const [isLoading, setIsLoading] = useState(true)
10

  
11
    useEffect(() => {
12
        
13
    }, [isLoading])
15
    const createRow = (): CatalogDto => ({
16
        name: 'Cell',
17
        certainty: 100,
18
        longitude: 1,
19
        latitude: 2,
20
        writtenForms: ['Written Form'],
21
        bibliography: ['Bibliography'],
22
        countries: ['Country'],
23
        alternativeNames: ['Alternative Name'],
24
    })
25
    const data: CatalogDto[] = Array(25).fill(createRow())
14 26

  
15 27
    return (
16 28
        <>
17
            <h1>Catalog</h1>
29
            <Paper sx={{ py: 2 }}>
30
                <Container sx={{ mt: 4 }}>
31
                    <Button variant="contained">Filter</Button>
32
                    <Grid container spacing={1} alignItems="stretch">
33
                        <Grid item xs={6}>
34
                            <Stack
35
                                direction="column"
36
                                spacing={1}
37
                                sx={{ mt: 2 }}
38
                            >
39
                                <Stack direction="row" spacing={2}>
40
                                    <TextField id="name" label="Name" />
41
                                    <TextField id="type" label="Type" />
42
                                    <TextField
43
                                        id="coordinates"
44
                                        label="Coordinates"
45
                                    />
46
                                </Stack>
47
                                <Stack direction="row" spacing={2}>
48
                                    <TextField
49
                                        id="writtenForm"
50
                                        label="Written form"
51
                                    />
52
                                    <TextField
53
                                        id="stateOrTerritory"
54
                                        label="State or territory"
55
                                    />
56
                                    <TextField id="groupBy" label="Group by" />
57
                                </Stack>
58
                            </Stack>
59
                        </Grid>
60
                        <Grid item xs sx={{ mt: 'auto', ml: 1, mb: 1 }}>
61
                            <Stack
62
                                direction="row"
63
                                justifyContent="flex-start"
64
                                alignItems="flex-end"
65
                            >
66
                                <Button variant="outlined">Search</Button>
67
                            </Stack>
68
                        </Grid>
69
                    </Grid>
70

  
71
                    <Box sx={{ mt: 4 }}>
72
                        <CatalogTable data={data} />
73
                    </Box>
74
                </Container>
75
            </Paper>
18 76
        </>
19 77
    )
20 78
}
frontend/src/features/Catalog/CatalogTable.tsx
1
import { useTheme } from '@mui/material/styles'
2
import {
3
    Box,
4
    Paper,
5
    Table,
6
    TableBody,
7
    TableCell,
8
    TableContainer,
9
    TableFooter,
10
    TablePagination,
11
    TableRow,
12
} from '@mui/material'
13
import TablePaginationActions, { TablePaginationActionsProps } from '@mui/material/TablePagination/TablePaginationActions'
14
import { FunctionComponent, useState } from 'react'
15
import IconButton from '@mui/material/IconButton'
16
import FirstPageIcon from '@mui/icons-material/FirstPage'
17
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'
18
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'
19
import LastPageIcon from '@mui/icons-material/LastPage'
20
import { CatalogDto } from '../../swagger/data-contracts'
21

  
22

  
23
const CatalogTableActions: FunctionComponent<TablePaginationActionsProps> = ({
24
    count,
25
    page,
26
    rowsPerPage,
27
    onPageChange: onChangePage,
28
}) => {
29
    const theme = useTheme()
30

  
31
    const handleFirstPageButtonClick = (
32
        event: React.MouseEvent<HTMLButtonElement>
33
    ) => {
34
        onChangePage(event, 0)
35
    }
36

  
37
    const handleBackButtonClick = (
38
        event: React.MouseEvent<HTMLButtonElement>
39
    ) => {
40
        onChangePage(event, page - 1)
41
    }
42

  
43
    const handleNextButtonClick = (
44
        event: React.MouseEvent<HTMLButtonElement>
45
    ) => {
46
        onChangePage(event, page + 1)
47
    }
48

  
49
    const handleLastPageButtonClick = (
50
        event: React.MouseEvent<HTMLButtonElement>
51
    ) => {
52
        onChangePage(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1))
53
    }
54

  
55
    return (
56
        <Box sx={{ flexShrink: 0, ml: 2.5 }}>
57
            <IconButton
58
                onClick={handleFirstPageButtonClick}
59
                disabled={page === 0}
60
                aria-label="first page"
61
            >
62
                {theme.direction === 'rtl' ? (
63
                    <LastPageIcon />
64
                ) : (
65
                    <FirstPageIcon />
66
                )}
67
            </IconButton>
68
            <IconButton
69
                onClick={handleBackButtonClick}
70
                disabled={page === 0}
71
                aria-label="previous page"
72
            >
73
                {theme.direction === 'rtl' ? (
74
                    <KeyboardArrowRight />
75
                ) : (
76
                    <KeyboardArrowLeft />
77
                )}
78
            </IconButton>
79
            <IconButton
80
                onClick={handleNextButtonClick}
81
                disabled={page >= Math.ceil(count / rowsPerPage) - 1}
82
                aria-label="next page"
83
            >
84
                {theme.direction === 'rtl' ? (
85
                    <KeyboardArrowLeft />
86
                ) : (
87
                    <KeyboardArrowRight />
88
                )}
89
            </IconButton>
90
            <IconButton
91
                onClick={handleLastPageButtonClick}
92
                disabled={page >= Math.ceil(count / rowsPerPage) - 1}
93
                aria-label="last page"
94
            >
95
                {theme.direction === 'rtl' ? (
96
                    <FirstPageIcon />
97
                ) : (
98
                    <LastPageIcon />
99
                )}
100
            </IconButton>
101
        </Box>
102
    )
103
}
104

  
105
function createData(name: string, calories: number, fat: number) {
106
    return { name, calories, fat }
107
}
108

  
109
const rows = [
110
    createData('Cupcake', 305, 3.7),
111
    createData('Donut', 452, 25.0),
112
    createData('Eclair', 262, 16.0),
113
    createData('Frozen yoghurt', 159, 6.0),
114
    createData('Gingerbread', 356, 16.0),
115
    createData('Honeycomb', 408, 3.2),
116
    createData('Ice cream sandwich', 237, 9.0),
117
    createData('Jelly Bean', 375, 0.0),
118
    createData('KitKat', 518, 26.0),
119
    createData('Lollipop', 392, 0.2),
120
    createData('Marshmallow', 318, 0),
121
    createData('Nougat', 360, 19.0),
122
    createData('Oreo', 437, 18.0),
123
].sort((a, b) => (a.calories < b.calories ? -1 : 1))
124

  
125

  
126
export interface CatalogTableProps {
127
    data: CatalogDto[]
128
}
129

  
130
const CatalogTable: FunctionComponent<CatalogTableProps> = ({ data }) => {
131
    const [page, setPage] = useState(0)
132
    const [rowsPerPage, setRowsPerPage] = useState(5)
133

  
134
    // Avoid a layout jump when reaching the last page with empty rows.
135
    const emptyRows =
136
        page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0
137

  
138
    const handleChangePage = (
139
        event: React.MouseEvent<HTMLButtonElement> | null,
140
        newPage: number
141
    ) => {
142
        setPage(newPage)
143
    }
144

  
145
    const handleChangeRowsPerPage = (
146
        event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
147
    ) => {
148
        setRowsPerPage(parseInt(event.target.value, 10))
149
        setPage(0)
150
    }
151

  
152
    return (
153
        <TableContainer component={Paper}>
154
            <Table sx={{ minWidth: 500 }} aria-label="custom pagination table">
155
                <TableBody>
156
                    {(rowsPerPage > 0
157
                        ? data.slice(
158
                              page * rowsPerPage,
159
                              page * rowsPerPage + rowsPerPage
160
                          )
161
                        : data
162
                    ).map((row) => (
163
                        <TableRow key={row.name}>
164
                            <TableCell component="th" scope="row">
165
                                {row.name}
166
                            </TableCell>
167
                            <TableCell style={{ width: 160 }} align="right">
168
                                {row?.alternativeNames ? row.alternativeNames.join(', ') : ''}
169
                            </TableCell>
170
                            <TableCell style={{ width: 160 }} align="right">
171
                                {row?.writtenForms ? row.writtenForms.join(', ') : ''}
172
                            </TableCell>
173
                            <TableCell style={{ width: 160 }} align="right">
174
                                {row?.writtenForms ? row.writtenForms.join(', ') : ''}
175
                            </TableCell>
176
                        </TableRow>
177
                    ))}
178
                    {emptyRows > 0 && (
179
                        <TableRow style={{ height: 53 * emptyRows }}>
180
                            <TableCell colSpan={6} />
181
                        </TableRow>
182
                    )}
183
                </TableBody>
184
                <TableFooter>
185
                    <TableRow>
186
                        <TablePagination
187
                            rowsPerPageOptions={[
188
                                5,
189
                                10,
190
                                25,
191
                                { label: 'All', value: -1 },
192
                            ]}
193
                            colSpan={3}
194
                            count={rows.length}
195
                            rowsPerPage={rowsPerPage}
196
                            page={page}
197
                            SelectProps={{
198
                                inputProps: {
199
                                    'aria-label': 'rows per page',
200
                                },
201
                                native: true,
202
                            }}
203
                            onPageChange={handleChangePage}
204
                            onRowsPerPageChange={handleChangeRowsPerPage}
205
                            ActionsComponent={CatalogTableActions}
206
                        />
207
                    </TableRow>
208
                </TableFooter>
209
            </Table>
210
        </TableContainer>
211
    )
212
}
213

  
214
export default CatalogTable

Také k dispozici: Unified diff