trithuc-mvc-react 1.4.0 → 1.5.1

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
@@ -18,6 +18,7 @@ export const getDatasFromTable = async ({ tableName, page, pageSize, data }) =>
18
18
  });
19
19
  return res.data;
20
20
  };
21
+
21
22
  export const getDataFromTable = async ({ tableName, id }) => {
22
23
  const res = await api.get(`/Admin/${tableName}/GetDetail`, {
23
24
  params: {
@@ -1,15 +1,10 @@
1
- import { Table, TableBody, TableContainer } from "@mui/material";
1
+ import { Table, TableBody, TableCell, TableContainer, TableRow } from "@mui/material";
2
2
  import TablePaginationCustom from "../table/TablePagination";
3
3
  import { useMemo, useState } from "react";
4
4
 
5
5
  import { TableHead } from "./TableHead";
6
6
  import { useMutation, useQuery, useQueryClient } from "react-query";
7
- import {
8
- changeStatusDataToTable,
9
- deleteDataFromTable,
10
- deleteMultipleDataFromTable,
11
- getDatasFromTable,
12
- } from "../../api";
7
+ import { changeStatusDataToTable, deleteDataFromTable, deleteMultipleDataFromTable, getDatasFromTable } from "../../api";
13
8
  import TableRowsLoader from "../table/TableRowsLoader";
14
9
  import { toast } from "react-toastify";
15
10
  import { useConfirm } from "material-ui-confirm";
@@ -33,14 +28,14 @@ const DataTable = () => {
33
28
  tableName: tableName,
34
29
  page: page + 1,
35
30
  pageSize: rowsPerPage,
36
- data: dataSearch,
31
+ data: dataSearch
37
32
  }),
38
33
  keepPreviousData: true,
39
34
  onSuccess: ({ PermissionModel, status }) => {
40
35
  if (!Permission && status) {
41
36
  setPermission(PermissionModel);
42
37
  }
43
- },
38
+ }
44
39
  });
45
40
  const changeStatusMutation = useMutation(changeStatusDataToTable, {
46
41
  onSuccess: () => {
@@ -49,7 +44,7 @@ const DataTable = () => {
49
44
  },
50
45
  onError: () => {
51
46
  toast.error(" Có lỗi xảy ra !");
52
- },
47
+ }
53
48
  });
54
49
  const deleteMutation = useMutation(deleteDataFromTable, {
55
50
  onSuccess: ({ status }) => {
@@ -63,7 +58,7 @@ const DataTable = () => {
63
58
  },
64
59
  onError: () => {
65
60
  toast.error(" Có lỗi xảy ra !");
66
- },
61
+ }
67
62
  });
68
63
  const deleteMultipleMutation = useMutation(deleteMultipleDataFromTable, {
69
64
  onSuccess: () => {
@@ -73,7 +68,7 @@ const DataTable = () => {
73
68
  },
74
69
  onError: () => {
75
70
  toast.error(" Có lỗi xảy ra !");
76
- },
71
+ }
77
72
  });
78
73
 
79
74
  const handleDelete = (id) => {
@@ -81,7 +76,7 @@ const DataTable = () => {
81
76
  .then(() => {
82
77
  deleteMutation.mutate({
83
78
  id,
84
- tableName,
79
+ tableName
85
80
  });
86
81
  })
87
82
  .catch(() => {});
@@ -89,7 +84,7 @@ const DataTable = () => {
89
84
  const handleChangeStatus = (Id) => {
90
85
  changeStatusMutation.mutate({
91
86
  tableName,
92
- id: Id,
87
+ id: Id
93
88
  });
94
89
  };
95
90
  const handlEdit = (item) => {
@@ -101,7 +96,7 @@ const DataTable = () => {
101
96
  let total = data?.total ?? 0;
102
97
  return {
103
98
  rows: rows,
104
- total,
99
+ total
105
100
  };
106
101
  }, [data]);
107
102
 
@@ -142,7 +137,7 @@ const DataTable = () => {
142
137
  .then(() => {
143
138
  deleteMultipleMutation.mutate({
144
139
  tableName,
145
- ids: selected,
140
+ ids: selected
146
141
  });
147
142
  })
148
143
  .catch(() => {});
@@ -157,12 +152,7 @@ const DataTable = () => {
157
152
  onDeleteMultiple={handleDeleteMultiple}
158
153
  />
159
154
  <Table className="border">
160
- <TableHead
161
- headLabel={columns}
162
- onSelectAllClick={handleSelectAllClick}
163
- numSelected={selected?.length}
164
- rowCount={rows.length}
165
- />
155
+ <TableHead headLabel={columns} onSelectAllClick={handleSelectAllClick} numSelected={selected?.length} rowCount={rows.length} />
166
156
  {isLoading ? (
167
157
  <TableRowsLoader rowsNum={5} colsNum={columns.length + 4} />
168
158
  ) : (
@@ -179,6 +169,12 @@ const DataTable = () => {
179
169
  onDelete={handleDelete}
180
170
  />
181
171
  ))}
172
+
173
+ {rows?.length == 0 && (
174
+ <TableRow>
175
+ <TableCell colSpan={columns.length + 4} align="center">Không có dữ liệu</TableCell>
176
+ </TableRow>
177
+ )}
182
178
  </TableBody>
183
179
  )}
184
180
  </Table>
@@ -3,7 +3,7 @@ import { exportExcel } from "../../api";
3
3
  import { Button } from "@mui/material";
4
4
  import { Download } from "@mui/icons-material";
5
5
 
6
- const ExportExcelButton = ({ tableName, data }) => {
6
+ const ExportExcelButton = ({ tableName, data, size = "small" }) => {
7
7
  const handleExportExcel = async (tableName, data) => {
8
8
  const _data = await exportExcel({ tableName, data });
9
9
  if (_data.status) {
@@ -14,14 +14,15 @@ const ExportExcelButton = ({ tableName, data }) => {
14
14
  };
15
15
  return (
16
16
  <Button
17
+ size={size}
17
18
  variant="outlined"
18
19
  startIcon={<Download />}
19
20
  onClick={() => {
20
- handleExportExcel(tableName,data);
21
+ handleExportExcel(tableName, data);
21
22
  }}
22
23
  >
23
24
  Excel
24
25
  </Button>
25
26
  );
26
- }
27
- export default ExportExcelButton;
27
+ };
28
+ export default ExportExcelButton;
@@ -11,7 +11,7 @@ import { useController, useFormContext } from "react-hook-form";
11
11
  import { debounce } from "lodash";
12
12
  import { useDataTable } from "./hooks";
13
13
 
14
- export function FilterElement({ name, type, label, keyValue, keyLabel, childrenFields, datas, loading = false, onChange = () => { } }) {
14
+ export function FilterElement({ name, type, label, keyValue, keyLabel, childrenFields, datas, loading = false, onChange = () => { }, size= "small" }) {
15
15
  const { control, setValue } = useFormContext();
16
16
 
17
17
  const {
@@ -29,6 +29,7 @@ export function FilterElement({ name, type, label, keyValue, keyLabel, childrenF
29
29
 
30
30
  switch (type) {
31
31
  case "search":
32
+ console.log(size)
32
33
  return (
33
34
  <OutlinedInput
34
35
  {...rest}
@@ -38,11 +39,14 @@ export function FilterElement({ name, type, label, keyValue, keyLabel, childrenF
38
39
  onFieldChange(e);
39
40
  handleFilterChangeDebounce(name, e.target.value);
40
41
  }}
41
- size="small"
42
+ size={size}
42
43
  placeholder={label}
43
- startAdornment={<InputAdornment position="start">
44
- <SearchOutlined />
45
- </InputAdornment>} />
44
+ startAdornment={
45
+ <InputAdornment position="start">
46
+ <SearchOutlined />
47
+ </InputAdornment>
48
+ }
49
+ />
46
50
  );
47
51
 
48
52
  case "autocomplete":
@@ -51,7 +55,7 @@ export function FilterElement({ name, type, label, keyValue, keyLabel, childrenF
51
55
  {...name}
52
56
  disablePortal
53
57
  loading={loading}
54
- size="small"
58
+ size={size}
55
59
  noOptionsText="Không có dữ liệu"
56
60
  fullWidth
57
61
  options={datas ?? []}
@@ -81,6 +85,7 @@ export function FilterElement({ name, type, label, keyValue, keyLabel, childrenF
81
85
  id="demo-simple-select"
82
86
  name={name}
83
87
  fullWidth
88
+ size={size}
84
89
  label={label}
85
90
  value={value ?? ""}
86
91
  onChange={(e) => {
@@ -1,11 +1,11 @@
1
- import { Slider, Toolbar } from "@mui/material";
1
+ import { Slider, Toolbar } from "@mui/material";
2
2
  import { useFormContext } from "react-hook-form";
3
3
  import Grid from "@mui/material/Unstable_Grid2";
4
4
  import DateRangePicker from "../date/DateRangePicker";
5
5
  import { FilterElement } from "./FilterElement";
6
6
  import { useDataTable } from "./hooks";
7
7
 
8
- export const FilterGod = ({ filters }) => {
8
+ export const FilterGod = ({ filters, elementSize = "small" }) => {
9
9
  const { handleSubmit } = useFormContext();
10
10
  const onSubmit = (data) => console.log(data);
11
11
  const { setDataSearch, dataSearch } = useDataTable();
@@ -17,8 +17,9 @@ export const FilterGod = ({ filters }) => {
17
17
  onSubmit={handleSubmit(onSubmit)}
18
18
  sx={{
19
19
  px: 1,
20
- my: 2,
21
- display: "block"
20
+ my: elementSize == "small" ? 0 : 2,
21
+ display: "flex",
22
+ alignItems: "center"
22
23
  }}
23
24
  >
24
25
  <Grid
@@ -26,7 +27,8 @@ export const FilterGod = ({ filters }) => {
26
27
  rowSpacing={2}
27
28
  columnSpacing={{ xs: 2 }}
28
29
  sx={{
29
- px: 1
30
+ px: 1,
31
+ flex: 1
30
32
  }}
31
33
  >
32
34
  {filters.map(({ field, ...rest }) => {
@@ -41,7 +43,7 @@ export const FilterGod = ({ filters }) => {
41
43
  [field[1]]: value[1]
42
44
  }));
43
45
  }}
44
- size="small"
46
+ size={elementSize}
45
47
  value={[dataSearch?.[field[0]], dataSearch?.[field[1]]]}
46
48
  />
47
49
  </Grid>
@@ -50,9 +52,16 @@ export const FilterGod = ({ filters }) => {
50
52
 
51
53
  if (rest.type === "slider-range") {
52
54
  return (
53
- <Grid key={field.toString()} xs={12} sm={6} md={4} xl={3} sx={{
54
- px:4
55
- }}>
55
+ <Grid
56
+ key={field.toString()}
57
+ xs={12}
58
+ sm={6}
59
+ md={4}
60
+ xl={3}
61
+ sx={{
62
+ px: 4
63
+ }}
64
+ >
56
65
  <Slider
57
66
  onChange={(e, value) => {
58
67
  setDataSearch({
@@ -61,7 +70,7 @@ export const FilterGod = ({ filters }) => {
61
70
  [field[1]]: value[1]
62
71
  });
63
72
  }}
64
- size="small"
73
+ size={elementSize}
65
74
  marks={rest.marks}
66
75
  defaultValue={rest.defaultValue}
67
76
  valueLabelDisplay="auto"
@@ -73,7 +82,7 @@ export const FilterGod = ({ filters }) => {
73
82
  }
74
83
  return (
75
84
  <Grid key={field} xs={12} sm={6} md={4} xl={3}>
76
- <FilterElement name={field} {...rest} />
85
+ <FilterElement name={field} {...rest} size={elementSize} />
77
86
  </Grid>
78
87
  );
79
88
  })}
@@ -218,18 +218,20 @@ function FormField({
218
218
  format={DEFAULT_DATE_FORMAT}
219
219
  slotProps={{
220
220
  textField: {
221
- fullWidth: true, error: Boolean(error),
221
+ fullWidth: true,
222
+ error: Boolean(error),
222
223
  helperText: error?.message
223
224
  },
224
225
  popper: {
225
226
  disablePortal: false,
226
227
  popperOptions: {
227
- strategy: 'fixed'
228
- },
228
+ strategy: "fixed"
229
+ }
230
+ },
231
+ actionBar: {
232
+ actions: ["clear"]
229
233
  }
230
234
  }}
231
-
232
-
233
235
  />
234
236
  );
235
237
  }}
@@ -3,18 +3,30 @@ import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
3
3
  import { EditOutlined } from "@mui/icons-material";
4
4
  import { useDataTable, usePermission } from "./hooks";
5
5
 
6
-
7
6
  // material
8
7
  import { styled } from "@mui/material/styles";
9
8
  import MoreMenu from "../MoreMenu";
10
-
11
-
12
-
9
+ import { useMemo } from "react";
13
10
 
14
11
  export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus, onDelete, onEdit }) => {
15
12
  const { selectedField, columns, statusKey, disableStatus, tableActions } = useDataTable();
16
- const { canEdit, canDelete } = usePermission();
17
- console.log("tableActions", tableActions);
13
+ const { canEdit, canDelete, canView, canAction } = usePermission();
14
+
15
+ const { tableActionsOnTable, tableActionsOnMoreMenu } = useMemo(() => {
16
+ const tableActionsAfterFilter = [...tableActions].filter(({ permissionType }) => {
17
+ if (permissionType == "view" && !canView) {
18
+ return false;
19
+ }
20
+ if (permissionType == "action" && !canAction) {
21
+ return false;
22
+ }
23
+ return true;
24
+ });
25
+ const tableActionsOnTable = tableActionsAfterFilter.filter(({ isOnTable = false }) => isOnTable);
26
+ const tableActionsOnMoreMenu = tableActionsAfterFilter.filter(({ isOnTable = false }) => !isOnTable);
27
+ return { tableActionsOnTable, tableActionsOnMoreMenu };
28
+ }, [canView, canAction, tableActions]);
29
+
18
30
  return (
19
31
  <TableRow hover key={row[selectedField]} selected={selected}>
20
32
  <TableCell padding="checkbox">
@@ -70,8 +82,19 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
70
82
  </IconButton>
71
83
  </Tooltip>
72
84
  )}
85
+ {tableActionsOnTable.map(({ title, onClick, element }) => (
86
+ <Tooltip key={title} title={title}>
87
+ <IconButton
88
+ onClick={() => {
89
+ onClick(row);
90
+ }}
91
+ >
92
+ {element}
93
+ </IconButton>
94
+ </Tooltip>
95
+ ))}
73
96
 
74
- {<MoreMenu actions={tableActions} data={row}/>}
97
+ {<MoreMenu actions={tableActionsOnMoreMenu} data={row} />}
75
98
  </TableCell>
76
99
  </TableRow>
77
100
  );
@@ -86,5 +109,3 @@ export const RootStyle = styled(Toolbar)(({ theme }) => ({
86
109
  height: 56,
87
110
  minHeight: 50
88
111
  }));
89
-
90
-
@@ -6,14 +6,16 @@ export function useDataTable() {
6
6
  }
7
7
  export function usePermission() {
8
8
  const { Permission, setPermission } = useContext(PermissionContext);
9
- const { canEdit, canDelete, canDeleteMulti, canSave, canCreate } = useMemo(() => {
9
+ const { canEdit, canDelete, canDeleteMulti, canSave, canCreate, canAction, canView } = useMemo(() => {
10
10
  const canEdit = !Permission || Permission.Edit;
11
11
  const canDelete = !Permission || Permission.Delete;
12
12
  const canDeleteMulti = !Permission || Permission.DeleteMulti;
13
13
  const canSave = !Permission || Permission.Save;
14
14
  const canCreate = !Permission || Permission.Create;
15
- return { canEdit, canDelete, canDeleteMulti, canSave, canCreate };
15
+ const canAction = !Permission || Permission.Action;
16
+ const canView = !Permission || Permission.View;
17
+ return { canEdit, canDelete, canDeleteMulti, canSave, canCreate, canAction, canView };
16
18
  }, [Permission]);
17
19
 
18
- return { Permission, setPermission, canEdit, canDelete, canDeleteMulti, canSave, canCreate };
20
+ return { Permission, setPermission, canEdit, canDelete, canDeleteMulti, canSave, canCreate, canAction, canView };
19
21
  }
@@ -1,4 +1,4 @@
1
- import { Button, IconButton, Stack, Tooltip, Typography } from "@mui/material";
1
+ import { Button, Card, IconButton, Stack, Tooltip, Typography, useTheme, useMediaQuery } from "@mui/material";
2
2
 
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
 
@@ -12,6 +12,7 @@ import EditorDialog from "./EditorDialog";
12
12
 
13
13
  import ExportExcelButton from "./ExportExcelButton";
14
14
  import { FilterGod } from "./FilterGod";
15
+ import { useDataTable } from "./hooks";
15
16
 
16
17
  DataManagement.propTypes = {
17
18
  columns: PropTypes.array,
@@ -34,31 +35,38 @@ DataManagement.propTypes = {
34
35
  statusKey: PropTypes.string,
35
36
  tableActions: PropTypes.array,
36
37
  disableEditor: PropTypes.bool,
37
- onAddClick: PropTypes.func
38
+ onAddClick: PropTypes.func,
39
+ onEditClick: PropTypes.func,
40
+ tabPanel: PropTypes.node
38
41
  };
39
42
 
43
+
40
44
  function DataManagement({
41
45
  columns = [],
42
46
  title,
43
47
  tableName,
44
48
  selectedField = "Id",
45
- filters : tableFilters = [],
49
+ filters: tableFilters = [],
46
50
  editorFields = [],
47
51
  validationSchema = {},
48
52
  statusKey = "Status",
49
53
  disableStatus = false,
50
54
  tableActions = [],
51
55
  disableEditor = false,
52
- onAddClick = () => {
53
-
54
- },
55
- onEditClick = () => {
56
-
56
+ onAddClick = () => {},
57
+ onEditClick = () => {},
58
+ tabPanel,
59
+ slotProps = {
60
+ header: {
61
+ sx: {
62
+
63
+ }
64
+ }
57
65
  }
58
66
  }) {
59
67
  const [openEditorDialog, setOpenEditorDialog] = useState(false);
60
68
  const [selectedEditItem, setSelectedEditItem] = useState(null);
61
-
69
+
62
70
  const [Permission, setPermission] = useState(null);
63
71
 
64
72
  const { defaults, filters } = useMemo(() => {
@@ -70,19 +78,24 @@ function DataManagement({
70
78
  return prev;
71
79
  }, {});
72
80
  return { defaults, filters };
73
-
74
81
  }, [tableFilters]);
75
82
 
83
+ const theme = useTheme();
84
+ const upXL = useMediaQuery(theme.breakpoints.up("xl"));
85
+ const elementSize = useMemo(() => {
86
+ const elementSize = upXL ? "medium" : "small";
87
+ return elementSize;
88
+ }, [upXL]);
89
+
76
90
  useEffect(() => {
77
- if(selectedEditItem && disableEditor){
91
+ if (selectedEditItem && disableEditor) {
78
92
  onEditClick(selectedEditItem);
79
93
  }
80
-
81
94
  }, [selectedEditItem]);
82
95
  useEffect(() => {
83
- setDataSearch({ ...dataSearch,...defaults });
84
-
96
+ setDataSearch({ ...dataSearch, ...defaults });
85
97
  }, [filters]);
98
+
86
99
  const [dataSearch, setDataSearch] = useState({ ...defaults });
87
100
  const values = useMemo(() => {
88
101
  return {
@@ -115,15 +128,16 @@ function DataManagement({
115
128
  <DataTableContext.Provider value={values}>
116
129
  <PermissionContext.Provider value={permissionValues}>
117
130
  <FormProvider {...methods}>
118
- <Stack direction="row" justifyContent={"space-between"} sx={{ px: 2, pt: 2 }}>
131
+ <Stack direction="row" justifyContent={"space-between"} sx={{ mb: 4, ...slotProps?.header?.sx }}>
119
132
  <Typography variant="h4">{title}</Typography>
120
133
  <Stack direction="row" spacing={1}>
121
134
  <Tooltip title="Làm mới">
122
135
  <IconButton
123
136
  variant="outlined"
124
137
  color="primary"
138
+ size={elementSize}
125
139
  onClick={() => {
126
- setDataSearch({...defaults});
140
+ setDataSearch({ ...defaults });
127
141
  [...filters].forEach((filter) => {
128
142
  filter?.onChange?.();
129
143
  });
@@ -135,9 +149,10 @@ function DataManagement({
135
149
  </IconButton>
136
150
  </Tooltip>
137
151
 
138
- <ExportExcelButton tableName={tableName} data={dataSearch} />
152
+ <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
139
153
  {(!Permission || Permission.Create) && (
140
154
  <Button
155
+ size={elementSize}
141
156
  variant="contained"
142
157
  startIcon={<Add />}
143
158
  onClick={(e) => {
@@ -153,11 +168,20 @@ function DataManagement({
153
168
  )}
154
169
  </Stack>
155
170
  </Stack>
156
- <FilterGod filters={filters} />
157
- <DataTable />
171
+
172
+ <Card>
173
+ {tabPanel}
174
+ <FilterGod filters={filters} elementSize={elementSize} />
175
+ <DataTable />
176
+ </Card>
158
177
  </FormProvider>
159
178
  {disableEditor || (
160
- <EditorDialog open={openEditorDialog} onClose={() => setOpenEditorDialog(false)} defaultValues={selectedEditItem} fields={editorFields} />
179
+ <EditorDialog
180
+ open={openEditorDialog}
181
+ onClose={() => setOpenEditorDialog(false)}
182
+ defaultValues={selectedEditItem}
183
+ fields={editorFields}
184
+ />
161
185
  )}
162
186
  </PermissionContext.Provider>
163
187
  </DataTableContext.Provider>
@@ -7,8 +7,6 @@ import MoreVertIcon from "@mui/icons-material/MoreVert";
7
7
  // ----------------------------------------------------------------------
8
8
 
9
9
  MoreMenu.propTypes = {
10
- onDelete: PropTypes.func,
11
- userName: PropTypes.string,
12
10
  actions: PropTypes.array,
13
11
  data: PropTypes.any
14
12
  };
@@ -1 +1,2 @@
1
1
  export const DEFAULT_DATE_FORMAT = "DD/MM/YYYY";
2
+ export const DEFAULT_DATETIME_FORMAT = "dd MMM yyyy HH:mm";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"