trithuc-mvc-react 1.1.0 → 1.3.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.
@@ -28,11 +28,12 @@ export const TableRowRender = ({ index, row, selected, onSelect, onChangeStatus,
28
28
  valueGetter = (row) => {
29
29
  return row[field];
30
30
  },
31
+ renderCell,
31
32
  valueFormat = (e) => e
32
33
  }) => {
33
34
  return (
34
35
  <TableCell key={`${row[selectedField]}-${field}`} align={alignRight ? "right" : "left"}>
35
- {valueFormat(valueGetter(row))}
36
+ {renderCell ? renderCell(row) : valueFormat(valueGetter(row))}
36
37
  </TableCell>
37
38
  );
38
39
  }
@@ -1,6 +1,6 @@
1
1
  import { Button, IconButton, Stack, Tooltip, Typography } from "@mui/material";
2
2
 
3
- import { useMemo, useState } from "react";
3
+ import { useEffect, useMemo, useState } from "react";
4
4
 
5
5
  import { DataTableContext, PermissionContext } from "./context";
6
6
  import PropTypes from "prop-types";
@@ -32,7 +32,9 @@ DataManagement.propTypes = {
32
32
  validationSchema: PropTypes.object,
33
33
  disableStatus: PropTypes.bool,
34
34
  statusKey: PropTypes.string,
35
- tableActions: PropTypes.array
35
+ tableActions: PropTypes.array,
36
+ disableEditor: PropTypes.bool,
37
+ onAddClick: PropTypes.func
36
38
  };
37
39
 
38
40
  function DataManagement({
@@ -40,18 +42,38 @@ function DataManagement({
40
42
  title,
41
43
  tableName,
42
44
  selectedField = "Id",
43
- filters,
44
- editorFields,
45
+ filters : tableFilters = [],
46
+ editorFields = [],
45
47
  validationSchema = {},
46
48
  statusKey = "Status",
47
49
  disableStatus = false,
48
- tableActions = []
50
+ tableActions = [],
51
+ disableEditor = false,
52
+ onAddClick = () => {
53
+
54
+ }
49
55
  }) {
50
56
  const [openEditorDialog, setOpenEditorDialog] = useState(false);
51
57
  const [selectedEditItem, setSelectedEditItem] = useState(null);
52
- const [dataSearch, setDataSearch] = useState({});
58
+
53
59
  const [Permission, setPermission] = useState(null);
54
60
 
61
+ const { defaults, filters } = useMemo(() => {
62
+ const filters = tableFilters.filter(({ type }) => type !== "default");
63
+ const defaultFilters = tableFilters.filter(({ type }) => type === "default");
64
+
65
+ const defaults = defaultFilters.reduce((prev, { field, value }) => {
66
+ prev[field] = value;
67
+ return prev;
68
+ }, {});
69
+ return { defaults, filters };
70
+
71
+ }, [tableFilters]);
72
+ useEffect(() => {
73
+ setDataSearch({ ...dataSearch,...defaults });
74
+
75
+ }, [filters]);
76
+ const [dataSearch, setDataSearch] = useState({ ...defaults });
55
77
  const values = useMemo(() => {
56
78
  return {
57
79
  tableName,
@@ -65,6 +87,7 @@ function DataManagement({
65
87
  validationSchema,
66
88
  statusKey,
67
89
  disableStatus,
90
+
68
91
  tableActions
69
92
  };
70
93
  }, [tableName, selectedField, columns, selectedEditItem, dataSearch, setDataSearch, validationSchema, tableActions]);
@@ -90,7 +113,7 @@ function DataManagement({
90
113
  variant="outlined"
91
114
  color="primary"
92
115
  onClick={() => {
93
- setDataSearch({});
116
+ setDataSearch({...defaults});
94
117
  [...filters].forEach((filter) => {
95
118
  filter?.onChange?.();
96
119
  });
@@ -107,9 +130,12 @@ function DataManagement({
107
130
  <Button
108
131
  variant="contained"
109
132
  startIcon={<Add />}
110
- onClick={() => {
111
- setOpenEditorDialog(true);
112
- setSelectedEditItem(null);
133
+ onClick={(e) => {
134
+ if (!disableEditor) {
135
+ setOpenEditorDialog(true);
136
+ setSelectedEditItem(null);
137
+ }
138
+ onAddClick(e);
113
139
  }}
114
140
  >
115
141
  Thêm
@@ -120,7 +146,9 @@ function DataManagement({
120
146
  <FilterGod filters={filters} />
121
147
  <DataTable />
122
148
  </FormProvider>
123
- <EditorDialog open={openEditorDialog} onClose={() => setOpenEditorDialog(false)} defaultValues={selectedEditItem} fields={editorFields} />
149
+ {disableEditor || (
150
+ <EditorDialog open={openEditorDialog} onClose={() => setOpenEditorDialog(false)} defaultValues={selectedEditItem} fields={editorFields} />
151
+ )}
124
152
  </PermissionContext.Provider>
125
153
  </DataTableContext.Provider>
126
154
  </>
@@ -0,0 +1,95 @@
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 moment from "moment/moment";
7
+
8
+
9
+ import FormField from "./FormField";
10
+ import { yupResolver } from "@hookform/resolvers/yup";
11
+ DynamicForm.propTypes = {
12
+ fields: PropTypes.array
13
+ };
14
+ function DynamicForm({ fields, elementSize = "medium", submitRef, validationSchema, onSubmit }) {
15
+
16
+ const methods = useForm({ defaultValues: {}, resolver: yupResolver(validationSchema) });
17
+
18
+ const handleSubmit = (data) => {
19
+ fields
20
+ .filter(({ type }) => type === "date")
21
+ .forEach(({ field }) => {
22
+ if (data[field]) {
23
+ data[field] = moment(data[field]).toDate();
24
+ }
25
+ });
26
+ fields
27
+ .filter(({ type }) => type === "autocomplete")
28
+ .forEach(({ field, datas, keyValueLabel, keyValue, keyLabel }) => {
29
+ if (data[field] && !data[keyValueLabel] && keyValueLabel) {
30
+ data[keyValueLabel] = datas.find((item) => item[keyValue] == data[field])?.[keyLabel];
31
+ }
32
+ });
33
+
34
+ onSubmit(data);
35
+ };
36
+
37
+ return (
38
+ <FormProvider {...methods}>
39
+ <Box component={"form"} sx={{ pt: 2 }} onSubmit={methods.handleSubmit(handleSubmit)}>
40
+ <Grid container spacing={2}>
41
+ {fields.map(
42
+ ({
43
+ field,
44
+ type = "text",
45
+ label,
46
+ childrenFields,
47
+ datas,
48
+ loading = false,
49
+ onChange = () => {},
50
+ keyLabel,
51
+ keyValue,
52
+ keyValueLabel,
53
+ required
54
+ }) => {
55
+ let sizes = {
56
+ xs: 12,
57
+ sm: 6,
58
+ md: 4
59
+ };
60
+ if (type == "textarea") {
61
+ sizes = {
62
+ xs: 12,
63
+ sm: 12,
64
+ md: 12
65
+ };
66
+ }
67
+ return (
68
+ <Grid {...sizes} key={field}>
69
+ <FormField
70
+ type={type}
71
+ label={label}
72
+ control={methods.control}
73
+ name={field}
74
+ loading={loading}
75
+ onChange={onChange}
76
+ keyLabel={keyLabel}
77
+ keyValueLabel={keyValueLabel}
78
+ datas={datas}
79
+ childrenFields={childrenFields}
80
+ keyValue={keyValue}
81
+ size={elementSize}
82
+ required={required}
83
+ />
84
+ </Grid>
85
+ );
86
+ }
87
+ )}
88
+ </Grid>
89
+ <button ref={submitRef} type="submit" style={{ display: "none" }} />
90
+ </Box>
91
+ </FormProvider>
92
+ );
93
+ }
94
+
95
+ export default DynamicForm;
@@ -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.1.0",
3
+ "version": "1.3.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"