@visns-studio/visns-components 3.4.12 → 3.5.1

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.
@@ -25,8 +25,6 @@ const SortableItem = ({
25
25
  const renderImage = (d) => {
26
26
  let htmlContainer = '';
27
27
 
28
- console.info(showImage, 'aaa');
29
-
30
28
  if (showImage) {
31
29
  const imageUrl = d.image_url || d.file_url;
32
30
  htmlContainer = imageUrl ? (
@@ -36,7 +34,10 @@ const SortableItem = ({
36
34
  ) : (
37
35
  <div className="sortimg">
38
36
  {/* Placeholder image */}
39
- <img src="data:image/png;base64,..." />
37
+ <img
38
+ src="data:image/png;base64,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"
39
+ alt="Placeholder"
40
+ />
40
41
  </div>
41
42
  );
42
43
  }
@@ -42,6 +42,7 @@ function Field({
42
42
  style,
43
43
  }) {
44
44
  const editorRef = useRef(null);
45
+ const fileImageInputRef = useRef(null);
45
46
  const [colorPickerShow, setColorPickerShow] = useState(false);
46
47
  const [dataOptions, setDataOptions] = useState([]);
47
48
  const [formItemStyle, setFormItemStyle] = useState({});
@@ -869,30 +870,34 @@ function Field({
869
870
  style={{
870
871
  display: 'inline-flex',
871
872
  alignItems: 'center',
873
+ cursor: 'pointer',
874
+ }}
875
+ onClick={() => {
876
+ fileImageInputRef.current.click();
872
877
  }}
873
878
  >
874
879
  <input
875
880
  type="file"
876
881
  data-name={settings.id}
877
- className={inputClass[settings.id]}
882
+ className={inputClass[settings.id]} // Assuming inputClass is defined elsewhere in your code
878
883
  onChange={onChange}
879
- />{' '}
880
- {inputValue && inputValue.key && inputValue.uuid && (
881
- <div
884
+ ref={fileImageInputRef}
885
+ style={{ display: 'none' }} // Hide the file input
886
+ />
887
+ {inputValue.imageSrc ? (
888
+ <img
889
+ src={inputValue.imageSrc}
890
+ alt="Uploaded"
882
891
  style={{
883
892
  marginLeft: '10px',
884
- cursor: 'pointer',
893
+ maxHeight: '100px', // Set a maximum height for the image
885
894
  }}
886
- data-tooltip-id="system-tooltip"
887
- data-tooltip-content={inputValue.filename}
888
- onClick={(e) => {
889
- e.preventDefault();
890
-
891
- onFileDownload(inputValue);
892
- }}
893
- >
894
- <CloudDownload size={24} />
895
- </div>
895
+ />
896
+ ) : (
897
+ <img
898
+ src="data:image/png;base64,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"
899
+ alt="Placeholder"
900
+ />
896
901
  )}
897
902
  </div>
898
903
  );
@@ -0,0 +1,544 @@
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
+
14
+ const updateField = (fields, name, value) => {
15
+ return Object.entries(fields).reduce((updatedFields, [key, field]) => {
16
+ // Check if the current field is the one to be updated
17
+ if (field.id === name) {
18
+ // Update the value of the field
19
+ updatedFields[key] = { ...field, value };
20
+ } else {
21
+ // Keep the field as is
22
+ updatedFields[key] = field;
23
+ }
24
+ return updatedFields;
25
+ }, {});
26
+ };
27
+
28
+ const renderValue = (field) => {
29
+ switch (field.type) {
30
+ case 'datetime':
31
+ case 'date':
32
+ case 'time':
33
+ return field.value && moment(field.value).isValid()
34
+ ? moment(field.value).toDate()
35
+ : '';
36
+ default:
37
+ return field.value || '';
38
+ }
39
+ };
40
+
41
+ const GenericDynamic = ({ data, fetchData, label, setData, setting, type }) => {
42
+ const toastId = useRef(null);
43
+ const [activeForm, setActiveForm] = useState([]);
44
+ const [activeFormKey, setActiveFormKey] = useState(0);
45
+
46
+ const handleChange = (e) => {
47
+ const {
48
+ checked,
49
+ dataset: { name },
50
+ files,
51
+ value,
52
+ } = e.target;
53
+
54
+ let newValue;
55
+
56
+ if (checked) {
57
+ newValue = checked;
58
+ setActiveForm((prevState) =>
59
+ updateField(prevState, name, newValue)
60
+ );
61
+ } else if (files && files[0]) {
62
+ // Start the upload process and read the file if it's an image
63
+ Vapor.store(files[0], {
64
+ progress: (progress) => {
65
+ // Update or show the toast based on upload progress
66
+ if (
67
+ toastId.current === null ||
68
+ !toast.isActive(toastId.current)
69
+ ) {
70
+ toastId.current = toast(`File Upload in Progress`, {
71
+ progress,
72
+ });
73
+ } else {
74
+ toast.update(toastId.current, { progress });
75
+ }
76
+ },
77
+ }).then((response) => {
78
+ if (files[0].type.startsWith('image/')) {
79
+ // Use FileReader to asynchronously read the file as a data URL for image preview
80
+ const reader = new FileReader();
81
+ reader.onload = (e) => {
82
+ const imageSrc = e.target.result; // This is the data URL
83
+
84
+ // Set active form state with file details and imageSrc for preview
85
+ setActiveForm((prevState) =>
86
+ updateField(prevState, name, {
87
+ extension: response.extension,
88
+ key: response.key,
89
+ url: response.url,
90
+ uuid: response.uuid,
91
+ bucket: response.bucket,
92
+ filename: files[0].name,
93
+ filesize: files[0].size,
94
+ imageSrc: imageSrc, // Add the data URL as imageSrc
95
+ })
96
+ );
97
+ };
98
+ reader.readAsDataURL(files[0]);
99
+ } else {
100
+ // Handle non-image files
101
+ setActiveForm((prevState) =>
102
+ updateField(prevState, name, {
103
+ extension: response.extension,
104
+ key: response.key,
105
+ url: response.url,
106
+ uuid: response.uuid,
107
+ bucket: response.bucket,
108
+ filename: files[0].name,
109
+ filesize: files[0].size,
110
+ })
111
+ );
112
+ }
113
+ toast.dismiss(toastId.current);
114
+ });
115
+ } else {
116
+ newValue = value;
117
+ setActiveForm((prevState) =>
118
+ updateField(prevState, name, newValue)
119
+ );
120
+ }
121
+ };
122
+
123
+ const handleChangeCheckbox = (e) => {
124
+ const { checked, name, value } = e.target;
125
+
126
+ let tmpValue;
127
+
128
+ Object.entries(activeForm).forEach(([key, field]) => {
129
+ if (field.id && field.id === name) {
130
+ if (
131
+ field.value === null ||
132
+ field.value === '' ||
133
+ field.value.length === 0
134
+ ) {
135
+ tmpValue = [];
136
+
137
+ field.options.forEach((option) => {
138
+ tmpValue.push(false);
139
+ });
140
+ } else {
141
+ tmpValue = [...field.value];
142
+ }
143
+
144
+ field.options.forEach((option, optionKey) => {
145
+ if (option.label === value) {
146
+ tmpValue[optionKey] = checked;
147
+ }
148
+ });
149
+ }
150
+ });
151
+
152
+ setActiveForm((prevState) => updateField(prevState, name, tmpValue));
153
+ };
154
+
155
+ const handleChangeDate = (value, id) => {
156
+ setActiveForm((prevState) => updateField(prevState, id, value));
157
+ };
158
+
159
+ const handleFileDownload = async (d) => {
160
+ try {
161
+ const res = await Download(setting.file, 'POST', {
162
+ extension: d.extension,
163
+ uuid: d.uuid,
164
+ filename: d.filename,
165
+ });
166
+
167
+ if (res.data) {
168
+ saveAs(res.data, d.filename);
169
+ } else {
170
+ toast.error('Error downloading file');
171
+ }
172
+ } catch (err) {
173
+ console.error(err);
174
+ toast.error('Error downloading file');
175
+ }
176
+ };
177
+
178
+ const handleSelectForm = (form, key) => {
179
+ setActiveForm(form);
180
+ setActiveFormKey(key);
181
+ };
182
+
183
+ const handleAddNewForm = async () => {
184
+ try {
185
+ if (activeForm && activeForm.id) {
186
+ await CustomFetch(setting.save, 'POST', {
187
+ client_id: data.id,
188
+ form_data: {
189
+ ...activeForm,
190
+ },
191
+ form_key: activeFormKey,
192
+ form_type: type,
193
+ });
194
+ }
195
+
196
+ const res = await CustomFetch(setting.add, 'POST', {
197
+ client_id: data.id,
198
+ form_name: type,
199
+ });
200
+ if (res.data.error === '') {
201
+ toast.success('Form added successfully');
202
+ fetchData();
203
+ }
204
+ } catch (err) {
205
+ console.error(err);
206
+ toast.error('Error adding form');
207
+ }
208
+ };
209
+
210
+ // Utility function to slugify a string
211
+ const slugify = (text) => {
212
+ return text
213
+ .toString()
214
+ .toLowerCase()
215
+ .replace(/\s+/g, '-') // Replace spaces with -
216
+ .replace(/[^\w\-]+/g, '') // Remove all non-word chars
217
+ .replace(/\-\-+/g, '-') // Replace multiple - with single -
218
+ .replace(/^-+/, '') // Trim - from start of text
219
+ .replace(/-+$/, ''); // Trim - from end of text
220
+ };
221
+
222
+ const handleDownloadForm = async (item, key) => {
223
+ try {
224
+ const res = await Download(setting.download, 'POST', {
225
+ data: item,
226
+ label: label,
227
+ });
228
+
229
+ if (res.data) {
230
+ const fileName = slugify(label) + '.pdf';
231
+ saveAs(res.data, fileName); // Use file-saver to save the file with a slugified label as the filename
232
+ } else {
233
+ toast.error('Error downloading form');
234
+ }
235
+ } catch (err) {
236
+ console.error(err);
237
+ toast.error('Error downloading form');
238
+ }
239
+ };
240
+
241
+ const handleDeleteForm = async (id, key) => {
242
+ const deleteForm = async () => {
243
+ try {
244
+ const res = await CustomFetch(setting.delete, 'POST', {
245
+ client_id: data.id,
246
+ form_id: id,
247
+ form_key: key,
248
+ form_type: type,
249
+ });
250
+
251
+ if (res.data.error === '') {
252
+ toast.success('Form deleted successfully.');
253
+ fetchData();
254
+ } else {
255
+ // Handle case where there is an error in the response
256
+ toast.error('Error: ' + res.data.error);
257
+ }
258
+ } catch (err) {
259
+ console.error(err);
260
+ toast.error('Error deleting form');
261
+ }
262
+ };
263
+
264
+ confirmAlert({
265
+ title: 'Delete File',
266
+ message: 'Are you sure?',
267
+ buttons: [
268
+ {
269
+ label: 'Yes',
270
+ onClick: deleteForm,
271
+ },
272
+ {
273
+ label: 'No',
274
+ onClick: () => close(), // Ensure 'close' function is defined or remove this line
275
+ },
276
+ ],
277
+ });
278
+ };
279
+
280
+ const handleSaveForm = async () => {
281
+ try {
282
+ let error = '';
283
+
284
+ Object.entries(activeForm).forEach(([key, field]) => {
285
+ if (
286
+ field.required === true &&
287
+ (field.value === null || field.value === '')
288
+ ) {
289
+ error += `<p>${field.label} is required</p>`;
290
+ }
291
+ });
292
+
293
+ if (error === '') {
294
+ const res = await CustomFetch(setting.save, 'POST', {
295
+ client_id: data.id,
296
+ form_data: {
297
+ ...activeForm,
298
+ },
299
+ form_key: activeFormKey,
300
+ form_type: type,
301
+ });
302
+
303
+ if (res.data.error === '') {
304
+ toast.success('Form saved successfully.');
305
+ fetchData();
306
+ }
307
+ } else {
308
+ toast.error(<div>{parse(error)}</div>);
309
+ }
310
+ } catch (err) {
311
+ console.log(err);
312
+ }
313
+ };
314
+
315
+ useEffect(() => {
316
+ if (data[type]) {
317
+ // Retrieve the last form of the specified type
318
+ let lastForm = data[type][data[type].length - 1];
319
+
320
+ Object.entries(data[type][data[type].length - 1]).forEach(
321
+ ([key, field]) => {
322
+ if (field.required) {
323
+ if (
324
+ field.required === 'yes' ||
325
+ field.required === 'no'
326
+ ) {
327
+ lastForm[key].required =
328
+ field.required === 'yes' ? true : false;
329
+ }
330
+ }
331
+ }
332
+ );
333
+
334
+ // Update the active form with the modified lastForm
335
+ setActiveForm(lastForm);
336
+ setActiveFormKey(data[type].length - 1);
337
+ } else {
338
+ handleAddNewForm();
339
+ }
340
+ }, [data, type]);
341
+
342
+ return (
343
+ <>
344
+ <div className="formSplit">
345
+ {data[type] && data[type].length > 1 && (
346
+ <>
347
+ <div className="gridtxt__header">
348
+ <span>Form List</span>
349
+ </div>
350
+ <table
351
+ className="content-table"
352
+ style={{ marginBottom: '30px' }}
353
+ >
354
+ <thead>
355
+ <tr className="grid-headers">
356
+ <th>ID</th>
357
+ <th>Created By</th>
358
+ <th>Created At</th>
359
+ <th>Updated By</th>
360
+ <th>Updated At</th>
361
+ <th></th>
362
+ </tr>
363
+ </thead>
364
+ <tbody>
365
+ {data[type].map((item, index) => {
366
+ const idAsNumber = parseFloat(item.id);
367
+ if (
368
+ !isNaN(idAsNumber) &&
369
+ Number.isInteger(idAsNumber)
370
+ ) {
371
+ return (
372
+ <tr
373
+ key={`form-list-${index}`}
374
+ onClick={(e) => {
375
+ e.preventDefault();
376
+
377
+ handleSelectForm(
378
+ item,
379
+ index
380
+ );
381
+ }}
382
+ >
383
+ <td>Form #{item.id}</td>
384
+ <td>{item.created_by}</td>
385
+ <td>
386
+ {item.created_at
387
+ ? moment(
388
+ item.created_at
389
+ ).format(
390
+ 'DD-MM-YYYY hh:mm A'
391
+ )
392
+ : null}
393
+ </td>
394
+ <td>{item.updated_by}</td>
395
+ <td>
396
+ {item.updated_at
397
+ ? moment(
398
+ item.updated_at
399
+ ).format(
400
+ 'DD-MM-YYYY hh:mm A'
401
+ )
402
+ : null}
403
+ </td>
404
+ <td>
405
+ <div className="tdactions">
406
+ <span
407
+ data-tooltip-id="system-tooltip"
408
+ data-tooltip-content={
409
+ 'Download Form'
410
+ }
411
+ className="tooltip"
412
+ onClick={() =>
413
+ handleDownloadForm(
414
+ item,
415
+ index
416
+ )
417
+ }
418
+ >
419
+ <CloudDownload
420
+ strokeWidth={2}
421
+ size={18}
422
+ className="tdaction"
423
+ />
424
+ </span>
425
+ <span
426
+ data-tooltip-id="system-tooltip"
427
+ data-tooltip-content={
428
+ 'Delete Form'
429
+ }
430
+ className="tooltip"
431
+ onClick={() =>
432
+ handleDeleteForm(
433
+ item.id,
434
+ index
435
+ )
436
+ }
437
+ >
438
+ <Backspace
439
+ strokeWidth={2}
440
+ size={18}
441
+ className="tdaction"
442
+ />
443
+ </span>
444
+ </div>
445
+ </td>
446
+ </tr>
447
+ );
448
+ }
449
+ return null;
450
+ })}
451
+ </tbody>
452
+ </table>
453
+ </>
454
+ )}
455
+
456
+ {activeForm && (
457
+ <>
458
+ <div className="gridtxt__header">
459
+ <span>
460
+ {label} - Form #{activeForm.id}
461
+ </span>
462
+ </div>
463
+ <div
464
+ className="formcontainer"
465
+ style={{ paddingBottom: '50px' }}
466
+ >
467
+ <div className="modalForm">
468
+ {activeForm &&
469
+ Object.entries(activeForm)
470
+ .filter(([key, value]) => {
471
+ // Check if the value is an object and has the required keys
472
+ return (
473
+ value &&
474
+ typeof value === 'object' &&
475
+ [
476
+ 'id',
477
+ 'label',
478
+ 'type',
479
+ 'size',
480
+ 'value',
481
+ ].every((k) =>
482
+ value.hasOwnProperty(k)
483
+ )
484
+ );
485
+ })
486
+ .map(([key, field]) => {
487
+ // Check if the field is not hidden
488
+ if (field.hide === 0) {
489
+ return (
490
+ <Field
491
+ settings={field}
492
+ key={`field-${key}-${field.id}`}
493
+ formData={activeForm}
494
+ inputClass={''}
495
+ inputValue={renderValue(
496
+ field
497
+ )}
498
+ onChange={handleChange}
499
+ onChangeCheckbox={
500
+ handleChangeCheckbox
501
+ }
502
+ onChangeColour={() => {}}
503
+ onChangeDate={
504
+ handleChangeDate
505
+ }
506
+ onChangeSelect={() => {}}
507
+ onChangeRicheditor={() => {}}
508
+ onChangeToggle={
509
+ handleChange
510
+ }
511
+ onChangeNumberFormat={() => {}}
512
+ onFileDownload={
513
+ handleFileDownload
514
+ }
515
+ autocompleteCallback={() => {}}
516
+ childDropdownCallback={() => {}}
517
+ setFormData={setData}
518
+ style={{}}
519
+ />
520
+ );
521
+ }
522
+ return null;
523
+ })}
524
+ </div>
525
+ </div>
526
+ </>
527
+ )}
528
+ </div>
529
+ <div className="polActions">
530
+ <button className="btn" onClick={handleAddNewForm}>
531
+ Add New Form
532
+ </button>
533
+
534
+ {activeForm && activeForm.form_status === 0 && (
535
+ <button className="btn" onClick={handleSaveForm}>
536
+ Save
537
+ </button>
538
+ )}
539
+ </div>
540
+ </>
541
+ );
542
+ };
543
+
544
+ export default GenericDynamic;