trithuc-mvc-react 1.6.13 → 1.7.0

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.
@@ -2,7 +2,7 @@ import { Button, Card, IconButton, Stack, Tooltip, Typography, useTheme, useMedi
2
2
 
3
3
  import { useEffect, useMemo, useState } from "react";
4
4
 
5
- import { DataTableContext, PermissionContext } from "./context";
5
+ import { DataTableContext } from "./context";
6
6
  import PropTypes from "prop-types";
7
7
  import DataTable from "./DataTable";
8
8
  import { Add, Refresh } from "@mui/icons-material";
@@ -13,7 +13,7 @@ import ViewDetailDialog from "./ViewDetailDialog";
13
13
 
14
14
  import ExportExcelButton from "./ExportExcelButton";
15
15
  import { FilterGod } from "./FilterGod";
16
-
16
+ import { usePermission } from "../../hooks";
17
17
 
18
18
  DataManagement.propTypes = {
19
19
  columns: PropTypes.array,
@@ -57,8 +57,8 @@ function DataManagement({
57
57
  disableCellThaoTac = false,
58
58
  tableActions = [],
59
59
  disableEditor = false,
60
- onAddClick = () => { },
61
- onEditClick = () => { },
60
+ onAddClick = () => {},
61
+ onEditClick = () => {},
62
62
  tabPanel,
63
63
  backParentNavigator,
64
64
  slotProps = {
@@ -71,8 +71,7 @@ function DataManagement({
71
71
  const [selectedEditItem, setSelectedEditItem] = useState(null);
72
72
  const [openViewDialog, setOpenViewDialog] = useState(false);
73
73
 
74
- const [Permission, setPermission] = useState(null);
75
-
74
+ const { canCreate } = usePermission(tableName);
76
75
  const { defaults, filters } = useMemo(() => {
77
76
  const filters = tableFilters.filter(({ type }) => type !== "default");
78
77
  const defaultFilters = tableFilters.filter(({ type }) => type === "default");
@@ -91,7 +90,6 @@ function DataManagement({
91
90
  return elementSize;
92
91
  }, [upXL]);
93
92
 
94
-
95
93
  useEffect(() => {
96
94
  setDataSearch({ ...dataSearch, ...defaults });
97
95
  }, [filters]);
@@ -116,80 +114,83 @@ function DataManagement({
116
114
  tableActions,
117
115
  onEditClick
118
116
  };
119
- }, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema, tableActions, openViewDialog, setOpenViewDialog, onEditClick]);
120
- const permissionValues = useMemo(() => {
121
- return {
122
- Permission,
123
- setPermission
124
- };
125
- }, [Permission, setPermission]);
117
+ }, [
118
+ tableName,
119
+ selectedField,
120
+ columns,
121
+ selectedEditItem,
122
+ dataSearch,
123
+ setDataSearch,
124
+ validationSchema,
125
+ tableActions,
126
+ openViewDialog,
127
+ setOpenViewDialog,
128
+ onEditClick
129
+ ]);
130
+
126
131
  const methods = useForm({ defaultValues: {} });
127
132
  const { reset, setValue } = methods;
128
133
  return (
129
134
  <>
130
135
  <DataTableContext.Provider value={values}>
131
- <PermissionContext.Provider value={permissionValues}>
132
- <FormProvider {...methods}>
133
- <Stack direction="row" justifyContent={"space-between"} sx={{ mb: upXL ? 2 : 1, ...slotProps?.header?.sx }}>
134
- <Typography variant="h4">{title}</Typography>
135
- <Stack direction="row" spacing={1}>
136
- <Tooltip title="Làm mới">
137
- <IconButton
138
- variant="outlined"
139
- color="primary"
140
- size={elementSize}
141
- onClick={() => {
142
- setDataSearch({ ...defaults });
143
- [...filters].forEach((filter) => {
144
- filter?.onChange?.();
145
- });
146
- reset();
147
- setValue("Search");
148
- }}
149
- >
150
- <Refresh fontSize="inherit" />
151
- </IconButton>
152
- </Tooltip>
153
-
154
- <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
155
- {(!Permission || Permission.Create) && (
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
- )}
173
- </Stack>
136
+ <FormProvider {...methods}>
137
+ <Stack direction="row" justifyContent={"space-between"} sx={{ mb: upXL ? 2 : 1, ...slotProps?.header?.sx }}>
138
+ <Typography variant="h4">{title}</Typography>
139
+ <Stack direction="row" spacing={1}>
140
+ <Tooltip title="Làm mới">
141
+ <IconButton
142
+ variant="outlined"
143
+ color="primary"
144
+ size={elementSize}
145
+ onClick={() => {
146
+ setDataSearch({ ...defaults });
147
+ [...filters].forEach((filter) => {
148
+ filter?.onChange?.();
149
+ });
150
+ reset();
151
+ setValue("Search");
152
+ }}
153
+ >
154
+ <Refresh fontSize="inherit" />
155
+ </IconButton>
156
+ </Tooltip>
157
+
158
+ <ExportExcelButton tableName={tableName} data={dataSearch} size={elementSize} />
159
+ {canCreate && !disableAdd && (
160
+ <Button
161
+ size={elementSize}
162
+ variant="contained"
163
+ startIcon={<Add fontSize="inherit" />}
164
+ onClick={(e) => {
165
+ if (!disableEditor) {
166
+ setOpenEditorDialog(true);
167
+ setSelectedEditItem(null);
168
+ }
169
+ onAddClick(e);
170
+ }}
171
+ >
172
+ Thêm
173
+ </Button>
174
+ )}
174
175
  </Stack>
175
-
176
- <Card>
177
- {tabPanel}
178
- <FilterGod filters={filters} elementSize={elementSize} />
179
- {backParentNavigator}
180
- <DataTable />
181
- </Card>
182
- </FormProvider>
183
- {disableEditor || (
184
- <EditorDialog
185
- open={openEditorDialog}
186
- onClose={() => setOpenEditorDialog(false)}
187
- defaultValues={selectedEditItem}
188
- fields={editorFields}
189
- />
190
- )}
191
- <ViewDetailDialog open={openViewDialog} onClose={() => setOpenViewDialog(false)} fields={editorFields} />
192
- </PermissionContext.Provider>
176
+ </Stack>
177
+
178
+ <Card>
179
+ {tabPanel}
180
+ <FilterGod filters={filters} elementSize={elementSize} />
181
+ {backParentNavigator}
182
+ <DataTable />
183
+ </Card>
184
+ </FormProvider>
185
+ {disableEditor || (
186
+ <EditorDialog
187
+ open={openEditorDialog}
188
+ onClose={() => setOpenEditorDialog(false)}
189
+ defaultValues={selectedEditItem}
190
+ fields={editorFields}
191
+ />
192
+ )}
193
+ <ViewDetailDialog open={openViewDialog} onClose={() => setOpenViewDialog(false)} fields={editorFields} />
193
194
  </DataTableContext.Provider>
194
195
  </>
195
196
  );
@@ -5,139 +5,182 @@ import StaticDateRangePicker from "./StaticDateRangePicker";
5
5
  import moment from "moment";
6
6
  import { memo, useEffect, useRef, useState } from "react";
7
7
  import * as dateFns from "date-fns";
8
+ import { useTheme, useMediaQuery } from "@mui/material";
9
+ import { useDateRangeContext } from ".";
10
+ let timeoutStart = null;
11
+ let timeoutEnd = null;
12
+ let timeoutProper = null;
8
13
 
9
-
10
- const DateRangePicker = ({ onChange = () => {}, value }) => {
14
+ const DateRangePicker = ({ label = ["Từ ngày", "Đến ngày"] }) => {
11
15
  const [anchorEl, setAnchorEl] = useState(null);
16
+ const { dateRange, setDateRange, setIsFocusStart, setIsFocusEnd, isFocusStart, isFocusEnd, setIsFocusPoper, isFocusPoper } =
17
+ useDateRangeContext();
12
18
 
13
- const [dateRange, setDateRange] = useState({
14
- startDate: value?.[0],
15
- endDate: value?.[1]
16
- });
17
-
18
- const handleFocus = (event) => {
19
+ const handleFocus1 = (event) => {
20
+ if (timeoutStart) {
21
+ clearTimeout(timeoutStart);
22
+ }
23
+ setIsFocusStart(true);
24
+ setAnchorEl(containerRef.current);
25
+ };
26
+ const handleFocus2 = (event) => {
27
+ if (timeoutEnd) {
28
+ clearTimeout(timeoutEnd);
29
+ }
30
+ setIsFocusEnd(true);
31
+ setAnchorEl(containerRef.current);
32
+ };
33
+ const handleBlur1 = (event) => {
34
+ if (timeoutStart) {
35
+ clearTimeout(timeoutStart);
36
+ }
37
+ timeoutStart = setTimeout(() => {
38
+ setIsFocusStart(false);
39
+ }, 300);
40
+ // setIsFocusStart(false);
19
41
  setAnchorEl(containerRef.current);
20
42
  };
43
+ const handleBlur2 = (event) => {
44
+ if (timeoutEnd) {
45
+ clearTimeout(timeoutEnd);
46
+ }
47
+ setAnchorEl(containerRef.current);
48
+ timeoutEnd = setTimeout(() => {
49
+ setIsFocusEnd(false);
50
+ }, 300);
51
+ };
21
52
  const handleClose = () => {
22
53
  setAnchorEl(null);
23
54
  };
24
- const handleClickAway = () => {
25
- setAnchorEl(null);
55
+ const handleClickAway = (e) => {
56
+ timeoutProper = setTimeout(() => {
57
+ setIsFocusPoper(false);
58
+ }, 500);
59
+ };
60
+ const handleClickPopper = (e) => {
61
+ if (timeoutProper) {
62
+ clearTimeout(timeoutProper);
63
+ }
64
+ if (!isFocusPoper) {
65
+ setIsFocusPoper(true);
66
+ }
26
67
  };
27
68
 
28
- const open = Boolean(anchorEl);
69
+ const open = isFocusStart || isFocusEnd || isFocusPoper;
70
+
71
+ // console.log("open", open);
29
72
  const containerRef = useRef(null);
73
+ const theme = useTheme();
74
+ const downXl = useMediaQuery(theme.breakpoints.down("xl"));
75
+ const elementSize = downXl ? "small" : "medium";
30
76
 
31
77
  useEffect(() => {
32
- if (dateFns.isBefore(dateRange.startDate, dateRange.endDate) || dateFns.isSameDay(dateRange.startDate, dateRange.endDate)) {
33
- onChange([dateRange.startDate, dateRange.endDate]);
34
- }
35
- }, [dateRange]);
78
+ setAnchorEl(containerRef.current);
79
+ }, []);
80
+ // useEffect(() => {
81
+ // console.log("CHANGEEEE");
82
+ // }, [isFocusStart, isFocusEnd]);
83
+ // useEffect(() => {
84
+ // if (dateFns.isBefore(dateRange.startDate, dateRange.endDate) || dateFns.isSameDay(dateRange.startDate, dateRange.endDate)) {
85
+ // onChange([dateRange.startDate, dateRange.endDate]);
86
+ // }
87
+ // }, [dateRange]);
36
88
 
37
- useEffect(() => {
38
- if (!value) return;
39
- if (dateFns.isSameDay(dateRange.startDate, value[0]) && dateFns.isSameDay(dateRange.endDate, value[1])) {
40
- return;
41
- }
42
- setDateRange({
43
- startDate: value?.[0],
44
- endDate: value?.[1]
45
- });
46
- }, [value]);
89
+ // useEffect(() => {
90
+ // if (!value) return;
91
+ // if (dateFns.isSameDay(dateRange.startDate, value[0]) && dateFns.isSameDay(dateRange.endDate, value[1])) {
92
+ // return;
93
+ // }
94
+ // setDateRange({
95
+ // startDate: value?.[0],
96
+ // endDate: value?.[1]
97
+ // });
98
+ // }, [value]);
47
99
 
48
100
  return (
49
101
  <>
102
+ <Stack ref={containerRef} direction={"row"} alignContent={"center"} spacing={1}>
103
+ <DateField
104
+ sx={{ flex: 1 }}
105
+ format={DEFAULT_DATE_FORMAT}
106
+ onFocus={handleFocus1}
107
+ onBlur={handleBlur1}
108
+ label={label[0]}
109
+ onChange={(value) => {
110
+ setDateRange((d) => ({
111
+ ...d,
112
+ startDate: value ? value.toDate() : null
113
+ }));
114
+ }}
115
+ value={dateRange.startDate && moment(dateRange.startDate)}
116
+ size={elementSize}
117
+ maxDate={dateRange.endDate ?? moment(dateRange.endDate)}
118
+ />
119
+ <Typography variant="body1" sx={{ height: "24px", alignSelf: "center" }}>
120
+
121
+ </Typography>
122
+ <DateField
123
+ sx={{ flex: 1 }}
124
+ format={DEFAULT_DATE_FORMAT}
125
+ label={label[1]}
126
+ onFocus={handleFocus2}
127
+ onBlur={handleBlur2}
128
+ onChange={(value) => {
129
+ setDateRange((d) => ({
130
+ ...d,
131
+ endDate: value ? value.toDate() : null
132
+ }));
133
+ }}
134
+ value={dateRange.endDate && moment(dateRange.endDate)}
135
+ size={elementSize}
136
+ minDate={dateRange.startDate ?? moment(dateRange.startDate)}
137
+ />
138
+ </Stack>
50
139
  <ClickAwayListener onClickAway={handleClickAway}>
51
- <Stack ref={containerRef} direction={"row"} alignContent={"center"} spacing={1}>
52
- <DateField
53
- sx={{ flex: 1 }}
54
- format={DEFAULT_DATE_FORMAT}
55
- onFocus={handleFocus}
56
- label="Từ ngày"
57
- onChange={(value) => {
58
- setDateRange((d) => ({
59
- ...d,
60
- startDate: value.toDate()
61
- }));
62
- }}
63
- value={dateRange.startDate && moment(dateRange.startDate)}
64
- size="small"
65
- maxDate={dateRange.endDate ?? moment(dateRange.endDate)}
66
- />
67
- <Typography variant="body1" sx={{ height: "24px", alignSelf: "center" }}>
68
-
69
- </Typography>
70
- <DateField
71
- sx={{ flex: 1 }}
72
- format={DEFAULT_DATE_FORMAT}
73
- label="Tới ngày"
74
- onFocus={handleFocus}
75
- onChange={(value) => {
76
- setDateRange((d) => ({
77
- ...d,
78
- endDate: value.toDate()
79
- }));
80
-
81
- }}
82
- value={dateRange.endDate && moment(dateRange.endDate)}
83
- size="small"
84
- minDate={dateRange.startDate ?? moment(dateRange.startDate)}
85
- />
140
+ <Popper
141
+ sx={{
142
+ zIndex: 1036
143
+ }}
144
+ keepMounted
145
+ placement="bottom-start"
146
+ open={open}
147
+ anchorEl={anchorEl}
148
+ onClose={handleClose}
149
+ onClick={handleClickPopper}
86
150
 
87
- <Popper
88
- sx={{
89
- zIndex: 1036
151
+ // modifiers={[
152
+ // {
153
+ // name: "flip",
154
+ // enabled: true,
155
+ // options: {
156
+ // altBoundary: true,
157
+ // rootBoundary: "viewport",
158
+ // padding: 8
159
+ // }
160
+ // },
161
+ // {
162
+ // name: "preventOverflow",
163
+ // enabled: true,
164
+ // options: {
165
+ // altAxis: true,
166
+ // altBoundary: true,
167
+ // tether: true,
168
+ // rootBoundary: "document",
169
+ // padding: 8
170
+ // }
171
+ // }
172
+ // ]}
173
+ >
174
+ <StaticDateRangePicker
175
+ value={dateRange}
176
+ onUpdate={(value) => {
177
+ setDateRange(value);
90
178
  }}
91
- keepMounted
92
- placement="bottom-start"
93
- open={open}
94
- anchorEl={anchorEl}
95
- onClose={handleClose}
96
- disablePortal={false}
97
- modifiers={[
98
- {
99
- name: "flip",
100
- enabled: true,
101
- options: {
102
- altBoundary: true,
103
- rootBoundary: "viewport",
104
- padding: 8
105
- }
106
- },
107
- {
108
- name: "preventOverflow",
109
- enabled: true,
110
- options: {
111
- altAxis: true,
112
- altBoundary: true,
113
- tether: true,
114
- rootBoundary: "document",
115
- padding: 8
116
- }
117
- }
118
- ]}
119
- >
120
- <StaticDateRangePicker
121
- value={dateRange}
122
- onUpdate={(value) => {
123
- setDateRange(value);
124
- }}
125
- />
126
- </Popper>
127
- </Stack>
179
+ />
180
+ </Popper>
128
181
  </ClickAwayListener>
129
182
  </>
130
183
  );
131
184
  };
132
- const DateRangePickerMemo = memo(DateRangePicker, (prevProps, nextProps) => {
133
- try {
134
- if (dateFns.isSameDay(prevProps.value[0], nextProps.value[0]) && dateFns.isSameDay(prevProps.value[1], nextProps.value[1])) {
135
- return true; // props are equal
136
- }
137
- } catch (error) {
138
- console.log(error);
139
- }
140
185
 
141
- return false; // props are not equal -> update the component
142
- });
143
- export default DateRangePickerMemo;
186
+ export default DateRangePicker;