trithuc-mvc-react 1.6.6 → 1.6.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.
- package/components/DataManagement/DataTable.jsx +14 -7
- package/components/DataManagement/EditorForm.jsx +3 -1
- package/components/DataManagement/FilterElement.jsx +0 -1
- package/components/DataManagement/FormField.jsx +11 -4
- package/components/DataManagement/TableRowRender.jsx +14 -7
- package/components/DataManagement/ViewDetailDialog.jsx +100 -0
- package/components/DataManagement/index.jsx +28 -27
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ import TableToolbar from "./TableToolbar";
|
|
|
14
14
|
import { useDataTable, usePermission } from "./hooks";
|
|
15
15
|
|
|
16
16
|
const DataTable = () => {
|
|
17
|
-
const { tableName, selectedField, columns, dataSearch, setOpenEditorDialog, setSelectedEditItem } = useDataTable();
|
|
17
|
+
const { tableName, selectedField, columns, dataSearch, setOpenEditorDialog, setSelectedEditItem, setOpenViewDialog, onEditClick } = useDataTable();
|
|
18
18
|
const { setPermission, Permission } = usePermission();
|
|
19
19
|
const queryClient = useQueryClient();
|
|
20
20
|
const confirm = useConfirm();
|
|
@@ -80,7 +80,7 @@ const DataTable = () => {
|
|
|
80
80
|
tableName
|
|
81
81
|
});
|
|
82
82
|
})
|
|
83
|
-
.catch(() => {});
|
|
83
|
+
.catch(() => { });
|
|
84
84
|
};
|
|
85
85
|
const handleChangeStatus = (Id) => {
|
|
86
86
|
changeStatusMutation.mutate({
|
|
@@ -91,21 +91,27 @@ const DataTable = () => {
|
|
|
91
91
|
const handlEdit = (item) => {
|
|
92
92
|
setOpenEditorDialog(true);
|
|
93
93
|
setSelectedEditItem(item);
|
|
94
|
+
onEditClick(item);
|
|
95
|
+
|
|
96
|
+
};
|
|
97
|
+
const handlViewDetail = (item) => {
|
|
98
|
+
setOpenViewDialog(true);
|
|
99
|
+
setSelectedEditItem(item);
|
|
94
100
|
};
|
|
95
101
|
const { rows, total } = useMemo(() => {
|
|
96
102
|
let rows = data?.data ?? [];
|
|
97
103
|
let total = data?.total ?? 0;
|
|
98
|
-
|
|
104
|
+
|
|
99
105
|
return {
|
|
100
106
|
rows: rows,
|
|
101
107
|
total
|
|
102
108
|
};
|
|
103
109
|
}, [data]);
|
|
104
110
|
|
|
105
|
-
useEffect(()=>{
|
|
111
|
+
useEffect(() => {
|
|
106
112
|
let PermissionModel = data?.PermissionModel;
|
|
107
|
-
PermissionModel&&setPermission(PermissionModel);
|
|
108
|
-
},[rows])
|
|
113
|
+
PermissionModel && setPermission(PermissionModel);
|
|
114
|
+
}, [rows])
|
|
109
115
|
|
|
110
116
|
const handleChangePage = (event, newPage) => {
|
|
111
117
|
setPage(newPage);
|
|
@@ -147,7 +153,7 @@ const DataTable = () => {
|
|
|
147
153
|
ids: selected
|
|
148
154
|
});
|
|
149
155
|
})
|
|
150
|
-
.catch(() => {});
|
|
156
|
+
.catch(() => { });
|
|
151
157
|
};
|
|
152
158
|
const theme = useTheme();
|
|
153
159
|
const downXL = useMediaQuery(theme.breakpoints.down("xl"));
|
|
@@ -180,6 +186,7 @@ const DataTable = () => {
|
|
|
180
186
|
selected={isSelected(row[selectedField])}
|
|
181
187
|
onSelect={handleSelect}
|
|
182
188
|
onEdit={handlEdit}
|
|
189
|
+
onView={handlViewDetail}
|
|
183
190
|
onChangeStatus={handleChangeStatus}
|
|
184
191
|
onDelete={handleDelete}
|
|
185
192
|
/>
|
|
@@ -115,7 +115,8 @@ function EditorForm({ fields, submitRef }) {
|
|
|
115
115
|
keyLabel,
|
|
116
116
|
keyValue,
|
|
117
117
|
keyValueLabel,
|
|
118
|
-
required
|
|
118
|
+
required,
|
|
119
|
+
disabled=false
|
|
119
120
|
}) => {
|
|
120
121
|
return (
|
|
121
122
|
<Grid item md={size?.md} xs={size?.xs} sm={size?.sm} key={field}>
|
|
@@ -133,6 +134,7 @@ function EditorForm({ fields, submitRef }) {
|
|
|
133
134
|
keyValue={keyValue}
|
|
134
135
|
size={elementSize}
|
|
135
136
|
required={required}
|
|
137
|
+
disabled={disabled}
|
|
136
138
|
/>
|
|
137
139
|
</Grid>
|
|
138
140
|
);
|
|
@@ -40,7 +40,8 @@ function FormField({
|
|
|
40
40
|
keyValueLabel,
|
|
41
41
|
childrenFields,
|
|
42
42
|
size,
|
|
43
|
-
required = false
|
|
43
|
+
required = false,
|
|
44
|
+
disabled = false
|
|
44
45
|
}) {
|
|
45
46
|
const { setValue, register } = useFormContext();
|
|
46
47
|
const getValueObject = useCallback(
|
|
@@ -71,6 +72,7 @@ function FormField({
|
|
|
71
72
|
<TextField
|
|
72
73
|
{...field}
|
|
73
74
|
multiline
|
|
75
|
+
disabled={disabled}
|
|
74
76
|
label={label + (required ? "*" : "")}
|
|
75
77
|
error={Boolean(error)}
|
|
76
78
|
helperText={error?.message}
|
|
@@ -98,6 +100,7 @@ function FormField({
|
|
|
98
100
|
helperText={error?.message}
|
|
99
101
|
fullWidth
|
|
100
102
|
placeholder={label}
|
|
103
|
+
disabled={disabled}
|
|
101
104
|
size={size}
|
|
102
105
|
/>
|
|
103
106
|
);
|
|
@@ -115,6 +118,7 @@ function FormField({
|
|
|
115
118
|
render={({ field: { onChange: onFieldChange, value }, fieldState: { error } }) => {
|
|
116
119
|
return (
|
|
117
120
|
<Autocomplete
|
|
121
|
+
disabled={disabled}
|
|
118
122
|
disablePortal
|
|
119
123
|
noOptionsText="Không có dữ liệu"
|
|
120
124
|
loading={loading}
|
|
@@ -145,7 +149,7 @@ function FormField({
|
|
|
145
149
|
render={({ field }) => (
|
|
146
150
|
<FormControl sx={{ minWidth: 160 }} fullWidth size={size}>
|
|
147
151
|
<InputLabel>{label}</InputLabel>
|
|
148
|
-
<Select {...field} label={label}>
|
|
152
|
+
<Select {...field} label={label} disabled={disabled}>
|
|
149
153
|
{[...datas].map(({ label, value }) => (
|
|
150
154
|
<MenuItem key={value} value={value}>
|
|
151
155
|
{label}
|
|
@@ -162,6 +166,7 @@ function FormField({
|
|
|
162
166
|
name={name}
|
|
163
167
|
control={control}
|
|
164
168
|
defaultValue={null}
|
|
169
|
+
disabled={disabled}
|
|
165
170
|
render={({ field }) => {
|
|
166
171
|
return (
|
|
167
172
|
<FormControl fullWidth size={size}>
|
|
@@ -182,7 +187,7 @@ function FormField({
|
|
|
182
187
|
<Controller
|
|
183
188
|
name={name}
|
|
184
189
|
control={control}
|
|
185
|
-
render={({ field }) => <FormControlLabel control={<Checkbox {...field} checked={field.value} />} label={label} />}
|
|
190
|
+
render={({ field }) => <FormControlLabel disabled={disabled} control={<Checkbox {...field} checked={field.value} />} label={label} />}
|
|
186
191
|
/>
|
|
187
192
|
|
|
188
193
|
{/* {errors.terms && (
|
|
@@ -197,6 +202,7 @@ function FormField({
|
|
|
197
202
|
<Controller
|
|
198
203
|
name={name}
|
|
199
204
|
control={control}
|
|
205
|
+
disabled={disabled}
|
|
200
206
|
defaultValue={true}
|
|
201
207
|
render={({ field }) => <FormControlLabel control={<Switch {...field} checked={field.value} />} label={label} />}
|
|
202
208
|
/>
|
|
@@ -212,6 +218,7 @@ function FormField({
|
|
|
212
218
|
}
|
|
213
219
|
return (
|
|
214
220
|
<DatePicker
|
|
221
|
+
disabled={disabled}
|
|
215
222
|
label={label + (required ? "*" : "")}
|
|
216
223
|
{...field}
|
|
217
224
|
format={DEFAULT_DATE_FORMAT}
|
|
@@ -271,7 +278,7 @@ function FormField({
|
|
|
271
278
|
render={({ field, fieldState: { error } }) => {
|
|
272
279
|
return (
|
|
273
280
|
<>
|
|
274
|
-
<UploadMultipleFile {...field} name="DinhKem" />
|
|
281
|
+
<UploadMultipleFile {...field} name="DinhKem" disabled={disabled} />
|
|
275
282
|
</>
|
|
276
283
|
);
|
|
277
284
|
}}
|
|
@@ -2,14 +2,15 @@ import { Checkbox, IconButton, Switch, TableCell, TableRow, Tooltip, Toolbar, us
|
|
|
2
2
|
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
|
|
3
3
|
import { EditOutlined } from "@mui/icons-material";
|
|
4
4
|
import { useDataTable, usePermission } from "./hooks";
|
|
5
|
+
import RemoveRedEyeOutlinedIcon from "@mui/icons-material/RemoveRedEyeOutlined";
|
|
5
6
|
|
|
6
7
|
// material
|
|
7
8
|
import { styled, useTheme } from "@mui/material/styles";
|
|
8
9
|
import MoreMenu from "../MoreMenu";
|
|
9
10
|
import { useMemo } from "react";
|
|
10
11
|
|
|
11
|
-
export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus, onDelete, onEdit }) => {
|
|
12
|
-
const { selectedField, columns, statusKey, disableStatus, tableActions,disableCellThaoTac } = useDataTable();
|
|
12
|
+
export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus, onDelete, onEdit, onView }) => {
|
|
13
|
+
const { selectedField, columns, statusKey, disableStatus, tableActions, disableCellThaoTac } = useDataTable();
|
|
13
14
|
const { canEdit, canDelete, canView, canAction } = usePermission();
|
|
14
15
|
|
|
15
16
|
const { tableActionsOnTable, tableActionsOnMoreMenu } = useMemo(() => {
|
|
@@ -26,8 +27,8 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
|
|
|
26
27
|
const tableActionsOnMoreMenu = tableActionsAfterFilter.filter(({ isOnTable = false }) => !isOnTable);
|
|
27
28
|
return { tableActionsOnTable, tableActionsOnMoreMenu };
|
|
28
29
|
}, [canView, canAction, tableActions]);
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const theme = useTheme();
|
|
31
|
+
const downXl = useMediaQuery(theme.breakpoints.down("xl"));
|
|
31
32
|
return (
|
|
32
33
|
<TableRow hover key={row[selectedField]} selected={selected}>
|
|
33
34
|
<TableCell padding="checkbox">
|
|
@@ -72,11 +73,17 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
|
|
|
72
73
|
{canEdit && (
|
|
73
74
|
<Tooltip title="Chỉnh sửa">
|
|
74
75
|
<IconButton size={downXl ? "small" : "medium"} onClick={() => onEdit(row)}>
|
|
75
|
-
<EditOutlined color="primary" />
|
|
76
|
+
<EditOutlined fontSize="inherit" color="primary" />
|
|
77
|
+
</IconButton>
|
|
78
|
+
</Tooltip>
|
|
79
|
+
)}
|
|
80
|
+
{canView && !tableActions?.some(({ permissionType }) => permissionType == "view") && (
|
|
81
|
+
<Tooltip title="Xem chi tiết modal">
|
|
82
|
+
<IconButton onClick={() => onView(row)}>
|
|
83
|
+
<RemoveRedEyeOutlinedIcon color="info" />
|
|
76
84
|
</IconButton>
|
|
77
85
|
</Tooltip>
|
|
78
86
|
)}
|
|
79
|
-
|
|
80
87
|
{canDelete && (
|
|
81
88
|
<Tooltip title="Xóa">
|
|
82
89
|
<IconButton size={downXl ? "small" : "medium"}
|
|
@@ -84,7 +91,7 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
|
|
|
84
91
|
onDelete(row[selectedField]);
|
|
85
92
|
}}
|
|
86
93
|
>
|
|
87
|
-
<DeleteOutlineIcon color="error" />
|
|
94
|
+
<DeleteOutlineIcon fontSize="inherit" color="error" />
|
|
88
95
|
</IconButton>
|
|
89
96
|
</Tooltip>
|
|
90
97
|
)}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Button from "@mui/material/Button";
|
|
2
|
+
|
|
3
|
+
import Dialog from "@mui/material/Dialog";
|
|
4
|
+
import DialogActions from "@mui/material/DialogActions";
|
|
5
|
+
import DialogContent from "@mui/material/DialogContent";
|
|
6
|
+
import DialogTitle from "@mui/material/DialogTitle";
|
|
7
|
+
import IconButton from "@mui/material/IconButton";
|
|
8
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
9
|
+
|
|
10
|
+
import { useEffect, useRef, useState } from "react";
|
|
11
|
+
import PropTypes from "prop-types";
|
|
12
|
+
|
|
13
|
+
import EditorForm from "trithuc-mvc-react/components/DataManagement/EditorForm";
|
|
14
|
+
import { useDataTable, usePermission } from "trithuc-mvc-react/components/DataManagement/hooks";
|
|
15
|
+
ViewDetailDialog.propTypes = {
|
|
16
|
+
open: PropTypes.bool,
|
|
17
|
+
onClose: PropTypes.func,
|
|
18
|
+
};
|
|
19
|
+
import { Stack, Typography, List, ListItem, ListItemText, ListItemIcon, Link, Grid } from "@mui/material";
|
|
20
|
+
import TextSnippetOutlinedIcon from "@mui/icons-material/TextSnippetOutlined";
|
|
21
|
+
export const FieldView = ({ label, value }) => {
|
|
22
|
+
return (
|
|
23
|
+
<Stack direction="column">
|
|
24
|
+
<Typography variant="body2" sx={{ color: "text.secondary", }}>
|
|
25
|
+
{label}
|
|
26
|
+
</Typography>
|
|
27
|
+
<Typography variant="subtitle2">{value}</Typography>
|
|
28
|
+
</Stack>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const ListFileView = ({ files }) => {
|
|
33
|
+
let hasFile = files?.length > 0;
|
|
34
|
+
|
|
35
|
+
if (!hasFile) {
|
|
36
|
+
return <Typography variant="subtitle2">Chưa có file</Typography>;
|
|
37
|
+
}
|
|
38
|
+
return (
|
|
39
|
+
<List disablePadding sx={{ ...(hasFile && { my: 3 }) }}>
|
|
40
|
+
{files?.map(({ urlFile, tenFile, Size }) => {
|
|
41
|
+
return (
|
|
42
|
+
<ListItem
|
|
43
|
+
key={urlFile}
|
|
44
|
+
// {...varFadeInRight}
|
|
45
|
+
sx={{
|
|
46
|
+
my: 1,
|
|
47
|
+
py: 0.75,
|
|
48
|
+
px: 2,
|
|
49
|
+
borderRadius: 1,
|
|
50
|
+
border: (theme) => `solid 1px ${theme.palette.divider}`,
|
|
51
|
+
bgcolor: "background.paper"
|
|
52
|
+
}}
|
|
53
|
+
>
|
|
54
|
+
<ListItemIcon>
|
|
55
|
+
{/* <Icon icon={fileFill} width={28} height={28} /> */}
|
|
56
|
+
<TextSnippetOutlinedIcon color="info" />
|
|
57
|
+
</ListItemIcon>
|
|
58
|
+
<ListItemText
|
|
59
|
+
primary={<Link href={urlFile} >{tenFile}</Link>}
|
|
60
|
+
secondary={Size}
|
|
61
|
+
primaryTypographyProps={{ variant: "subtitle2" }}
|
|
62
|
+
secondaryTypographyProps={{ variant: "caption" }}
|
|
63
|
+
/>
|
|
64
|
+
</ListItem>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</List>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function ViewDetailDialog({ open, onClose = () => { }, fields = [] }) {
|
|
72
|
+
|
|
73
|
+
const fieldsView = fields.map(field=>({...field, disabled: true}));
|
|
74
|
+
return (
|
|
75
|
+
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth={true} scroll={'body'}>
|
|
76
|
+
<DialogTitle>
|
|
77
|
+
Xem
|
|
78
|
+
<IconButton
|
|
79
|
+
aria-label="close"
|
|
80
|
+
onClick={onClose}
|
|
81
|
+
sx={{
|
|
82
|
+
position: "absolute",
|
|
83
|
+
right: 8,
|
|
84
|
+
top: 8,
|
|
85
|
+
color: (theme) => theme.palette.grey[500]
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<CloseIcon />
|
|
89
|
+
</IconButton>
|
|
90
|
+
</DialogTitle>
|
|
91
|
+
<DialogContent dividers={true}>
|
|
92
|
+
<EditorForm fields={fieldsView} />
|
|
93
|
+
</DialogContent>
|
|
94
|
+
<DialogActions>
|
|
95
|
+
<Button onClick={onClose}>Đóng</Button>
|
|
96
|
+
</DialogActions>
|
|
97
|
+
</Dialog>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
export default ViewDetailDialog;
|
|
@@ -9,10 +9,11 @@ import { Add, Refresh } from "@mui/icons-material";
|
|
|
9
9
|
|
|
10
10
|
import { FormProvider, useForm } from "react-hook-form";
|
|
11
11
|
import EditorDialog from "./EditorDialog";
|
|
12
|
+
import ViewDetailDialog from "./ViewDetailDialog";
|
|
12
13
|
|
|
13
14
|
import ExportExcelButton from "./ExportExcelButton";
|
|
14
15
|
import { FilterGod } from "./FilterGod";
|
|
15
|
-
|
|
16
|
+
|
|
16
17
|
|
|
17
18
|
DataManagement.propTypes = {
|
|
18
19
|
columns: PropTypes.array,
|
|
@@ -56,8 +57,8 @@ function DataManagement({
|
|
|
56
57
|
disableCellThaoTac = false,
|
|
57
58
|
tableActions = [],
|
|
58
59
|
disableEditor = false,
|
|
59
|
-
onAddClick = () => {},
|
|
60
|
-
onEditClick = () => {},
|
|
60
|
+
onAddClick = () => { },
|
|
61
|
+
onEditClick = () => { },
|
|
61
62
|
tabPanel,
|
|
62
63
|
backParentNavigator,
|
|
63
64
|
slotProps = {
|
|
@@ -68,6 +69,7 @@ function DataManagement({
|
|
|
68
69
|
}) {
|
|
69
70
|
const [openEditorDialog, setOpenEditorDialog] = useState(false);
|
|
70
71
|
const [selectedEditItem, setSelectedEditItem] = useState(null);
|
|
72
|
+
const [openViewDialog, setOpenViewDialog] = useState(false);
|
|
71
73
|
|
|
72
74
|
const [Permission, setPermission] = useState(null);
|
|
73
75
|
|
|
@@ -89,11 +91,7 @@ function DataManagement({
|
|
|
89
91
|
return elementSize;
|
|
90
92
|
}, [upXL]);
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
if (selectedEditItem && disableEditor) {
|
|
94
|
-
onEditClick(selectedEditItem);
|
|
95
|
-
}
|
|
96
|
-
}, [selectedEditItem]);
|
|
94
|
+
|
|
97
95
|
useEffect(() => {
|
|
98
96
|
setDataSearch({ ...dataSearch, ...defaults });
|
|
99
97
|
}, [filters]);
|
|
@@ -107,6 +105,7 @@ function DataManagement({
|
|
|
107
105
|
selectedEditItem,
|
|
108
106
|
setSelectedEditItem,
|
|
109
107
|
setOpenEditorDialog,
|
|
108
|
+
setOpenViewDialog,
|
|
110
109
|
dataSearch,
|
|
111
110
|
setDataSearch,
|
|
112
111
|
validationSchema,
|
|
@@ -114,9 +113,10 @@ function DataManagement({
|
|
|
114
113
|
disableStatus,
|
|
115
114
|
disableCellThaoTac,
|
|
116
115
|
disableAdd,
|
|
117
|
-
tableActions
|
|
116
|
+
tableActions,
|
|
117
|
+
onEditClick
|
|
118
118
|
};
|
|
119
|
-
}, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema, tableActions]);
|
|
119
|
+
}, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema, tableActions, openViewDialog, setOpenViewDialog, onEditClick]);
|
|
120
120
|
const permissionValues = useMemo(() => {
|
|
121
121
|
return {
|
|
122
122
|
Permission,
|
|
@@ -147,28 +147,28 @@ function DataManagement({
|
|
|
147
147
|
setValue("Search");
|
|
148
148
|
}}
|
|
149
149
|
>
|
|
150
|
-
<Refresh />
|
|
150
|
+
<Refresh fontSize="inherit" />
|
|
151
151
|
</IconButton>
|
|
152
152
|
</Tooltip>
|
|
153
153
|
|
|
154
154
|
<ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
|
|
155
155
|
{(!Permission || Permission.Create) && (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
156
|
+
(!disableAdd) && (
|
|
157
|
+
<Button
|
|
158
|
+
size={elementSize}
|
|
159
|
+
variant="contained"
|
|
160
|
+
startIcon={<Add fontSize="inherit" />}
|
|
161
|
+
onClick={(e) => {
|
|
162
|
+
if (!disableEditor) {
|
|
163
|
+
setOpenEditorDialog(true);
|
|
164
|
+
setSelectedEditItem(null);
|
|
165
|
+
}
|
|
166
|
+
onAddClick(e);
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
Thêm
|
|
170
|
+
</Button>
|
|
171
|
+
)
|
|
172
172
|
)}
|
|
173
173
|
</Stack>
|
|
174
174
|
</Stack>
|
|
@@ -188,6 +188,7 @@ function DataManagement({
|
|
|
188
188
|
fields={editorFields}
|
|
189
189
|
/>
|
|
190
190
|
)}
|
|
191
|
+
<ViewDetailDialog open={openViewDialog} onClose={() => setOpenViewDialog(false)} fields={editorFields} />
|
|
191
192
|
</PermissionContext.Provider>
|
|
192
193
|
</DataTableContext.Provider>
|
|
193
194
|
</>
|