Skip to content

Commit cf302b3

Browse files
joaorodrsJoão Paulo
and
João Paulo
authored
Standard Table (#23)
Co-authored-by: João Paulo <joao.paulo@inovan.do>
1 parent 35d6c27 commit cf302b3

File tree

7 files changed

+375
-217
lines changed

7 files changed

+375
-217
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import { ComponentMeta } from "@storybook/react";
3+
import DataTable, { Pagination } from "./DataTable";
4+
import { HStack, IconButton } from "@chakra-ui/react";
5+
import { MdArrowRightAlt, MdDelete } from "react-icons/md";
6+
import { format, parseISO } from "date-fns";
7+
8+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
9+
10+
const firstPage = {
11+
data: [
12+
{
13+
name: "John",
14+
email: "john@email.com",
15+
created_at: new Date().toISOString(),
16+
},
17+
{
18+
name: "Patrick",
19+
email: "patrick@email.com",
20+
created_at: new Date().toISOString(),
21+
},
22+
],
23+
pagination: {
24+
total: 4,
25+
perPage: 2,
26+
page: 1,
27+
lastPage: 2,
28+
},
29+
};
30+
31+
const secondPage = {
32+
data: [
33+
{
34+
name: "James",
35+
email: "james@email.com",
36+
created_at: new Date().toISOString(),
37+
},
38+
{
39+
name: "Neymar",
40+
email: "neymar@email.com",
41+
created_at: new Date().toISOString(),
42+
},
43+
],
44+
pagination: {
45+
total: 4,
46+
perPage: 2,
47+
page: 2,
48+
lastPage: 2,
49+
},
50+
};
51+
52+
const getColumns = () => [
53+
{
54+
Header: "Name",
55+
accessor: "name",
56+
},
57+
{
58+
Header: "Email",
59+
accessor: "email",
60+
},
61+
{
62+
Header: "Created at",
63+
accessor: (row: { created_at: string }) =>
64+
format(parseISO(row.created_at), "Pp"),
65+
id: "createdAt",
66+
},
67+
{
68+
Header: "Actions",
69+
Cell: () => (
70+
<HStack>
71+
<IconButton
72+
aria-label="Edit user"
73+
icon={<MdArrowRightAlt size={22} />}
74+
/>
75+
<IconButton aria-label="Delete user" icon={<MdDelete size={22} />} />
76+
</HStack>
77+
),
78+
},
79+
];
80+
81+
export const Primary = () => {
82+
const [loading, setLoading] = useState(false);
83+
const [data, setData] = useState<typeof firstPage["data"]>([]);
84+
const [pagination, setPagination] = useState<Pagination>({} as Pagination);
85+
const [page, setPage] = useState(1);
86+
87+
const loadData = useCallback(async () => {
88+
setLoading(true);
89+
const dataToUse: { [x: number]: typeof firstPage } = {
90+
1: firstPage,
91+
2: secondPage,
92+
};
93+
94+
await sleep(1000);
95+
setData(dataToUse[page].data);
96+
setPagination(dataToUse[page].pagination);
97+
setLoading(false);
98+
}, [page]);
99+
100+
useEffect(() => {
101+
loadData();
102+
}, [loadData]);
103+
104+
return (
105+
<DataTable
106+
data={data}
107+
columns={getColumns()}
108+
perPage={2}
109+
pagination={pagination}
110+
page={page}
111+
onChangePage={setPage}
112+
isLoading={loading}
113+
/>
114+
);
115+
};
116+
117+
const config = {
118+
title: "DataTable",
119+
component: DataTable,
120+
} as ComponentMeta<typeof DataTable>;
121+
122+
export default config;

src/components/DataTable/DataTable.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { useTable } from "react-table";
2424
import PerfectScrollbar from "react-perfect-scrollbar";
2525
import { Container } from "./styles";
2626

27-
type Pagination = {
27+
export type Pagination = {
2828
total: number;
2929
perPage: number;
3030
page: number;
@@ -85,6 +85,8 @@ function DataTable({
8585
background="gray.200"
8686
color="gray.600"
8787
fontSize="sm"
88+
borderRadius="var(--chakra-radii-md)"
89+
borderBottomRadius={0}
8890
>
8991
<HStack width="100%">
9092
<InputGroup>
@@ -186,6 +188,8 @@ function DataTable({
186188
background="gray.200"
187189
color="gray.600"
188190
fontSize="sm"
191+
borderRadius="var(--chakra-radii-md)"
192+
borderTopRadius={0}
189193
>
190194
<p>
191195
{page} - {totalPages} of {pagination.total}
@@ -200,8 +204,6 @@ function DataTable({
200204
aria-label="Go to previous page"
201205
icon={<ChevronLeftIcon />}
202206
disabled={page === 1}
203-
colorScheme="brand"
204-
variant="outline"
205207
/>
206208
<IconButton
207209
aria-label="Go to next page"
@@ -211,8 +213,6 @@ function DataTable({
211213
}}
212214
icon={<ChevronRightIcon />}
213215
disabled={page === pagination.lastPage}
214-
colorScheme="brand"
215-
variant="outline"
216216
/>
217217
</Stack>
218218
</Box>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import DataTable from "@/components/DataTable";
2+
3+
import { useMutation, useQuery, useQueryClient } from "react-query";
4+
import api from "@/services/api";
5+
import { Dispatch, SetStateAction, useCallback, useState } from "react";
6+
import { toast } from "react-toastify";
7+
import ConfirmDialog from "@/components/ConfirmDialog";
8+
9+
export type ColumnsProps = {
10+
currentCell: any;
11+
currentText: string;
12+
setCurrentCell: Dispatch<SetStateAction<null>>;
13+
setCurrentText: Dispatch<SetStateAction<string>>;
14+
onClickDelete: (id: string) => void;
15+
page: number;
16+
searchTerm: string;
17+
appliedFilters: any;
18+
};
19+
20+
type Props = {
21+
endpoint: string;
22+
columns: (args: ColumnsProps) => void;
23+
appliedFilters: any;
24+
onClickFilter: () => void;
25+
};
26+
27+
const StandardTable = ({
28+
endpoint,
29+
columns,
30+
appliedFilters,
31+
onClickFilter,
32+
}: Props) => {
33+
const perPage = 5;
34+
const [page, setPage] = useState(1);
35+
const [searchTerm, setSearchTerm] = useState("");
36+
const [currentCell, setCurrentCell] = useState(null);
37+
const [currentText, setCurrentText] = useState("");
38+
const [idToDelete, setIdToDelete] = useState<string | null>(null);
39+
const queryClient = useQueryClient();
40+
const { mutateAsync, isLoading: isLoadingDeletion } = useMutation(() =>
41+
api.delete(`/${endpoint}/${idToDelete}`)
42+
);
43+
44+
const { data, isLoading, error } = useQuery(
45+
[endpoint, page, searchTerm, appliedFilters],
46+
() =>
47+
api
48+
.get(endpoint, {
49+
params: {
50+
q: searchTerm,
51+
page,
52+
perPage,
53+
order: "created_at",
54+
...appliedFilters,
55+
},
56+
})
57+
.then((response) => response.data)
58+
);
59+
60+
const onSearchDebounced = useCallback((searchTerm: string) => {
61+
setSearchTerm(searchTerm);
62+
}, []);
63+
64+
const onConfirmDeletion = async () => {
65+
try {
66+
await mutateAsync();
67+
queryClient.invalidateQueries([endpoint, page, searchTerm]);
68+
setIdToDelete(null);
69+
toast.success("Item inativado com sucesso!");
70+
} catch (error: any) {
71+
toast.error(error.response?.data.message);
72+
}
73+
};
74+
75+
if (error) {
76+
return <div>Houve um erro: "{(error as { message: string }).message}"</div>;
77+
}
78+
79+
return (
80+
<>
81+
<ConfirmDialog
82+
isOpen={!!idToDelete}
83+
onConfirm={onConfirmDeletion}
84+
onClose={() => setIdToDelete(null)}
85+
isLoading={isLoadingDeletion}
86+
/>
87+
<DataTable
88+
columns={columns({
89+
currentCell,
90+
currentText,
91+
onClickDelete: (id) => {
92+
setIdToDelete(id);
93+
},
94+
setCurrentCell,
95+
setCurrentText,
96+
page,
97+
searchTerm,
98+
appliedFilters,
99+
})}
100+
data={data?.data}
101+
pagination={data?.pagination}
102+
page={page}
103+
onChangePage={setPage}
104+
perPage={perPage}
105+
isLoading={isLoading}
106+
onSearchDebounced={onSearchDebounced}
107+
inputPlaceholder="Procure por nome..."
108+
onClickFilter={onClickFilter}
109+
/>
110+
</>
111+
);
112+
};
113+
114+
export default StandardTable;

src/components/StandardTable/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./StandardTable";

0 commit comments

Comments
 (0)