trithuc-mvc-react 2.7.3 → 2.7.4
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.
- package/api/index.js +2 -1
- package/components/DataManagement/DataTableSM.jsx +314 -0
- package/components/DataManagement/ExportExcelButton.jsx +21 -5
- package/components/DataManagement/TableRowRenderSM.jsx +149 -0
- package/components/DataManagement/index.jsx +55 -24
- package/package.json +1 -1
- package/utils/storage.js +120 -0
package/api/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { storeGetToken } from "@/utils/storage";
|
|
1
2
|
import axios from "axios";
|
|
2
3
|
|
|
3
4
|
let baseUrl = "";
|
|
@@ -30,7 +31,7 @@ export function setAuthToken(token) {
|
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
// Cài đặt token từ localStorage khi khởi tạo
|
|
33
|
-
const token =
|
|
34
|
+
const token = storeGetToken();
|
|
34
35
|
if (token) {
|
|
35
36
|
setAuthToken(token);
|
|
36
37
|
}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { Grid, Table, TableBody, TableCell, TableContainer, TableRow, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
2
|
+
import TablePaginationCustom from "../table/TablePagination";
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { TableHead } from "./TableHead";
|
|
6
|
+
import { useMutation, useQuery, useQueryClient } from "react-query";
|
|
7
|
+
import { changeStatusDataToTable, deleteDataFromTable, deleteMultipleDataFromTable, getDatasFromTable } from "../../api";
|
|
8
|
+
import TableRowsLoader from "../table/TableRowsLoader";
|
|
9
|
+
import { toast } from "react-toastify";
|
|
10
|
+
import { useConfirm } from "material-ui-confirm";
|
|
11
|
+
|
|
12
|
+
import { TableRowRender } from "./TableRowRender";
|
|
13
|
+
import TableToolbar from "./TableToolbar";
|
|
14
|
+
import { useDataTable } from "./hooks";
|
|
15
|
+
import { usePermission } from "../../hooks";
|
|
16
|
+
import { URL_APPLICATION, URL_APPLICATION_API } from "@/constants";
|
|
17
|
+
import { TableRowRenderSM } from "./TableRowRenderSM";
|
|
18
|
+
const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEdit, disableDelete }) => {
|
|
19
|
+
const {
|
|
20
|
+
tableName,
|
|
21
|
+
selectedField,
|
|
22
|
+
columns,
|
|
23
|
+
dataSearch,
|
|
24
|
+
setOpenEditorDialog,
|
|
25
|
+
setSelectedEditItem,
|
|
26
|
+
setOpenViewDialog,
|
|
27
|
+
onEditClick,
|
|
28
|
+
hasTabpanel,
|
|
29
|
+
defaultRowsPerPage = 5
|
|
30
|
+
} = useDataTable();
|
|
31
|
+
|
|
32
|
+
const { set: setPermission } = usePermission(tableName);
|
|
33
|
+
const queryClient = useQueryClient();
|
|
34
|
+
const confirm = useConfirm();
|
|
35
|
+
const [selectedItems, setSelectedItems] = useState([]);
|
|
36
|
+
// const [page, setPage] = useState(0);
|
|
37
|
+
const [rowsPerPage, setRowsPerPage] = useState(defaultRowsPerPage);
|
|
38
|
+
|
|
39
|
+
const { data, isLoading } = useQuery({
|
|
40
|
+
queryKey: [tableName, page, rowsPerPage, dataSearch],
|
|
41
|
+
queryFn: () =>
|
|
42
|
+
getDatasFromTable({
|
|
43
|
+
tableName: tableName,
|
|
44
|
+
page: page + 1,
|
|
45
|
+
pageSize: rowsPerPage,
|
|
46
|
+
data: dataSearch
|
|
47
|
+
}),
|
|
48
|
+
// keepPreviousData: true,
|
|
49
|
+
onSuccess: ({ PermissionModel, status }) => {
|
|
50
|
+
if (dataSearch?.TrangThaiXuLy !== undefined) {
|
|
51
|
+
PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
|
|
52
|
+
}
|
|
53
|
+
if (status) {
|
|
54
|
+
setPermission(PermissionModel);
|
|
55
|
+
// console.log("LOAD LAI PermissionModel");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const changeStatusMutation = useMutation(changeStatusDataToTable, {
|
|
60
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
61
|
+
if (URL_APPLICATION_API) {
|
|
62
|
+
toast.success(message);
|
|
63
|
+
} else {
|
|
64
|
+
toast.success("Thay đổi trạng thái thành công !");
|
|
65
|
+
}
|
|
66
|
+
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
67
|
+
},
|
|
68
|
+
onError: (error) => {
|
|
69
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
70
|
+
const errors = error.response.data.errors;
|
|
71
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
72
|
+
const errorMessages = Object.entries(errors)
|
|
73
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
74
|
+
.join("\n");
|
|
75
|
+
toast.error(errorMessages);
|
|
76
|
+
} else {
|
|
77
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
78
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
const deleteMutation = useMutation(deleteDataFromTable, {
|
|
83
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
84
|
+
if (status) {
|
|
85
|
+
if (URL_APPLICATION_API) {
|
|
86
|
+
toast.success(message);
|
|
87
|
+
} else {
|
|
88
|
+
toast.success("Xóa thành công !");
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
toast.error(" Có lỗi xảy ra !");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
95
|
+
},
|
|
96
|
+
onError: (error) => {
|
|
97
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
98
|
+
const errors = error.response.data.errors;
|
|
99
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
100
|
+
const errorMessages = Object.entries(errors)
|
|
101
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
102
|
+
.join("\n");
|
|
103
|
+
toast.error(errorMessages);
|
|
104
|
+
} else {
|
|
105
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
106
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
const deleteMultipleMutation = useMutation(deleteMultipleDataFromTable, {
|
|
111
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
112
|
+
if (URL_APPLICATION_API) {
|
|
113
|
+
toast.success(message);
|
|
114
|
+
} else {
|
|
115
|
+
toast.success("Xóa thành công !");
|
|
116
|
+
}
|
|
117
|
+
setSelectedItems([]);
|
|
118
|
+
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
119
|
+
},
|
|
120
|
+
onError: (error) => {
|
|
121
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
122
|
+
const errors = error.response.data.errors;
|
|
123
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
124
|
+
const errorMessages = Object.entries(errors)
|
|
125
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
126
|
+
.join("\n");
|
|
127
|
+
toast.error(errorMessages);
|
|
128
|
+
} else {
|
|
129
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
130
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const handleDelete = (id) => {
|
|
136
|
+
confirm({
|
|
137
|
+
description: "Bạn có chắc chắn muốn xóa bản ghi này không?",
|
|
138
|
+
title: "Xác nhận",
|
|
139
|
+
cancellationText: "Hủy",
|
|
140
|
+
confirmationText: "Xóa"
|
|
141
|
+
})
|
|
142
|
+
.then(() => {
|
|
143
|
+
deleteMutation.mutate({
|
|
144
|
+
id,
|
|
145
|
+
tableName
|
|
146
|
+
});
|
|
147
|
+
})
|
|
148
|
+
.catch(() => {});
|
|
149
|
+
};
|
|
150
|
+
const handleChangeStatus = (Id) => {
|
|
151
|
+
changeStatusMutation.mutate({
|
|
152
|
+
tableName,
|
|
153
|
+
id: Id
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
const handlEdit = (item) => {
|
|
157
|
+
setOpenEditorDialog(true);
|
|
158
|
+
setSelectedEditItem(item);
|
|
159
|
+
onEditClick(item);
|
|
160
|
+
};
|
|
161
|
+
const handlViewDetail = (item) => {
|
|
162
|
+
setOpenViewDialog(true);
|
|
163
|
+
setSelectedEditItem(item);
|
|
164
|
+
};
|
|
165
|
+
const { rows, total } = useMemo(() => {
|
|
166
|
+
let rows = data?.data ?? [];
|
|
167
|
+
let total = data?.total ?? 0;
|
|
168
|
+
return {
|
|
169
|
+
rows: rows,
|
|
170
|
+
total
|
|
171
|
+
};
|
|
172
|
+
}, [data]);
|
|
173
|
+
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
let PermissionModel = data?.PermissionModel;
|
|
176
|
+
PermissionModel && setPermission(PermissionModel);
|
|
177
|
+
}, [data]);
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const newSelectedItems = selectedItems.filter((selectedId) => {
|
|
180
|
+
return rows?.some((row) => row.Id == selectedId);
|
|
181
|
+
});
|
|
182
|
+
setSelectedItems(newSelectedItems);
|
|
183
|
+
}, [rows]);
|
|
184
|
+
const handleChangePage = (event, newPage) => {
|
|
185
|
+
setPage(newPage);
|
|
186
|
+
};
|
|
187
|
+
const isSelected = (Id) => selectedItems.indexOf(Id) !== -1;
|
|
188
|
+
|
|
189
|
+
const handleSelect = (event, Id) => {
|
|
190
|
+
const selectedIndex = selectedItems.indexOf(Id);
|
|
191
|
+
let newSelected = [];
|
|
192
|
+
|
|
193
|
+
if (selectedIndex === -1) {
|
|
194
|
+
newSelected = newSelected.concat(selectedItems, Id);
|
|
195
|
+
} else if (selectedIndex === 0) {
|
|
196
|
+
newSelected = newSelected.concat(selectedItems.slice(1));
|
|
197
|
+
} else if (selectedIndex === selectedItems.length - 1) {
|
|
198
|
+
newSelected = newSelected.concat(selectedItems.slice(0, -1));
|
|
199
|
+
} else if (selectedIndex > 0) {
|
|
200
|
+
newSelected = newSelected.concat(selectedItems.slice(0, selectedIndex), selectedItems.slice(selectedIndex + 1));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setSelectedItems(newSelected);
|
|
204
|
+
};
|
|
205
|
+
const handleSelectAllClick = (event) => {
|
|
206
|
+
if (event.target.checked) {
|
|
207
|
+
const newSelected = rows.map((n) => n[selectedField]);
|
|
208
|
+
setSelectedItems(newSelected);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
setSelectedItems([]);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const handleChangeRowsPerPage = (event) => {
|
|
215
|
+
const newRowsPerPage = parseInt(event.target.value, 10); // Chuyển đổi giá trị thành số nguyên
|
|
216
|
+
// Lưu giá trị mới vào localStorage với khóa tùy chỉnh theo tableName
|
|
217
|
+
localStorage.setItem(`${tableName}rowsPerPage`, newRowsPerPage);
|
|
218
|
+
setRowsPerPage(newRowsPerPage);
|
|
219
|
+
setPage(0);
|
|
220
|
+
};
|
|
221
|
+
const handleDeleteMultiple = () => {
|
|
222
|
+
confirm({
|
|
223
|
+
description: `Bạn có chắc chắn muốn xóa ${selectedItems?.length} bản ghi này không?`,
|
|
224
|
+
title: "Xác nhận",
|
|
225
|
+
cancellationText: "Hủy",
|
|
226
|
+
confirmationText: "Đồng ý"
|
|
227
|
+
})
|
|
228
|
+
.then(() => {
|
|
229
|
+
deleteMultipleMutation.mutate({
|
|
230
|
+
tableName,
|
|
231
|
+
ids: selectedItems
|
|
232
|
+
});
|
|
233
|
+
})
|
|
234
|
+
.catch(() => {});
|
|
235
|
+
};
|
|
236
|
+
const theme = useTheme();
|
|
237
|
+
const downXL = useMediaQuery(theme.breakpoints.down("xl"));
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<>
|
|
241
|
+
<TableContainer sx={{ position: "relative" }}>
|
|
242
|
+
<TableToolbar
|
|
243
|
+
onSelectAllClick={handleSelectAllClick}
|
|
244
|
+
numSelected={selectedItems?.length}
|
|
245
|
+
rowCount={rows.length}
|
|
246
|
+
onDeleteMultiple={handleDeleteMultiple}
|
|
247
|
+
selected={selectedItems}
|
|
248
|
+
multipleActions={multipleActions}
|
|
249
|
+
/>
|
|
250
|
+
|
|
251
|
+
<Table className="border" size={downXL ? "small" : "medium"}>
|
|
252
|
+
{/* <TableHead
|
|
253
|
+
headLabel={columns}
|
|
254
|
+
onSelectAllClick={handleSelectAllClick}
|
|
255
|
+
numSelected={selectedItems?.length}
|
|
256
|
+
rowCount={rows.length}
|
|
257
|
+
/> */}
|
|
258
|
+
{isLoading ? (
|
|
259
|
+
<TableRowsLoader rowsNum={5} colsNum={columns.length + 4} />
|
|
260
|
+
) : (
|
|
261
|
+
<TableBody>
|
|
262
|
+
{[...rows].map((row, index) => (
|
|
263
|
+
<TableRowRenderSM
|
|
264
|
+
key={row.Id}
|
|
265
|
+
index={index}
|
|
266
|
+
row={row}
|
|
267
|
+
page={page}
|
|
268
|
+
rowsPerPage={rowsPerPage}
|
|
269
|
+
selected={isSelected(row[selectedField])}
|
|
270
|
+
onSelect={handleSelect}
|
|
271
|
+
onEdit={handlEdit}
|
|
272
|
+
disableEdit={disableEdit}
|
|
273
|
+
disableDelete={disableDelete}
|
|
274
|
+
onView={handlViewDetail}
|
|
275
|
+
onChangeStatus={handleChangeStatus}
|
|
276
|
+
onDelete={handleDelete}
|
|
277
|
+
/>
|
|
278
|
+
))}
|
|
279
|
+
|
|
280
|
+
{rows?.length == 0 && (
|
|
281
|
+
<TableRow>
|
|
282
|
+
<TableCell colSpan={columns.length + 4} align="center">
|
|
283
|
+
Không có dữ liệu
|
|
284
|
+
</TableCell>
|
|
285
|
+
</TableRow>
|
|
286
|
+
)}
|
|
287
|
+
</TableBody>
|
|
288
|
+
)}
|
|
289
|
+
</Table>
|
|
290
|
+
</TableContainer>
|
|
291
|
+
|
|
292
|
+
<Grid container>
|
|
293
|
+
{!hasTabpanel && (
|
|
294
|
+
<Grid item alignSelf={"center"} sx={{ pl: 2 }}>
|
|
295
|
+
<Typography variant="body1">
|
|
296
|
+
Tổng số lượng: <span>{total}</span>
|
|
297
|
+
</Typography>
|
|
298
|
+
</Grid>
|
|
299
|
+
)}
|
|
300
|
+
|
|
301
|
+
<Grid item xs>
|
|
302
|
+
<TablePaginationCustom
|
|
303
|
+
count={total}
|
|
304
|
+
rowsPerPage={rowsPerPage}
|
|
305
|
+
page={page}
|
|
306
|
+
onPageChange={handleChangePage}
|
|
307
|
+
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
308
|
+
/>
|
|
309
|
+
</Grid>
|
|
310
|
+
</Grid>
|
|
311
|
+
</>
|
|
312
|
+
);
|
|
313
|
+
};
|
|
314
|
+
export default DataTableSM;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { toast } from "react-toastify";
|
|
2
2
|
import { exportExcel } from "../../api";
|
|
3
|
-
import { Button } from "@mui/material";
|
|
3
|
+
import { Button, IconButton, Tooltip, useMediaQuery, useTheme } from "@mui/material";
|
|
4
4
|
import { Download } from "@mui/icons-material";
|
|
5
|
-
import { URL_APPLICATION
|
|
5
|
+
import { URL_APPLICATION, URL_APPLICATION_API } from "@/constants";
|
|
6
6
|
|
|
7
7
|
const ExportExcelButton = ({ tableName, data, size = "small" }) => {
|
|
8
|
+
const theme = useTheme();
|
|
9
|
+
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); // Xác định màn hình nhỏ
|
|
8
10
|
const handleExportExcel = async (tableName, data) => {
|
|
9
11
|
const _data = await exportExcel({ tableName, data });
|
|
10
12
|
if (_data.status) {
|
|
11
|
-
if(URL_APPLICATION_API){
|
|
13
|
+
if (URL_APPLICATION_API) {
|
|
12
14
|
window.open(URL_APPLICATION + _data.url, "_blank").focus();
|
|
13
|
-
}else{
|
|
15
|
+
} else {
|
|
14
16
|
window.open(_data.url, "_blank").focus();
|
|
15
17
|
}
|
|
16
18
|
} else {
|
|
17
19
|
toast.error("Xuất file thất bại!");
|
|
18
20
|
}
|
|
19
21
|
};
|
|
20
|
-
return (
|
|
22
|
+
return !isSmallScreen ? (
|
|
21
23
|
<Button
|
|
22
24
|
size={size}
|
|
23
25
|
variant="outlined"
|
|
@@ -28,6 +30,20 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
|
|
|
28
30
|
>
|
|
29
31
|
Excel
|
|
30
32
|
</Button>
|
|
33
|
+
) : (
|
|
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>
|
|
31
47
|
);
|
|
32
48
|
};
|
|
33
49
|
export default ExportExcelButton;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Checkbox, IconButton, Switch, TableCell, TableRow, Tooltip, Toolbar, useMediaQuery, Box, Grid } from "@mui/material";
|
|
2
|
+
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
3
|
+
import { EditOutlined } from "@mui/icons-material";
|
|
4
|
+
import { useDataTable } from "./hooks";
|
|
5
|
+
import RemoveRedEyeOutlinedIcon from "@mui/icons-material/RemoveRedEyeOutlined";
|
|
6
|
+
import { usePermission } from "../../hooks";
|
|
7
|
+
import { styled, useTheme } from "@mui/material/styles";
|
|
8
|
+
import MoreMenu from "../MoreMenu";
|
|
9
|
+
import { useMemo } from "react";
|
|
10
|
+
|
|
11
|
+
export const TableRowRenderSM = ({
|
|
12
|
+
index,
|
|
13
|
+
row,
|
|
14
|
+
page,
|
|
15
|
+
rowsPerPage,
|
|
16
|
+
selected,
|
|
17
|
+
onSelect,
|
|
18
|
+
onChangeStatus,
|
|
19
|
+
onDelete,
|
|
20
|
+
onEdit,
|
|
21
|
+
onView,
|
|
22
|
+
disableEdit,
|
|
23
|
+
disableDelete
|
|
24
|
+
}) => {
|
|
25
|
+
const { selectedField, columns, statusKey, disableStatus, tableActions, disableCellThaoTac, tableName, sttLuyKe } =
|
|
26
|
+
useDataTable();
|
|
27
|
+
const { canEdit, canDelete, canView, canAction } = usePermission(tableName);
|
|
28
|
+
|
|
29
|
+
const { tableActionsOnTable, tableActionsOnMoreMenu } = useMemo(() => {
|
|
30
|
+
const tableActionsAfterFilter = [...tableActions]
|
|
31
|
+
.filter((x) => x)
|
|
32
|
+
.filter(({ permissionType }) => {
|
|
33
|
+
if (permissionType === "view" && !canView) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (permissionType === "action" && !canAction) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
});
|
|
41
|
+
const tableActionsOnTable = tableActionsAfterFilter.filter(({ isOnTable = false }) => isOnTable);
|
|
42
|
+
const tableActionsOnMoreMenu = tableActionsAfterFilter.filter(({ isOnTable = false }) => !isOnTable);
|
|
43
|
+
return { tableActionsOnTable, tableActionsOnMoreMenu };
|
|
44
|
+
}, [canView, canAction, tableActions]);
|
|
45
|
+
|
|
46
|
+
const theme = useTheme();
|
|
47
|
+
const downXl = useMediaQuery(theme.breakpoints.down("xl"));
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<TableRow hover key={row[selectedField]} selected={selected}>
|
|
51
|
+
{/* <TableCell padding="checkbox">
|
|
52
|
+
<Checkbox
|
|
53
|
+
checked={selected}
|
|
54
|
+
size={downXl ? "small" : "medium"}
|
|
55
|
+
onChange={(event) => onSelect(event, row[selectedField])}
|
|
56
|
+
/>
|
|
57
|
+
</TableCell>
|
|
58
|
+
{sttLuyKe ? (
|
|
59
|
+
page === 0 ? (
|
|
60
|
+
<TableCell align="center">{index + 1}</TableCell>
|
|
61
|
+
) : (
|
|
62
|
+
<TableCell align="center">{rowsPerPage * page + index + 1}</TableCell>
|
|
63
|
+
)
|
|
64
|
+
) : (
|
|
65
|
+
<TableCell align="center">{index + 1}</TableCell>
|
|
66
|
+
)} */}
|
|
67
|
+
|
|
68
|
+
{/* Hiển thị dữ liệu từ cột thành hàng */}
|
|
69
|
+
<TableCell colSpan={columns.length} align="left">
|
|
70
|
+
{columns.map(({ field, label, type = "text", valueGetter = (row) => row[field], renderCell, valueFormat = (e) => e }) => {
|
|
71
|
+
const value = renderCell ? renderCell(row) : valueFormat(valueGetter(row));
|
|
72
|
+
return (
|
|
73
|
+
<div key={`${row[selectedField]}-${field}`}>
|
|
74
|
+
<strong>{label}: </strong>
|
|
75
|
+
{value}
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
})}
|
|
79
|
+
{!disableStatus && canEdit && (
|
|
80
|
+
<Switch
|
|
81
|
+
checked={row[statusKey]}
|
|
82
|
+
onChange={() => {
|
|
83
|
+
onChangeStatus(row[selectedField]);
|
|
84
|
+
}}
|
|
85
|
+
inputProps={{ "aria-label": "controlled" }}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
<Grid item xs={12} sm={12} md={12} lg={12}>
|
|
89
|
+
{!disableCellThaoTac && (
|
|
90
|
+
<Box display="flex" flexDirection="row" alignItems="center" justifyContent="flex-end">
|
|
91
|
+
<Box display="flex" gap={1}>
|
|
92
|
+
{!disableEdit && canEdit && (
|
|
93
|
+
<Tooltip title="Chỉnh sửa">
|
|
94
|
+
<IconButton size={downXl ? "small" : "medium"} onClick={() => onEdit(row)}>
|
|
95
|
+
<EditOutlined fontSize="inherit" color="primary" />
|
|
96
|
+
</IconButton>
|
|
97
|
+
</Tooltip>
|
|
98
|
+
)}
|
|
99
|
+
{canView && !tableActions?.some(({ permissionType }) => permissionType === "view") && (
|
|
100
|
+
<Tooltip title="Xem chi tiết">
|
|
101
|
+
<IconButton onClick={() => onView(row)}>
|
|
102
|
+
<RemoveRedEyeOutlinedIcon color="info" />
|
|
103
|
+
</IconButton>
|
|
104
|
+
</Tooltip>
|
|
105
|
+
)}
|
|
106
|
+
{!disableDelete && canDelete && (
|
|
107
|
+
<Tooltip title="Xóa">
|
|
108
|
+
<IconButton
|
|
109
|
+
size={downXl ? "small" : "medium"}
|
|
110
|
+
onClick={() => {
|
|
111
|
+
onDelete(row[selectedField]);
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<DeleteOutlineIcon fontSize="inherit" color="error" />
|
|
115
|
+
</IconButton>
|
|
116
|
+
</Tooltip>
|
|
117
|
+
)}
|
|
118
|
+
</Box>
|
|
119
|
+
{/* Hiển thị các hành động khác trong MoreMenu */}
|
|
120
|
+
{tableActionsOnTable.map(({ title, onClick, element }) => (
|
|
121
|
+
<Tooltip key={title} title={title}>
|
|
122
|
+
<IconButton
|
|
123
|
+
size={downXl ? "small" : "medium"}
|
|
124
|
+
onClick={() => {
|
|
125
|
+
onClick(row);
|
|
126
|
+
}}
|
|
127
|
+
>
|
|
128
|
+
{element}
|
|
129
|
+
</IconButton>
|
|
130
|
+
</Tooltip>
|
|
131
|
+
))}
|
|
132
|
+
<MoreMenu actions={tableActionsOnMoreMenu} data={row} />
|
|
133
|
+
</Box>
|
|
134
|
+
)}
|
|
135
|
+
</Grid>
|
|
136
|
+
</TableCell>
|
|
137
|
+
</TableRow>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// ----------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
export const RootStyle = styled(Toolbar)(({ theme }) => ({
|
|
144
|
+
display: "flex",
|
|
145
|
+
justifyContent: "space-between",
|
|
146
|
+
padding: theme.spacing(0, 1, 0, 1),
|
|
147
|
+
height: 56,
|
|
148
|
+
minHeight: 50
|
|
149
|
+
}));
|
|
@@ -15,6 +15,7 @@ import ExportExcelButton from "./ExportExcelButton";
|
|
|
15
15
|
import { FilterGod } from "./FilterGod";
|
|
16
16
|
import { usePermission } from "../../hooks";
|
|
17
17
|
import HuongDanButton from "./HuongDanButton";
|
|
18
|
+
import DataTableSM from "./DataTableSM";
|
|
18
19
|
|
|
19
20
|
DataManagement.propTypes = {
|
|
20
21
|
columns: PropTypes.array,
|
|
@@ -168,7 +169,7 @@ function DataManagement({
|
|
|
168
169
|
backParentNavigator,
|
|
169
170
|
defaultRowsPerPage
|
|
170
171
|
]);
|
|
171
|
-
|
|
172
|
+
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); // Xác định màn hình nhỏ
|
|
172
173
|
const methods = useForm({ defaultValues: getDefaultValues(filters) });
|
|
173
174
|
const { reset, setValue } = methods;
|
|
174
175
|
return (
|
|
@@ -204,22 +205,42 @@ function DataManagement({
|
|
|
204
205
|
</div>
|
|
205
206
|
))}
|
|
206
207
|
<ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
|
|
207
|
-
{canCreate &&
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
208
|
+
{canCreate &&
|
|
209
|
+
!disableAdd &&
|
|
210
|
+
(!isSmallScreen ? (
|
|
211
|
+
<Button
|
|
212
|
+
size={elementSize}
|
|
213
|
+
variant="contained"
|
|
214
|
+
startIcon={<Add fontSize="inherit" />}
|
|
215
|
+
onClick={(e) => {
|
|
216
|
+
if (!disableEditor) {
|
|
217
|
+
setOpenEditorDialog(true);
|
|
218
|
+
setSelectedEditItem(null);
|
|
219
|
+
}
|
|
220
|
+
onAddClick(e);
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
Thêm
|
|
224
|
+
</Button>
|
|
225
|
+
) : (
|
|
226
|
+
<Tooltip title="Thêm">
|
|
227
|
+
<IconButton
|
|
228
|
+
variant="outlined" // Thêm variant ở đây
|
|
229
|
+
color="primary"
|
|
230
|
+
size={elementSize}
|
|
231
|
+
onClick={(e) => {
|
|
232
|
+
if (!disableEditor) {
|
|
233
|
+
setOpenEditorDialog(true);
|
|
234
|
+
setSelectedEditItem(null);
|
|
235
|
+
}
|
|
236
|
+
onAddClick(e);
|
|
237
|
+
}}
|
|
238
|
+
sx={{ border: "1px solid", borderColor: "primary.main" }} // Điều chỉnh kiểu dáng nếu cần
|
|
239
|
+
>
|
|
240
|
+
<Add fontSize="inherit" />
|
|
241
|
+
</IconButton>
|
|
242
|
+
</Tooltip>
|
|
243
|
+
))}
|
|
223
244
|
</Stack>
|
|
224
245
|
</Stack>
|
|
225
246
|
|
|
@@ -227,13 +248,23 @@ function DataManagement({
|
|
|
227
248
|
{tabPanel}
|
|
228
249
|
<FilterGod filters={filters} elementSize={elementSize} setPage={setPage} />
|
|
229
250
|
{backParentNavigator}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
251
|
+
{!isSmallScreen ? (
|
|
252
|
+
<DataTable
|
|
253
|
+
multipleActions={multipleActions}
|
|
254
|
+
page={page}
|
|
255
|
+
setPage={setPage}
|
|
256
|
+
disableEdit={disableEdit}
|
|
257
|
+
disableDelete={disableDelete}
|
|
258
|
+
/>
|
|
259
|
+
) : (
|
|
260
|
+
<DataTableSM
|
|
261
|
+
multipleActions={multipleActions}
|
|
262
|
+
page={page}
|
|
263
|
+
setPage={setPage}
|
|
264
|
+
disableEdit={disableEdit}
|
|
265
|
+
disableDelete={disableDelete}
|
|
266
|
+
/>
|
|
267
|
+
)}
|
|
237
268
|
</Card>
|
|
238
269
|
</FormProvider>
|
|
239
270
|
|
package/package.json
CHANGED
package/utils/storage.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import CryptoJS from "crypto-js";
|
|
2
|
+
|
|
3
|
+
// Khóa bí mật để mã hóa và giải mã MD5 https://zozo.vn/ws/tools/md5encode
|
|
4
|
+
const secretKey = "b0dc70c6a34c0555ac9c63907617f5a5"; // Bạn nên lưu trữ key này an toàn
|
|
5
|
+
|
|
6
|
+
// Hàm mã hóa dữ liệu
|
|
7
|
+
const encryptData = (data) => {
|
|
8
|
+
try {
|
|
9
|
+
return CryptoJS.AES.encrypt(JSON.stringify(data), secretKey).toString();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
console.log("Error encrypting data:", error);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Hàm giải mã dữ liệu
|
|
16
|
+
const decryptData = (ciphertext) => {
|
|
17
|
+
try {
|
|
18
|
+
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
|
|
19
|
+
const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
|
|
20
|
+
if (!decryptedData) {
|
|
21
|
+
throw new Error("Decrypted data is null or invalid.");
|
|
22
|
+
}
|
|
23
|
+
return JSON.parse(decryptedData);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
console.log("Error decrypting data:", error);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const storeUserData = (data) => {
|
|
31
|
+
if (!data) return;
|
|
32
|
+
|
|
33
|
+
// Mã hóa và lưu token
|
|
34
|
+
const encryptedToken = encryptData(data.Token);
|
|
35
|
+
localStorage.setItem("token", encryptedToken);
|
|
36
|
+
|
|
37
|
+
// Tạo object user từ dữ liệu response
|
|
38
|
+
const userObj = {
|
|
39
|
+
userName: data?.UserName,
|
|
40
|
+
email: data?.Email,
|
|
41
|
+
FullName: data?.FullName,
|
|
42
|
+
Avatar: data?.Avatar,
|
|
43
|
+
Navigate: data?.Navigate
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Mã hóa và lưu user object vào localStorage
|
|
47
|
+
const encryptedUserObj = encryptData(userObj);
|
|
48
|
+
localStorage.setItem("user", encryptedUserObj);
|
|
49
|
+
};
|
|
50
|
+
export const storeSetCachedSidebarTops = (data) => {
|
|
51
|
+
if (!data) return;
|
|
52
|
+
// Mã hóa và lưu
|
|
53
|
+
const encryptedData = encryptData(data);
|
|
54
|
+
localStorage.setItem("cachedSidebarTops",encryptedData);
|
|
55
|
+
};
|
|
56
|
+
export const storeGetCachedSidebarTops = () => {
|
|
57
|
+
const encryptedData = localStorage.getItem("cachedSidebarTops");
|
|
58
|
+
if (!encryptedData) return null;
|
|
59
|
+
// console.log("encryptedData",encryptedData);
|
|
60
|
+
// Giải mã dữ liệu khi lấy ra từ localStorage
|
|
61
|
+
return decryptData(encryptedData);
|
|
62
|
+
};
|
|
63
|
+
export const storeSetCurrentInfor = (data) => {
|
|
64
|
+
if (!data) return;
|
|
65
|
+
// Mã hóa và lưu
|
|
66
|
+
const encryptedData = encryptData(data);
|
|
67
|
+
localStorage.setItem("GetCurrentInfor",encryptedData);
|
|
68
|
+
};
|
|
69
|
+
export const storeGetUser = () => {
|
|
70
|
+
const encryptedData = localStorage.getItem("user");
|
|
71
|
+
if (!encryptedData) return null;
|
|
72
|
+
|
|
73
|
+
// Giải mã dữ liệu khi lấy ra từ localStorage
|
|
74
|
+
const decryptedData = decryptData(encryptedData);
|
|
75
|
+
// Kiểm tra nếu dữ liệu giải mã không hợp lệ
|
|
76
|
+
// if (!decryptedData || typeof decryptedData !== 'string') {
|
|
77
|
+
// console.log("Decrypted user data is not a valid string.");
|
|
78
|
+
// return null;
|
|
79
|
+
// }
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
// Chuyển đổi thành JSON nếu dữ liệu giải mã hợp lệ
|
|
83
|
+
return decryptedData;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.log("Error parsing decrypted user data:", error);
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const storeGetToken = () => {
|
|
91
|
+
const encryptedData = localStorage.getItem("token");
|
|
92
|
+
if (!encryptedData) return null;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const decryptedToken = decryptData(encryptedData);
|
|
96
|
+
if (!decryptedToken || typeof decryptedToken !== 'string') {
|
|
97
|
+
throw new Error("Decrypted token is null or not a valid string.");
|
|
98
|
+
}
|
|
99
|
+
return decryptedToken; // Không cần JSON.parse nếu đã là chuỗi hợp lệ
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.log("Error decrypting token:", error);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const storeGetCurrentInfor = () => {
|
|
107
|
+
const encryptedData = localStorage.getItem("GetCurrentInfor");
|
|
108
|
+
if (!encryptedData) return null;
|
|
109
|
+
|
|
110
|
+
// Giải mã dữ liệu khi lấy ra từ localStorage
|
|
111
|
+
return decryptData(encryptedData);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const storeGetThamSoHeThong = (TenThamSo) => {
|
|
115
|
+
const encryptedData = localStorage.getItem("GetCurrentInfor");
|
|
116
|
+
if (!encryptedData) return "";
|
|
117
|
+
|
|
118
|
+
const getCurrentInfor =decryptData(encryptedData) || {};
|
|
119
|
+
return getCurrentInfor?.ThamSoHeThong?.find((item) => item.TenThamSo === TenThamSo)?.GiaTri || "";
|
|
120
|
+
};
|