@visns-studio/visns-components 3.6.8 → 3.6.9

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.
@@ -83,7 +83,7 @@ function Form(props) {
83
83
  }
84
84
  };
85
85
 
86
- const handleChangeRicheditor = (value, name) => {
86
+ const handleChangeRicheditor = (e, value, name) => {
87
87
  setData((prevState) => ({
88
88
  ...prevState,
89
89
  [name]: value,
@@ -709,7 +709,7 @@ const DataGrid = forwardRef(
709
709
  {(form.create &&
710
710
  form.create.title &&
711
711
  form.create.title !== '') ||
712
- (form.url && form.create?.title) ? (
712
+ (form.url && form.create?.title !== '') ? (
713
713
  <button
714
714
  className="btn"
715
715
  onClick={(e) => {
@@ -734,7 +734,7 @@ const DataGrid = forwardRef(
734
734
  >
735
735
  <div className="icon-plus"></div> Create
736
736
  </button>
737
- ) : form.url ? (
737
+ ) : form.url && form.modal === false ? (
738
738
  <button
739
739
  className="btn"
740
740
  onClick={(e) => {
@@ -75,12 +75,14 @@ function TableFilter({ filters, setFilters, setSettings, type }) {
75
75
  },
76
76
  };
77
77
  } else {
78
- let w = prevState.where.map((a) => {
79
- if (id === a.id) {
80
- return { ...a, value: value };
81
- }
82
- return a;
83
- });
78
+ let w = prevState.where
79
+ ? prevState.where.map((a) => {
80
+ if (id === a.id) {
81
+ return { ...a, value: value };
82
+ }
83
+ return a;
84
+ })
85
+ : [];
84
86
 
85
87
  return { ...prevState, where: w };
86
88
  }
@@ -1,212 +1,291 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import { Outlet } from 'react-router-dom';
3
3
  import { toast } from 'react-toastify';
4
+ import { saveAs } from 'file-saver';
4
5
  import CustomFetch from '../Fetch';
6
+ import Download from '../Download';
7
+ import Field from '../Field';
5
8
  import Table from '../DataGrid';
6
9
  import TableFilter from '../TableFilter';
7
10
 
8
- function GenericIndex({ layout = 'full', setting, userProfile }) {
9
- const {
10
- ajaxSetting,
11
- columns,
12
- filters,
13
- form,
14
- functions,
15
- page,
16
- settings,
17
- tableSetting,
18
- tabs,
19
- } = setting;
20
- const gridRef = useRef(null);
11
+ function useWindowHeight() {
12
+ const [windowHeight, setWindowHeight] = useState(window.innerHeight);
13
+
14
+ useEffect(() => {
15
+ const handleResize = () => setWindowHeight(window.innerHeight);
16
+ window.addEventListener('resize', handleResize);
17
+ return () => window.removeEventListener('resize', handleResize);
18
+ }, []);
21
19
 
20
+ return windowHeight;
21
+ }
22
+
23
+ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
22
24
  const [config, setConfig] = useState({});
23
- const [rowsSelected, setRowsSelected] = useState({});
24
25
  const [subnav, setSubnav] = useState(filters || []);
25
- const [total, setTotal] = useState(0);
26
- const [windowHeight, setWindowHeight] = useState(window.innerHeight);
27
- const pageTitle = page?.title;
28
26
 
29
- const isObjectEmpty = (obj) => {
30
- return Object.keys(obj).length === 0;
27
+ useEffect(() => {
28
+ setConfig({
29
+ ajaxSetting: setting.ajaxSetting,
30
+ form: setting.form,
31
+ columns: setting.columns,
32
+ setRowsSelected,
33
+ settings: setting.settings,
34
+ tableSetting,
35
+ type: setting.type ? setting.type : 'table',
36
+ });
37
+ setSubnav(filters);
38
+ }, [setting, filters, setRowsSelected, tableSetting]);
39
+
40
+ useEffect(() => {
41
+ if (tabs?.length > 0 && subnav) {
42
+ const activeTab = subnav.find((s) => s.show);
43
+ const activeTabConfig = tabs.find((t) => t.id === activeTab?.id);
44
+
45
+ if (activeTabConfig) {
46
+ setConfig((currentConfig) => ({
47
+ ...currentConfig,
48
+ ajaxSetting: activeTabConfig.ajaxSetting,
49
+ form: activeTabConfig.form,
50
+ columns: activeTabConfig.columns,
51
+ settings: activeTabConfig.settings,
52
+ type: activeTabConfig.type ? activeTabConfig.type : 'table',
53
+ }));
54
+ }
55
+ }
56
+ }, [subnav, tabs]);
57
+
58
+ return { config, setConfig, subnav, setSubnav };
59
+ }
60
+
61
+ const ReportForm = ({ config, userProfile }) => {
62
+ const [formData, setFormData] = useState({});
63
+
64
+ const handleChange = (e) => {
65
+ const {
66
+ value,
67
+ dataset: { name },
68
+ } = e.target;
69
+
70
+ setFormData((prevData) => ({ ...prevData, [name]: value }));
71
+ };
72
+
73
+ const handleChangeDate = (date, id) => {
74
+ setFormData((prevData) => ({ ...prevData, [id]: date }));
75
+ };
76
+
77
+ const handleChangeSelect = (inputValue, action, id) => {
78
+ setFormData((prevData) => ({ ...prevData, [id]: inputValue }));
31
79
  };
32
80
 
33
- const handleCheckboxUpdate = async (e) => {
81
+ const handleExport = async (e) => {
34
82
  try {
35
- e.preventDefault();
36
- const { data, method, message, url } = functions.checkboxUpdate;
83
+ if (e) {
84
+ e.preventDefault();
85
+ }
37
86
 
38
- if (isObjectEmpty(rowsSelected)) {
39
- toast.warning(message.warning);
87
+ const missingFields = config.form.filters
88
+ .filter(
89
+ (item) => item.required === true && formData[item.id] === ''
90
+ )
91
+ .map((item) => item.label);
92
+
93
+ if (missingFields.length > 0) {
94
+ const fieldNames = missingFields.join(', ');
95
+ const errorMessage = `Please fill in a value for the following field(s): ${fieldNames}.`;
96
+ toast.error(errorMessage);
40
97
  } else {
41
- const res = await CustomFetch(url, method, {
42
- ...data,
43
- rows: rowsSelected,
44
- });
45
-
46
- if (res.data.error === '') {
47
- toast.success(message.success);
48
- gridRef.current.reload();
98
+ const res = await Download(
99
+ config.form.url,
100
+ config.form.method,
101
+ formData
102
+ );
103
+
104
+ if (res.data.error) {
105
+ toast.error(res.data.error);
49
106
  } else {
50
- toast.error(message.error);
107
+ // Assuming the response contains a Blob or similar data
108
+ if (res.data && res.data instanceof Blob) {
109
+ const fileName = `${config.form.filename}`;
110
+ saveAs(res.data, fileName);
111
+ } else {
112
+ // Handle the case where the response is not a Blob
113
+ toast.error('Invalid file format received.');
114
+ }
51
115
  }
116
+
117
+ // Proceed with the export functionality
118
+ console.info(formData, res);
52
119
  }
53
- } catch (error) {
54
- console.error(error);
120
+ } catch (err) {
121
+ console.error(err);
55
122
  }
56
123
  };
57
124
 
58
125
  useEffect(() => {
59
- if (!tabs || tabs.length === 0) {
60
- setConfig(() => ({
61
- ajaxSetting,
62
- form,
63
- columns,
64
- setRowsSelected,
65
- settings,
66
- tableSetting,
67
- }));
68
- }
126
+ const tmpForm = config.form?.filters.reduce((acc, item) => {
127
+ acc[item.id] = '';
128
+ return acc;
129
+ }, {});
130
+ setFormData(tmpForm);
131
+ }, [config.form?.filters]);
69
132
 
70
- setSubnav(filters);
71
- }, [setting]);
133
+ return (
134
+ <div className="formcontainer">
135
+ <form className="modalForm">
136
+ {config.form?.filters.map((item, key) => (
137
+ <Field
138
+ api={userProfile.settings.api}
139
+ autocompleteCallback={() => {}}
140
+ childDropdownCallback={() => {}}
141
+ fetchData={() => {}}
142
+ formData={formData}
143
+ inputClass={''}
144
+ inputValue={formData[item.id]}
145
+ key={`filter-fields-${key}`}
146
+ mapbox={{}}
147
+ onChange={handleChange}
148
+ onChangeCheckbox={() => {}}
149
+ onChangeDate={handleChangeDate}
150
+ onChangeNumberFormat={() => {}}
151
+ onChangeRicheditor={() => {}}
152
+ onChangeSelect={handleChangeSelect}
153
+ onChangeToggle={() => {}}
154
+ settings={item}
155
+ setFormData={setFormData}
156
+ style={userProfile.settings.style}
157
+ uploadProgress={0}
158
+ userProfile={userProfile}
159
+ />
160
+ ))}
161
+ <div className="formItem fwItem lastItem">
162
+ <button className="btn modalsave" onClick={handleExport}>
163
+ Export
164
+ </button>
165
+ </div>
166
+ </form>
167
+ </div>
168
+ );
169
+ };
72
170
 
73
- useEffect(() => {
74
- if (tabs && tabs.length > 0 && subnav) {
75
- const activeTab = subnav.find((s) => s.show === true);
76
-
77
- if (activeTab) {
78
- const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
79
-
80
- if (activeTabConfig) {
81
- setConfig(() => ({
82
- ajaxSetting: activeTabConfig.ajaxSetting,
83
- form: activeTabConfig.form,
84
- columns: activeTabConfig.columns,
85
- setRowsSelected,
86
- settings: activeTabConfig.settings,
87
- tableSetting,
88
- }));
89
- }
171
+ function GenericIndex({ layout = 'full', setting, userProfile }) {
172
+ const gridRef = useRef(null);
173
+ const [rowsSelected, setRowsSelected] = useState({});
174
+ const [total, setTotal] = useState(0);
175
+ const windowHeight = useWindowHeight();
176
+ const { config, setConfig, subnav, setSubnav } = useConfig(
177
+ setting,
178
+ setting.tabs,
179
+ setting.filters,
180
+ setRowsSelected,
181
+ setting.tableSetting
182
+ );
183
+
184
+ const handleCheckboxUpdate = useCallback(async () => {
185
+ try {
186
+ const { data, method, message, url } =
187
+ setting.functions.checkboxUpdate;
188
+ if (!Object.keys(rowsSelected).length) {
189
+ toast.warning(message.warning);
190
+ return;
90
191
  }
192
+ const res = await CustomFetch(url, method, {
193
+ ...data,
194
+ rows: rowsSelected,
195
+ });
196
+ if (res.data.error === '') {
197
+ toast.success(message.success);
198
+ gridRef.current.reload();
199
+ } else {
200
+ toast.error(message.error);
201
+ }
202
+ } catch (error) {
203
+ console.error(error);
91
204
  }
92
- }, [subnav, tabs]);
205
+ }, [rowsSelected, setting?.functions?.checkboxUpdate]);
93
206
 
94
- useEffect(() => {
95
- const handleResize = () => {
96
- setWindowHeight(window.innerHeight);
97
- };
207
+ const renderTable = useCallback(
208
+ () =>
209
+ config.ajaxSetting &&
210
+ config.form &&
211
+ config.columns &&
212
+ config.settings ? (
213
+ <Table
214
+ {...config}
215
+ ref={gridRef}
216
+ gridHeight={windowHeight * 0.65}
217
+ setConfig={setConfig}
218
+ setTotal={setTotal}
219
+ style={userProfile?.settings?.style || {}}
220
+ />
221
+ ) : null,
222
+ [config, windowHeight, userProfile, setConfig, setTotal]
223
+ );
98
224
 
99
- window.addEventListener('resize', handleResize);
225
+ const renderContentBasedOnType = useCallback(() => {
226
+ switch (config.type) {
227
+ case 'table':
228
+ return renderTable();
229
+ case 'report':
230
+ return <ReportForm config={config} userProfile={userProfile} />;
231
+ default:
232
+ return renderTable(); // Fallback to table if no type is specified or recognized
233
+ }
234
+ }, [config, renderTable]);
100
235
 
101
- // Clean up the event listener
102
- return () => {
103
- window.removeEventListener('resize', handleResize);
104
- };
105
- }, []);
236
+ const renderContent = useCallback(
237
+ () => (
238
+ <div className={`grid__${subnav?.length > 0 ? 'subrow' : 'full'}`}>
239
+ {subnav?.length > 0 && (
240
+ <div
241
+ className={`grid__${
242
+ layout === 'full' ? 'subnav' : 'full'
243
+ }`}
244
+ >
245
+ <TableFilter
246
+ filters={subnav}
247
+ setFilters={setSubnav}
248
+ setSettings={setConfig}
249
+ type={layout}
250
+ />
251
+ </div>
252
+ )}
253
+ <div
254
+ className={`grid__${
255
+ subnav?.length > 0 ? 'subcontent' : 'full'
256
+ }`}
257
+ >
258
+ {renderContentBasedOnType()}
259
+ </div>
260
+ </div>
261
+ ),
262
+ [layout, subnav, setSubnav, setConfig, renderTable]
263
+ );
106
264
 
107
265
  return (
108
266
  <>
109
267
  <div className="grid">
110
268
  <div className="grid__row">
111
269
  <div className="grid__full crmtitle">
112
- <h1>{pageTitle}</h1>
270
+ <h1>{setting.page?.title}</h1>
113
271
  <div className="titleInfo">
114
272
  <span>
115
- [<strong>{total}</strong> Total {pageTitle}]
273
+ [<strong>{total}</strong> Total{' '}
274
+ {setting.page?.title}]
116
275
  </span>
117
276
  </div>
118
277
  </div>
119
278
  </div>
279
+ {renderContent()}
120
280
  </div>
121
-
122
- {config.ajaxSetting &&
123
- config.form &&
124
- config.columns &&
125
- config.settings ? (
126
- <>
127
- <div className="grid">
128
- {subnav && subnav.length > 0 ? (
129
- <div className="grid__subrow">
130
- {layout === 'full' ? (
131
- <>
132
- <div className={`grid__subnav`}>
133
- <TableFilter
134
- filters={subnav}
135
- setFilters={setSubnav}
136
- setSettings={setConfig}
137
- type={layout}
138
- />
139
- </div>
140
- <div className={`grid__subcontent`}>
141
- <Table
142
- {...config}
143
- ref={gridRef}
144
- gridHeight={windowHeight * 0.65}
145
- setConfig={setConfig}
146
- setTotal={setTotal}
147
- style={
148
- userProfile?.settings
149
- ?.style || {}
150
- }
151
- />
152
- </div>
153
- </>
154
- ) : (
155
- <div className="grid__full">
156
- <div className="tabcontent">
157
- <div className={`tabcontent__menu`}>
158
- <TableFilter
159
- filters={subnav}
160
- setFilters={setSubnav}
161
- setSettings={setConfig}
162
- type={layout}
163
- />
164
- </div>
165
- <div className={`tabcontent__main`}>
166
- <Table
167
- {...config}
168
- ref={gridRef}
169
- gridHeight={
170
- windowHeight * 0.65
171
- }
172
- setConfig={setConfig}
173
- setTotal={setTotal}
174
- style={
175
- userProfile?.settings
176
- ?.style || {}
177
- }
178
- />
179
- </div>
180
- </div>
181
- </div>
182
- )}
183
- </div>
184
- ) : (
185
- <div className="grid__full">
186
- <Table
187
- {...config}
188
- ref={gridRef}
189
- gridHeight={windowHeight * 0.65}
190
- setConfig={setConfig}
191
- setTotal={setTotal}
192
- style={userProfile?.settings?.style || {}}
193
- />
194
- </div>
195
- )}
196
- </div>
197
- <Outlet />
198
- </>
199
- ) : null}
200
-
201
- {functions && functions.checkboxUpdate ? (
281
+ <Outlet />
282
+ {setting.functions?.checkboxUpdate && (
202
283
  <div className="polActions">
203
284
  <button className="btn" onClick={handleCheckboxUpdate}>
204
- {functions.checkboxUpdate.label
205
- ? functions.checkboxUpdate.label
206
- : 'Update'}
285
+ {setting.functions.checkboxUpdate.label || 'Update'}
207
286
  </button>
208
287
  </div>
209
- ) : null}
288
+ )}
210
289
  </>
211
290
  );
212
291
  }
package/package.json CHANGED
@@ -50,7 +50,7 @@
50
50
  "react-dom": "^17.0.1"
51
51
  },
52
52
  "name": "@visns-studio/visns-components",
53
- "version": "3.6.8",
53
+ "version": "3.6.9",
54
54
  "description": "Various packages to assist in the development of our Custom Applications.",
55
55
  "main": "index.js",
56
56
  "scripts": {