@visns-studio/visns-components 3.9.6 → 3.9.7

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.
@@ -18,6 +18,7 @@ import NumberFilter from '@inovua/reactdatagrid-community/NumberFilter';
18
18
  import SelectFilter from '@inovua/reactdatagrid-community/SelectFilter';
19
19
  import DateFilter from '@inovua/reactdatagrid-community/DateFilter';
20
20
  import ReactDataGrid from '@inovua/reactdatagrid-community';
21
+ import { Gallery } from 'react-grid-gallery';
21
22
  import { confirmAlert } from 'react-confirm-alert';
22
23
  import { toast } from 'react-toastify';
23
24
  import {
@@ -26,12 +27,14 @@ import {
26
27
  Backspace,
27
28
  Check,
28
29
  CircleCheck,
30
+ CircleX,
29
31
  Clock,
30
32
  CloudDownload,
31
33
  Copy,
32
34
  Edit,
33
35
  Envelope,
34
36
  Folder,
37
+ Image,
35
38
  Inbox,
36
39
  LinkOut,
37
40
  Network,
@@ -137,6 +140,19 @@ const DataGrid = forwardRef(
137
140
  }
138
141
  }, [audioSource]);
139
142
 
143
+ /** Gallery Modal States */
144
+ const [modalGalleryShow, setModalGalleryShow] = useState(false);
145
+ const [galleryData, setGalleryData] = useState([]);
146
+
147
+ /** Gallery Modal Functions */
148
+ const modalGalleryOpen = () => {
149
+ setModalGalleryShow(true);
150
+ };
151
+
152
+ const modalGalleryClose = () => {
153
+ setModalGalleryShow(false);
154
+ };
155
+
140
156
  /** Modal States */
141
157
  const [formData, setFormData] = useState({});
142
158
  const [formType, setFormType] = useState('');
@@ -435,6 +451,30 @@ const DataGrid = forwardRef(
435
451
  toast.success(result.message);
436
452
  });
437
453
  break;
454
+ case 'gallery':
455
+ // Function to access nested property using a series of keys
456
+ const getNestedValue = (data, keys) =>
457
+ keys.reduce(
458
+ (currentValue, key) =>
459
+ currentValue && currentValue[key] !== undefined
460
+ ? currentValue[key]
461
+ : undefined,
462
+ data
463
+ );
464
+
465
+ // Extracting nested value using predefined keys from 's.relation'
466
+ const nestedValue = getNestedValue(d, s.relation);
467
+
468
+ // Mapping nested values to create gallery array with source and captions
469
+ const galleryArray = nestedValue.map((item) => ({
470
+ src: item[s.key],
471
+ caption: item.file_name,
472
+ }));
473
+
474
+ setGalleryData(galleryArray);
475
+
476
+ modalGalleryOpen();
477
+ break;
438
478
  case 'link':
439
479
  const url = `${s.baseUrl}/${d[s.key]}`;
440
480
  navigator.clipboard.writeText(url).then(
@@ -970,6 +1010,8 @@ const DataGrid = forwardRef(
970
1010
  return getIconComponent(Envelope);
971
1011
  case 'family':
972
1012
  return getIconComponent(Network);
1013
+ case 'gallery':
1014
+ return getIconComponent(Image);
973
1015
  case 'link':
974
1016
  return getIconComponent(LinkOut);
975
1017
  case 'primary':
@@ -2509,11 +2551,39 @@ const DataGrid = forwardRef(
2509
2551
  formType={formType}
2510
2552
  gridRef={gridRef}
2511
2553
  mapbox={mapbox}
2554
+ modalOpen={modalOpen}
2512
2555
  paramValue={paramValue}
2513
2556
  style={style}
2514
2557
  updateForm={setFormData}
2515
2558
  />
2516
2559
  </Popup>
2560
+ <Popup
2561
+ open={modalGalleryShow}
2562
+ onClose={modalGalleryClose}
2563
+ closeOnDocumentClick={false}
2564
+ >
2565
+ <div className="modalwrap top--modal">
2566
+ <div className="modal">
2567
+ <div className="modal__header">
2568
+ <h1>Gallery</h1>
2569
+ <button
2570
+ className="modal__close"
2571
+ onClick={modalGalleryClose}
2572
+ >
2573
+ <CircleX strokeWidth={1} size={24} />
2574
+ </button>
2575
+ </div>
2576
+ <div className="modal__content">
2577
+ <div className="formcontainer">
2578
+ <Gallery
2579
+ images={galleryData}
2580
+ enableImageSelection={false}
2581
+ />
2582
+ </div>
2583
+ </div>
2584
+ </div>
2585
+ </div>
2586
+ </Popup>
2517
2587
  </>
2518
2588
  );
2519
2589
  }
@@ -826,7 +826,11 @@ function Field({
826
826
  : false
827
827
  }
828
828
  onChange={onChangeSelect}
829
- inputValue={inputValue === null ? '' : inputValue}
829
+ inputValue={
830
+ inputValue === null || inputValue === undefined
831
+ ? ''
832
+ : inputValue
833
+ }
830
834
  options={
831
835
  settings.type === 'multi-dropdown'
832
836
  ? settings.options
@@ -847,7 +851,11 @@ function Field({
847
851
  : false
848
852
  }
849
853
  onChange={onChangeSelect}
850
- inputValue={inputValue === null ? '' : inputValue}
854
+ inputValue={
855
+ inputValue === null || inputValue === undefined
856
+ ? ''
857
+ : inputValue
858
+ }
851
859
  options={options}
852
860
  dataOptions={dataOptions}
853
861
  />
@@ -25,6 +25,7 @@ function Form({
25
25
  formSettings,
26
26
  fetchTable,
27
27
  mapbox,
28
+ modalOpen,
28
29
  paramValue,
29
30
  style,
30
31
  type,
@@ -65,7 +66,6 @@ function Form({
65
66
  }
66
67
 
67
68
  if (e.target.files) {
68
- console.info(e.target);
69
69
  setFormData((prevState) => ({
70
70
  ...prevState,
71
71
  [id]:
@@ -459,29 +459,43 @@ function Form({
459
459
  try {
460
460
  if (e) {
461
461
  e.preventDefault();
462
- }
463
462
 
464
- let fields = formSettings.fields;
465
- let _inputClass = inputClass;
466
- let validation = '';
463
+ const { type } = e.target.dataset;
467
464
 
468
- if (['create', 'update'].includes(formType)) {
469
- fields.forEach((item) => {
470
- // Skip validation if formType is 'update' and field has createOnly property
471
- if (formType === 'update' && item.createOnly) {
472
- return; // Skip this field
473
- }
465
+ let fields = formSettings.fields;
466
+ let _inputClass = inputClass;
467
+ let validation = '';
474
468
 
475
- if (item.required === true) {
476
- if (
477
- formData[item.id] === '' ||
478
- formData[item.id] === undefined
479
- ) {
469
+ if (['create', 'update'].includes(formType)) {
470
+ fields.forEach((item) => {
471
+ // Skip validation if formType is 'update' and field has createOnly property
472
+ if (formType === 'update' && item.createOnly) {
473
+ return; // Skip this field
474
+ }
475
+
476
+ if (item.required === true) {
480
477
  if (
481
- item.hasOwnProperty('required_check') &&
482
- item.required_check !== ''
478
+ formData[item.id] === '' ||
479
+ formData[item.id] === undefined
483
480
  ) {
484
- if (formData[item.required_check] === '') {
481
+ if (
482
+ item.hasOwnProperty('required_check') &&
483
+ item.required_check !== ''
484
+ ) {
485
+ if (formData[item.required_check] === '') {
486
+ if (
487
+ !validation.includes(
488
+ item.label +
489
+ ' is a required field.<br/>'
490
+ )
491
+ ) {
492
+ validation +=
493
+ item.label +
494
+ ' is a required field.<br/>';
495
+ }
496
+ _inputClass[item.id] = 'inputError';
497
+ }
498
+ } else {
485
499
  if (
486
500
  !validation.includes(
487
501
  item.label +
@@ -496,245 +510,250 @@ function Form({
496
510
  }
497
511
  } else {
498
512
  if (
499
- !validation.includes(
500
- item.label +
501
- ' is a required field.<br/>'
502
- )
513
+ item.hasOwnProperty('required_check') &&
514
+ item.required_check !== ''
503
515
  ) {
504
- validation +=
505
- item.label +
506
- ' is a required field.<br/>';
516
+ _inputClass[item.required_check] = '';
507
517
  }
508
- _inputClass[item.id] = 'inputError';
518
+ _inputClass[item.id] = '';
509
519
  }
510
520
  } else {
511
521
  if (
512
- item.hasOwnProperty('required_check') &&
513
- item.required_check !== ''
522
+ item.hasOwnProperty('required_rely') &&
523
+ item.required_rely !== ''
514
524
  ) {
515
- _inputClass[item.required_check] = '';
525
+ if (formData[item.required_rely] === '') {
526
+ _inputClass[item.id] = '';
527
+ } else {
528
+ if (formData[item.id] === '') {
529
+ if (
530
+ !validation.includes(
531
+ item.label +
532
+ ' is a required field.<br/>'
533
+ )
534
+ ) {
535
+ validation +=
536
+ item.label +
537
+ ' is a required field.<br/>';
538
+ }
539
+ _inputClass[item.id] = 'inputError';
540
+ } else {
541
+ _inputClass[item.id] = '';
542
+ }
543
+ }
544
+ } else {
545
+ _inputClass[item.id] = '';
516
546
  }
517
- _inputClass[item.id] = '';
518
547
  }
548
+ });
549
+
550
+ setInputClass(_inputClass);
551
+ }
552
+
553
+ if (validation !== '') {
554
+ toast.error(<div>{parse(validation)}</div>);
555
+ } else {
556
+ let url = formSettings.url;
557
+ let method = 'POST';
558
+ let fileUpload = false;
559
+ let fileKey = '';
560
+
561
+ if (formSettings.customUrl && formSettings.customMethod) {
562
+ url = formSettings.customUrl;
563
+ if (formSettings.customDataId) {
564
+ url += `/${formData[formSettings.primaryKey]}`;
565
+ }
566
+ method = formSettings.customMethod;
519
567
  } else {
520
- if (
521
- item.hasOwnProperty('required_rely') &&
522
- item.required_rely !== ''
523
- ) {
524
- if (formData[item.required_rely] === '') {
525
- _inputClass[item.id] = '';
526
- } else {
527
- if (formData[item.id] === '') {
528
- if (
529
- !validation.includes(
530
- item.label +
531
- ' is a required field.<br/>'
532
- )
533
- ) {
534
- validation +=
535
- item.label +
536
- ' is a required field.<br/>';
568
+ switch (formType) {
569
+ case 'create':
570
+ if (
571
+ formSettings.hasOwnProperty('json') &&
572
+ formSettings.json === true
573
+ ) {
574
+ url += '/jsonStore';
575
+ }
576
+ break;
577
+ case 'update':
578
+ case 'delete':
579
+ if (
580
+ formSettings.hasOwnProperty('json') &&
581
+ formSettings.json === true
582
+ ) {
583
+ if (formType === 'update') {
584
+ url += `/jsonUpdate`;
585
+ } else {
586
+ url += `/jsonDelete/${
587
+ formData[formSettings.primaryKey]
588
+ }`;
537
589
  }
538
- _inputClass[item.id] = 'inputError';
539
590
  } else {
540
- _inputClass[item.id] = '';
591
+ url += `/${
592
+ formData[formSettings.primaryKey]
593
+ }`;
541
594
  }
542
- }
543
- } else {
544
- _inputClass[item.id] = '';
595
+
596
+ method =
597
+ formType === 'update' ? 'PUT' : 'DELETE';
598
+ break;
545
599
  }
546
600
  }
547
- });
548
601
 
549
- setInputClass(_inputClass);
550
- }
551
- if (validation !== '') {
552
- toast.error(<div>{parse(validation)}</div>);
553
- } else {
554
- let url = formSettings.url;
555
- let method = 'POST';
556
- let fileUpload = false;
557
- let fileKey = '';
558
-
559
- if (formSettings.customUrl && formSettings.customMethod) {
560
- url = formSettings.customUrl;
561
- if (formSettings.customDataId) {
562
- url += `/${formData[formSettings.primaryKey]}`;
563
- }
564
- method = formSettings.customMethod;
565
- } else {
566
- switch (formType) {
567
- case 'create':
602
+ if (!_.isEmpty(formData)) {
603
+ Object.keys(formData).forEach((item) => {
568
604
  if (
569
- formSettings.hasOwnProperty('json') &&
570
- formSettings.json === true
605
+ formData[item] !== undefined &&
606
+ (item === 'file' ||
607
+ (formData[item] instanceof File &&
608
+ formData[item] !== null))
571
609
  ) {
572
- url += '/jsonStore';
573
- }
574
- break;
575
- case 'update':
576
- case 'delete':
577
- if (
578
- formSettings.hasOwnProperty('json') &&
579
- formSettings.json === true
580
- ) {
581
- if (formType === 'update') {
582
- url += `/jsonUpdate`;
583
- } else {
584
- url += `/jsonDelete/${
585
- formData[formSettings.primaryKey]
586
- }`;
587
- }
588
- } else {
589
- url += `/${formData[formSettings.primaryKey]}`;
610
+ fileUpload = true;
611
+ fileKey = item;
590
612
  }
591
-
592
- method = formType === 'update' ? 'PUT' : 'DELETE';
593
- break;
613
+ });
594
614
  }
595
- }
596
615
 
597
- if (!_.isEmpty(formData)) {
598
- Object.keys(formData).forEach((item) => {
599
- if (
600
- formData[item] !== undefined &&
601
- (item === 'file' ||
602
- (formData[item] instanceof File &&
603
- formData[item] !== null))
604
- ) {
605
- fileUpload = true;
606
- fileKey = item;
607
- }
608
- });
609
- }
616
+ if (fileUpload === true && formData[fileKey] !== null) {
617
+ // Check if formData[fileKey] is an array of file objects
618
+ if (Array.isArray(formData[fileKey])) {
619
+ // Already an array, so no need to convert
620
+ const files = formData[fileKey];
621
+
622
+ // Use Promise.all to wait for all file uploads to complete
623
+ const uploadPromises = files.map(async (file) => {
624
+ const fileRes = await Vapor.store(file, {
625
+ progress: (progress) => {
626
+ setUploadProgress(progress * 100);
627
+ },
628
+ });
610
629
 
611
- if (fileUpload === true && formData[fileKey] !== null) {
612
- // Check if formData[fileKey] is an array of file objects
613
- if (Array.isArray(formData[fileKey])) {
614
- // Already an array, so no need to convert
615
- const files = formData[fileKey];
616
-
617
- // Use Promise.all to wait for all file uploads to complete
618
- const uploadPromises = files.map(async (file) => {
619
- const fileRes = await Vapor.store(file, {
620
- progress: (progress) => {
621
- setUploadProgress(progress * 100);
622
- },
630
+ // Return an object for each file with its upload details
631
+ return {
632
+ uuid: fileRes.uuid,
633
+ key: fileRes.key,
634
+ bucket: fileRes.bucket,
635
+ filename: file.name,
636
+ filesize: file.size,
637
+ extension: fileRes.extension,
638
+ file_relationship: fileKey,
639
+ };
623
640
  });
624
641
 
625
- // Return an object for each file with its upload details
626
- return {
627
- uuid: fileRes.uuid,
628
- key: fileRes.key,
629
- bucket: fileRes.bucket,
630
- filename: file.name,
631
- filesize: file.size,
632
- extension: fileRes.extension,
633
- file_relationship: fileKey,
634
- };
635
- });
642
+ // Wait for all file uploads to complete
643
+ const uploadedFiles = await Promise.all(
644
+ uploadPromises
645
+ );
636
646
 
637
- // Wait for all file uploads to complete
638
- const uploadedFiles = await Promise.all(uploadPromises);
639
-
640
- // Store the details of uploaded files in formData, under a specific key
641
- formData['uploadedFiles'] = uploadedFiles;
642
- } else if (formData[fileKey] instanceof File) {
643
- // Handle single file upload (original logic remains the same)
644
- const fileRes = await Vapor.store(formData[fileKey], {
645
- progress: (progress) => {
646
- setUploadProgress(progress * 100);
647
- },
648
- });
647
+ // Store the details of uploaded files in formData, under a specific key
648
+ formData['uploadedFiles'] = uploadedFiles;
649
+ } else if (formData[fileKey] instanceof File) {
650
+ // Handle single file upload (original logic remains the same)
651
+ const fileRes = await Vapor.store(
652
+ formData[fileKey],
653
+ {
654
+ progress: (progress) => {
655
+ setUploadProgress(progress * 100);
656
+ },
657
+ }
658
+ );
649
659
 
650
- formData['uuid'] = fileRes.uuid;
651
- formData['key'] = fileRes.key;
652
- formData['bucket'] = fileRes.bucket;
653
- formData['filename'] = formData[fileKey].name;
654
- formData['filesize'] = formData[fileKey].size;
655
- formData['extension'] = fileRes.extension;
656
- formData['file_relationship'] = fileKey;
660
+ formData['uuid'] = fileRes.uuid;
661
+ formData['key'] = fileRes.key;
662
+ formData['bucket'] = fileRes.bucket;
663
+ formData['filename'] = formData[fileKey].name;
664
+ formData['filesize'] = formData[fileKey].size;
665
+ formData['extension'] = fileRes.extension;
666
+ formData['file_relationship'] = fileKey;
667
+ }
657
668
  }
658
- }
659
669
 
660
- const fieldMap = formSettings.fields.reduce((acc, field) => {
661
- acc[field.id] = field;
662
- return acc;
663
- }, {});
670
+ const fieldMap = formSettings.fields.reduce(
671
+ (acc, field) => {
672
+ acc[field.id] = field;
673
+ return acc;
674
+ },
675
+ {}
676
+ );
664
677
 
665
- let payload = { ...formData };
666
- Object.keys(formData).forEach((key) => {
667
- const value = formData[key];
668
- const field = fieldMap[key];
678
+ let payload = { ...formData };
679
+ Object.keys(formData).forEach((key) => {
680
+ const value = formData[key];
681
+ const field = fieldMap[key];
669
682
 
670
- // Check if the field exists and is of type 'multi-dropdown-ajax'
671
- if (field && field.type === 'multi-dropdown-ajax') {
672
- // First check if 'value' is an object and then check if 'value' and 'label' exist in it
673
- if (
674
- typeof value === 'object' &&
675
- value !== null &&
676
- 'value' in value &&
677
- 'label' in value
678
- ) {
679
- payload[key] = value.value;
683
+ // Check if the field exists and is of type 'multi-dropdown-ajax'
684
+ if (field && field.type === 'multi-dropdown-ajax') {
685
+ // First check if 'value' is an object and then check if 'value' and 'label' exist in it
686
+ if (
687
+ typeof value === 'object' &&
688
+ value !== null &&
689
+ 'value' in value &&
690
+ 'label' in value
691
+ ) {
692
+ payload[key] = value.value;
693
+ }
680
694
  }
681
- }
682
- });
695
+ });
683
696
 
684
- const res = await CustomFetch(url, method, payload);
697
+ const res = await CustomFetch(url, method, payload);
685
698
 
686
- if (res.data.error === '') {
687
- if (fetchTable) {
688
- fetchTable();
689
- }
699
+ if (res.data.error === '') {
700
+ if (fetchTable) {
701
+ fetchTable();
702
+ }
690
703
 
691
- if (closeModal) {
692
- closeModal();
693
- }
704
+ if (closeModal) {
705
+ closeModal();
694
706
 
695
- toast.success(
696
- formSettings?.save?.toast?.success
697
- ? formSettings.save.toast.success
698
- : 'Entry successfully saved into the system.'
699
- );
707
+ if (type === 'saveAnother') {
708
+ fetchData();
709
+ modalOpen('create', 0);
710
+ }
711
+ }
700
712
 
701
- if (
702
- formSettings.hasOwnProperty('redirect') &&
703
- formSettings.redirect &&
704
- res.data.data &&
705
- res.data.data.id
706
- ) {
707
- navigate(formSettings.redirect + res.data.data.id);
708
- }
713
+ toast.success(
714
+ formSettings?.save?.toast?.success
715
+ ? formSettings.save.toast.success
716
+ : 'Entry successfully saved into the system.'
717
+ );
709
718
 
710
- if (res.data.redirect) {
711
- navigate(res.data.redirect);
712
- }
719
+ if (
720
+ formSettings.hasOwnProperty('redirect') &&
721
+ formSettings.redirect &&
722
+ res.data.data &&
723
+ res.data.data.id
724
+ ) {
725
+ navigate(formSettings.redirect + res.data.data.id);
726
+ }
713
727
 
714
- if (
715
- formSettings?.save?.return?.type &&
716
- formSettings?.save?.return?.param
717
- ) {
718
- if (formSettings.save.return.type === 'table') {
719
- if (
720
- result &&
721
- result[formSettings.save.return.param].length >
722
- 0
723
- ) {
724
- const td = parseTableData(
728
+ if (res.data.redirect) {
729
+ navigate(res.data.redirect);
730
+ }
731
+
732
+ if (
733
+ formSettings?.save?.return?.type &&
734
+ formSettings?.save?.return?.param
735
+ ) {
736
+ if (formSettings.save.return.type === 'table') {
737
+ if (
738
+ result &&
725
739
  result[formSettings.save.return.param]
726
- );
740
+ .length > 0
741
+ ) {
742
+ const td = parseTableData(
743
+ result[formSettings.save.return.param]
744
+ );
727
745
 
728
- setTableData((prevState) => ({
729
- ...prevState,
730
- ...td,
731
- }));
732
- } else {
733
- setTableData((prevState) => ({
734
- ...prevState,
735
- columns: [],
736
- dataSource: [],
737
- }));
746
+ setTableData((prevState) => ({
747
+ ...prevState,
748
+ ...td,
749
+ }));
750
+ } else {
751
+ setTableData((prevState) => ({
752
+ ...prevState,
753
+ columns: [],
754
+ dataSource: [],
755
+ }));
756
+ }
738
757
  }
739
758
  }
740
759
  }
@@ -1240,7 +1259,7 @@ function Form({
1240
1259
  </div>
1241
1260
  <div className="modal__content">
1242
1261
  <div className="formcontainer">
1243
- <form className="modalForm" onSubmit={handleSubmit}>
1262
+ <form className="modalForm">
1244
1263
  {formSettings.fields.map((item, index) => {
1245
1264
  if (
1246
1265
  item.hasOwnProperty('hide') &&
@@ -1323,14 +1342,41 @@ function Form({
1323
1342
  }}
1324
1343
  />
1325
1344
  </div>
1326
- <div className="formItem fwItem lastItem">
1327
- <button
1328
- className="btn modalsave"
1329
- type="submit"
1330
- >
1331
- Save
1332
- </button>
1333
- </div>
1345
+ {formType === 'create' &&
1346
+ formSettings.create?.hasOwnProperty(
1347
+ 'saveAnother'
1348
+ ) ? (
1349
+ <>
1350
+ <div className="formItem lastItem">
1351
+ <button
1352
+ className="btn modalsave"
1353
+ onClick={handleSubmit}
1354
+ data-type="save"
1355
+ >
1356
+ Save & Close
1357
+ </button>
1358
+ </div>
1359
+ <div className="formItem lastItem">
1360
+ <button
1361
+ className="btn modalsave"
1362
+ onClick={handleSubmit}
1363
+ data-type="saveAnother"
1364
+ >
1365
+ Save & Create Another
1366
+ </button>
1367
+ </div>
1368
+ </>
1369
+ ) : (
1370
+ <div className="formItem fwItem lastItem">
1371
+ <button
1372
+ className="btn modalsave"
1373
+ onClick={handleSubmit}
1374
+ data-type="save"
1375
+ >
1376
+ Save
1377
+ </button>
1378
+ </div>
1379
+ )}
1334
1380
  </form>
1335
1381
  </div>
1336
1382
  </div>
package/package.json CHANGED
@@ -27,6 +27,7 @@
27
27
  "react-copy-to-clipboard": "^5.1.0",
28
28
  "react-datepicker": "^6.9.0",
29
29
  "react-dropzone": "^14.2.3",
30
+ "react-grid-gallery": "^1.0.1",
30
31
  "react-number-format": "^5.3.4",
31
32
  "react-password-strength-bar": "^0.4.1",
32
33
  "react-promise-tracker": "^2.1.1",
@@ -55,7 +56,7 @@
55
56
  "react-dom": "^17.0.1"
56
57
  },
57
58
  "name": "@visns-studio/visns-components",
58
- "version": "3.9.6",
59
+ "version": "3.9.7",
59
60
  "description": "Various packages to assist in the development of our Custom Applications.",
60
61
  "main": "index.js",
61
62
  "scripts": {