trithuc-mvc-react 3.4.7 → 3.4.9
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/components/DataManagement/DataTable.jsx +32 -11
- package/components/DataManagement/DataTableSM.jsx +32 -12
- package/components/DataManagement/ExportExcelButton.jsx +45 -45
- package/components/DataManagement/TableRowRenderSM.jsx +179 -78
- package/components/DataManagement/index.jsx +30 -39
- package/package.json +1 -1
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Grid,
|
|
3
|
+
Table,
|
|
4
|
+
TableBody,
|
|
5
|
+
TableCell,
|
|
6
|
+
TableContainer,
|
|
7
|
+
TableRow,
|
|
8
|
+
Typography,
|
|
9
|
+
useMediaQuery,
|
|
10
|
+
useTheme
|
|
11
|
+
} from "@mui/material";
|
|
2
12
|
import { useEffect, useMemo, useState } from "react";
|
|
3
13
|
import TablePaginationCustom from "../table/TablePagination";
|
|
4
14
|
|
|
5
15
|
import { useConfirm } from "material-ui-confirm";
|
|
6
16
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
|
7
17
|
import { toast } from "react-toastify";
|
|
8
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
changeStatusDataToTable,
|
|
20
|
+
deleteDataFromTable,
|
|
21
|
+
deleteMultipleDataFromTable,
|
|
22
|
+
getDatasFromTable
|
|
23
|
+
} from "../../api";
|
|
9
24
|
import TableRowsLoader from "../table/TableRowsLoader";
|
|
10
25
|
import { TableHead } from "./TableHead";
|
|
11
26
|
|
|
@@ -48,16 +63,17 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
|
|
|
48
63
|
const [rowsPerPage, setRowsPerPage] = useState(defaultRowsPerPage);
|
|
49
64
|
const [openDialog, setOpenDialog] = useState(false);
|
|
50
65
|
const [deleteId, setDeleteId] = useState(null);
|
|
51
|
-
const defaultSortColumn = columns.find((c) => c.defaultSort);
|
|
52
66
|
|
|
53
|
-
const [orderBy, setOrderBy] = useState(
|
|
54
|
-
const [order, setOrder] = useState(
|
|
67
|
+
const [orderBy, setOrderBy] = useState("");
|
|
68
|
+
const [order, setOrder] = useState("asc");
|
|
69
|
+
|
|
55
70
|
useEffect(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
if (!Array.isArray(columns) || columns.length === 0) return;
|
|
72
|
+
|
|
73
|
+
const defaultSortColumn = columns.find((c) => c.defaultSort) || columns[0];
|
|
74
|
+
|
|
75
|
+
setOrderBy(defaultSortColumn.field);
|
|
76
|
+
setOrder(defaultSortColumn.defaultOrder || "asc");
|
|
61
77
|
}, [columns]);
|
|
62
78
|
|
|
63
79
|
const handleRequestSort = (event, property) => {
|
|
@@ -370,7 +386,12 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
|
|
|
370
386
|
/>
|
|
371
387
|
</Grid>
|
|
372
388
|
</Grid>
|
|
373
|
-
<DeleteConfirmationDialog
|
|
389
|
+
<DeleteConfirmationDialog
|
|
390
|
+
open={openDialog}
|
|
391
|
+
onClose={handleDialogClose}
|
|
392
|
+
onConfirm={handleDialogClose}
|
|
393
|
+
id={deleteId}
|
|
394
|
+
/>
|
|
374
395
|
<DeleteMultipleConfirmationDialog
|
|
375
396
|
open={openDeleteMultipleDialog}
|
|
376
397
|
onClose={handleDeleteMultipleDialogClose}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Grid,
|
|
3
|
+
Table,
|
|
4
|
+
TableBody,
|
|
5
|
+
TableCell,
|
|
6
|
+
TableContainer,
|
|
7
|
+
TableRow,
|
|
8
|
+
Typography,
|
|
9
|
+
useMediaQuery,
|
|
10
|
+
useTheme
|
|
11
|
+
} from "@mui/material";
|
|
2
12
|
import { useEffect, useMemo, useState } from "react";
|
|
3
13
|
import TablePaginationCustom from "../table/TablePagination";
|
|
4
14
|
|
|
5
15
|
import { useConfirm } from "material-ui-confirm";
|
|
6
16
|
import { useMutation, useQuery, useQueryClient } from "react-query";
|
|
7
17
|
import { toast } from "react-toastify";
|
|
8
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
changeStatusDataToTable,
|
|
20
|
+
deleteDataFromTable,
|
|
21
|
+
deleteMultipleDataFromTable,
|
|
22
|
+
getDatasFromTable
|
|
23
|
+
} from "../../api";
|
|
9
24
|
import TableRowsLoader from "../table/TableRowsLoader";
|
|
10
25
|
|
|
11
26
|
import { URL_APPLICATION_API } from "@/constants";
|
|
@@ -47,16 +62,16 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
47
62
|
const [rowsPerPage, setRowsPerPage] = useState(defaultRowsPerPage);
|
|
48
63
|
const [openDialog, setOpenDialog] = useState(false);
|
|
49
64
|
const [deleteId, setDeleteId] = useState(null);
|
|
50
|
-
const
|
|
65
|
+
const [orderBy, setOrderBy] = useState("");
|
|
66
|
+
const [order, setOrder] = useState("asc");
|
|
51
67
|
|
|
52
|
-
const [orderBy, setOrderBy] = useState(defaultSortColumn?.field || "");
|
|
53
|
-
const [order, setOrder] = useState(defaultSortColumn?.defaultOrder || "asc");
|
|
54
68
|
useEffect(() => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
if (!Array.isArray(columns) || columns.length === 0) return;
|
|
70
|
+
|
|
71
|
+
const defaultSortColumn = columns.find((c) => c.defaultSort) || columns[0];
|
|
72
|
+
|
|
73
|
+
setOrderBy(defaultSortColumn.field);
|
|
74
|
+
setOrder(defaultSortColumn.defaultOrder || "asc");
|
|
60
75
|
}, [columns]);
|
|
61
76
|
|
|
62
77
|
const handleRequestSort = (event, property) => {
|
|
@@ -364,7 +379,7 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
364
379
|
/> */}
|
|
365
380
|
{isLoading ? (
|
|
366
381
|
<TableBody>
|
|
367
|
-
<TableRowsLoader rowsNum={10} colsNum={
|
|
382
|
+
<TableRowsLoader rowsNum={10} colsNum={1} />
|
|
368
383
|
</TableBody>
|
|
369
384
|
) : (
|
|
370
385
|
<TableBody>
|
|
@@ -409,7 +424,12 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
409
424
|
/>
|
|
410
425
|
</Grid>
|
|
411
426
|
</Grid>
|
|
412
|
-
<DeleteConfirmationDialog
|
|
427
|
+
<DeleteConfirmationDialog
|
|
428
|
+
open={openDialog}
|
|
429
|
+
onClose={handleDialogClose}
|
|
430
|
+
onConfirm={handleDialogClose}
|
|
431
|
+
id={deleteId}
|
|
432
|
+
/>
|
|
413
433
|
<DeleteMultipleConfirmationDialog
|
|
414
434
|
open={openDeleteMultipleDialog}
|
|
415
435
|
onClose={handleDeleteMultipleDialogClose}
|
|
@@ -56,53 +56,53 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
|
|
|
56
56
|
</Typography>
|
|
57
57
|
</Button>
|
|
58
58
|
) : (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// }}
|
|
67
|
-
// >
|
|
68
|
-
// <Download fontSize="inherit" />
|
|
69
|
-
// </IconButton>
|
|
70
|
-
// </Tooltip>
|
|
71
|
-
<Button
|
|
72
|
-
size={size}
|
|
73
|
-
variant="outlined"
|
|
74
|
-
startIcon={<Download />}
|
|
75
|
-
onClick={() => {
|
|
76
|
-
handleExportExcel(tableName, data);
|
|
77
|
-
}}
|
|
78
|
-
sx={{
|
|
79
|
-
ml: 0.5,
|
|
80
|
-
mr: 0.5,
|
|
81
|
-
fontSize: {
|
|
82
|
-
xs: "0.75rem",
|
|
83
|
-
sm: "0.875rem",
|
|
84
|
-
md: "1rem"
|
|
85
|
-
},
|
|
86
|
-
textAlign: "center",
|
|
87
|
-
textTransform: "none"
|
|
88
|
-
}}
|
|
89
|
-
>
|
|
90
|
-
<Typography
|
|
91
|
-
noWrap
|
|
92
|
-
sx={{
|
|
93
|
-
width: "100%",
|
|
94
|
-
fontSize: {
|
|
95
|
-
xs: "0.75rem",
|
|
96
|
-
sm: "0.875rem",
|
|
97
|
-
md: "1rem"
|
|
98
|
-
},
|
|
99
|
-
textAlign: "center",
|
|
100
|
-
textTransform: "none" // ✅ Đảm bảo chữ gốc giữ nguyên
|
|
59
|
+
<Tooltip title="Excel">
|
|
60
|
+
<IconButton
|
|
61
|
+
variant="outlined"
|
|
62
|
+
color="primary"
|
|
63
|
+
size={size}
|
|
64
|
+
onClick={() => {
|
|
65
|
+
handleExportExcel(tableName, data);
|
|
101
66
|
}}
|
|
102
67
|
>
|
|
103
|
-
|
|
104
|
-
</
|
|
105
|
-
</
|
|
68
|
+
<Download fontSize="inherit" />
|
|
69
|
+
</IconButton>
|
|
70
|
+
</Tooltip>
|
|
71
|
+
// <Button
|
|
72
|
+
// size={size}
|
|
73
|
+
// variant="outlined"
|
|
74
|
+
// startIcon={<Download />}
|
|
75
|
+
// onClick={() => {
|
|
76
|
+
// handleExportExcel(tableName, data);
|
|
77
|
+
// }}
|
|
78
|
+
// sx={{
|
|
79
|
+
// ml: 0.5,
|
|
80
|
+
// mr: 0.5,
|
|
81
|
+
// fontSize: {
|
|
82
|
+
// xs: "0.75rem",
|
|
83
|
+
// sm: "0.875rem",
|
|
84
|
+
// md: "1rem"
|
|
85
|
+
// },
|
|
86
|
+
// textAlign: "center",
|
|
87
|
+
// textTransform: "none"
|
|
88
|
+
// }}
|
|
89
|
+
// >
|
|
90
|
+
// <Typography
|
|
91
|
+
// noWrap
|
|
92
|
+
// sx={{
|
|
93
|
+
// width: "100%",
|
|
94
|
+
// fontSize: {
|
|
95
|
+
// xs: "0.75rem",
|
|
96
|
+
// sm: "0.875rem",
|
|
97
|
+
// md: "1rem"
|
|
98
|
+
// },
|
|
99
|
+
// textAlign: "center",
|
|
100
|
+
// textTransform: "none" // ✅ Đảm bảo chữ gốc giữ nguyên
|
|
101
|
+
// }}
|
|
102
|
+
// >
|
|
103
|
+
// Excel
|
|
104
|
+
// </Typography>
|
|
105
|
+
// </Button>
|
|
106
106
|
);
|
|
107
107
|
};
|
|
108
108
|
export default ExportExcelButton;
|
|
@@ -44,7 +44,7 @@ export const TableRowRenderSM = ({
|
|
|
44
44
|
|
|
45
45
|
const theme = useTheme();
|
|
46
46
|
const downXl = useMediaQuery(theme.breakpoints.down("xl"));
|
|
47
|
-
|
|
47
|
+
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
|
48
48
|
return (
|
|
49
49
|
<TableRow hover key={row[selectedField]} selected={selected}>
|
|
50
50
|
<TableCell
|
|
@@ -77,17 +77,54 @@ export const TableRowRenderSM = ({
|
|
|
77
77
|
>
|
|
78
78
|
{/* Hiển thị các cột dữ liệu */}
|
|
79
79
|
<Grid container spacing={1}>
|
|
80
|
-
{columns.map(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
{columns.map(({ field, label, valueGetter = (row) => row[field], renderCell, valueFormat = (e) => e }) => {
|
|
81
|
+
const rawValue = renderCell ? renderCell(row) : valueFormat(valueGetter(row));
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Grid
|
|
85
|
+
key={`${row[selectedField]}-${field}`}
|
|
86
|
+
size={12}
|
|
87
|
+
sx={{
|
|
88
|
+
minWidth: 0,
|
|
89
|
+
maxWidth: "100%",
|
|
90
|
+
overflow: "hidden"
|
|
91
|
+
}}
|
|
92
|
+
>
|
|
93
|
+
<Typography
|
|
94
|
+
component="div"
|
|
95
|
+
variant="body2"
|
|
96
|
+
sx={{
|
|
97
|
+
lineHeight: 1.4,
|
|
98
|
+
wordBreak: "break-word",
|
|
99
|
+
overflowWrap: "anywhere",
|
|
100
|
+
whiteSpace: "normal"
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<Box component="span" fontWeight={600}>
|
|
104
|
+
{label}:
|
|
105
|
+
</Box>
|
|
106
|
+
|
|
107
|
+
<Box
|
|
108
|
+
sx={{
|
|
109
|
+
maxWidth: "100%",
|
|
110
|
+
overflow: "hidden",
|
|
111
|
+
|
|
112
|
+
// 🔥 ÉP CO TOÀN BỘ COMPONENT CON
|
|
113
|
+
"& *": {
|
|
114
|
+
maxWidth: "100%",
|
|
115
|
+
minWidth: 0,
|
|
116
|
+
boxSizing: "border-box"
|
|
117
|
+
}
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
{rawValue}
|
|
121
|
+
</Box>
|
|
122
|
+
</Typography>
|
|
123
|
+
</Grid>
|
|
124
|
+
);
|
|
125
|
+
})}
|
|
90
126
|
</Grid>
|
|
127
|
+
|
|
91
128
|
{/* Công tắc trạng thái */}
|
|
92
129
|
{!disableStatus && canEdit && (
|
|
93
130
|
<Box sx={{ mt: 1 }}>
|
|
@@ -100,74 +137,138 @@ export const TableRowRenderSM = ({
|
|
|
100
137
|
)}
|
|
101
138
|
|
|
102
139
|
{/* Các nút thao tác */}
|
|
103
|
-
<Box
|
|
104
|
-
mt={2}
|
|
105
|
-
pt={1}
|
|
106
|
-
display="flex"
|
|
107
|
-
gap={1}
|
|
108
|
-
flexWrap="wrap"
|
|
109
|
-
justifyContent="flex-end"
|
|
110
|
-
borderTop="1px solid"
|
|
111
|
-
borderColor="divider"
|
|
112
|
-
>
|
|
140
|
+
<Box mt={2} pt={1} borderTop="1px solid" borderColor="divider" display="flex" flexDirection="column" gap={1}>
|
|
113
141
|
{!disableCellThaoTac && (
|
|
114
142
|
<>
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
143
|
+
<Box display="grid" gridTemplateColumns={isMobile ? "repeat(3, 1fr)" : "auto"} gap={1}>
|
|
144
|
+
{!disableEdit && canEdit && (
|
|
145
|
+
<Button
|
|
146
|
+
fullWidth
|
|
147
|
+
size={isMobile ? "small" : "medium"}
|
|
148
|
+
variant="contained"
|
|
149
|
+
color="primary"
|
|
150
|
+
onClick={() => onEdit(row)}
|
|
151
|
+
startIcon={<EditOutlined />}
|
|
152
|
+
sx={{
|
|
153
|
+
...modernButtonStyle,
|
|
154
|
+
|
|
155
|
+
// compact mobile
|
|
156
|
+
minHeight: 36,
|
|
157
|
+
paddingX: 1,
|
|
158
|
+
|
|
159
|
+
// icon - text spacing
|
|
160
|
+
gap: 0.5,
|
|
161
|
+
"& .MuiButton-startIcon": {
|
|
162
|
+
marginRight: 0.5,
|
|
163
|
+
marginLeft: 0
|
|
164
|
+
},
|
|
165
|
+
"& .MuiButton-startIcon svg": {
|
|
166
|
+
fontSize: 18
|
|
167
|
+
}
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
Cập nhật
|
|
171
|
+
</Button>
|
|
172
|
+
)}
|
|
173
|
+
|
|
174
|
+
{canView && !tableActions?.some(({ permissionType }) => permissionType === "view") && (
|
|
175
|
+
<Button
|
|
176
|
+
fullWidth
|
|
177
|
+
size={isMobile ? "small" : "medium"}
|
|
178
|
+
variant="outlined"
|
|
179
|
+
color="info"
|
|
180
|
+
onClick={() => onView(row)}
|
|
181
|
+
startIcon={<RemoveRedEyeOutlinedIcon />}
|
|
182
|
+
sx={{
|
|
183
|
+
...modernButtonStyle,
|
|
184
|
+
|
|
185
|
+
// compact mobile
|
|
186
|
+
minHeight: 36,
|
|
187
|
+
paddingX: 1,
|
|
188
|
+
|
|
189
|
+
// icon - text spacing
|
|
190
|
+
gap: 0.5,
|
|
191
|
+
"& .MuiButton-startIcon": {
|
|
192
|
+
marginRight: 0.5,
|
|
193
|
+
marginLeft: 0
|
|
194
|
+
},
|
|
195
|
+
"& .MuiButton-startIcon svg": {
|
|
196
|
+
fontSize: 18
|
|
197
|
+
}
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
Xem
|
|
201
|
+
</Button>
|
|
202
|
+
)}
|
|
203
|
+
|
|
204
|
+
{!disableDelete && canDelete && (
|
|
205
|
+
<Button
|
|
206
|
+
fullWidth
|
|
207
|
+
size={isMobile ? "small" : "medium"}
|
|
208
|
+
variant="outlined"
|
|
209
|
+
color="error"
|
|
210
|
+
onClick={() => onDelete(row[selectedField])}
|
|
211
|
+
startIcon={<DeleteOutlineIcon />}
|
|
212
|
+
sx={{
|
|
213
|
+
...modernButtonStyle,
|
|
214
|
+
|
|
215
|
+
// compact mobile
|
|
216
|
+
minHeight: 36,
|
|
217
|
+
paddingX: 1,
|
|
218
|
+
|
|
219
|
+
// icon - text spacing
|
|
220
|
+
gap: 0.5,
|
|
221
|
+
"& .MuiButton-startIcon": {
|
|
222
|
+
marginRight: 0.5,
|
|
223
|
+
marginLeft: 0
|
|
224
|
+
},
|
|
225
|
+
"& .MuiButton-startIcon svg": {
|
|
226
|
+
fontSize: 18
|
|
227
|
+
}
|
|
228
|
+
}}
|
|
229
|
+
>
|
|
230
|
+
Xóa
|
|
231
|
+
</Button>
|
|
232
|
+
)}
|
|
233
|
+
|
|
234
|
+
{tableActionsOnTable.map(
|
|
235
|
+
({ title, onClick, element, visible = true }, idx) =>
|
|
236
|
+
(typeof visible === "function" ? visible(row) : visible) && (
|
|
237
|
+
<Button
|
|
238
|
+
key={idx}
|
|
239
|
+
fullWidth
|
|
240
|
+
size="small"
|
|
241
|
+
variant="outlined"
|
|
242
|
+
onClick={() => onClick(row)}
|
|
243
|
+
startIcon={element}
|
|
244
|
+
sx={{
|
|
245
|
+
...modernButtonStyle,
|
|
246
|
+
|
|
247
|
+
// compact mobile
|
|
248
|
+
minHeight: 36,
|
|
249
|
+
paddingX: 1,
|
|
250
|
+
|
|
251
|
+
// icon - text spacing
|
|
252
|
+
gap: 0.5,
|
|
253
|
+
"& .MuiButton-startIcon": {
|
|
254
|
+
marginRight: 0.5,
|
|
255
|
+
marginLeft: 0
|
|
256
|
+
},
|
|
257
|
+
"& .MuiButton-startIcon svg": {
|
|
258
|
+
fontSize: 18
|
|
259
|
+
}
|
|
260
|
+
}}
|
|
261
|
+
>
|
|
262
|
+
{title === "xem chi tiết" ? "Xem" : title}
|
|
263
|
+
</Button>
|
|
264
|
+
)
|
|
265
|
+
)}
|
|
266
|
+
</Box>
|
|
267
|
+
|
|
268
|
+
{/* More menu luôn nằm riêng 1 hàng */}
|
|
269
|
+
<Box display="flex" justifyContent="flex-end">
|
|
270
|
+
<MoreMenu actions={tableActionsOnMoreMenu} data={row} />
|
|
271
|
+
</Box>
|
|
171
272
|
</>
|
|
172
273
|
)}
|
|
173
274
|
</Box>
|
|
@@ -180,7 +281,7 @@ const modernButtonStyle = {
|
|
|
180
281
|
fontWeight: 400,
|
|
181
282
|
fontSize: "0.65rem",
|
|
182
283
|
borderRadius: 2,
|
|
183
|
-
minWidth:
|
|
284
|
+
minWidth: 0,
|
|
184
285
|
textTransform: "none",
|
|
185
286
|
px: 1,
|
|
186
287
|
py: 0.5,
|
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
Button,
|
|
4
|
+
Card,
|
|
5
|
+
Grid,
|
|
6
|
+
IconButton,
|
|
7
|
+
Stack,
|
|
8
|
+
Tooltip,
|
|
9
|
+
Typography,
|
|
10
|
+
useMediaQuery,
|
|
11
|
+
useTheme
|
|
12
|
+
} from "@mui/material";
|
|
2
13
|
|
|
3
14
|
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
4
15
|
|
|
@@ -277,37 +288,32 @@ function DataManagement({
|
|
|
277
288
|
{!disableHead && (
|
|
278
289
|
<Card sx={{ p: 0, mb: 0 }}>
|
|
279
290
|
<Stack
|
|
280
|
-
direction=
|
|
291
|
+
direction="row"
|
|
292
|
+
flexWrap="wrap"
|
|
293
|
+
alignItems="center"
|
|
294
|
+
gap={1}
|
|
281
295
|
sx={{
|
|
282
|
-
|
|
283
|
-
flexWrap: isFullRow ? "nowrap" : "wrap",
|
|
284
|
-
alignItems: isFullRow ? "stretch" : "center",
|
|
285
|
-
gap: 1,
|
|
296
|
+
width: "100%",
|
|
286
297
|
...slotProps?.header?.sx
|
|
287
298
|
}}
|
|
288
299
|
>
|
|
289
|
-
{/*
|
|
300
|
+
{/* ===== LEFT: TITLE ===== */}
|
|
290
301
|
<Box
|
|
291
|
-
ref={leftBoxRef}
|
|
292
302
|
sx={{
|
|
293
|
-
flexGrow:
|
|
294
|
-
|
|
295
|
-
textAlign: "left"
|
|
296
|
-
whiteSpace: "normal",
|
|
297
|
-
wordBreak: "break-word"
|
|
303
|
+
flexGrow: 1,
|
|
304
|
+
minWidth: 0, // ⭐ bắt buộc để flex wrap đúng
|
|
305
|
+
textAlign: "left"
|
|
298
306
|
}}
|
|
299
307
|
>
|
|
300
308
|
<Typography
|
|
301
|
-
ref={titleRef}
|
|
302
|
-
noWrap
|
|
303
309
|
sx={{
|
|
304
|
-
width: "100%",
|
|
305
310
|
fontSize: {
|
|
306
311
|
xs: "0.85rem",
|
|
307
312
|
sm: "0.95rem",
|
|
308
313
|
md: "1rem"
|
|
309
314
|
},
|
|
310
|
-
|
|
315
|
+
whiteSpace: "normal",
|
|
316
|
+
wordBreak: "break-word",
|
|
311
317
|
textTransform: "none"
|
|
312
318
|
}}
|
|
313
319
|
>
|
|
@@ -315,26 +321,20 @@ function DataManagement({
|
|
|
315
321
|
</Typography>
|
|
316
322
|
</Box>
|
|
317
323
|
|
|
318
|
-
{/*
|
|
324
|
+
{/* ===== RIGHT: ACTIONS ===== */}
|
|
319
325
|
<Box
|
|
320
|
-
ref={rightBoxRef}
|
|
321
326
|
sx={{
|
|
322
327
|
display: "flex",
|
|
323
328
|
flexWrap: "wrap",
|
|
324
|
-
justifyContent: isFullRow ? "flex-end" : "flex-end",
|
|
325
329
|
alignItems: "center",
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
justifyContent: "flex-end",
|
|
331
|
+
ml: "auto", // ⭐ chìa khóa: cùng dòng thì đẩy phải, thiếu chỗ thì xuống dòng
|
|
332
|
+
gap: 0.5
|
|
328
333
|
}}
|
|
329
334
|
>
|
|
330
335
|
{(viewMode === "thongke" || viewMode === "bieudo") && (
|
|
331
336
|
<Tooltip title="Danh sách dữ liệu">
|
|
332
|
-
<IconButton
|
|
333
|
-
size={elementSize}
|
|
334
|
-
color="primary"
|
|
335
|
-
onClick={() => setViewMode("table")}
|
|
336
|
-
sx={{ ml: 0.5, mr: 0.5 }}
|
|
337
|
-
>
|
|
337
|
+
<IconButton size={elementSize} color="primary" onClick={() => setViewMode("table")}>
|
|
338
338
|
<TableChartIcon fontSize="inherit" />
|
|
339
339
|
</IconButton>
|
|
340
340
|
</Tooltip>
|
|
@@ -346,7 +346,6 @@ function DataManagement({
|
|
|
346
346
|
size={elementSize}
|
|
347
347
|
color={viewMode === "thongke" ? "secondary" : "primary"}
|
|
348
348
|
onClick={() => setViewMode("thongke")}
|
|
349
|
-
sx={{ ml: 0.5, mr: 0.5 }}
|
|
350
349
|
>
|
|
351
350
|
<BarChartIcon fontSize="inherit" />
|
|
352
351
|
</IconButton>
|
|
@@ -359,7 +358,6 @@ function DataManagement({
|
|
|
359
358
|
size={elementSize}
|
|
360
359
|
color={viewMode === "bieudo" ? "secondary" : "primary"}
|
|
361
360
|
onClick={() => setViewMode("bieudo")}
|
|
362
|
-
sx={{ ml: 0.5, mr: 0.5 }}
|
|
363
361
|
>
|
|
364
362
|
<PieChartIcon fontSize="inherit" />
|
|
365
363
|
</IconButton>
|
|
@@ -367,16 +365,14 @@ function DataManagement({
|
|
|
367
365
|
)}
|
|
368
366
|
|
|
369
367
|
<HuongDanButton tableName={tableName} size={elementSize} />
|
|
368
|
+
|
|
370
369
|
<Tooltip title="Làm mới">
|
|
371
370
|
<IconButton
|
|
372
|
-
variant="outlined"
|
|
373
371
|
color="primary"
|
|
374
372
|
size={elementSize}
|
|
375
373
|
onClick={() => {
|
|
376
374
|
setDataSearch({ ...defaults });
|
|
377
|
-
[...filters].forEach((filter) =>
|
|
378
|
-
filter?.onChange?.();
|
|
379
|
-
});
|
|
375
|
+
[...filters].forEach((filter) => filter?.onChange?.());
|
|
380
376
|
reset();
|
|
381
377
|
setValue("Search");
|
|
382
378
|
}}
|
|
@@ -403,20 +399,15 @@ function DataManagement({
|
|
|
403
399
|
}
|
|
404
400
|
onAddClick(e);
|
|
405
401
|
}}
|
|
406
|
-
sx={{
|
|
407
|
-
ml: 0.5
|
|
408
|
-
}}
|
|
409
402
|
>
|
|
410
403
|
<Typography
|
|
411
404
|
noWrap
|
|
412
405
|
sx={{
|
|
413
|
-
width: "100%",
|
|
414
406
|
fontSize: {
|
|
415
407
|
xs: "0.65rem",
|
|
416
408
|
sm: "0.75rem",
|
|
417
409
|
md: "0.85rem"
|
|
418
410
|
},
|
|
419
|
-
textAlign: "center",
|
|
420
411
|
textTransform: "none"
|
|
421
412
|
}}
|
|
422
413
|
>
|