trithuc-mvc-react 1.0.13 → 1.2.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.
@@ -198,7 +198,7 @@ function FormField({
198
198
  <Controller
199
199
  name={name}
200
200
  control={control}
201
- // defaultValue={true}
201
+ defaultValue={true}
202
202
  render={({ field }) => <FormControlLabel control={<Switch {...field} checked={field.value} />} label={label} />}
203
203
  />
204
204
  );
@@ -2,6 +2,7 @@ import { Checkbox, TableRow, TableCell, TableHead as MuiTableHead } from "@mui/m
2
2
  import { useDataTable, usePermission } from "./hooks";
3
3
  export function TableHead({ numSelected, rowCount, onSelectAllClick, headLabel }) {
4
4
  const { canEdit } = usePermission();
5
+ const { disableStatus } = useDataTable();
5
6
  return (
6
7
  <MuiTableHead sx={{ height: 56, visibility: numSelected > 0 ? "hidden" : "visible" }}>
7
8
  <TableRow>
@@ -18,8 +19,8 @@ export function TableHead({ numSelected, rowCount, onSelectAllClick, headLabel }
18
19
  {headCell.label}
19
20
  </TableCell>
20
21
  ))}
21
- {canEdit && <TableCell>Kích hoạt</TableCell>}
22
- <TableCell>Thao tác</TableCell>
22
+ {!disableStatus && canEdit && <TableCell>Kích hoạt</TableCell>}
23
+ <TableCell sx={{minWidth: '136px'}}>Thao tác</TableCell>
23
24
  </TableRow>
24
25
  </MuiTableHead>
25
26
  );
@@ -6,10 +6,15 @@ import { useDataTable, usePermission } from "./hooks";
6
6
 
7
7
  // material
8
8
  import { styled } from "@mui/material/styles";
9
+ import MoreMenu from "../MoreMenu";
10
+
11
+
12
+
9
13
 
10
14
  export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus, onDelete, onEdit }) => {
11
- const { selectedField, columns } = useDataTable();
15
+ const { selectedField, columns, statusKey, disableStatus, tableActions } = useDataTable();
12
16
  const { canEdit, canDelete } = usePermission();
17
+ console.log("tableActions", tableActions);
13
18
  return (
14
19
  <TableRow hover key={row[selectedField]} selected={selected}>
15
20
  <TableCell padding="checkbox">
@@ -23,19 +28,20 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
23
28
  valueGetter = (row) => {
24
29
  return row[field];
25
30
  },
31
+ renderCell,
26
32
  valueFormat = (e) => e
27
33
  }) => {
28
34
  return (
29
35
  <TableCell key={`${row[selectedField]}-${field}`} align={alignRight ? "right" : "left"}>
30
- {valueFormat(valueGetter(row))}
36
+ {renderCell ? renderCell(row) : valueFormat(valueGetter(row))}
31
37
  </TableCell>
32
38
  );
33
39
  }
34
40
  )}
35
- {canEdit && (
41
+ {!disableStatus && canEdit && (
36
42
  <TableCell>
37
43
  <Switch
38
- checked={row.Status}
44
+ checked={row[statusKey]}
39
45
  onChange={() => {
40
46
  onChangeStatus(row[selectedField]);
41
47
  }}
@@ -64,6 +70,8 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
64
70
  </IconButton>
65
71
  </Tooltip>
66
72
  )}
73
+
74
+ {<MoreMenu actions={tableActions} data={row}/>}
67
75
  </TableCell>
68
76
  </TableRow>
69
77
  );
@@ -27,10 +27,32 @@ DataManagement.propTypes = {
27
27
  onChangeAfter: PropTypes.func,
28
28
  filters: PropTypes.array
29
29
  })
30
- )
30
+ ),
31
+ editorFields: PropTypes.array,
32
+ validationSchema: PropTypes.object,
33
+ disableStatus: PropTypes.bool,
34
+ statusKey: PropTypes.string,
35
+ tableActions: PropTypes.array,
36
+ disableEditor: PropTypes.bool,
37
+ onAddClick: PropTypes.func
31
38
  };
32
39
 
33
- function DataManagement({ columns = [], title, tableName, selectedField = "Id", filters, editorFields, validationSchema = {} }) {
40
+ function DataManagement({
41
+ columns = [],
42
+ title,
43
+ tableName,
44
+ selectedField = "Id",
45
+ filters,
46
+ editorFields = [],
47
+ validationSchema = {},
48
+ statusKey = "Status",
49
+ disableStatus = false,
50
+ tableActions = [],
51
+ disableEditor = false,
52
+ onAddClick = () => {
53
+
54
+ }
55
+ }) {
34
56
  const [openEditorDialog, setOpenEditorDialog] = useState(false);
35
57
  const [selectedEditItem, setSelectedEditItem] = useState(null);
36
58
  const [dataSearch, setDataSearch] = useState({});
@@ -46,9 +68,13 @@ function DataManagement({ columns = [], title, tableName, selectedField = "Id",
46
68
  setOpenEditorDialog,
47
69
  dataSearch,
48
70
  setDataSearch,
49
- validationSchema
71
+ validationSchema,
72
+ statusKey,
73
+ disableStatus,
74
+
75
+ tableActions
50
76
  };
51
- }, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema]);
77
+ }, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema, tableActions]);
52
78
  const permissionValues = useMemo(() => {
53
79
  return {
54
80
  Permission,
@@ -88,9 +114,12 @@ function DataManagement({ columns = [], title, tableName, selectedField = "Id",
88
114
  <Button
89
115
  variant="contained"
90
116
  startIcon={<Add />}
91
- onClick={() => {
92
- setOpenEditorDialog(true);
93
- setSelectedEditItem(null);
117
+ onClick={(e) => {
118
+ if (!disableEditor) {
119
+ setOpenEditorDialog(true);
120
+ setSelectedEditItem(null);
121
+ }
122
+ onAddClick(e);
94
123
  }}
95
124
  >
96
125
  Thêm
@@ -101,7 +130,9 @@ function DataManagement({ columns = [], title, tableName, selectedField = "Id",
101
130
  <FilterGod filters={filters} />
102
131
  <DataTable />
103
132
  </FormProvider>
104
- <EditorDialog open={openEditorDialog} onClose={() => setOpenEditorDialog(false)} defaultValues={selectedEditItem} fields={editorFields} />
133
+ {disableEditor || (
134
+ <EditorDialog open={openEditorDialog} onClose={() => setOpenEditorDialog(false)} defaultValues={selectedEditItem} fields={editorFields} />
135
+ )}
105
136
  </PermissionContext.Provider>
106
137
  </DataTableContext.Provider>
107
138
  </>
@@ -0,0 +1,50 @@
1
+ import PropTypes from "prop-types";
2
+ import { useRef, useState } from "react";
3
+
4
+ // material
5
+ import { Menu, MenuItem, IconButton, ListItemIcon, ListItemText } from "@mui/material";
6
+ import MoreVertIcon from "@mui/icons-material/MoreVert";
7
+ // ----------------------------------------------------------------------
8
+
9
+ MoreMenu.propTypes = {
10
+ onDelete: PropTypes.func,
11
+ userName: PropTypes.string,
12
+ actions: PropTypes.array,
13
+ data: PropTypes.any
14
+ };
15
+
16
+ export default function MoreMenu({ data, actions }) {
17
+ const ref = useRef(null);
18
+ const [isOpen, setIsOpen] = useState(false);
19
+
20
+ if (actions?.length == 0) {
21
+ return null;
22
+ }
23
+ return (
24
+ <>
25
+ <IconButton ref={ref} onClick={() => setIsOpen(true)}>
26
+ <MoreVertIcon />
27
+ </IconButton>
28
+
29
+ <Menu
30
+ open={isOpen}
31
+ anchorEl={ref.current}
32
+ onClose={() => setIsOpen(false)}
33
+ slotProps={{
34
+ paper: {
35
+ sx: { width: 200, maxWidth: "100%" }
36
+ }
37
+ }}
38
+ anchorOrigin={{ vertical: "top", horizontal: "right" }}
39
+ transformOrigin={{ vertical: "top", horizontal: "right" }}
40
+ >
41
+ {[...actions].map(({ title, onClick, element }) => (
42
+ <MenuItem key={title} onClick={() => onClick(data)} sx={{ color: "text.secondary" }}>
43
+ <ListItemIcon>{element}</ListItemIcon>
44
+ <ListItemText primary={title} primaryTypographyProps={{ variant: "body2" }} />
45
+ </MenuItem>
46
+ ))}
47
+ </Menu>
48
+ </>
49
+ );
50
+ }
@@ -0,0 +1,99 @@
1
+ import { Box } from "@mui/material";
2
+ import { FormProvider, useForm } from "react-hook-form";
3
+ import Grid from "@mui/material/Unstable_Grid2"; // Grid version 2
4
+ import PropTypes from "prop-types";
5
+
6
+ import { useEffect } from "react";
7
+ import moment from "moment/moment";
8
+
9
+ import { useMutation, useQueryClient } from "react-query";
10
+ import { saveDataToTable } from "../../api";
11
+ import { toast } from "react-toastify";
12
+
13
+ import FormField from "./FormField";
14
+ import { yupResolver } from "@hookform/resolvers/yup";
15
+ EditorForm.propTypes = {
16
+ fields: PropTypes.array
17
+ };
18
+ function EditorForm({ fields, elementSize = "medium", submitRef, validationSchema, onSubmit }) {
19
+
20
+ const methods = useForm({ defaultValues: {}, resolver: yupResolver(validationSchema) });
21
+
22
+ const handleSubmit = (data) => {
23
+ fields
24
+ .filter(({ type }) => type === "date")
25
+ .forEach(({ field }) => {
26
+ if (data[field]) {
27
+ data[field] = moment(data[field]).toDate();
28
+ }
29
+ });
30
+ fields
31
+ .filter(({ type }) => type === "autocomplete")
32
+ .forEach(({ field, datas, keyValueLabel, keyValue, keyLabel }) => {
33
+ if (data[field] && !data[keyValueLabel] && keyValueLabel) {
34
+ data[keyValueLabel] = datas.find((item) => item[keyValue] == data[field])?.[keyLabel];
35
+ }
36
+ });
37
+
38
+ onSubmit(data);
39
+ };
40
+
41
+ return (
42
+ <FormProvider {...methods}>
43
+ <Box component={"form"} sx={{ pt: 2 }} onSubmit={methods.handleSubmit(handleSubmit)}>
44
+ <Grid container spacing={2}>
45
+ {fields.map(
46
+ ({
47
+ field,
48
+ type = "text",
49
+ label,
50
+ childrenFields,
51
+ datas,
52
+ loading = false,
53
+ onChange = () => {},
54
+ keyLabel,
55
+ keyValue,
56
+ keyValueLabel,
57
+ required
58
+ }) => {
59
+ let sizes = {
60
+ xs: 12,
61
+ sm: 6,
62
+ md: 4
63
+ };
64
+ if (type == "textarea") {
65
+ sizes = {
66
+ xs: 12,
67
+ sm: 12,
68
+ md: 12
69
+ };
70
+ }
71
+ return (
72
+ <Grid {...sizes} key={field}>
73
+ <FormField
74
+ type={type}
75
+ label={label}
76
+ control={methods.control}
77
+ name={field}
78
+ loading={loading}
79
+ onChange={onChange}
80
+ keyLabel={keyLabel}
81
+ keyValueLabel={keyValueLabel}
82
+ datas={datas}
83
+ childrenFields={childrenFields}
84
+ keyValue={keyValue}
85
+ size={elementSize}
86
+ required={required}
87
+ />
88
+ </Grid>
89
+ );
90
+ }
91
+ )}
92
+ </Grid>
93
+ <button ref={submitRef} type="submit" style={{ display: "none" }} />
94
+ </Box>
95
+ </FormProvider>
96
+ );
97
+ }
98
+
99
+ export default EditorForm;
@@ -0,0 +1,241 @@
1
+ import {
2
+ Autocomplete,
3
+ Checkbox,
4
+ FormControl,
5
+ FormControlLabel,
6
+ FormLabel,
7
+ InputLabel,
8
+ MenuItem,
9
+ Radio,
10
+ RadioGroup,
11
+ Select,
12
+ Switch,
13
+ TextField
14
+ } from "@mui/material";
15
+ import { Controller, useFormContext } from "react-hook-form";
16
+ // Grid version 2
17
+ import PropTypes from "prop-types";
18
+ import { DatePicker } from "@mui/x-date-pickers";
19
+ import { useCallback, useEffect, } from "react";
20
+ import moment from "moment/moment";
21
+ import { DEFAULT_DATE_FORMAT } from "../../constants";
22
+
23
+
24
+ FormField.propTypes = {
25
+ datas: PropTypes.array,
26
+ loading: PropTypes.bool
27
+ };
28
+ function FormField({
29
+ type = "text",
30
+ control,
31
+ label,
32
+ name,
33
+ loading,
34
+ datas = [],
35
+ onChange,
36
+ keyLabel,
37
+ keyValue,
38
+ keyValueLabel,
39
+ childrenFields,
40
+ size,
41
+ required = false
42
+ }) {
43
+ const { setValue, register } = useFormContext();
44
+ const getValueObject = useCallback(
45
+ (value) => {
46
+ if (!value) return null;
47
+ const result = [...datas].find((item) => item[keyValue] == value);
48
+
49
+ return result;
50
+ },
51
+ [datas]
52
+ );
53
+
54
+ useEffect(() => {
55
+ if (keyValueLabel) {
56
+ register(keyValueLabel);
57
+ }
58
+ }, []);
59
+
60
+
61
+ switch (type) {
62
+ case "textarea":
63
+ return (
64
+ <Controller
65
+ name={name}
66
+ control={control}
67
+ render={({ field, fieldState: { error } }) => {
68
+ return (
69
+ <TextField
70
+ {...field}
71
+ multiline
72
+ label={label + (required ? "*" : "")}
73
+ error={Boolean(error)}
74
+ helperText={error?.message}
75
+ fullWidth
76
+ placeholder={label}
77
+ size={size}
78
+ />
79
+ );
80
+ }}
81
+ />
82
+ );
83
+
84
+ case "text":
85
+ case "number":
86
+ return (
87
+ <Controller
88
+ name={name}
89
+ control={control}
90
+ render={({ field, fieldState: { error } }) => {
91
+ return (
92
+ <TextField
93
+ {...field}
94
+ type={type}
95
+ label={label + (required ? "*" : "")}
96
+ error={Boolean(error)}
97
+ helperText={error?.message}
98
+ fullWidth
99
+ placeholder={label}
100
+ size={size}
101
+ />
102
+ );
103
+ }}
104
+ />
105
+ );
106
+
107
+ case "autocomplete": {
108
+ if (keyValueLabel) {
109
+ register(keyValueLabel);
110
+ }
111
+ return (
112
+ <Controller
113
+ name={name}
114
+ control={control}
115
+ render={({ field: { onChange: onFieldChange, value }, fieldState: { error } }) => {
116
+ return (
117
+ <Autocomplete
118
+ disablePortal
119
+ noOptionsText="Không có dữ liệu"
120
+ loading={loading}
121
+ size={size}
122
+ options={datas ?? []}
123
+ onChange={(event, newValue) => {
124
+ onFieldChange(newValue?.[keyValue]);
125
+ onChange(newValue);
126
+ if (keyValueLabel) {
127
+ setValue(keyValueLabel, newValue?.[keyLabel]);
128
+ }
129
+ }}
130
+ value={getValueObject(value)}
131
+ fullWidth
132
+ getOptionLabel={(option) => option[keyLabel]}
133
+ renderInput={(params) => <TextField {...params} label={label} error={Boolean(error)} helperText={error?.message} />}
134
+ />
135
+ );
136
+ }}
137
+ />
138
+ );
139
+ }
140
+
141
+ case "select":
142
+ return (
143
+ <Controller
144
+ name={name}
145
+ control={control}
146
+ render={({ field }) => (
147
+ <FormControl sx={{ minWidth: 160 }} fullWidth size={size}>
148
+ <InputLabel>{label}</InputLabel>
149
+ <Select {...field} label={label}>
150
+ {[...datas].map(({ label, value }) => (
151
+ <MenuItem key={value} value={value}>
152
+ {label}
153
+ </MenuItem>
154
+ ))}
155
+ </Select>
156
+ </FormControl>
157
+ )}
158
+ />
159
+ );
160
+ case "radios":
161
+ return (
162
+ <Controller
163
+ name={name}
164
+ control={control}
165
+ defaultValue={null}
166
+ render={({ field }) => {
167
+ return (
168
+ <FormControl fullWidth size={size}>
169
+ <FormLabel>{label}</FormLabel>
170
+ <RadioGroup row {...field}>
171
+ {[...datas].map(({ label, value }) => (
172
+ <FormControlLabel key={value} value={value} control={<Radio />} label={label} />
173
+ ))}
174
+ </RadioGroup>
175
+ </FormControl>
176
+ );
177
+ }}
178
+ />
179
+ );
180
+ case "checkbox":
181
+ return (
182
+ <>
183
+ <Controller
184
+ name={name}
185
+ control={control}
186
+ render={({ field }) => <FormControlLabel control={<Checkbox {...field} checked={field.value} />} label={label} />}
187
+ />
188
+
189
+ {/* {errors.terms && (
190
+ <FormHelperText sx={{ px: 2 }} error>
191
+ {errors.terms.message}
192
+ </FormHelperText>
193
+ )} */}
194
+ </>
195
+ );
196
+ case "switch":
197
+ return (
198
+ <Controller
199
+ name={name}
200
+ control={control}
201
+ defaultValue={true}
202
+ render={({ field }) => <FormControlLabel control={<Switch {...field} checked={field.value} />} label={label} />}
203
+ />
204
+ );
205
+ case "date":
206
+ return (
207
+ <Controller
208
+ name={name}
209
+ control={control}
210
+ render={({ field, fieldState: { error } }) => {
211
+ if (field.value && typeof field.value === "string") {
212
+ field.value = moment(field.value);
213
+ }
214
+ return (
215
+ <DatePicker
216
+ label={label + (required ? "*" : "")}
217
+ {...field}
218
+ format={DEFAULT_DATE_FORMAT}
219
+ slotProps={{
220
+ textField: {
221
+ fullWidth: true, error: Boolean(error),
222
+ helperText: error?.message
223
+ },
224
+ popper: {
225
+ disablePortal: false,
226
+ popperOptions: {
227
+ strategy: 'fixed'
228
+ },
229
+ }
230
+ }}
231
+
232
+
233
+ />
234
+ );
235
+ }}
236
+ />
237
+ );
238
+ }
239
+ }
240
+
241
+ export default FormField;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "1.0.13",
3
+ "version": "1.2.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"