trithuc-mvc-react 2.7.3 → 2.7.5

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 CHANGED
@@ -1,3 +1,4 @@
1
+ import { storeGetlanguage, storeGetToken } from "@/utils/storage";
1
2
  import axios from "axios";
2
3
 
3
4
  let baseUrl = "";
@@ -30,10 +31,25 @@ export function setAuthToken(token) {
30
31
  }
31
32
 
32
33
  // Cài đặt token từ localStorage khi khởi tạo
33
- const token = localStorage.getItem("token");
34
+ const token = storeGetToken();
34
35
  if (token) {
35
36
  setAuthToken(token);
36
37
  }
38
+ // Sử dụng interceptor để thêm ngôn ngữ vào headers
39
+ api.interceptors.request.use((config) => {
40
+ const token = storeGetToken();
41
+ const language = storeGetlanguage(); // Lấy ngôn ngữ từ local storage
42
+
43
+ if (token) {
44
+ config.headers["Authorization"] = `Bearer ${token}`; // Gán token vào headers
45
+ }
46
+
47
+ if (language) {
48
+ config.headers["Accept-Language"] = language; // Gán ngôn ngữ vào headers
49
+ }
50
+
51
+ return config;
52
+ });
37
53
  export default api;
38
54
 
39
55
  export const getDatasFromTable = async ({ tableName, page, pageSize, data }) => {
@@ -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 ,URL_APPLICATION_API } from "@/constants";
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 && !disableAdd && (
208
- <Button
209
- size={elementSize}
210
- variant="contained"
211
- startIcon={<Add fontSize="inherit" />}
212
- onClick={(e) => {
213
- if (!disableEditor) {
214
- setOpenEditorDialog(true);
215
- setSelectedEditItem(null);
216
- }
217
- onAddClick(e);
218
- }}
219
- >
220
- Thêm
221
- </Button>
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
- <DataTable
231
- multipleActions={multipleActions}
232
- page={page}
233
- setPage={setPage}
234
- disableEdit={disableEdit}
235
- disableDelete={disableDelete}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "2.7.3",
3
+ "version": "2.7.5",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -0,0 +1,178 @@
1
+ import CryptoJS from "crypto-js";
2
+ import moment from "moment";
3
+
4
+ // Khóa bí mật để mã hóa và giải mã MD5 https://zozo.vn/ws/tools/md5encode
5
+ const secretKey = "b0dc70c6a34c0555ac9c63907617f5a5"; // Bạn nên lưu trữ key này an toàn
6
+
7
+ // Hàm mã hóa dữ liệu
8
+ const encryptData = (data) => {
9
+ if (!data) {
10
+ return null;
11
+ }
12
+
13
+ try {
14
+ return CryptoJS.AES.encrypt(JSON.stringify(data), secretKey).toString();
15
+ } catch (error) {
16
+ console.log("Error encrypting data:", error);
17
+ return null;
18
+ }
19
+ };
20
+
21
+ const decryptData = (ciphertext) => {
22
+ if (!ciphertext) {
23
+ return null;
24
+ }
25
+
26
+ try {
27
+ const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
28
+ const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
29
+
30
+ if (!decryptedData) {
31
+ throw new Error("Decrypted data is null or invalid.");
32
+ }
33
+
34
+ return JSON.parse(decryptedData);
35
+ } catch (error) {
36
+ console.error("Error decrypting data:", error, "Ciphertext:", ciphertext);
37
+ return null;
38
+ }
39
+ };
40
+
41
+
42
+ export const storeUserData = (data) => {
43
+ if (!data) return;
44
+
45
+ // Mã hóa và lưu token
46
+ const encryptedToken = encryptData(data.Token);
47
+ localStorage.setItem("token", encryptedToken);
48
+
49
+ // Tạo object user từ dữ liệu response
50
+ const userObj = {
51
+ userName: data?.UserName,
52
+ email: data?.Email,
53
+ FullName: data?.FullName,
54
+ Avatar: data?.Avatar,
55
+ Navigate: data?.Navigate
56
+ };
57
+
58
+ // Mã hóa và lưu user object vào localStorage
59
+ const encryptedUserObj = encryptData(userObj);
60
+ localStorage.setItem("user", encryptedUserObj);
61
+ };
62
+ export const storeSetCachedSidebarTops = (data) => {
63
+ if (!data) return;
64
+ // Mã hóa và lưu
65
+ const encryptedData = encryptData(data);
66
+ localStorage.setItem("cachedSidebarTops",encryptedData);
67
+ };
68
+ export const storeGetCachedSidebarTops = () => {
69
+ const encryptedData = localStorage.getItem("cachedSidebarTops");
70
+ if (!encryptedData) return null;
71
+ // console.log("encryptedData",encryptedData);
72
+ // Giải mã dữ liệu khi lấy ra từ localStorage
73
+ return decryptData(encryptedData);
74
+ };
75
+ export const storeSetCurrentInfor = (data) => {
76
+ if (!data) return;
77
+ // Mã hóa và lưu
78
+ const encryptedData = encryptData(data);
79
+ localStorage.setItem("GetCurrentInfor",encryptedData);
80
+ };
81
+ export const storeGetUser = () => {
82
+ const encryptedData = localStorage.getItem("user");
83
+ if (!encryptedData) return null;
84
+
85
+ // Giải mã dữ liệu khi lấy ra từ localStorage
86
+ const decryptedData = decryptData(encryptedData);
87
+ // Kiểm tra nếu dữ liệu giải mã không hợp lệ
88
+ // if (!decryptedData || typeof decryptedData !== 'string') {
89
+ // console.log("Decrypted user data is not a valid string.");
90
+ // return null;
91
+ // }
92
+
93
+ try {
94
+ // Chuyển đổi thành JSON nếu dữ liệu giải mã hợp lệ
95
+ return decryptedData;
96
+ } catch (error) {
97
+ console.log("Error parsing decrypted user data:", error);
98
+ return null;
99
+ }
100
+ };
101
+
102
+ export const storeGetToken = () => {
103
+ const encryptedData = localStorage.getItem("token");
104
+ if (!encryptedData) return null;
105
+
106
+ try {
107
+ const decryptedToken = decryptData(encryptedData);
108
+ if (!decryptedToken || typeof decryptedToken !== 'string') {
109
+ throw new Error("Decrypted token is null or not a valid string.");
110
+ }
111
+ return decryptedToken; // Không cần JSON.parse nếu đã là chuỗi hợp lệ
112
+ } catch (error) {
113
+ console.log("Error decrypting token:", error);
114
+ return null;
115
+ }
116
+ };
117
+
118
+ export const storeGetCurrentInfor = () => {
119
+ const encryptedData = localStorage.getItem("GetCurrentInfor");
120
+ if (!encryptedData) return null;
121
+
122
+ // Giải mã dữ liệu khi lấy ra từ localStorage
123
+ return decryptData(encryptedData);
124
+ };
125
+
126
+ export const storeGetThamSoHeThong = (TenThamSo) => {
127
+ const encryptedData = localStorage.getItem("GetCurrentInfor");
128
+ if (!encryptedData) return "";
129
+
130
+ const getCurrentInfor =decryptData(encryptedData) || {};
131
+ return getCurrentInfor?.ThamSoHeThong?.find((item) => item.TenThamSo === TenThamSo)?.GiaTri || "";
132
+ };
133
+ export const storeGetcolor= () => {
134
+ const encryptedData = localStorage.getItem("color");
135
+ if (!encryptedData) return "defaultColor";
136
+ return decryptData(encryptedData);
137
+ };
138
+ export const storeSetcolor = (data) => {
139
+ if (!data) return;
140
+ const encryptedData = encryptData(data);
141
+ localStorage.setItem("color",encryptedData);
142
+ };
143
+ export const storeGetlanguage = () => {
144
+ const encryptedData = localStorage.getItem("language");
145
+ if (!encryptedData) return "vi";
146
+ return decryptData(encryptedData);
147
+ };
148
+ export const storeSetlanguage = (data) => {
149
+ if (!data) return;
150
+ const encryptedData = encryptData(data);
151
+ localStorage.setItem("language",encryptedData);
152
+ };
153
+ export const storeGetlanguageVersion = () => {
154
+ const encryptedData = localStorage.getItem("languageVersion");
155
+ if (!encryptedData) return moment(Date.now()).format("DD/MM/YYYY HH:mm:ss");
156
+ return decryptData(encryptedData);
157
+ };
158
+ export const storeSetlanguageVersion = (data) => {
159
+ if (!data) return;
160
+ const encryptedData = encryptData(data);
161
+ localStorage.setItem("languageVersion",encryptedData);
162
+ };
163
+ export const storeGetlanguageData = () => {
164
+ const encryptedData = localStorage.getItem("languageData");
165
+ if (!encryptedData) return null;
166
+ return decryptData(encryptedData);
167
+ };
168
+ export const storeSetlanguageData = (data) => {
169
+ if (!data) return;
170
+ const encryptedData = encryptData(data);
171
+ localStorage.setItem("languageData",encryptedData);
172
+ };
173
+ export const storeGetlanguageSystemKey = (TypeKey,SystemKey,TypeLanguage) => {
174
+ const encryptedData = localStorage.getItem("languageData");
175
+ if (!encryptedData) return null;
176
+ const languageData =decryptData(encryptedData) || {};
177
+ return languageData?.find((item) => item.TypeKey === TypeKey && item.SystemKey === SystemKey && item.TypeLanguage === TypeLanguage)?.Description || "";
178
+ };