trithuc-mvc-react 3.3.6 → 3.3.8
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.
|
@@ -27,25 +27,47 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
|
|
|
27
27
|
onClick={() => {
|
|
28
28
|
handleExportExcel(tableName, data);
|
|
29
29
|
}}
|
|
30
|
-
sx={{
|
|
30
|
+
sx={{
|
|
31
|
+
ml: 0.5,
|
|
32
|
+
mr: 0.5
|
|
33
|
+
// fontSize: {
|
|
34
|
+
// xs: "0.75rem",
|
|
35
|
+
// sm: "0.85rem",
|
|
36
|
+
// md: "0.95rem"
|
|
37
|
+
// },
|
|
38
|
+
// textAlign: "center",
|
|
39
|
+
// textTransform: "none"
|
|
40
|
+
}}
|
|
31
41
|
>
|
|
32
42
|
<Typography
|
|
33
43
|
noWrap
|
|
34
44
|
sx={{
|
|
35
45
|
width: "100%",
|
|
36
46
|
fontSize: {
|
|
37
|
-
xs: "0.
|
|
38
|
-
sm: "0.
|
|
39
|
-
md: "
|
|
47
|
+
xs: "0.65rem",
|
|
48
|
+
sm: "0.75rem",
|
|
49
|
+
md: "0.95rem"
|
|
40
50
|
},
|
|
41
51
|
textAlign: "center",
|
|
42
|
-
textTransform: "none"
|
|
52
|
+
textTransform: "none"
|
|
43
53
|
}}
|
|
44
54
|
>
|
|
45
55
|
Excel
|
|
46
56
|
</Typography>
|
|
47
57
|
</Button>
|
|
48
58
|
) : (
|
|
59
|
+
// <Tooltip title="Excel">
|
|
60
|
+
// <IconButton
|
|
61
|
+
// variant="outlined"
|
|
62
|
+
// color="primary"
|
|
63
|
+
// size={size}
|
|
64
|
+
// onClick={() => {
|
|
65
|
+
// handleExportExcel(tableName, data);
|
|
66
|
+
// }}
|
|
67
|
+
// >
|
|
68
|
+
// <Download fontSize="inherit" />
|
|
69
|
+
// </IconButton>
|
|
70
|
+
// </Tooltip>
|
|
49
71
|
<Button
|
|
50
72
|
size={size}
|
|
51
73
|
variant="outlined"
|
|
@@ -53,7 +75,17 @@ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
|
|
|
53
75
|
onClick={() => {
|
|
54
76
|
handleExportExcel(tableName, data);
|
|
55
77
|
}}
|
|
56
|
-
sx={{
|
|
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
|
+
}}
|
|
57
89
|
>
|
|
58
90
|
<Typography
|
|
59
91
|
noWrap
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { EditOutlined } from "@mui/icons-material";
|
|
2
2
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
3
3
|
import RemoveRedEyeOutlinedIcon from "@mui/icons-material/RemoveRedEyeOutlined";
|
|
4
|
-
import { Box, Button, Grid, Switch, TableCell, TableRow, Toolbar, useMediaQuery } from "@mui/material";
|
|
4
|
+
import { Box, Button, Grid, Switch, TableCell, TableRow, Toolbar, Typography, useMediaQuery } from "@mui/material";
|
|
5
5
|
import { styled, useTheme } from "@mui/material/styles";
|
|
6
6
|
import { useMemo } from "react";
|
|
7
7
|
import { usePermission } from "../../hooks";
|
|
8
8
|
import MoreMenu from "../MoreMenu";
|
|
9
9
|
import { useDataTable } from "./hooks";
|
|
10
|
+
import { fontSize } from "@mui/system";
|
|
10
11
|
|
|
11
12
|
export const TableRowRenderSM = ({
|
|
12
13
|
index,
|
|
@@ -56,105 +57,139 @@ export const TableRowRenderSM = ({
|
|
|
56
57
|
overflowWrap: "break-word"
|
|
57
58
|
}}
|
|
58
59
|
>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
60
|
+
<Box
|
|
61
|
+
display="flex"
|
|
62
|
+
flexDirection="column"
|
|
63
|
+
gap={1}
|
|
64
|
+
border="1px solid"
|
|
65
|
+
borderColor="divider"
|
|
66
|
+
borderRadius={2}
|
|
67
|
+
boxShadow={1}
|
|
68
|
+
p={2}
|
|
69
|
+
bgcolor="background.paper"
|
|
70
|
+
sx={{
|
|
71
|
+
transition: "all 0.2s ease",
|
|
72
|
+
"&:hover": {
|
|
73
|
+
boxShadow: 3,
|
|
74
|
+
borderColor: "primary.light"
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
{/* Hiển thị các cột dữ liệu */}
|
|
79
|
+
<Grid container spacing={1}>
|
|
80
|
+
{columns.map(
|
|
81
|
+
({ field, label, type = "text", valueGetter = (row) => row[field], renderCell, valueFormat = (e) => e }) => {
|
|
82
|
+
const value = renderCell ? renderCell(row) : valueFormat(valueGetter(row));
|
|
83
|
+
return (
|
|
84
|
+
<Grid size={12} key={`${row[selectedField]}-${field}`}>
|
|
85
|
+
<strong>{label}: </strong> {value}
|
|
86
|
+
</Grid>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
)}
|
|
90
|
+
</Grid>
|
|
91
|
+
{/* Công tắc trạng thái */}
|
|
92
|
+
{!disableStatus && canEdit && (
|
|
93
|
+
<Box sx={{ mt: 1 }}>
|
|
94
|
+
<Switch
|
|
95
|
+
checked={row[statusKey]}
|
|
96
|
+
onChange={() => onChangeStatus(row[selectedField])}
|
|
97
|
+
inputProps={{ "aria-label": "controlled" }}
|
|
98
|
+
/>
|
|
99
|
+
</Box>
|
|
100
|
+
)}
|
|
80
101
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
102
|
+
{/* 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
|
+
>
|
|
113
|
+
{!disableCellThaoTac && (
|
|
114
|
+
<>
|
|
115
|
+
{!disableEdit && canEdit && (
|
|
87
116
|
<Button
|
|
88
|
-
fullWidth
|
|
89
117
|
size="small"
|
|
90
|
-
variant="
|
|
118
|
+
variant="contained"
|
|
91
119
|
color="primary"
|
|
92
120
|
onClick={() => onEdit(row)}
|
|
93
|
-
startIcon={<EditOutlined
|
|
94
|
-
sx={
|
|
121
|
+
startIcon={<EditOutlined />}
|
|
122
|
+
sx={modernButtonStyle}
|
|
95
123
|
>
|
|
96
124
|
Cập nhật
|
|
97
125
|
</Button>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<Grid size={4}>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{canView && !tableActions?.some(({ permissionType }) => permissionType === "view") && (
|
|
102
129
|
<Button
|
|
103
|
-
fullWidth
|
|
104
130
|
size="small"
|
|
105
131
|
variant="outlined"
|
|
106
132
|
color="info"
|
|
107
133
|
onClick={() => onView(row)}
|
|
108
134
|
startIcon={<RemoveRedEyeOutlinedIcon />}
|
|
109
|
-
sx={
|
|
135
|
+
sx={modernButtonStyle}
|
|
110
136
|
>
|
|
111
137
|
Xem
|
|
112
138
|
</Button>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<Grid size={4}>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{!disableDelete && canDelete && (
|
|
117
142
|
<Button
|
|
118
|
-
fullWidth
|
|
119
143
|
size="small"
|
|
120
144
|
variant="outlined"
|
|
121
145
|
color="error"
|
|
122
146
|
onClick={() => onDelete(row[selectedField])}
|
|
123
|
-
startIcon={<DeleteOutlineIcon
|
|
124
|
-
sx={
|
|
147
|
+
startIcon={<DeleteOutlineIcon />}
|
|
148
|
+
sx={modernButtonStyle}
|
|
125
149
|
>
|
|
126
150
|
Xóa
|
|
127
151
|
</Button>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<Grid key={row.Id} size={4}>
|
|
152
|
+
)}
|
|
153
|
+
|
|
154
|
+
{tableActionsOnTable.map(
|
|
155
|
+
({ title, size, onClick, element, visible = true }, idx) =>
|
|
156
|
+
(typeof visible === "function" ? visible(row) : visible) && (
|
|
134
157
|
<Button
|
|
135
|
-
fullWidth
|
|
136
158
|
key={idx}
|
|
137
159
|
size="small"
|
|
138
160
|
variant="outlined"
|
|
139
161
|
onClick={() => onClick(row)}
|
|
140
162
|
startIcon={element}
|
|
141
|
-
sx={
|
|
142
|
-
...buttonStyle
|
|
143
|
-
}}
|
|
163
|
+
sx={modernButtonStyle}
|
|
144
164
|
>
|
|
145
165
|
{title === "xem chi tiết" ? "Xem" : title}
|
|
146
166
|
</Button>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
167
|
+
)
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
<MoreMenu actions={tableActionsOnMoreMenu} data={row} />
|
|
171
|
+
</>
|
|
172
|
+
)}
|
|
173
|
+
</Box>
|
|
174
|
+
</Box>
|
|
154
175
|
</TableCell>
|
|
155
176
|
</TableRow>
|
|
156
177
|
);
|
|
157
178
|
};
|
|
179
|
+
const modernButtonStyle = {
|
|
180
|
+
fontWeight: 400,
|
|
181
|
+
fontSize: "0.7rem",
|
|
182
|
+
borderRadius: 2,
|
|
183
|
+
minWidth: 90,
|
|
184
|
+
textTransform: "none",
|
|
185
|
+
px: 1,
|
|
186
|
+
py: 0.5,
|
|
187
|
+
boxShadow: "none",
|
|
188
|
+
transition: "all 0.2s ease-in-out",
|
|
189
|
+
"&:hover": {
|
|
190
|
+
boxShadow: "0px 2px 6px rgba(0,0,0,0.1)"
|
|
191
|
+
}
|
|
192
|
+
};
|
|
158
193
|
|
|
159
194
|
// Button style dùng chung
|
|
160
195
|
const buttonStyle = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, Button, Card, Grid, IconButton, Stack, Tooltip, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useEffect, useLayoutEffect, useMemo, useState } from "react";
|
|
4
4
|
|
|
5
5
|
import { Add, Refresh } from "@mui/icons-material";
|
|
6
6
|
import BarChartIcon from "@mui/icons-material/BarChart";
|
|
@@ -20,6 +20,7 @@ import ExportExcelButton from "./ExportExcelButton";
|
|
|
20
20
|
import { FilterGod } from "./FilterGod";
|
|
21
21
|
import HuongDanButton from "./HuongDanButton";
|
|
22
22
|
import { useRef } from "react";
|
|
23
|
+
import { debounce } from "lodash";
|
|
23
24
|
|
|
24
25
|
DataManagement.propTypes = {
|
|
25
26
|
columns: PropTypes.array,
|
|
@@ -134,6 +135,7 @@ function DataManagement({
|
|
|
134
135
|
|
|
135
136
|
const theme = useTheme();
|
|
136
137
|
const upXL = useMediaQuery(theme.breakpoints.up("xl"));
|
|
138
|
+
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
|
137
139
|
const elementSize = useMemo(() => {
|
|
138
140
|
const elementSize = upXL ? "medium" : "small";
|
|
139
141
|
return elementSize;
|
|
@@ -211,239 +213,215 @@ function DataManagement({
|
|
|
211
213
|
bieuDo,
|
|
212
214
|
disableHead
|
|
213
215
|
]);
|
|
214
|
-
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm")); // Xác định màn hình nhỏ
|
|
215
216
|
const methods = useForm({ defaultValues: getDefaultValues(filters) });
|
|
216
217
|
const { reset, setValue } = methods;
|
|
217
218
|
|
|
219
|
+
const titleRef = useRef(null);
|
|
218
220
|
const titleText = title || "";
|
|
219
221
|
const [titleWidth, setTitleWidth] = useState(0);
|
|
220
|
-
const [minWidthValue, setMinWidthValue] = useState(0);
|
|
222
|
+
const [minWidthValue, setMinWidthValue] = useState(0);
|
|
221
223
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
titleElement.style.font = "normal 14px Arial"; // Đảm bảo phông chữ và kích thước giống như bạn dùng trong UI
|
|
226
|
-
titleElement.style.visibility = "hidden"; // Ẩn element tạm thời
|
|
227
|
-
titleElement.textContent = titleText;
|
|
224
|
+
const leftBoxRef = useRef(null);
|
|
225
|
+
const rightBoxRef = useRef(null);
|
|
226
|
+
const [isFullRow, setIsFullRow] = useState(false);
|
|
228
227
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}, [titleText]);
|
|
228
|
+
const checkLayout = () => {
|
|
229
|
+
if (titleRef.current && rightBoxRef.current) {
|
|
230
|
+
const titleTextWidth = titleRef.current.scrollWidth;
|
|
233
231
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
// Tính tổng độ rộng thật của tất cả các phần tử bên trong rightBox
|
|
233
|
+
const rightChildren = Array.from(rightBoxRef.current.children);
|
|
234
|
+
const totalRightWidth = rightChildren.reduce((acc, child) => {
|
|
235
|
+
const childWidth = child.scrollWidth || 0;
|
|
236
|
+
const style = window.getComputedStyle(child);
|
|
237
|
+
const marginLeft = parseFloat(style.marginLeft) || 0;
|
|
238
|
+
const marginRight = parseFloat(style.marginRight) || 0;
|
|
239
|
+
return acc + childWidth + marginLeft + marginRight;
|
|
240
|
+
}, 0);
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
const containerWidth = window.innerWidth - 32; // padding 16px trái/phải
|
|
243
|
+
const spacing = 16; // khoảng cách giữa 2 Box
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
useEffect(() => {
|
|
247
|
-
// Hàm xử lý resize
|
|
248
|
-
const handleResize = () => {
|
|
249
|
-
const currentWindowWidth = window.innerWidth;
|
|
250
|
-
|
|
251
|
-
// Đo chiều rộng của khối phải trực tiếp từ ref
|
|
252
|
-
const rightBoxWidth = rightBoxRef.current ? rightBoxRef.current.offsetWidth : 0;
|
|
253
|
-
|
|
254
|
-
// Cập nhật chiều rộng khối phải (rightBoxMinWidth) tùy theo kích thước của cửa sổ và khối
|
|
255
|
-
if (currentWindowWidth - (minWidthValue + 30) < rightBoxWidth) {
|
|
256
|
-
// console.log(
|
|
257
|
-
// `currentWindowWidth - minWidthValue 1: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
|
|
258
|
-
// );
|
|
259
|
-
setRightBoxMinWidth(currentWindowWidth - 30); // Điều chỉnh chiều rộng khối phải nếu không đủ không gian
|
|
260
|
-
} else {
|
|
261
|
-
// console.log(
|
|
262
|
-
// `currentWindowWidth - minWidthValue 2: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
|
|
263
|
-
// );
|
|
264
|
-
setRightBoxMinWidth(rightBoxWidth); // Nếu đủ không gian, dùng chiều rộng khối phải
|
|
265
|
-
}
|
|
266
|
-
};
|
|
245
|
+
const totalContentWidth = titleTextWidth + totalRightWidth + spacing;
|
|
267
246
|
|
|
268
|
-
|
|
269
|
-
const initialResize = () => {
|
|
270
|
-
const rightBoxWidth = rightBoxRef.current ? rightBoxRef.current.offsetWidth : 0;
|
|
271
|
-
const currentWindowWidth = window.innerWidth;
|
|
272
|
-
|
|
273
|
-
// Cập nhật giá trị minWidth ngay khi render lần đầu
|
|
274
|
-
if (currentWindowWidth - (minWidthValue + 30) < rightBoxWidth) {
|
|
275
|
-
// console.log(
|
|
276
|
-
// `currentWindowWidth - minWidthValue 3: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
|
|
277
|
-
// );
|
|
278
|
-
setRightBoxMinWidth(currentWindowWidth - 30); // Điều chỉnh chiều rộng khối phải nếu không đủ không gian
|
|
279
|
-
} else {
|
|
280
|
-
// console.log(
|
|
281
|
-
// `currentWindowWidth - minWidthValue 4: ${currentWindowWidth - minWidthValue} - rightBoxWidth: ${rightBoxWidth}`
|
|
282
|
-
// );
|
|
283
|
-
setRightBoxMinWidth(rightBoxWidth); // Nếu đủ không gian, dùng chiều rộng của khối phải
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
// Gọi hàm tính toán ngay khi render lần đầu
|
|
288
|
-
if (minWidthValue > 0) {
|
|
289
|
-
initialResize();
|
|
247
|
+
setIsFullRow(totalContentWidth > containerWidth);
|
|
290
248
|
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
useEffect(() => {
|
|
252
|
+
const observer = new ResizeObserver(() => {
|
|
253
|
+
checkLayout();
|
|
254
|
+
});
|
|
291
255
|
|
|
292
|
-
|
|
293
|
-
|
|
256
|
+
if (titleRef.current) observer.observe(titleRef.current);
|
|
257
|
+
if (rightBoxRef.current) observer.observe(rightBoxRef.current);
|
|
258
|
+
|
|
259
|
+
window.addEventListener("resize", checkLayout);
|
|
294
260
|
|
|
295
|
-
// Dọn dẹp sự kiện khi component bị hủy
|
|
296
261
|
return () => {
|
|
297
|
-
|
|
262
|
+
observer.disconnect();
|
|
263
|
+
window.removeEventListener("resize", checkLayout);
|
|
298
264
|
};
|
|
299
|
-
}, [
|
|
300
|
-
|
|
301
|
-
// In các giá trị trong console để kiểm tra
|
|
302
|
-
// useEffect(() => {
|
|
303
|
-
// console.log("minWidthValue:", minWidthValue);
|
|
304
|
-
// console.log("rightBoxMinWidth:", rightBoxMinWidth);
|
|
305
|
-
// }, [rightBoxMinWidth]);
|
|
265
|
+
}, []);
|
|
306
266
|
|
|
307
267
|
return (
|
|
308
268
|
<>
|
|
309
269
|
<DataTableContext.Provider value={values}>
|
|
310
270
|
<FormProvider {...methods}>
|
|
311
271
|
{!disableHead && (
|
|
312
|
-
<
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
mb: upXL ? 1 : 1,
|
|
316
|
-
flexWrap: "wrap",
|
|
317
|
-
alignItems: "center",
|
|
318
|
-
gap: 1,
|
|
319
|
-
...slotProps?.header?.sx
|
|
320
|
-
}}
|
|
321
|
-
>
|
|
322
|
-
{/* Khối trái */}
|
|
323
|
-
<Box
|
|
324
|
-
sx={{
|
|
325
|
-
flexGrow: 1,
|
|
326
|
-
Width: `${minWidthValue}px`,
|
|
327
|
-
textAlign: "left",
|
|
328
|
-
whiteSpace: "normal",
|
|
329
|
-
wordBreak: "break-word"
|
|
330
|
-
}}
|
|
331
|
-
>
|
|
332
|
-
<Typography variant={isSmallScreen ? "body2" : "subtitle1"}>{titleText}</Typography>
|
|
333
|
-
</Box>
|
|
334
|
-
|
|
335
|
-
{/* Khối phải */}
|
|
336
|
-
<Box
|
|
337
|
-
ref={rightBoxRef} // Tham chiếu đến phần tử này
|
|
272
|
+
<Card sx={{ p: 0, mb: 0 }}>
|
|
273
|
+
<Stack
|
|
274
|
+
direction={isFullRow ? "column" : "row"}
|
|
338
275
|
sx={{
|
|
339
|
-
|
|
340
|
-
flexWrap: "nowrap",
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
// minWidth: "300px", // Đảm bảo chiều rộng tối thiểu
|
|
345
|
-
width: rightBoxMinWidth ? `${rightBoxMinWidth}px` : "auto" // Sử dụng chiều rộng đo được
|
|
276
|
+
// mb: upXL ? 1 : 1,
|
|
277
|
+
flexWrap: isFullRow ? "nowrap" : "wrap",
|
|
278
|
+
alignItems: isFullRow ? "stretch" : "center",
|
|
279
|
+
gap: 1,
|
|
280
|
+
...slotProps?.header?.sx
|
|
346
281
|
}}
|
|
347
282
|
>
|
|
348
|
-
{
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
283
|
+
{/* Left side */}
|
|
284
|
+
<Box
|
|
285
|
+
ref={leftBoxRef}
|
|
286
|
+
sx={{
|
|
287
|
+
flexGrow: isFullRow ? 0 : 1,
|
|
288
|
+
width: isFullRow ? "100%" : `${minWidthValue}px`,
|
|
289
|
+
textAlign: "left",
|
|
290
|
+
whiteSpace: "normal",
|
|
291
|
+
wordBreak: "break-word"
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
<Typography
|
|
295
|
+
ref={titleRef}
|
|
296
|
+
noWrap
|
|
297
|
+
sx={{
|
|
298
|
+
width: "100%",
|
|
299
|
+
fontSize: {
|
|
300
|
+
xs: "0.85rem",
|
|
301
|
+
sm: "0.95rem",
|
|
302
|
+
md: "1rem"
|
|
303
|
+
},
|
|
304
|
+
textAlign: "left",
|
|
305
|
+
textTransform: "none"
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
308
|
+
{titleText}
|
|
309
|
+
</Typography>
|
|
310
|
+
</Box>
|
|
311
|
+
|
|
312
|
+
{/* Right side */}
|
|
313
|
+
<Box
|
|
314
|
+
ref={rightBoxRef}
|
|
315
|
+
sx={{
|
|
316
|
+
display: "flex",
|
|
317
|
+
flexWrap: "wrap",
|
|
318
|
+
justifyContent: isFullRow ? "flex-end" : "flex-end",
|
|
319
|
+
alignItems: "center",
|
|
320
|
+
width: isFullRow ? "100%" : "auto",
|
|
321
|
+
flexShrink: 0
|
|
322
|
+
}}
|
|
323
|
+
>
|
|
324
|
+
{(viewMode === "thongke" || viewMode === "bieudo") && (
|
|
325
|
+
<Tooltip title="Danh sách dữ liệu">
|
|
326
|
+
<IconButton
|
|
327
|
+
size={elementSize}
|
|
328
|
+
color="primary"
|
|
329
|
+
onClick={() => setViewMode("table")}
|
|
330
|
+
sx={{ ml: 0.5, mr: 0.5 }}
|
|
331
|
+
>
|
|
332
|
+
<TableChartIcon fontSize="inherit" />
|
|
333
|
+
</IconButton>
|
|
334
|
+
</Tooltip>
|
|
335
|
+
)}
|
|
336
|
+
|
|
337
|
+
{canThongKe && (
|
|
338
|
+
<Tooltip title="Thống kê">
|
|
339
|
+
<IconButton
|
|
340
|
+
size={elementSize}
|
|
341
|
+
color={viewMode === "thongke" ? "secondary" : "primary"}
|
|
342
|
+
onClick={() => setViewMode("thongke")}
|
|
343
|
+
sx={{ ml: 0.5, mr: 0.5 }}
|
|
344
|
+
>
|
|
345
|
+
<BarChartIcon fontSize="inherit" />
|
|
346
|
+
</IconButton>
|
|
347
|
+
</Tooltip>
|
|
348
|
+
)}
|
|
349
|
+
|
|
350
|
+
{canBieuDo && (
|
|
351
|
+
<Tooltip title="Biểu đồ">
|
|
352
|
+
<IconButton
|
|
353
|
+
size={elementSize}
|
|
354
|
+
color={viewMode === "bieudo" ? "secondary" : "primary"}
|
|
355
|
+
onClick={() => setViewMode("bieudo")}
|
|
356
|
+
sx={{ ml: 0.5, mr: 0.5 }}
|
|
357
|
+
>
|
|
358
|
+
<PieChartIcon fontSize="inherit" />
|
|
359
|
+
</IconButton>
|
|
360
|
+
</Tooltip>
|
|
361
|
+
)}
|
|
362
|
+
|
|
363
|
+
<HuongDanButton tableName={tableName} size={elementSize} />
|
|
364
|
+
<Tooltip title="Làm mới">
|
|
358
365
|
<IconButton
|
|
366
|
+
variant="outlined"
|
|
367
|
+
color="primary"
|
|
359
368
|
size={elementSize}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
369
|
+
onClick={() => {
|
|
370
|
+
setDataSearch({ ...defaults });
|
|
371
|
+
[...filters].forEach((filter) => {
|
|
372
|
+
filter?.onChange?.();
|
|
373
|
+
});
|
|
374
|
+
reset();
|
|
375
|
+
setValue("Search");
|
|
376
|
+
}}
|
|
363
377
|
>
|
|
364
|
-
<
|
|
378
|
+
<Refresh fontSize="inherit" />
|
|
365
379
|
</IconButton>
|
|
366
380
|
</Tooltip>
|
|
367
|
-
)}
|
|
368
381
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
size={elementSize}
|
|
373
|
-
color={viewMode === "bieudo" ? "secondary" : "primary"}
|
|
374
|
-
onClick={() => setViewMode("bieudo")}
|
|
375
|
-
sx={{ ml: 0.5, mr: 0.5 }}
|
|
376
|
-
>
|
|
377
|
-
<PieChartIcon fontSize="inherit" />
|
|
378
|
-
</IconButton>
|
|
379
|
-
</Tooltip>
|
|
380
|
-
)}
|
|
382
|
+
{titleButtons?.map((button, index) => (
|
|
383
|
+
<div key={index}>{button}</div>
|
|
384
|
+
))}
|
|
381
385
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
>
|
|
397
|
-
<Refresh fontSize="inherit" />
|
|
398
|
-
</IconButton>
|
|
399
|
-
</Tooltip>
|
|
400
|
-
|
|
401
|
-
{titleButtons?.map((button, index) => (
|
|
402
|
-
<div key={index}>{button}</div>
|
|
403
|
-
))}
|
|
404
|
-
|
|
405
|
-
<ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
|
|
406
|
-
|
|
407
|
-
{canCreate && !disableAdd && (
|
|
408
|
-
<Button
|
|
409
|
-
size={elementSize}
|
|
410
|
-
variant="contained"
|
|
411
|
-
startIcon={<Add fontSize="inherit" />}
|
|
412
|
-
onClick={(e) => {
|
|
413
|
-
if (!disableEditor) {
|
|
414
|
-
setOpenEditorDialog(true);
|
|
415
|
-
setSelectedEditItem(null);
|
|
416
|
-
}
|
|
417
|
-
onAddClick(e);
|
|
418
|
-
}}
|
|
419
|
-
sx={{
|
|
420
|
-
ml: 0.5,
|
|
421
|
-
mr: 0.5,
|
|
422
|
-
minWidth: isSmallScreen ? "100px" : "130px",
|
|
423
|
-
textTransform: "none" // ✅ Không viết hoa tự động
|
|
424
|
-
}}
|
|
425
|
-
>
|
|
426
|
-
<Typography
|
|
427
|
-
noWrap
|
|
386
|
+
<ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
|
|
387
|
+
|
|
388
|
+
{canCreate && !disableAdd && (
|
|
389
|
+
<Button
|
|
390
|
+
size={elementSize}
|
|
391
|
+
variant="contained"
|
|
392
|
+
startIcon={<Add fontSize="inherit" />}
|
|
393
|
+
onClick={(e) => {
|
|
394
|
+
if (!disableEditor) {
|
|
395
|
+
setOpenEditorDialog(true);
|
|
396
|
+
setSelectedEditItem(null);
|
|
397
|
+
}
|
|
398
|
+
onAddClick(e);
|
|
399
|
+
}}
|
|
428
400
|
sx={{
|
|
429
|
-
|
|
430
|
-
fontSize: {
|
|
431
|
-
xs: "0.75rem",
|
|
432
|
-
sm: "0.875rem",
|
|
433
|
-
md: "1rem"
|
|
434
|
-
},
|
|
435
|
-
textAlign: "center",
|
|
436
|
-
textTransform: "none" // ✅ Đảm bảo chữ gốc giữ nguyên
|
|
401
|
+
ml: 0.5
|
|
437
402
|
}}
|
|
438
403
|
>
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
404
|
+
<Typography
|
|
405
|
+
noWrap
|
|
406
|
+
sx={{
|
|
407
|
+
width: "100%",
|
|
408
|
+
fontSize: {
|
|
409
|
+
xs: "0.65rem",
|
|
410
|
+
sm: "0.75rem",
|
|
411
|
+
md: "0.85rem"
|
|
412
|
+
},
|
|
413
|
+
textAlign: "center",
|
|
414
|
+
textTransform: "none"
|
|
415
|
+
}}
|
|
416
|
+
>
|
|
417
|
+
{titleAddButton}
|
|
418
|
+
</Typography>
|
|
419
|
+
</Button>
|
|
420
|
+
)}
|
|
421
|
+
</Box>
|
|
422
|
+
</Stack>
|
|
423
|
+
</Card>
|
|
445
424
|
)}
|
|
446
|
-
|
|
447
425
|
<Card className="custom-card-DataManagement">
|
|
448
426
|
{tabPanel}
|
|
449
427
|
{viewMode === "table" && (
|