@visns-studio/visns-components 4.6.11 → 4.6.13
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.
- package/components/crm/DataGrid.jsx +11 -6
- package/components/crm/Form.jsx +86 -174
- package/components/crm/generic/GenericSort.jsx +42 -43
- package/components/crm/generic/styles/GenericSort.module.css +60 -4
- package/components/crm/sorting/Item.jsx +117 -0
- package/components/crm/sorting/List.jsx +32 -0
- package/components/crm/sorting/styles/Item.module.css +68 -0
- package/components/crm/sorting/styles/List.module.css +68 -0
- package/components/crm/styles/Field.module.css +2 -2
- package/package.json +1 -1
|
@@ -756,20 +756,25 @@ const DataGrid = forwardRef(
|
|
|
756
756
|
};
|
|
757
757
|
|
|
758
758
|
const sortTrigger = () => {
|
|
759
|
-
|
|
759
|
+
let baseUrl = form.url.replace('/ajax', '');
|
|
760
|
+
let sortUrl = paramValue
|
|
761
|
+
? `${baseUrl}/${paramValue}/sort`
|
|
762
|
+
: `${baseUrl}/sort`;
|
|
763
|
+
|
|
764
|
+
if (form.sort?.url) {
|
|
765
|
+
sortUrl = form.sort.url;
|
|
766
|
+
}
|
|
760
767
|
|
|
761
768
|
const newState = {
|
|
762
|
-
form
|
|
769
|
+
form,
|
|
763
770
|
page: {
|
|
764
771
|
data: pageData,
|
|
765
772
|
setting: pageSetting,
|
|
766
773
|
},
|
|
767
|
-
paramValue
|
|
774
|
+
paramValue,
|
|
768
775
|
};
|
|
769
776
|
|
|
770
|
-
navigate(
|
|
771
|
-
state: newState,
|
|
772
|
-
});
|
|
777
|
+
navigate(sortUrl, { state: newState });
|
|
773
778
|
};
|
|
774
779
|
|
|
775
780
|
const renderCreateButton = () => {
|
package/components/crm/Form.jsx
CHANGED
|
@@ -545,7 +545,6 @@ function Form({
|
|
|
545
545
|
e.preventDefault();
|
|
546
546
|
|
|
547
547
|
const { type } = e.target.dataset;
|
|
548
|
-
|
|
549
548
|
let fields = formSettings.fields;
|
|
550
549
|
let _inputClass = inputClass;
|
|
551
550
|
let validation = '';
|
|
@@ -556,86 +555,39 @@ function Form({
|
|
|
556
555
|
return;
|
|
557
556
|
}
|
|
558
557
|
|
|
559
|
-
if (item.required
|
|
560
|
-
if (
|
|
561
|
-
formData[item.id] === '' ||
|
|
562
|
-
formData[item.id] === undefined
|
|
563
|
-
) {
|
|
558
|
+
if (item.required) {
|
|
559
|
+
if (!formData[item.id]) {
|
|
564
560
|
if (
|
|
565
561
|
item.hasOwnProperty('required_check') &&
|
|
566
|
-
item.required_check
|
|
562
|
+
item.required_check &&
|
|
563
|
+
!formData[item.required_check]
|
|
567
564
|
) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
!validation.includes(
|
|
571
|
-
item.label +
|
|
572
|
-
' is a required field.<br/>'
|
|
573
|
-
)
|
|
574
|
-
) {
|
|
575
|
-
validation +=
|
|
576
|
-
item.label +
|
|
577
|
-
' is a required field.<br/>';
|
|
578
|
-
}
|
|
579
|
-
_inputClass[item.id] =
|
|
580
|
-
styles.inputError;
|
|
581
|
-
}
|
|
565
|
+
validation += `${item.label} is a required field.<br/>`;
|
|
566
|
+
_inputClass[item.id] = styles.inputError;
|
|
582
567
|
} else {
|
|
583
|
-
|
|
584
|
-
!validation.includes(
|
|
585
|
-
item.label +
|
|
586
|
-
' is a required field.<br/>'
|
|
587
|
-
)
|
|
588
|
-
) {
|
|
589
|
-
validation +=
|
|
590
|
-
item.label +
|
|
591
|
-
' is a required field.<br/>';
|
|
592
|
-
}
|
|
568
|
+
validation += `${item.label} is a required field.<br/>`;
|
|
593
569
|
_inputClass[item.id] = styles.inputError;
|
|
594
570
|
}
|
|
595
571
|
} else {
|
|
596
|
-
if (
|
|
597
|
-
item.hasOwnProperty('required_check') &&
|
|
598
|
-
item.required_check !== ''
|
|
599
|
-
) {
|
|
600
|
-
_inputClass[item.required_check] = '';
|
|
601
|
-
}
|
|
602
572
|
_inputClass[item.id] = '';
|
|
603
573
|
}
|
|
574
|
+
} else if (
|
|
575
|
+
item.hasOwnProperty('required_rely') &&
|
|
576
|
+
item.required_rely &&
|
|
577
|
+
formData[item.required_rely] &&
|
|
578
|
+
!formData[item.id]
|
|
579
|
+
) {
|
|
580
|
+
validation += `${item.label} is a required field.<br/>`;
|
|
581
|
+
_inputClass[item.id] = styles.inputError;
|
|
604
582
|
} else {
|
|
605
|
-
|
|
606
|
-
item.hasOwnProperty('required_rely') &&
|
|
607
|
-
item.required_rely !== ''
|
|
608
|
-
) {
|
|
609
|
-
if (formData[item.required_rely] === '') {
|
|
610
|
-
_inputClass[item.id] = '';
|
|
611
|
-
} else {
|
|
612
|
-
if (formData[item.id] === '') {
|
|
613
|
-
if (
|
|
614
|
-
!validation.includes(
|
|
615
|
-
item.label +
|
|
616
|
-
' is a required field.<br/>'
|
|
617
|
-
)
|
|
618
|
-
) {
|
|
619
|
-
validation +=
|
|
620
|
-
item.label +
|
|
621
|
-
' is a required field.<br/>';
|
|
622
|
-
}
|
|
623
|
-
_inputClass[item.id] =
|
|
624
|
-
styles.inputError;
|
|
625
|
-
} else {
|
|
626
|
-
_inputClass[item.id] = '';
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
} else {
|
|
630
|
-
_inputClass[item.id] = '';
|
|
631
|
-
}
|
|
583
|
+
_inputClass[item.id] = '';
|
|
632
584
|
}
|
|
633
585
|
});
|
|
634
586
|
|
|
635
587
|
setInputClass(_inputClass);
|
|
636
588
|
}
|
|
637
589
|
|
|
638
|
-
if (validation
|
|
590
|
+
if (validation) {
|
|
639
591
|
toast.error(<div>{parse(validation)}</div>);
|
|
640
592
|
} else {
|
|
641
593
|
let url = formSettings.url;
|
|
@@ -652,32 +604,26 @@ function Form({
|
|
|
652
604
|
} else {
|
|
653
605
|
switch (formType) {
|
|
654
606
|
case 'create':
|
|
655
|
-
if (
|
|
656
|
-
formSettings.hasOwnProperty('json') &&
|
|
657
|
-
formSettings.json === true
|
|
658
|
-
) {
|
|
607
|
+
if (formSettings.json) {
|
|
659
608
|
url += '/jsonStore';
|
|
660
609
|
}
|
|
661
610
|
break;
|
|
662
611
|
case 'update':
|
|
663
612
|
case 'delete':
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}`;
|
|
674
|
-
}
|
|
613
|
+
if (formSettings.json) {
|
|
614
|
+
url +=
|
|
615
|
+
formType === 'update'
|
|
616
|
+
? '/jsonUpdate'
|
|
617
|
+
: `/jsonDelete/${
|
|
618
|
+
formData[
|
|
619
|
+
formSettings.primaryKey
|
|
620
|
+
]
|
|
621
|
+
}`;
|
|
675
622
|
} else {
|
|
676
623
|
url += `/${
|
|
677
624
|
formData[formSettings.primaryKey]
|
|
678
625
|
}`;
|
|
679
626
|
}
|
|
680
|
-
|
|
681
627
|
method =
|
|
682
628
|
formType === 'update' ? 'PUT' : 'DELETE';
|
|
683
629
|
break;
|
|
@@ -686,46 +632,37 @@ function Form({
|
|
|
686
632
|
|
|
687
633
|
if (!_.isEmpty(formData)) {
|
|
688
634
|
Object.keys(formData).forEach((item) => {
|
|
689
|
-
if (
|
|
690
|
-
formData[item] !== undefined &&
|
|
691
|
-
(item === 'file' ||
|
|
692
|
-
(formData[item] instanceof File &&
|
|
693
|
-
formData[item] !== null))
|
|
694
|
-
) {
|
|
635
|
+
if (formData[item] instanceof File) {
|
|
695
636
|
fileUpload = true;
|
|
696
637
|
fileKey = item;
|
|
697
638
|
}
|
|
698
639
|
});
|
|
699
640
|
}
|
|
700
641
|
|
|
701
|
-
if (fileUpload
|
|
642
|
+
if (fileUpload && formData[fileKey]) {
|
|
702
643
|
if (Array.isArray(formData[fileKey])) {
|
|
703
|
-
const
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
const uploadedFiles = await Promise.all(
|
|
644
|
+
const uploadPromises = formData[fileKey].map(
|
|
645
|
+
async (file) => {
|
|
646
|
+
const fileRes = await Vapor.store(file, {
|
|
647
|
+
progress: (progress) => {
|
|
648
|
+
setUploadProgress(progress * 100);
|
|
649
|
+
},
|
|
650
|
+
});
|
|
651
|
+
return {
|
|
652
|
+
uuid: fileRes.uuid,
|
|
653
|
+
key: fileRes.key,
|
|
654
|
+
bucket: fileRes.bucket,
|
|
655
|
+
filename: file.name,
|
|
656
|
+
filesize: file.size,
|
|
657
|
+
extension: fileRes.extension,
|
|
658
|
+
file_relationship: fileKey,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
formData['uploadedFiles'] = await Promise.all(
|
|
724
663
|
uploadPromises
|
|
725
664
|
);
|
|
726
|
-
|
|
727
|
-
formData['uploadedFiles'] = uploadedFiles;
|
|
728
|
-
} else if (formData[fileKey] instanceof File) {
|
|
665
|
+
} else {
|
|
729
666
|
const fileRes = await Vapor.store(
|
|
730
667
|
formData[fileKey],
|
|
731
668
|
{
|
|
@@ -734,7 +671,6 @@ function Form({
|
|
|
734
671
|
},
|
|
735
672
|
}
|
|
736
673
|
);
|
|
737
|
-
|
|
738
674
|
formData['uuid'] = fileRes.uuid;
|
|
739
675
|
formData['key'] = fileRes.key;
|
|
740
676
|
formData['bucket'] = fileRes.bucket;
|
|
@@ -745,60 +681,40 @@ function Form({
|
|
|
745
681
|
}
|
|
746
682
|
}
|
|
747
683
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
let payload = { ...formData };
|
|
757
|
-
Object.keys(formData).forEach((key) => {
|
|
758
|
-
const value = formData[key];
|
|
759
|
-
const field = fieldMap[key];
|
|
760
|
-
|
|
761
|
-
if (field && field.type === 'multi-dropdown-ajax') {
|
|
762
|
-
if (
|
|
763
|
-
typeof value === 'object' &&
|
|
764
|
-
value !== null &&
|
|
765
|
-
'value' in value &&
|
|
766
|
-
'label' in value
|
|
767
|
-
) {
|
|
768
|
-
payload[key] = value.value;
|
|
769
|
-
}
|
|
684
|
+
// Transform keys with dots into nested objects
|
|
685
|
+
const payload = Object.keys(formData).reduce((acc, key) => {
|
|
686
|
+
if (key.includes('.')) {
|
|
687
|
+
const [root, sub] = key.split('.');
|
|
688
|
+
if (!acc[root]) acc[root] = {};
|
|
689
|
+
acc[root][sub] = formData[key];
|
|
690
|
+
} else {
|
|
691
|
+
acc[key] = formData[key];
|
|
770
692
|
}
|
|
771
|
-
|
|
693
|
+
return acc;
|
|
694
|
+
}, {});
|
|
695
|
+
|
|
696
|
+
console.info(payload);
|
|
772
697
|
|
|
773
698
|
const res = await CustomFetch(url, method, payload);
|
|
774
699
|
|
|
775
700
|
if (res.data.error === '') {
|
|
776
|
-
if (fetchTable)
|
|
777
|
-
fetchTable();
|
|
778
|
-
}
|
|
779
|
-
|
|
701
|
+
if (fetchTable) fetchTable();
|
|
780
702
|
if (closeModal) {
|
|
781
703
|
closeModal();
|
|
782
|
-
|
|
783
704
|
if (type === 'saveAnother') {
|
|
784
705
|
fetchData();
|
|
785
706
|
modalOpen('create', 0);
|
|
786
707
|
}
|
|
787
708
|
}
|
|
788
|
-
|
|
789
709
|
toast.success(
|
|
790
|
-
formSettings?.save?.toast?.success
|
|
791
|
-
|
|
792
|
-
: 'Entry successfully saved into the system.'
|
|
710
|
+
formSettings?.save?.toast?.success ||
|
|
711
|
+
'Entry successfully saved into the system.'
|
|
793
712
|
);
|
|
794
713
|
|
|
795
|
-
if (
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
res.data.data.id
|
|
800
|
-
) {
|
|
801
|
-
navigate(formSettings.redirect + res.data.data.id);
|
|
714
|
+
if (formSettings.redirect && res.data.data?.id) {
|
|
715
|
+
navigate(
|
|
716
|
+
`${formSettings.redirect}${res.data.data.id}`
|
|
717
|
+
);
|
|
802
718
|
}
|
|
803
719
|
|
|
804
720
|
if (res.data.redirect) {
|
|
@@ -806,30 +722,26 @@ function Form({
|
|
|
806
722
|
}
|
|
807
723
|
|
|
808
724
|
if (
|
|
809
|
-
formSettings?.save?.return?.type &&
|
|
725
|
+
formSettings?.save?.return?.type === 'table' &&
|
|
810
726
|
formSettings?.save?.return?.param
|
|
811
727
|
) {
|
|
812
|
-
if (
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
columns: [],
|
|
830
|
-
dataSource: [],
|
|
831
|
-
}));
|
|
832
|
-
}
|
|
728
|
+
if (
|
|
729
|
+
res.data[formSettings.save.return.param]
|
|
730
|
+
?.length > 0
|
|
731
|
+
) {
|
|
732
|
+
const td = parseTableData(
|
|
733
|
+
res.data[formSettings.save.return.param]
|
|
734
|
+
);
|
|
735
|
+
setTableData((prevState) => ({
|
|
736
|
+
...prevState,
|
|
737
|
+
...td,
|
|
738
|
+
}));
|
|
739
|
+
} else {
|
|
740
|
+
setTableData((prevState) => ({
|
|
741
|
+
...prevState,
|
|
742
|
+
columns: [],
|
|
743
|
+
dataSource: [],
|
|
744
|
+
}));
|
|
833
745
|
}
|
|
834
746
|
}
|
|
835
747
|
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useLocation, useParams } from 'react-router-dom';
|
|
3
3
|
import { arrayMoveImmutable } from 'array-move';
|
|
4
4
|
import { toast } from 'react-toastify';
|
|
5
|
-
import { CircleChevronRight } from 'akar-icons';
|
|
6
5
|
import CustomFetch from '../../crm/Fetch';
|
|
7
|
-
import SortableList from '
|
|
6
|
+
import SortableList from '../sorting/List';
|
|
7
|
+
import Breadcrumb from '../Breadcrumb';
|
|
8
8
|
import styles from './styles/GenericSort.module.css';
|
|
9
9
|
|
|
10
|
+
// Utility function to convert camelCase to space-separated words with uppercase first letters
|
|
11
|
+
function camelCaseToUcFirst(str) {
|
|
12
|
+
return str
|
|
13
|
+
.replace(/([A-Z])/g, ' $1') // Add a space before each uppercase letter
|
|
14
|
+
.replace(/^./, function (str) {
|
|
15
|
+
return str.toUpperCase();
|
|
16
|
+
}) // Capitalize the first letter
|
|
17
|
+
.trim(); // Remove leading and trailing whitespace
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
const ChildSort = ({ item, url }) => {
|
|
11
21
|
const [data, setData] = useState(item.child);
|
|
12
22
|
|
|
@@ -17,13 +27,14 @@ const ChildSort = ({ item, url }) => {
|
|
|
17
27
|
useEffect(() => {
|
|
18
28
|
if (data.length > 0) {
|
|
19
29
|
CustomFetch(
|
|
20
|
-
url
|
|
30
|
+
`${url}/sort_update`,
|
|
21
31
|
'POST',
|
|
22
32
|
{
|
|
23
33
|
list: data,
|
|
24
34
|
},
|
|
25
35
|
function (result) {
|
|
26
36
|
if (result.error === '') {
|
|
37
|
+
toast.success('Sort updated successfully');
|
|
27
38
|
} else {
|
|
28
39
|
toast.error(String(result.error));
|
|
29
40
|
}
|
|
@@ -47,15 +58,18 @@ const ChildSort = ({ item, url }) => {
|
|
|
47
58
|
);
|
|
48
59
|
};
|
|
49
60
|
|
|
50
|
-
const GenericSort = ({ category = false }) => {
|
|
61
|
+
const GenericSort = ({ category = false, layout = 'full' }) => {
|
|
51
62
|
const location = useLocation();
|
|
63
|
+
const routeParams = useParams();
|
|
52
64
|
const {
|
|
53
65
|
form,
|
|
54
|
-
page
|
|
66
|
+
page = {}, // Provide a default empty object for page
|
|
55
67
|
paramValue,
|
|
56
|
-
} = location.state;
|
|
68
|
+
} = location.state || {}; // Safely access location.state
|
|
69
|
+
|
|
57
70
|
const [sortData, setSortData] = useState([]);
|
|
58
|
-
const url = form
|
|
71
|
+
const url = form?.url;
|
|
72
|
+
const data = page?.data || {}; // Safely access data from page, default to an empty object
|
|
59
73
|
|
|
60
74
|
const onSortEnd = ({ oldIndex, newIndex }) => {
|
|
61
75
|
setSortData((prevItem) =>
|
|
@@ -68,7 +82,7 @@ const GenericSort = ({ category = false }) => {
|
|
|
68
82
|
let fetchUrl = url;
|
|
69
83
|
let payload = {};
|
|
70
84
|
|
|
71
|
-
if (form
|
|
85
|
+
if (form?.json) {
|
|
72
86
|
fetchUrl = fetchUrl + '/jsonSortList';
|
|
73
87
|
payload.id = paramValue;
|
|
74
88
|
} else {
|
|
@@ -76,7 +90,6 @@ const GenericSort = ({ category = false }) => {
|
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
const res = await CustomFetch(fetchUrl, 'POST', payload);
|
|
79
|
-
|
|
80
93
|
setSortData(res.data);
|
|
81
94
|
} catch (err) {
|
|
82
95
|
console.log(err);
|
|
@@ -90,7 +103,7 @@ const GenericSort = ({ category = false }) => {
|
|
|
90
103
|
list: sortData,
|
|
91
104
|
};
|
|
92
105
|
|
|
93
|
-
if (form
|
|
106
|
+
if (form?.json) {
|
|
94
107
|
fetchUrl = fetchUrl + '/jsonSortUpdate';
|
|
95
108
|
payload.id = paramValue;
|
|
96
109
|
} else {
|
|
@@ -123,37 +136,23 @@ const GenericSort = ({ category = false }) => {
|
|
|
123
136
|
<>
|
|
124
137
|
<div className={styles.grid}>
|
|
125
138
|
<div className={styles.grid__row}>
|
|
126
|
-
<div
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
? `${setting.previousUrl}/${paramValue}`
|
|
144
|
-
: null
|
|
145
|
-
}
|
|
146
|
-
>
|
|
147
|
-
<CircleChevronRight
|
|
148
|
-
strokeWidth={2}
|
|
149
|
-
size={18}
|
|
150
|
-
/>{' '}
|
|
151
|
-
{data[setting.titleKey]}{' '}
|
|
152
|
-
</Link>
|
|
153
|
-
) : null}
|
|
154
|
-
<CircleChevronRight strokeWidth={2} size={18} />{' '}
|
|
155
|
-
{form?.sort?.title}
|
|
156
|
-
</h1>
|
|
139
|
+
<div
|
|
140
|
+
className={`${styles.grid__full} ${
|
|
141
|
+
layout === 'full'
|
|
142
|
+
? styles.crmtitle
|
|
143
|
+
: styles.crmtitleSimple
|
|
144
|
+
}`}
|
|
145
|
+
>
|
|
146
|
+
<Breadcrumb
|
|
147
|
+
data={{
|
|
148
|
+
label: camelCaseToUcFirst(routeParams.dataId),
|
|
149
|
+
}} // Use utility function here
|
|
150
|
+
page={{
|
|
151
|
+
parentTitle: 'Settings',
|
|
152
|
+
parentUrl: '/settings',
|
|
153
|
+
titleKey: 'label',
|
|
154
|
+
}}
|
|
155
|
+
/>
|
|
157
156
|
</div>
|
|
158
157
|
</div>
|
|
159
158
|
</div>
|
|
@@ -165,7 +164,7 @@ const GenericSort = ({ category = false }) => {
|
|
|
165
164
|
items={sortData}
|
|
166
165
|
onSortEnd={onSortEnd}
|
|
167
166
|
axis="xy"
|
|
168
|
-
helperClass=
|
|
167
|
+
helperClass={styles.dragitem}
|
|
169
168
|
transitionDuration={250}
|
|
170
169
|
useDragHandle
|
|
171
170
|
/>
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
min-height: auto;
|
|
35
35
|
padding: 5px 20px;
|
|
36
36
|
margin: 0;
|
|
37
|
-
background: var(--
|
|
37
|
+
background: var(--tertiary-color);
|
|
38
38
|
border-radius: var(--br);
|
|
39
39
|
border: 1px solid rgba(var(--item-color-rgb), 1.15);
|
|
40
40
|
position: relative;
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
align-items: center;
|
|
43
43
|
|
|
44
44
|
h1 {
|
|
45
|
-
color: var(--background-color);
|
|
46
45
|
font-weight: 700;
|
|
47
46
|
font-size: 1.15em;
|
|
48
47
|
margin: 0;
|
|
@@ -53,11 +52,11 @@
|
|
|
53
52
|
|
|
54
53
|
a {
|
|
55
54
|
text-decoration: none;
|
|
56
|
-
color: var(--secondary-color);
|
|
55
|
+
color: rgba(var(--secondary-color-rgb), 1.1);
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
svg {
|
|
60
|
-
color: rgba(var(--
|
|
59
|
+
color: rgba(var(--primary-color-rgb), 0.25);
|
|
61
60
|
position: relative;
|
|
62
61
|
top: 0;
|
|
63
62
|
margin: 0 0.5rem;
|
|
@@ -65,3 +64,60 @@
|
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
}
|
|
67
|
+
|
|
68
|
+
.dragitem {
|
|
69
|
+
list-style: none;
|
|
70
|
+
display: flex;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
align-items: center;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
border-radius: 5px;
|
|
75
|
+
border: 1px solid rgba(var(--primary-color-rgb), 0.2);
|
|
76
|
+
box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.25);
|
|
77
|
+
padding: 1.25rem;
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
cursor: grab;
|
|
80
|
+
list-style: none;
|
|
81
|
+
color: var(--paragraph-color);
|
|
82
|
+
|
|
83
|
+
svg {
|
|
84
|
+
width: 100%;
|
|
85
|
+
max-width: 18px;
|
|
86
|
+
position: absolute;
|
|
87
|
+
left: 5px;
|
|
88
|
+
top: 5px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.pencil-edit {
|
|
92
|
+
width: 100%;
|
|
93
|
+
max-width: 18px;
|
|
94
|
+
position: absolute;
|
|
95
|
+
left: 5px;
|
|
96
|
+
top: 30px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.trash-delete {
|
|
100
|
+
width: 100%;
|
|
101
|
+
max-width: 18px;
|
|
102
|
+
position: absolute;
|
|
103
|
+
left: 5px;
|
|
104
|
+
top: 60px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sortimg {
|
|
108
|
+
width: 50px;
|
|
109
|
+
position: relative;
|
|
110
|
+
height: 50px;
|
|
111
|
+
margin-left: auto;
|
|
112
|
+
border-radius: 5px;
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
|
|
115
|
+
img {
|
|
116
|
+
display: block;
|
|
117
|
+
object-fit: cover;
|
|
118
|
+
height: 100%;
|
|
119
|
+
width: 100%;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { SortableElement, sortableHandle } from 'react-sortable-hoc';
|
|
3
|
+
import { ChevronVertical, Pencil, TrashCan } from 'akar-icons';
|
|
4
|
+
|
|
5
|
+
import styles from './styles/Item.module.css';
|
|
6
|
+
|
|
7
|
+
const DragHandle = sortableHandle(() => (
|
|
8
|
+
<ChevronVertical className="drag-handle" strokeWidth={1} size={26} />
|
|
9
|
+
));
|
|
10
|
+
|
|
11
|
+
// Utility function to truncate text
|
|
12
|
+
const truncateText = (text, maxLength) => {
|
|
13
|
+
if (text && text.length > maxLength) {
|
|
14
|
+
return `${text.substring(0, maxLength)}...`;
|
|
15
|
+
}
|
|
16
|
+
return text;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SortableItem = ({
|
|
20
|
+
containerIndex,
|
|
21
|
+
handleClick,
|
|
22
|
+
handleEdit,
|
|
23
|
+
handleDelete,
|
|
24
|
+
showImage,
|
|
25
|
+
value,
|
|
26
|
+
}) => {
|
|
27
|
+
const renderImage = (d) => {
|
|
28
|
+
let htmlContainer = '';
|
|
29
|
+
|
|
30
|
+
if (showImage) {
|
|
31
|
+
const imageUrl = d.image_url || d.file_url;
|
|
32
|
+
htmlContainer = imageUrl ? (
|
|
33
|
+
<div className={styles.sortimg}>
|
|
34
|
+
<img src={imageUrl} alt="" />
|
|
35
|
+
</div>
|
|
36
|
+
) : (
|
|
37
|
+
<div className={styles.sortimg}>
|
|
38
|
+
{/* Placeholder image */}
|
|
39
|
+
<img
|
|
40
|
+
src="data:image/png;base64,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"
|
|
41
|
+
alt="Placeholder"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return htmlContainer;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const renderEditButton = () => {
|
|
51
|
+
if (!handleEdit) return null;
|
|
52
|
+
|
|
53
|
+
if (showImage) {
|
|
54
|
+
if (
|
|
55
|
+
value.hasOwnProperty('file_title') &&
|
|
56
|
+
value.hasOwnProperty('file_description')
|
|
57
|
+
) {
|
|
58
|
+
return <EditButton />;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return <EditButton />;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const EditButton = () => (
|
|
67
|
+
<Pencil
|
|
68
|
+
className={styles['pencil-edit']}
|
|
69
|
+
strokeWidth={1}
|
|
70
|
+
size={26}
|
|
71
|
+
onClick={(e) => {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
handleEdit(value);
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<li
|
|
81
|
+
onClick={(e) => {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
if (handleClick) {
|
|
84
|
+
handleClick(containerIndex);
|
|
85
|
+
}
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<DragHandle />
|
|
89
|
+
{truncateText(
|
|
90
|
+
value.label || value.file_title || value.file_name,
|
|
91
|
+
30
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{renderImage(value)}
|
|
95
|
+
|
|
96
|
+
{renderEditButton()}
|
|
97
|
+
|
|
98
|
+
{handleDelete && (
|
|
99
|
+
<TrashCan
|
|
100
|
+
className={styles['trash-delete']}
|
|
101
|
+
strokeWidth={1}
|
|
102
|
+
size={26}
|
|
103
|
+
onClick={(e) => {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
handleDelete(
|
|
107
|
+
value.id || containerIndex,
|
|
108
|
+
value.label || value.file_name
|
|
109
|
+
);
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
)}
|
|
113
|
+
</li>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default SortableElement(SortableItem);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import VisnsSortableItem from './Item';
|
|
3
|
+
import { SortableContainer } from 'react-sortable-hoc';
|
|
4
|
+
|
|
5
|
+
import styles from './styles/List.module.css';
|
|
6
|
+
|
|
7
|
+
const SortableList = ({
|
|
8
|
+
handleClick,
|
|
9
|
+
handleDelete,
|
|
10
|
+
handleEdit,
|
|
11
|
+
items,
|
|
12
|
+
showImage,
|
|
13
|
+
}) => {
|
|
14
|
+
return (
|
|
15
|
+
<ul className={styles.sortlist}>
|
|
16
|
+
{items.map((value, index) => (
|
|
17
|
+
<VisnsSortableItem
|
|
18
|
+
key={`item-${index}`}
|
|
19
|
+
containerIndex={index}
|
|
20
|
+
index={index}
|
|
21
|
+
handleClick={handleClick}
|
|
22
|
+
handleDelete={handleDelete}
|
|
23
|
+
handleEdit={handleEdit}
|
|
24
|
+
showImage={showImage}
|
|
25
|
+
value={value}
|
|
26
|
+
/>
|
|
27
|
+
))}
|
|
28
|
+
</ul>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default SortableContainer(SortableList);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.sortlist {
|
|
2
|
+
width: 100%;
|
|
3
|
+
display: grid;
|
|
4
|
+
grid-gap: 0.5rem;
|
|
5
|
+
grid-template-columns: repeat(4, 1fr);
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
padding: 0;
|
|
8
|
+
margin: 0;
|
|
9
|
+
list-style: none;
|
|
10
|
+
|
|
11
|
+
li {
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
border-radius: 5px;
|
|
17
|
+
border: 1px solid rgba(var(--primary-color-rgb), 0.2);
|
|
18
|
+
box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.1);
|
|
19
|
+
padding: 1.25rem;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
list-style: none;
|
|
23
|
+
position: relative;
|
|
24
|
+
color: var(--paragraph-color);
|
|
25
|
+
user-select: none;
|
|
26
|
+
|
|
27
|
+
svg {
|
|
28
|
+
width: 100%;
|
|
29
|
+
max-width: 18px;
|
|
30
|
+
position: absolute;
|
|
31
|
+
left: 5px;
|
|
32
|
+
top: 5px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.pencil-edit {
|
|
36
|
+
width: 100%;
|
|
37
|
+
max-width: 18px;
|
|
38
|
+
position: absolute;
|
|
39
|
+
left: 5px;
|
|
40
|
+
top: 30px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.trash-delete {
|
|
44
|
+
width: 100%;
|
|
45
|
+
max-width: 18px;
|
|
46
|
+
position: absolute;
|
|
47
|
+
left: 5px;
|
|
48
|
+
top: 60px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sortimg {
|
|
53
|
+
width: 50px;
|
|
54
|
+
position: relative;
|
|
55
|
+
height: 50px;
|
|
56
|
+
margin-left: auto;
|
|
57
|
+
border-radius: 5px;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
|
|
60
|
+
img {
|
|
61
|
+
display: block;
|
|
62
|
+
object-fit: cover;
|
|
63
|
+
height: 100%;
|
|
64
|
+
width: 100%;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
.sortlist {
|
|
2
|
+
width: 100%;
|
|
3
|
+
display: grid;
|
|
4
|
+
grid-gap: 0.5rem;
|
|
5
|
+
grid-template-columns: repeat(4, 1fr);
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
padding: 0;
|
|
8
|
+
margin: 0;
|
|
9
|
+
list-style: none;
|
|
10
|
+
|
|
11
|
+
li {
|
|
12
|
+
display: flex;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
align-items: center;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
border-radius: 5px;
|
|
17
|
+
border: 1px solid rgba(var(--primary-color-rgb), 0.2);
|
|
18
|
+
box-shadow: 0px 0px 20px rgba(var(--primary-color-rgb), 0.1);
|
|
19
|
+
padding: 1.25rem;
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
list-style: none;
|
|
23
|
+
position: relative;
|
|
24
|
+
color: var(--paragraph-color);
|
|
25
|
+
user-select: none;
|
|
26
|
+
|
|
27
|
+
svg {
|
|
28
|
+
width: 100%;
|
|
29
|
+
max-width: 18px;
|
|
30
|
+
position: absolute;
|
|
31
|
+
left: 5px;
|
|
32
|
+
top: 5px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.pencil-edit {
|
|
36
|
+
width: 100%;
|
|
37
|
+
max-width: 18px;
|
|
38
|
+
position: absolute;
|
|
39
|
+
left: 5px;
|
|
40
|
+
top: 30px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.trash-delete {
|
|
44
|
+
width: 100%;
|
|
45
|
+
max-width: 18px;
|
|
46
|
+
position: absolute;
|
|
47
|
+
left: 5px;
|
|
48
|
+
top: 60px;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sortimg {
|
|
53
|
+
width: 50px;
|
|
54
|
+
position: relative;
|
|
55
|
+
height: 50px;
|
|
56
|
+
margin-left: auto;
|
|
57
|
+
border-radius: 5px;
|
|
58
|
+
overflow: hidden;
|
|
59
|
+
|
|
60
|
+
img {
|
|
61
|
+
display: block;
|
|
62
|
+
object-fit: cover;
|
|
63
|
+
height: 100%;
|
|
64
|
+
width: 100%;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -160,7 +160,7 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
160
160
|
):not(.inovua-react-toolkit-combo-box__input):not(
|
|
161
161
|
.inovua-react-toolkit-date-input__input
|
|
162
162
|
):not(.inovua-react-toolkit-numeric-input__input):hover {
|
|
163
|
-
border: 1px solid rgba(var(--
|
|
163
|
+
border: 1px solid rgba(var(--paragraph-color-rgb), 0.85);
|
|
164
164
|
transition: border 0.15s linear;
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -172,7 +172,7 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
172
172
|
):not(.inovua-react-toolkit-combo-box__input):not(
|
|
173
173
|
.inovua-react-toolkit-date-input__input
|
|
174
174
|
):not(.inovua-react-toolkit-numeric-input__input):focus {
|
|
175
|
-
border: 1px solid rgba(var(--
|
|
175
|
+
border: 1px solid rgba(var(--paragraph-color-rgb), 0.85);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
input[type='file'] {
|
package/package.json
CHANGED