@visns-studio/visns-components 3.6.7 → 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
  }
@@ -74,6 +74,7 @@ const GenericDynamic = ({
74
74
  primaryKey,
75
75
  setData,
76
76
  setting,
77
+ templateStatus = true,
77
78
  type,
78
79
  }) => {
79
80
  const toastId = useRef(null);
@@ -266,13 +267,14 @@ const GenericDynamic = ({
266
267
  }
267
268
  };
268
269
 
269
- const handleDownloadForm = async (item, key) => {
270
+ const handleDownloadForm = async (item, label, data) => {
270
271
  // Show loading toast and keep its ID
271
272
  const toastId = toast.loading('Downloading form...');
272
273
 
273
274
  try {
274
275
  const res = await Download(setting.download, 'POST', {
275
276
  data: item,
277
+ dynamicData: data,
276
278
  label: label,
277
279
  });
278
280
 
@@ -391,28 +393,30 @@ const GenericDynamic = ({
391
393
  useEffect(() => {
392
394
  if (data[type] && data[type].length > 0) {
393
395
  if (multiple) {
394
- // Retrieve the last form of the specified type
395
- let lastForm = data[type][data[type].length - 1];
396
-
397
- if (lastForm && typeof lastForm === 'object') {
398
- Object.entries(lastForm).forEach(([key, field]) => {
399
- if (field && field.required) {
400
- if (
401
- field.required === 'yes' ||
402
- field.required === 'no'
403
- ) {
404
- lastForm[key].required =
405
- field.required === 'yes';
396
+ if (templateStatus) {
397
+ // Retrieve the last form of the specified type
398
+ let lastForm = data[type][data[type].length - 1];
399
+
400
+ if (lastForm && typeof lastForm === 'object') {
401
+ Object.entries(lastForm).forEach(([key, field]) => {
402
+ if (field && field.required) {
403
+ if (
404
+ field.required === 'yes' ||
405
+ field.required === 'no'
406
+ ) {
407
+ lastForm[key].required =
408
+ field.required === 'yes';
409
+ }
406
410
  }
407
- }
408
- });
411
+ });
409
412
 
410
- // Update the active form with the modified lastForm
411
- setActiveForm(lastForm);
412
- setActiveFormKey(data[type].length - 1);
413
+ // Update the active form with the modified lastForm
414
+ setActiveForm(lastForm);
415
+ setActiveFormKey(data[type].length - 1);
416
+ }
413
417
  }
414
418
  } else {
415
- if (activeForm.length === 0) {
419
+ if (activeForm.length === 0 && templateStatus) {
416
420
  setActiveForm(data[type]);
417
421
  setActiveFormKey(0);
418
422
  }
@@ -438,6 +442,7 @@ const GenericDynamic = ({
438
442
  <thead>
439
443
  <tr className="grid-headers">
440
444
  <th>ID</th>
445
+ <th>Form</th>
441
446
  <th>Created By</th>
442
447
  <th>Created At</th>
443
448
  <th>Updated By</th>
@@ -464,7 +469,8 @@ const GenericDynamic = ({
464
469
  );
465
470
  }}
466
471
  >
467
- <td>Form #{item.id}</td>
472
+ <td>{item.id}</td>
473
+ <td>{item.form_label}</td>
468
474
  <td>{item.created_by}</td>
469
475
  <td>
470
476
  {item.created_at
@@ -494,7 +500,8 @@ const GenericDynamic = ({
494
500
  onClick={() =>
495
501
  handleDownloadForm(
496
502
  item,
497
- index
503
+ label,
504
+ data
498
505
  )
499
506
  }
500
507
  >
@@ -642,7 +649,7 @@ const GenericDynamic = ({
642
649
  )}
643
650
  </div>
644
651
  <div className="polActions">
645
- {multiple && (
652
+ {multiple && templateStatus && (
646
653
  <button className="btn" onClick={handleAddNewForm}>
647
654
  Add New Form
648
655
  </button>
@@ -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.7",
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": {