trithuc-mvc-react 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,14 @@ import { usePermission } from "../../hooks";
14
14
  import { TableRowRender } from "./TableRowRender";
15
15
  import TableToolbar from "./TableToolbar";
16
16
  import { useDataTable } from "./hooks";
17
+ const defaultQueryOptions = {
18
+ keepPreviousData: true, // Giữ dữ liệu cũ trong khi tải dữ liệu mới
19
+ staleTime: 1000 * 60 * 1, // Thời gian dữ liệu có thể sử dụng lại trước khi gọi lại API
20
+ cacheTime: 1000 * 60 * 30, // Thời gian dữ liệu được lưu trong cache trước khi bị xóa
21
+ refetchOnWindowFocus: true, // Tự động lấy lại dữ liệu khi người dùng quay lại tab
22
+ refetchInterval: 1000 * 30, // Mỗi 30 giây
23
+ keepPreviousData: true
24
+ };
17
25
  const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit, disableDelete }) => {
18
26
  const {
19
27
  tableName,
@@ -44,6 +52,7 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
44
52
  pageSize: rowsPerPage,
45
53
  data: dataSearch
46
54
  }),
55
+ defaultQueryOptions,
47
56
  // keepPreviousData: true,
48
57
  onSuccess: ({ PermissionModel, status }) => {
49
58
  if (dataSearch?.TrangThaiXuLy !== undefined) {
@@ -52,6 +61,11 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
52
61
  if (status) {
53
62
  setPermission(PermissionModel);
54
63
  // console.log("LOAD LAI PermissionModel");
64
+ // Cuộn lên đầu trang khi tải dữ liệu thành công
65
+ window.scrollTo({
66
+ top: 0, // Vị trí pixel muốn cuộn tới
67
+ behavior: "smooth" // Tùy chọn cuộn mượt
68
+ });
55
69
  }
56
70
  }
57
71
  });
@@ -13,6 +13,14 @@ import { usePermission } from "../../hooks";
13
13
  import { TableRowRenderSM } from "./TableRowRenderSM";
14
14
  import TableToolbar from "./TableToolbar";
15
15
  import { useDataTable } from "./hooks";
16
+ const defaultQueryOptions = {
17
+ keepPreviousData: true, // Giữ dữ liệu cũ trong khi tải dữ liệu mới
18
+ staleTime: 1000 * 60 * 1, // Thời gian dữ liệu có thể sử dụng lại trước khi gọi lại API
19
+ cacheTime: 1000 * 60 * 30, // Thời gian dữ liệu được lưu trong cache trước khi bị xóa
20
+ refetchOnWindowFocus: true, // Tự động lấy lại dữ liệu khi người dùng quay lại tab
21
+ refetchInterval: 1000 * 30, // Mỗi 30 giây
22
+ keepPreviousData: true
23
+ };
16
24
  const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEdit, disableDelete }) => {
17
25
  const {
18
26
  tableName,
@@ -43,6 +51,7 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
43
51
  pageSize: rowsPerPage,
44
52
  data: dataSearch
45
53
  }),
54
+ defaultQueryOptions,
46
55
  // keepPreviousData: true,
47
56
  onSuccess: ({ PermissionModel, status }) => {
48
57
  if (dataSearch?.TrangThaiXuLy !== undefined) {
@@ -51,6 +60,11 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
51
60
  if (status) {
52
61
  setPermission(PermissionModel);
53
62
  // console.log("LOAD LAI PermissionModel");
63
+ // Cuộn lên đầu trang khi tải dữ liệu thành công
64
+ window.scrollTo({
65
+ top: 0, // Vị trí pixel muốn cuộn tới
66
+ behavior: "smooth" // Tùy chọn cuộn mượt
67
+ });
54
68
  }
55
69
  }
56
70
  });
@@ -246,7 +260,7 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
246
260
  multipleActions={multipleActions}
247
261
  />
248
262
 
249
- <Table className="border" size={downXL ? "small" : "medium"}>
263
+ <Table size={downXL ? "small" : "medium"}>
250
264
  {/* <TableHead
251
265
  headLabel={columns}
252
266
  onSelectAllClick={handleSelectAllClick}
@@ -27,23 +27,22 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
27
27
  onClick={() => {
28
28
  handleExportExcel(tableName, data);
29
29
  }}
30
+ sx={{ ml: 0.5, mr: 0.5 }}
30
31
  >
31
32
  Excel
32
33
  </Button>
33
34
  ) : (
34
- <Tooltip title="Excel">
35
- <IconButton
36
- variant="outlined" // Thêm variant ở đây
37
- color="primary"
38
- size={size}
39
- onClick={() => {
40
- handleExportExcel(tableName, data);
41
- }}
42
- sx={{ border: "1px solid", borderColor: "primary.main" }} // Điều chỉnh kiểu dáng nếu cần
43
- >
44
- <Download fontSize="inherit" />
45
- </IconButton>
46
- </Tooltip>
35
+ <Button
36
+ size={size}
37
+ variant="outlined"
38
+ startIcon={<Download />}
39
+ onClick={() => {
40
+ handleExportExcel(tableName, data);
41
+ }}
42
+ sx={{ ml: 0.5, mr: 0.5 }}
43
+ >
44
+ Excel
45
+ </Button>
47
46
  );
48
47
  };
49
48
  export default ExportExcelButton;
@@ -16,7 +16,7 @@ export const FilterGod = ({ tableName, filters, elementSize = "small", setPage =
16
16
  const { setDataSearch, dataSearch } = useDataTable();
17
17
  // Lấy trạng thái từ localStorage hoặc mặc định là true
18
18
  const [isFilterVisible, setFilterVisible] = useState(
19
- () => JSON.parse(localStorage.getItem(`${tableName}-isFilterVisible`)) ?? true
19
+ () => JSON.parse(localStorage.getItem(`${tableName}-isFilterVisible`)) ?? false
20
20
  );
21
21
 
22
22
  // Lưu trạng thái vào localStorage mỗi khi thay đổi
@@ -68,23 +68,35 @@ export const TableRowRender = ({
68
68
  ({
69
69
  field,
70
70
  type = "text",
71
- valueGetter = (row) => {
72
- return row[field];
73
- },
71
+ valueGetter = (row) => row[field], // Giá trị mặc định
74
72
  renderCell,
75
73
  valueFormat = (e) => e
76
74
  }) => {
77
- let align = type == "image" || type == "number" || type == "date" || type == "datetime" ? "right" : "left";
78
- if (type == "textCenter") align = "center";
75
+ // Xác định `align`
76
+ const alignMap = {
77
+ image: "right",
78
+ number: "right",
79
+ date: "right",
80
+ datetime: "right",
81
+ textCenter: "center",
82
+ textLeft: "left",
83
+ textRight: "right"
84
+ };
85
+ const align = alignMap[type] || "left"; // Mặc định là "left"
86
+
79
87
  return (
80
- <TableCell key={`${row[selectedField]}-${field}`} align={align}>
88
+ <TableCell
89
+ key={`${row[selectedField] || "unknown"}-${field}`} // Thêm fallback cho `key`
90
+ align={align}
91
+ >
81
92
  {renderCell ? renderCell(row) : valueFormat(valueGetter(row))}
82
93
  </TableCell>
83
94
  );
84
95
  }
85
96
  )}
97
+
86
98
  {!disableStatus && canEdit && (
87
- <TableCell>
99
+ <TableCell align="center">
88
100
  <Switch
89
101
  checked={row[statusKey]}
90
102
  onChange={() => {
@@ -47,8 +47,8 @@ export const TableRowRenderSM = ({
47
47
  const downXl = useMediaQuery(theme.breakpoints.down("xl"));
48
48
 
49
49
  return (
50
- <TableRow hover key={row[selectedField]} selected={selected}>
51
- <TableCell colSpan={columns.length} align="left">
50
+ <TableRow hover key={row[selectedField]} selected={selected} sx={{ width: "100%" }}>
51
+ <TableCell colSpan={columns.length} align="left" sx={{ width: "100%" }}>
52
52
  {columns.map(({ field, label, type = "text", valueGetter = (row) => row[field], renderCell, valueFormat = (e) => e }) => {
53
53
  const value = renderCell ? renderCell(row) : valueFormat(valueGetter(row));
54
54
  return (
@@ -67,117 +67,109 @@ export const TableRowRenderSM = ({
67
67
  inputProps={{ "aria-label": "controlled" }}
68
68
  />
69
69
  )}
70
- <Grid item xs={12} sm={12} md={12} lg={12} xl={12} sx={{ pt: 1 }}>
71
- {!disableCellThaoTac && (
72
- <Box display="flex" flexDirection="row" alignItems="center" justifyContent="flex-end">
73
- <Box
74
- sx={{
75
- maxWidth: "100%", // Giới hạn chiều rộng bằng với chiều rộng màn hình
76
- overflowX: "auto", // Cho phép cuộn ngang nếu hàng nút vượt quá chiều rộng
77
- whiteSpace: "nowrap" // Giữ các nút trên một dòng
78
- }}
79
- >
80
- <Box display="flex" gap={1} alignItems="center">
81
- {!disableEdit && canEdit && (
82
- <Tooltip title="Chỉnh sửa">
83
- <Button
84
- size="small"
85
- variant="outlined"
86
- color="primary"
87
- onClick={() => onEdit(row)}
88
- startIcon={<EditOutlined fontSize="inherit" />}
89
- sx={{
90
- borderRadius: 14,
91
- minWidth: 60,
92
- padding: "2px 8px",
93
- height: "20px",
94
- fontSize: "0.6rem",
95
- whiteSpace: "nowrap",
96
- overflow: "hidden",
97
- textOverflow: "ellipsis"
98
- }}
99
- >
100
- Sửa
101
- </Button>
102
- </Tooltip>
103
- )}
104
- {canView && !tableActions?.some(({ permissionType }) => permissionType === "view") && (
105
- <Tooltip title="Xem chi tiết">
106
- <Button
107
- size="small"
108
- variant="outlined"
109
- color="info"
110
- onClick={() => onView(row)}
111
- startIcon={<RemoveRedEyeOutlinedIcon />}
112
- sx={{
113
- borderRadius: 14,
114
- minWidth: 60,
115
- padding: "2px 8px",
116
- height: "20px",
117
- fontSize: "0.6rem",
118
- whiteSpace: "nowrap",
119
- overflow: "hidden",
120
- textOverflow: "ellipsis"
121
- }}
122
- >
123
- Xem
124
- </Button>
125
- </Tooltip>
126
- )}
127
- {!disableDelete && canDelete && (
128
- <Tooltip title="Xóa">
129
- <Button
130
- size="small"
131
- variant="outlined"
132
- color="error"
133
- onClick={() => {
134
- onDelete(row[selectedField]);
135
- }}
136
- startIcon={<DeleteOutlineIcon fontSize="inherit" />}
137
- sx={{
138
- borderRadius: 14,
139
- minWidth: 60,
140
- padding: "2px 8px",
141
- height: "20px",
142
- fontSize: "0.6rem",
143
- whiteSpace: "nowrap",
144
- overflow: "hidden",
145
- textOverflow: "ellipsis"
146
- }}
147
- >
148
- Xóa
149
- </Button>
150
- </Tooltip>
151
- )}
152
- {tableActionsOnTable.map(({ title, onClick, element }) => (
153
- <Tooltip key={title} title={title}>
154
- <Button
155
- size="small"
156
- variant="outlined"
157
- onClick={() => {
158
- onClick(row);
159
- }}
160
- startIcon={element}
161
- sx={{
162
- borderRadius: 14,
163
- minWidth: 60,
164
- padding: "2px 8px",
165
- height: "20px",
166
- fontSize: "0.6rem",
167
- whiteSpace: "nowrap",
168
- overflow: "hidden",
169
- textOverflow: "ellipsis"
170
- }}
171
- >
172
- {title.toLowerCase() === "xem chi tiết" ? "Xem" : title}
173
- </Button>
174
- </Tooltip>
175
- ))}
176
- <MoreMenu actions={tableActionsOnMoreMenu} data={row} />
177
- </Box>
178
- </Box>
179
- </Box>
180
- )}
70
+ <Grid container spacing={{ xs: 2, xl: 2 }} sx={{ width: "100%", pt: 2, pb: 2 }}>
71
+ <Grid item xs={12} sx={{ display: "flex", justifyContent: "flex-end", alignItems: "center", width: "100%" }}>
72
+ {!disableCellThaoTac && (
73
+ <>
74
+ {!disableEdit && canEdit && (
75
+ <Button
76
+ size="small"
77
+ variant="outlined"
78
+ color="primary"
79
+ onClick={() => onEdit(row)}
80
+ startIcon={<EditOutlined fontSize="inherit" />}
81
+ sx={{
82
+ marginLeft: 0.5,
83
+ marginRight: 0.5,
84
+ borderRadius: 14,
85
+ minWidth: 60,
86
+ padding: "2px 8px",
87
+ height: "20px",
88
+ fontSize: "0.6rem",
89
+ whiteSpace: "nowrap",
90
+ overflow: "hidden",
91
+ textOverflow: "ellipsis"
92
+ }}
93
+ >
94
+ Sửa
95
+ </Button>
96
+ )}
97
+ {canView && !tableActions?.some(({ permissionType }) => permissionType === "view") && (
98
+ <Button
99
+ size="small"
100
+ variant="outlined"
101
+ color="info"
102
+ onClick={() => onView(row)}
103
+ startIcon={<RemoveRedEyeOutlinedIcon />}
104
+ sx={{
105
+ marginLeft: 0.5,
106
+ marginRight: 0.5,
107
+ borderRadius: 14,
108
+ minWidth: 60,
109
+ padding: "2px 8px",
110
+ height: "20px",
111
+ fontSize: "0.6rem",
112
+ whiteSpace: "nowrap",
113
+ overflow: "hidden",
114
+ textOverflow: "ellipsis"
115
+ }}
116
+ >
117
+ Xem
118
+ </Button>
119
+ )}
120
+ {!disableDelete && canDelete && (
121
+ <Button
122
+ size="small"
123
+ variant="outlined"
124
+ color="error"
125
+ onClick={() => {
126
+ onDelete(row[selectedField]);
127
+ }}
128
+ startIcon={<DeleteOutlineIcon fontSize="inherit" />}
129
+ sx={{
130
+ marginLeft: 0.5,
131
+ marginRight: 0.5,
132
+ borderRadius: 14,
133
+ minWidth: 60,
134
+ padding: "2px 8px",
135
+ height: "20px",
136
+ fontSize: "0.6rem",
137
+ whiteSpace: "nowrap",
138
+ overflow: "hidden",
139
+ textOverflow: "ellipsis"
140
+ }}
141
+ >
142
+ Xóa
143
+ </Button>
144
+ )}
145
+ {tableActionsOnTable.map(({ title, onClick, element }) => (
146
+ <Button
147
+ size="small"
148
+ variant="outlined"
149
+ onClick={() => {
150
+ onClick(row);
151
+ }}
152
+ startIcon={element}
153
+ sx={{
154
+ marginLeft: 0.5,
155
+ marginRight: 0.5,
156
+ borderRadius: 14,
157
+ minWidth: 60,
158
+ padding: "2px 8px",
159
+ height: "20px",
160
+ fontSize: "0.6rem",
161
+ whiteSpace: "nowrap",
162
+ overflow: "hidden",
163
+ textOverflow: "ellipsis"
164
+ }}
165
+ >
166
+ {title.toLowerCase() === "xem chi tiết" ? "Xem" : title}
167
+ </Button>
168
+ ))}
169
+ <MoreMenu actions={tableActionsOnMoreMenu} data={row} />
170
+ </>
171
+ )}
172
+ </Grid>
181
173
  </Grid>
182
174
  </TableCell>
183
175
  </TableRow>
@@ -1,4 +1,4 @@
1
- import { Button, Card, IconButton, Stack, Tooltip, Typography, useMediaQuery, useTheme } from "@mui/material";
1
+ import { Button, Card, Grid, IconButton, Stack, Tooltip, Typography, useMediaQuery, useTheme } from "@mui/material";
2
2
 
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
 
@@ -189,57 +189,44 @@ function DataManagement({
189
189
  <DataTableContext.Provider value={values}>
190
190
  <FormProvider {...methods}>
191
191
  <Stack direction="row" justifyContent={"space-between"} sx={{ mb: upXL ? 2 : 1, ...slotProps?.header?.sx }}>
192
- <Typography variant="h4">{title}</Typography>
193
- <Stack direction="row" spacing={1}>
194
- <HuongDanButton tableName={tableName} size={elementSize} />
195
- <Tooltip title="Làm mới">
196
- <IconButton
197
- variant="outlined"
198
- color="primary"
199
- size={elementSize}
200
- onClick={() => {
201
- setDataSearch({ ...defaults });
202
- [...filters].forEach((filter) => {
203
- filter?.onChange?.();
204
- });
205
- reset();
206
- setValue("Search");
207
- }}
208
- >
209
- <Refresh fontSize="inherit" />
210
- </IconButton>
211
- </Tooltip>
212
-
213
- {titleButtons?.map((button, index) => (
214
- <div key={index}>
215
- {button}
216
- {/* Các phần tử khác có thể được thêm ở đây nếu cần */}
217
- </div>
218
- ))}
219
- <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
220
- {canCreate &&
221
- !disableAdd &&
222
- (!isSmallScreen ? (
223
- <Button
192
+ <Grid container spacing={{ xs: 1, xl: 1 }}>
193
+ <Grid item md={9} xs={12}>
194
+ <Typography variant="h4">{title}</Typography>
195
+ </Grid>
196
+ <Grid item md={3} xs={12} sx={{ display: "flex", justifyContent: "flex-end", alignItems: "center" }}>
197
+ <HuongDanButton tableName={tableName} size={elementSize} />
198
+ <Tooltip title="Làm mới">
199
+ <IconButton
200
+ variant="outlined"
201
+ color="primary"
224
202
  size={elementSize}
225
- variant="contained"
226
- startIcon={<Add fontSize="inherit" />}
227
- onClick={(e) => {
228
- if (!disableEditor) {
229
- setOpenEditorDialog(true);
230
- setSelectedEditItem(null);
231
- }
232
- onAddClick(e);
203
+ onClick={() => {
204
+ setDataSearch({ ...defaults });
205
+ [...filters].forEach((filter) => {
206
+ filter?.onChange?.();
207
+ });
208
+ reset();
209
+ setValue("Search");
233
210
  }}
234
211
  >
235
- Thêm
236
- </Button>
237
- ) : (
238
- <Tooltip title="Thêm">
239
- <IconButton
240
- variant="outlined" // Thêm variant ở đây
241
- color="primary"
212
+ <Refresh fontSize="inherit" />
213
+ </IconButton>
214
+ </Tooltip>
215
+
216
+ {titleButtons?.map((button, index) => (
217
+ <div key={index}>
218
+ {button}
219
+ {/* Các phần tử khác có thể được thêm ở đây nếu cần */}
220
+ </div>
221
+ ))}
222
+ <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
223
+ {canCreate &&
224
+ !disableAdd &&
225
+ (!isSmallScreen ? (
226
+ <Button
242
227
  size={elementSize}
228
+ variant="contained"
229
+ startIcon={<Add fontSize="inherit" />}
243
230
  onClick={(e) => {
244
231
  if (!disableEditor) {
245
232
  setOpenEditorDialog(true);
@@ -247,13 +234,29 @@ function DataManagement({
247
234
  }
248
235
  onAddClick(e);
249
236
  }}
250
- sx={{ border: "1px solid", borderColor: "primary.main" }} // Điều chỉnh kiểu dáng nếu cần
237
+ sx={{ ml: 0.5, mr: 0.5 }}
251
238
  >
252
- <Add fontSize="inherit" />
253
- </IconButton>
254
- </Tooltip>
255
- ))}
256
- </Stack>
239
+ Thêm
240
+ </Button>
241
+ ) : (
242
+ <Button
243
+ size={elementSize}
244
+ variant="contained"
245
+ startIcon={<Add fontSize="inherit" />}
246
+ onClick={(e) => {
247
+ if (!disableEditor) {
248
+ setOpenEditorDialog(true);
249
+ setSelectedEditItem(null);
250
+ }
251
+ onAddClick(e);
252
+ }}
253
+ sx={{ ml: 0.5, mr: 0.5 }}
254
+ >
255
+ Thêm
256
+ </Button>
257
+ ))}
258
+ </Grid>
259
+ </Grid>
257
260
  </Stack>
258
261
 
259
262
  <Card>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"