@visns-studio/visns-components 4.7.2 → 4.7.4

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.
Files changed (99) hide show
  1. package/package.json +15 -14
  2. package/src/components/cms/DataGrid.jsx +1461 -0
  3. package/src/components/cms/DropZone.jsx +349 -0
  4. package/src/components/cms/Field.jsx +802 -0
  5. package/src/components/cms/Form.jsx +476 -0
  6. package/src/components/cms/generic/CmsDetail.jsx +36 -0
  7. package/src/components/cms/generic/CmsIndex.jsx +73 -0
  8. package/src/components/cms/generic/CmsSort.jsx +125 -0
  9. package/src/components/cms/sorting/Item.jsx +115 -0
  10. package/src/components/cms/sorting/List.jsx +30 -0
  11. package/src/components/crm/AsyncSelect.jsx +125 -0
  12. package/src/components/crm/Autocomplete.jsx +174 -0
  13. package/src/components/crm/Breadcrumb.jsx +52 -0
  14. package/src/components/crm/Call.jsx +220 -0
  15. package/src/components/crm/DataGrid.jsx +2701 -0
  16. package/src/components/crm/Download.jsx +101 -0
  17. package/src/components/crm/Fetch.jsx +119 -0
  18. package/src/components/crm/Field.jsx +1637 -0
  19. package/src/components/crm/Form.jsx +1409 -0
  20. package/src/components/crm/Loader.jsx +29 -0
  21. package/src/components/crm/MultiSelect.jsx +78 -0
  22. package/src/components/crm/Navigation.jsx +619 -0
  23. package/src/components/crm/Notification.jsx +197 -0
  24. package/src/components/crm/QrCode.jsx +83 -0
  25. package/src/components/crm/Select.jsx +58 -0
  26. package/src/components/crm/SelectList.jsx +21 -0
  27. package/src/components/crm/TableFilter.jsx +198 -0
  28. package/src/components/crm/auth/Login.jsx +223 -0
  29. package/src/components/crm/auth/Profile.jsx +280 -0
  30. package/src/components/crm/auth/Reset.jsx +111 -0
  31. package/src/components/crm/auth/Verify.jsx +174 -0
  32. package/src/components/crm/auth/styles/Login.module.scss +243 -0
  33. package/src/components/crm/auth/styles/Profile.module.scss +222 -0
  34. package/src/components/crm/auth/styles/Reset.module.scss +216 -0
  35. package/src/components/crm/auth/styles/Verify.module.scss +216 -0
  36. package/src/components/crm/generic/AuditLog.jsx +108 -0
  37. package/src/components/crm/generic/AuditLogs.jsx +115 -0
  38. package/src/components/crm/generic/GenericAuth.jsx +166 -0
  39. package/src/components/crm/generic/GenericDashboard.jsx +42 -0
  40. package/src/components/crm/generic/GenericDetail.jsx +1499 -0
  41. package/src/components/crm/generic/GenericDynamic.jsx +916 -0
  42. package/src/components/crm/generic/GenericFormBuilder.jsx +1909 -0
  43. package/src/components/crm/generic/GenericIndex.jsx +376 -0
  44. package/src/components/crm/generic/GenericMain.jsx +129 -0
  45. package/src/components/crm/generic/GenericSort.jsx +188 -0
  46. package/src/components/crm/generic/NotificationList.jsx +64 -0
  47. package/src/components/crm/generic/styles/AuditLog.module.scss +147 -0
  48. package/src/components/crm/generic/styles/AuditLogs.module.scss +92 -0
  49. package/src/components/crm/generic/styles/GenericDetail.module.scss +634 -0
  50. package/src/components/crm/generic/styles/GenericDynamic.module.scss +932 -0
  51. package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +944 -0
  52. package/src/components/crm/generic/styles/GenericIndex.module.scss +247 -0
  53. package/src/components/crm/generic/styles/GenericMain.module.scss +91 -0
  54. package/src/components/crm/generic/styles/GenericSort.module.scss +123 -0
  55. package/src/components/crm/generic/styles/NotificationList.module.scss +67 -0
  56. package/src/components/crm/sorting/Item.jsx +117 -0
  57. package/src/components/crm/sorting/List.jsx +32 -0
  58. package/src/components/crm/sorting/styles/Item.module.scss +68 -0
  59. package/src/components/crm/sorting/styles/List.module.scss +68 -0
  60. package/src/components/crm/styles/AsyncSelect.module.scss +8 -0
  61. package/src/components/crm/styles/Autocomplete.module.scss +44 -0
  62. package/src/components/crm/styles/DataGrid.module.scss +210 -0
  63. package/src/components/crm/styles/Field.module.scss +311 -0
  64. package/src/components/crm/styles/Form.module.scss +499 -0
  65. package/src/components/crm/styles/Loader.module.scss +94 -0
  66. package/src/components/crm/styles/MultiSelect.module.scss +8 -0
  67. package/src/components/crm/styles/Navigation.module.scss +978 -0
  68. package/src/components/crm/styles/Notification.module.scss +147 -0
  69. package/src/components/crm/styles/QrCode.module.scss +87 -0
  70. package/src/components/crm/styles/TableFilter.module.scss +52 -0
  71. package/src/components/styles/global.css +73 -0
  72. package/src/index.js +86 -0
  73. package/dist/104e5636d8ddb791614c.woff2 +0 -0
  74. package/dist/11fe6a83d647de5daf95.woff +0 -0
  75. package/dist/141e2a8c7836495afe2a.woff +0 -0
  76. package/dist/2ba5877f7079830b5383.woff +0 -0
  77. package/dist/2d6836f45b2037be41f3.woff +0 -0
  78. package/dist/399.0a0f3e0d21be1057d599.css +0 -8
  79. package/dist/399.b8f57fa3f8c0c10119e4.js +0 -1
  80. package/dist/47d566c3b1293dfd9d77.woff +0 -0
  81. package/dist/6282a13cbba671a71913.woff2 +0 -0
  82. package/dist/78266f551031aa9db127.woff +0 -0
  83. package/dist/7b7da89f1e3fdcbfe36c.woff2 +0 -0
  84. package/dist/7e78e8338af7efeda712.woff2 +0 -0
  85. package/dist/7fa387951673abf164b1.woff2 +0 -0
  86. package/dist/819d59862c2d0640aad8.woff +0 -0
  87. package/dist/b615e1a6c838ea059a56.woff +0 -0
  88. package/dist/b9b1aa739c660c4ce67c.woff2 +0 -0
  89. package/dist/c5f3640b11df9414facb.woff +0 -0
  90. package/dist/da26d162ff5af71d6590.woff2 +0 -0
  91. package/dist/dd5b2912dbf896310865.woff2 +0 -0
  92. package/dist/f282b331272463c1aa6c.woff2 +0 -0
  93. package/dist/index.css +0 -2902
  94. package/dist/index.js +0 -2
  95. package/dist/index.js.LICENSE.txt +0 -150
  96. package/dist/main.83b033670d64419bb497.css +0 -1
  97. package/dist/main.b1723d17cd2d3513ab62.js +0 -1
  98. package/dist/report.html +0 -39
  99. package/dist/runtime.33c276593b23f8035b8d.js +0 -1
@@ -0,0 +1,916 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import moment from 'moment';
3
+ import 'react-confirm-alert/src/react-confirm-alert.css';
4
+ import { toast } from 'react-toastify';
5
+ import { confirmAlert } from 'react-confirm-alert';
6
+ import { saveAs } from 'file-saver';
7
+ import parse from 'html-react-parser';
8
+ import { Backspace, CloudDownload } from 'akar-icons';
9
+
10
+ import CustomFetch from '../Fetch';
11
+ import Download from '../Download';
12
+ import Field from '../Field';
13
+ import styles from './styles/GenericDynamic.module.scss';
14
+
15
+ const updateField = (fields, name, value) => {
16
+ if (Array.isArray(fields)) {
17
+ return fields.map((field) => ({
18
+ ...field,
19
+ value: field.id === name ? value : field.value,
20
+ }));
21
+ }
22
+
23
+ return Object.entries(fields).reduce(
24
+ (acc, [key, field]) => ({
25
+ ...acc,
26
+ [key]: field.id === name ? { ...field, value } : field,
27
+ }),
28
+ {}
29
+ );
30
+ };
31
+
32
+ const renderValue = (field, data) => {
33
+ switch (field.type) {
34
+ case 'datetime':
35
+ case 'date':
36
+ return field.value && moment(field.value).isValid()
37
+ ? moment(field.value).toDate()
38
+ : '';
39
+
40
+ case 'dynamicdata':
41
+ if (field.dynamicfield && typeof field.dynamicfield === 'string') {
42
+ const [table, key] = field.dynamicfield.split('::');
43
+
44
+ if (table && key) {
45
+ switch (table) {
46
+ case 'opportunities':
47
+ return data[table][key];
48
+ case 'clients':
49
+ return data['client'] ? data['client'][key] : null;
50
+ default:
51
+ if (key === 'surname' && !data[key]) {
52
+ return data['lastname'] || '';
53
+ }
54
+ return data[key];
55
+ }
56
+ } else {
57
+ console.warn(
58
+ `Dynamic field not found or invalid: ${field.dynamicfield}`
59
+ );
60
+ }
61
+ } else {
62
+ console.warn('Dynamic field is missing or not a string');
63
+ }
64
+ break;
65
+
66
+ case 'time':
67
+ return field.value && moment(field.value).isValid()
68
+ ? moment(field.value).toDate()
69
+ : '';
70
+ default:
71
+ return field.value || '';
72
+ }
73
+ };
74
+
75
+ const GenericDynamic = ({
76
+ buttons,
77
+ data,
78
+ fetchData,
79
+ label,
80
+ multiple = true,
81
+ originalData = {},
82
+ primaryKey,
83
+ setData,
84
+ setting,
85
+ templateStatus = true,
86
+ type,
87
+ userProfile,
88
+ }) => {
89
+ const [activeForm, setActiveForm] = useState({});
90
+ const [activeFormKey, setActiveFormKey] = useState(0);
91
+
92
+ const handleChange = (e) => {
93
+ const {
94
+ checked,
95
+ dataset: { name },
96
+ files,
97
+ value,
98
+ } = e.target;
99
+
100
+ let newValue;
101
+
102
+ if (checked) {
103
+ newValue = checked;
104
+ setActiveForm((prevState) =>
105
+ updateField(prevState, name, newValue)
106
+ );
107
+ } else if (files && files[0]) {
108
+ const toastId = toast.loading('Uploading file...');
109
+ Vapor.store(files[0], {
110
+ progress: (progress) => {
111
+ console.info(`Upload Progress: ${progress}`);
112
+ },
113
+ }).then((response) => {
114
+ if (files[0].type.startsWith('image/')) {
115
+ const reader = new FileReader();
116
+ reader.onload = (e) => {
117
+ const imageSrc = e.target.result;
118
+
119
+ setActiveForm((prevState) =>
120
+ updateField(prevState, name, {
121
+ extension: response.extension,
122
+ key: response.key,
123
+ url: response.url,
124
+ uuid: response.uuid,
125
+ bucket: response.bucket,
126
+ filename: files[0].name,
127
+ filesize: files[0].size,
128
+ imageSrc: imageSrc,
129
+ })
130
+ );
131
+ };
132
+ reader.readAsDataURL(files[0]);
133
+ } else {
134
+ setActiveForm((prevState) =>
135
+ updateField(prevState, name, {
136
+ extension: response.extension,
137
+ key: response.key,
138
+ url: response.url,
139
+ uuid: response.uuid,
140
+ bucket: response.bucket,
141
+ filename: files[0].name,
142
+ filesize: files[0].size,
143
+ })
144
+ );
145
+ }
146
+ toast.update(toastId, {
147
+ render: 'Upload successful!',
148
+ type: 'success',
149
+ isLoading: false,
150
+ autoClose: 5000,
151
+ closeButton: true,
152
+ });
153
+ });
154
+ } else {
155
+ newValue = value;
156
+ setActiveForm((prevState) =>
157
+ updateField(prevState, name, newValue)
158
+ );
159
+ }
160
+ };
161
+
162
+ const handleChangeCheckbox = (e) => {
163
+ const { checked, name, value } = e.target;
164
+
165
+ let tmpValue;
166
+
167
+ Object.entries(activeForm).forEach(([key, field]) => {
168
+ if (field.id && field.id === name) {
169
+ if (
170
+ field.value === null ||
171
+ field.value === '' ||
172
+ field.value.length === 0
173
+ ) {
174
+ tmpValue = [];
175
+
176
+ field.options.forEach((option) => {
177
+ tmpValue.push(false);
178
+ });
179
+ } else {
180
+ tmpValue = [...field.value];
181
+ }
182
+
183
+ field.options.forEach((option, optionKey) => {
184
+ if (option.label === value) {
185
+ tmpValue[optionKey] = checked;
186
+ }
187
+ });
188
+ }
189
+ });
190
+
191
+ setActiveForm((prevState) => updateField(prevState, name, tmpValue));
192
+ };
193
+
194
+ const handleChangeDate = (value, id) => {
195
+ setActiveForm((prevState) => updateField(prevState, id, value));
196
+ };
197
+
198
+ const handleChangeNumberFormat = (value, id) => {
199
+ setActiveForm((prevState) => updateField(prevState, id, value.value));
200
+ };
201
+
202
+ const handleChangeSignature = (id) => {
203
+ if (userProfile.signature) {
204
+ setActiveForm((prevState) =>
205
+ updateField(prevState, id, {
206
+ signature: userProfile.signature,
207
+ name: userProfile.name,
208
+ date: moment().toString(),
209
+ })
210
+ );
211
+ }
212
+ };
213
+
214
+ const handleChangeTableRadio = (e) => {
215
+ const {
216
+ dataset: { id, rowId, columnId },
217
+ } = e.target;
218
+
219
+ setActiveForm((prevActiveForm) => {
220
+ // Find the form element that matches the given id
221
+ const formKey = Object.keys(prevActiveForm).find(
222
+ (key) => prevActiveForm[key].id === id
223
+ );
224
+
225
+ if (!formKey || !prevActiveForm[formKey]?.rows) {
226
+ console.info(
227
+ `Form with id ${id} does not exist or has no rows`
228
+ );
229
+ return prevActiveForm;
230
+ }
231
+
232
+ // Create a new rows array with toggled value properties
233
+ const updatedRows = prevActiveForm[formKey].rows.map((row) => {
234
+ if (row.id === rowId) {
235
+ // Ensure the value is an object to handle multiple columns
236
+ const updatedValue = {
237
+ ...row.value,
238
+ [columnId]: !row.value?.[columnId], // Toggle the specific column value
239
+ };
240
+ return {
241
+ ...row,
242
+ value: updatedValue,
243
+ };
244
+ }
245
+ return row;
246
+ });
247
+
248
+ // Return the updated activeForm state
249
+ return {
250
+ ...prevActiveForm,
251
+ [formKey]: {
252
+ ...prevActiveForm[formKey],
253
+ rows: updatedRows,
254
+ },
255
+ };
256
+ });
257
+ };
258
+
259
+ const handleChangeAddTableTextRow = (id) => {
260
+ setActiveForm((prevActiveForm) => {
261
+ // Find the key of the form that matches the id
262
+ const formKey = Object.keys(prevActiveForm).find(
263
+ (key) => prevActiveForm[key].id === id
264
+ );
265
+
266
+ if (!formKey) {
267
+ console.info(`Form with id ${id} does not exist.`);
268
+ return prevActiveForm;
269
+ }
270
+
271
+ // Create a new row with placeholders based on the options length
272
+ const numOptions = prevActiveForm[formKey].options.length;
273
+ const newRow = Array(numOptions).fill(''); // Create an array of empty strings
274
+
275
+ return {
276
+ ...prevActiveForm,
277
+ [formKey]: {
278
+ ...prevActiveForm[formKey],
279
+ value: [
280
+ ...(prevActiveForm[formKey].value || []), // Handle null by initializing to empty array
281
+ newRow,
282
+ ],
283
+ },
284
+ };
285
+ });
286
+ };
287
+
288
+ const handleChangeTableText = (e, rowIndex, columnIndex) => {
289
+ const { value, checked, type } = e.target;
290
+ const { id } = e.target.dataset;
291
+
292
+ setActiveForm((prevActiveForm) => {
293
+ const formKey = Object.keys(prevActiveForm).find(
294
+ (key) => prevActiveForm[key].id === id
295
+ );
296
+
297
+ if (!formKey) return prevActiveForm;
298
+
299
+ const updatedRows = (prevActiveForm[formKey].value || []).map(
300
+ (row, index) =>
301
+ index === rowIndex
302
+ ? row.map((cell, colIndex) =>
303
+ colIndex === columnIndex
304
+ ? type === 'checkbox' || type === 'toggle'
305
+ ? checked
306
+ : value
307
+ : cell
308
+ )
309
+ : row
310
+ );
311
+
312
+ return {
313
+ ...prevActiveForm,
314
+ [formKey]: {
315
+ ...prevActiveForm[formKey],
316
+ value: updatedRows,
317
+ },
318
+ };
319
+ });
320
+ };
321
+
322
+ const handleChangeToggle = (e) => {
323
+ if (!e) {
324
+ return;
325
+ }
326
+
327
+ const { name, checked } = e.target;
328
+
329
+ setActiveForm((prevState) => updateField(prevState, name, checked));
330
+ };
331
+
332
+ const handleFileDownload = async (d) => {
333
+ const toastId = toast.loading('Downloading file...');
334
+ try {
335
+ const res = await Download(setting.file, 'POST', {
336
+ extension: d.extension,
337
+ uuid: d.uuid,
338
+ filename: d.filename,
339
+ });
340
+
341
+ if (res.data) {
342
+ saveAs(res.data, d.filename);
343
+ toast.update(toastId, {
344
+ render: 'Download successful!',
345
+ type: 'success',
346
+ isLoading: false,
347
+ autoClose: 5000,
348
+ closeButton: true,
349
+ });
350
+ } else {
351
+ toast.update(toastId, {
352
+ render: 'Error downloading form',
353
+ type: 'error',
354
+ isLoading: false,
355
+ autoClose: 5000,
356
+ closeButton: true,
357
+ });
358
+ }
359
+ } catch (err) {
360
+ toast.update(toastId, {
361
+ render: 'Error downloading form',
362
+ type: 'error',
363
+ isLoading: false,
364
+ autoClose: 5000,
365
+ closeButton: true,
366
+ });
367
+ }
368
+ };
369
+
370
+ const handleSelectForm = (form, key) => {
371
+ setActiveForm(form);
372
+ setActiveFormKey(key);
373
+ };
374
+
375
+ const handleCustomButton = async (s) => {
376
+ let payload = {};
377
+ switch (s.type) {
378
+ case 'save':
379
+ if (s.payload?.length > 0) {
380
+ s.payload.forEach((p) => {
381
+ if (data[p]) {
382
+ payload[p] = data[p];
383
+ }
384
+ });
385
+ }
386
+
387
+ const res = await Download(s.url, s.method, payload);
388
+
389
+ if (res.data) {
390
+ saveAs(res.data);
391
+ }
392
+ break;
393
+ default:
394
+ console.info(s, 'default');
395
+ break;
396
+ }
397
+ };
398
+
399
+ const handleAddNewForm = async (showToast = true) => {
400
+ try {
401
+ if (activeForm && activeForm.id) {
402
+ await CustomFetch(setting.save, 'POST', {
403
+ client_id: data.id,
404
+ form_data: {
405
+ ...activeForm,
406
+ },
407
+ form_key: activeFormKey,
408
+ form_type: type,
409
+ });
410
+ }
411
+
412
+ if (setting.add) {
413
+ const res = await CustomFetch(setting.add, 'POST', {
414
+ client_id: data.id,
415
+ form_name: type,
416
+ });
417
+
418
+ if (res.data.error === '') {
419
+ if (showToast) {
420
+ toast.success('Form added successfully');
421
+ }
422
+ fetchData();
423
+ }
424
+ }
425
+ } catch (err) {
426
+ console.error(err);
427
+ toast.error('Error adding form');
428
+ }
429
+ };
430
+
431
+ const handleDownloadForm = async (item, label, data) => {
432
+ const toastId = toast.loading('Downloading form...');
433
+
434
+ try {
435
+ const response = await Download(setting.download, 'POST', {
436
+ data: item,
437
+ dynamicData: data,
438
+ label: label,
439
+ });
440
+
441
+ if (response && response.data) {
442
+ const contentDisposition = response.headers.get(
443
+ 'Content-Disposition'
444
+ );
445
+ let filename = null;
446
+
447
+ if (contentDisposition) {
448
+ const filenameMatch = contentDisposition.match(
449
+ /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
450
+ );
451
+ if (filenameMatch && filenameMatch[1]) {
452
+ filename = filenameMatch[1].replace(/['"]/g, '');
453
+ }
454
+ }
455
+
456
+ if (filename) {
457
+ saveAs(response.data, filename);
458
+ } else {
459
+ saveAs(response.data);
460
+ }
461
+
462
+ toast.update(toastId, {
463
+ render: 'Download successful!',
464
+ type: 'success',
465
+ isLoading: false,
466
+ autoClose: 5000,
467
+ closeButton: true,
468
+ });
469
+ } else {
470
+ throw new Error('No data received from the server');
471
+ }
472
+ } catch (err) {
473
+ console.error(err);
474
+ toast.update(toastId, {
475
+ render: 'Error downloading form: ' + err.message,
476
+ type: 'error',
477
+ isLoading: false,
478
+ autoClose: 5000,
479
+ closeButton: true,
480
+ });
481
+ }
482
+ };
483
+
484
+ const handleDeleteForm = async (id, key) => {
485
+ const deleteForm = async () => {
486
+ try {
487
+ const res = await CustomFetch(setting.delete, 'POST', {
488
+ client_id: data.id,
489
+ form_id: id,
490
+ form_key: key,
491
+ form_type: type,
492
+ });
493
+
494
+ if (res.data.error === '') {
495
+ toast.success('Form deleted successfully.');
496
+ fetchData();
497
+ } else {
498
+ toast.error('Error: ' + res.data.error);
499
+ }
500
+ } catch (err) {
501
+ console.error(err);
502
+ toast.error('Error deleting form');
503
+ }
504
+ };
505
+
506
+ confirmAlert({
507
+ title: 'Delete File',
508
+ message: 'Are you sure?',
509
+ buttons: [
510
+ {
511
+ label: 'Yes',
512
+ onClick: deleteForm,
513
+ },
514
+ {
515
+ label: 'No',
516
+ },
517
+ ],
518
+ });
519
+ };
520
+
521
+ const handleSaveForm = async () => {
522
+ try {
523
+ let error = '';
524
+
525
+ Object.entries(activeForm).forEach(([key, field]) => {
526
+ if (
527
+ field.required === true &&
528
+ field.type !== 'dynamicdata' &&
529
+ (field.value === null || field.value === '')
530
+ ) {
531
+ error += `<p>${field.label} is required</p>`;
532
+ }
533
+ });
534
+
535
+ if (error === '') {
536
+ const res = await CustomFetch(setting.save, 'POST', {
537
+ [primaryKey]: data.id,
538
+ form_data: {
539
+ ...activeForm,
540
+ },
541
+ form_key: activeFormKey,
542
+ form_type: type,
543
+ });
544
+
545
+ if (res.data.error === '') {
546
+ toast.success('Form saved successfully.');
547
+ fetchData();
548
+ }
549
+ } else {
550
+ toast.error(<div>{parse(error)}</div>);
551
+ }
552
+ } catch (err) {
553
+ console.log(err);
554
+ }
555
+ };
556
+
557
+ useEffect(() => {
558
+ if (data[type] && data[type].length > 0) {
559
+ if (multiple) {
560
+ if (templateStatus) {
561
+ let lastForm = data[type][data[type].length - 1];
562
+
563
+ if (lastForm && typeof lastForm === 'object') {
564
+ Object.entries(lastForm).forEach(([key, field]) => {
565
+ if (field && field.required) {
566
+ if (
567
+ field.required === 'yes' ||
568
+ field.required === 'no'
569
+ ) {
570
+ lastForm[key].required =
571
+ field.required === 'yes';
572
+ }
573
+ }
574
+ });
575
+
576
+ setActiveForm(lastForm);
577
+ setActiveFormKey(data[type].length - 1);
578
+ }
579
+ }
580
+ } else {
581
+ if (data[type].hasOwnProperty('id')) {
582
+ if (
583
+ Object.keys(activeForm).length === 0 &&
584
+ templateStatus &&
585
+ data[type][0]
586
+ ) {
587
+ setActiveForm(data[type][0]);
588
+ setActiveFormKey(0);
589
+ }
590
+ } else {
591
+ setActiveForm(data[type]);
592
+ setActiveFormKey(0);
593
+ }
594
+ }
595
+ } else {
596
+ setActiveForm({});
597
+ }
598
+ }, [data, type]);
599
+
600
+ useEffect(() => {
601
+ if (data[type] && data[type].length > 0) {
602
+ } else {
603
+ if (templateStatus) {
604
+ if (multiple) {
605
+ if (activeForm.hasOwnProperty('id') === false) {
606
+ handleAddNewForm(false);
607
+ }
608
+ } else {
609
+ if (Object.keys(activeForm).length === 0) {
610
+ handleAddNewForm(false);
611
+ }
612
+ }
613
+ }
614
+ }
615
+ }, [activeForm]);
616
+
617
+ const shouldDisplayFormList = (data, type) => {
618
+ if (!data[type] || !Array.isArray(data[type])) {
619
+ return false;
620
+ }
621
+ return (
622
+ data[type].length !== 1 ||
623
+ moment(data[type][0].updated_at).isAfter(
624
+ moment(data[type][0].created_at)
625
+ )
626
+ );
627
+ };
628
+
629
+ return (
630
+ <>
631
+ <div className={styles.formSplit}>
632
+ {multiple && shouldDisplayFormList(data, type) && (
633
+ <>
634
+ <div className={styles.gridtxt__header}>
635
+ <span>Form List</span>
636
+ </div>
637
+ <table className={styles.contentTable}>
638
+ <thead>
639
+ <tr>
640
+ <th>ID</th>
641
+ <th>Form</th>
642
+ <th>Created By</th>
643
+ <th>Created At</th>
644
+ <th>Updated By</th>
645
+ <th>Updated At</th>
646
+ <th></th>
647
+ </tr>
648
+ </thead>
649
+ <tbody>
650
+ {typeof data[type]?.map === 'function' &&
651
+ data[type].map((item, index) => {
652
+ const idAsNumber = parseFloat(item.id);
653
+ if (
654
+ !isNaN(idAsNumber) &&
655
+ Number.isInteger(idAsNumber)
656
+ ) {
657
+ return (
658
+ <tr
659
+ key={`form-list-${index}`}
660
+ onClick={(e) => {
661
+ e.preventDefault();
662
+ handleSelectForm(
663
+ item,
664
+ index
665
+ );
666
+ }}
667
+ >
668
+ <td>{item.id}</td>
669
+ <td>{item.form_label}</td>
670
+ <td>{item.created_by}</td>
671
+ <td>
672
+ {item.created_at
673
+ ? moment(
674
+ item.created_at
675
+ ).format(
676
+ 'DD-MM-YYYY hh:mm A'
677
+ )
678
+ : null}
679
+ </td>
680
+ <td>{item.updated_by}</td>
681
+ <td>
682
+ {item.updated_at
683
+ ? moment(
684
+ item.updated_at
685
+ ).format(
686
+ 'DD-MM-YYYY hh:mm A'
687
+ )
688
+ : null}
689
+ </td>
690
+ <td>
691
+ <div
692
+ className={
693
+ styles.tdActions
694
+ }
695
+ >
696
+ <span
697
+ data-tooltip-id="system-tooltip"
698
+ data-tooltip-content="Download Form"
699
+ className="tooltip"
700
+ onClick={() =>
701
+ handleDownloadForm(
702
+ item,
703
+ label,
704
+ originalData
705
+ )
706
+ }
707
+ >
708
+ <CloudDownload
709
+ strokeWidth={
710
+ 2
711
+ }
712
+ size={18}
713
+ className={
714
+ styles.tdAction
715
+ }
716
+ />
717
+ </span>
718
+ <span
719
+ data-tooltip-id="system-tooltip"
720
+ data-tooltip-content="Delete Form"
721
+ className="tooltip"
722
+ onClick={() =>
723
+ handleDeleteForm(
724
+ item.id,
725
+ index
726
+ )
727
+ }
728
+ >
729
+ <Backspace
730
+ strokeWidth={
731
+ 2
732
+ }
733
+ size={18}
734
+ className={
735
+ styles.tdAction
736
+ }
737
+ />
738
+ </span>
739
+ </div>
740
+ </td>
741
+ </tr>
742
+ );
743
+ }
744
+ return null;
745
+ })}
746
+ </tbody>
747
+ </table>
748
+ </>
749
+ )}
750
+
751
+ {activeForm && (
752
+ <>
753
+ <div className={styles.gridtxt__header}>
754
+ <span>
755
+ {label}
756
+ {multiple && ` - Form #${activeForm?.id ?? ''}`}
757
+ </span>
758
+ </div>
759
+ <div className={styles.formcontainer}>
760
+ <div className={styles.modalForm}>
761
+ {typeof activeForm === 'object' &&
762
+ !Array.isArray(activeForm)
763
+ ? Object.entries(activeForm)
764
+ .filter(
765
+ ([key, value]) =>
766
+ typeof value === 'object' &&
767
+ [
768
+ 'id',
769
+ 'label',
770
+ 'type',
771
+ 'size',
772
+ 'value',
773
+ ].every((k) =>
774
+ value.hasOwnProperty(k)
775
+ )
776
+ )
777
+ .map(
778
+ ([key, field]) =>
779
+ field.hide === 0 && (
780
+ <Field
781
+ settings={field}
782
+ key={`field-${key}-${field.id}`}
783
+ formData={activeForm}
784
+ inputClass={''}
785
+ inputValue={renderValue(
786
+ field,
787
+ originalData
788
+ )}
789
+ onChange={
790
+ handleChange
791
+ }
792
+ onChangeCheckbox={
793
+ handleChangeCheckbox
794
+ }
795
+ onChangeDate={
796
+ handleChangeDate
797
+ }
798
+ onChangeNumberFormat={
799
+ handleChangeNumberFormat
800
+ }
801
+ onChangeSignature={
802
+ handleChangeSignature
803
+ }
804
+ onChangeTableRadio={
805
+ handleChangeTableRadio
806
+ }
807
+ onChangeAddTableTextRow={
808
+ handleChangeAddTableTextRow
809
+ }
810
+ onChangeTableText={
811
+ handleChangeTableText
812
+ }
813
+ onChangeToggle={
814
+ handleChangeToggle
815
+ }
816
+ onFileDownload={
817
+ handleFileDownload
818
+ }
819
+ style={{}}
820
+ userProfile={
821
+ userProfile
822
+ }
823
+ />
824
+ )
825
+ )
826
+ : typeof activeForm?.map === 'function' &&
827
+ activeForm.map(
828
+ (field, index) =>
829
+ field.hide === 0 && (
830
+ <Field
831
+ settings={field}
832
+ key={`field-array-${index}-${field.id}`}
833
+ formData={activeForm}
834
+ inputClass={''}
835
+ inputValue={renderValue(
836
+ field,
837
+ originalData
838
+ )}
839
+ onChange={handleChange}
840
+ onChangeCheckbox={
841
+ handleChangeCheckbox
842
+ }
843
+ onChangeDate={
844
+ handleChangeDate
845
+ }
846
+ onChangeTableRadio={
847
+ handleChangeTableRadio
848
+ }
849
+ onChangeAddTableTextRow={
850
+ handleChangeAddTableTextRow
851
+ }
852
+ onChangeTableText={
853
+ handleChangeTableText
854
+ }
855
+ onChangeToggle={
856
+ handleChangeToggle
857
+ }
858
+ onFileDownload={
859
+ handleFileDownload
860
+ }
861
+ style={{}}
862
+ userProfile={userProfile}
863
+ />
864
+ )
865
+ )}
866
+ </div>
867
+ </div>
868
+ </>
869
+ )}
870
+ </div>
871
+ <div className={styles.polActions}>
872
+ {buttons &&
873
+ buttons.length > 0 &&
874
+ buttons.map((button, buttonKey) => (
875
+ <button
876
+ className={button.className}
877
+ key={`button-${buttonKey}`}
878
+ onClick={(e) => {
879
+ e.preventDefault();
880
+
881
+ handleCustomButton(button);
882
+ }}
883
+ >
884
+ {button.label}
885
+ </button>
886
+ ))}
887
+
888
+ {multiple && templateStatus && (
889
+ <button className="btn" onClick={handleAddNewForm}>
890
+ Add New Form
891
+ </button>
892
+ )}
893
+
894
+ {activeForm.id ? (
895
+ <>
896
+ {multiple && activeForm.form_status === 0 ? (
897
+ <button className="btn" onClick={handleSaveForm}>
898
+ Save
899
+ </button>
900
+ ) : (
901
+ <button className="btn" onClick={handleSaveForm}>
902
+ Save
903
+ </button>
904
+ )}
905
+ </>
906
+ ) : (
907
+ <button className="btn" onClick={handleSaveForm}>
908
+ Save
909
+ </button>
910
+ )}
911
+ </div>
912
+ </>
913
+ );
914
+ };
915
+
916
+ export default GenericDynamic;