Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acd1c589d7 | ||
|
|
de62052453 | ||
|
|
de120dae39 | ||
|
|
08d1e9a360 | ||
|
|
74a9d08550 | ||
|
|
6fc86e026b | ||
|
|
72da56a916 | ||
|
|
4ba7ffbb1a | ||
|
|
0d9a0b8bb1 | ||
|
|
084aa69702 | ||
|
|
597635800f | ||
|
|
8da2c7fad4 | ||
|
|
2b0276ead9 | ||
|
|
45a96ece54 | ||
|
|
729d19dfd3 | ||
|
|
29aa123669 | ||
|
|
f69caf016a | ||
|
|
48e0e0ee15 | ||
|
|
a71278f95c | ||
|
|
f7bb11ede1 | ||
|
|
6a365f7734 | ||
|
|
e4d946e519 | ||
|
|
149a16f150 | ||
|
|
d8ff3d9faa | ||
|
|
fe34001e42 | ||
|
|
fcdd7e2758 | ||
|
|
025c30b685 | ||
|
|
e742e85f0e | ||
|
|
476a99feae | ||
|
|
29464585b8 | ||
|
|
cdd078699b | ||
|
|
9ef1e22fda | ||
|
|
655401e04b | ||
|
|
b550c07ca8 | ||
|
|
fd72094b8a | ||
|
|
4d7348abc2 | ||
|
|
ec68479e98 | ||
|
|
ac05942e58 | ||
|
|
b387b6b402 | ||
|
|
e13baeb2fc | ||
|
|
52eb8e4a68 | ||
|
|
71189783a8 | ||
|
|
aec9396524 | ||
|
|
d74390b974 | ||
|
|
998fe013c8 | ||
|
|
06d4c9aeaf | ||
|
|
bc77a9b0a1 | ||
|
|
07c0390401 | ||
|
|
c50d753d74 | ||
|
|
65eef5739d | ||
|
|
01eed3664b | ||
|
|
685e09d406 | ||
|
|
7bacca1f78 | ||
|
|
3764ab5c30 | ||
|
|
2f7a1d67b6 | ||
|
|
8726efb0aa | ||
|
|
e073e83ad6 | ||
|
|
69140d2139 | ||
|
|
aeed48c0ef | ||
|
|
1f9504b9cc | ||
|
|
5518d6932e | ||
|
|
f14e301b6c | ||
|
|
901ddf35bb | ||
|
|
9aa3166528 | ||
|
|
a90e679228 | ||
|
|
90b5e3c860 | ||
|
|
74f6612271 | ||
|
|
2862a97c4e | ||
|
|
5c3f967128 | ||
|
|
6fde71e947 | ||
|
|
2900464272 | ||
|
|
10bac3c11f | ||
|
|
302f38d472 | ||
|
|
8ecdbc43a3 | ||
|
|
6721b334cf | ||
|
|
454f557d14 | ||
|
|
635693b394 | ||
|
|
0be1816046 | ||
|
|
feb162e03e | ||
|
|
c8801846fa | ||
|
|
a793fc3722 | ||
|
|
ffdabe4a00 | ||
|
|
5536c99052 | ||
|
|
ba1c3145f3 | ||
|
|
0fbe6aeb6c | ||
|
|
4898c4a851 | ||
|
|
85aa6b2947 | ||
|
|
3b02d2bcdb | ||
|
|
ed01f4a7a0 | ||
|
|
1f395cf25f | ||
|
|
cf98e73d13 | ||
|
|
10112ac20d | ||
|
|
9a0183274f | ||
|
|
6b2e16c21d | ||
|
|
3c51baf1c9 | ||
|
|
3f4be1c4f3 | ||
|
|
62b36f58f7 | ||
|
|
ba3591c7bc | ||
|
|
1ec11b8733 | ||
|
|
ab89d3b71f | ||
|
|
1811173648 | ||
|
|
b218e96107 | ||
|
|
64a0be8776 | ||
|
|
414e2d819c | ||
|
|
0769101f28 | ||
|
|
5dc86119d3 | ||
|
|
4999dc2b18 | ||
|
|
962d36c088 | ||
|
|
d071986aac | ||
|
|
afb4de9d0c | ||
|
|
bc5c8cac33 | ||
|
|
be93e6a214 | ||
|
|
37748057e3 | ||
|
|
eb3bb77692 | ||
|
|
df0ce3f086 | ||
|
|
0a99b5181e | ||
|
|
8a5d3a0368 | ||
|
|
c00406e401 | ||
|
|
aeead23c86 | ||
|
|
83fe88911f | ||
|
|
527bca32d2 | ||
|
|
9bf4940c2e | ||
|
|
a653df6935 | ||
|
|
dfc6da388c | ||
|
|
3e2c822bbc | ||
|
|
1a20841a94 | ||
|
|
c6be81923b | ||
|
|
6b47f6a27b | ||
|
|
33b0111ed0 | ||
|
|
48fd9e52b3 | ||
|
|
d85074b127 | ||
|
|
5ada9a411b | ||
|
|
6f50de1ef1 | ||
|
|
612150cf4b | ||
|
|
634db83e8e | ||
|
|
b32c8068ef | ||
|
|
ae464274b0 | ||
|
|
c5ceba8d57 | ||
|
|
903d626c27 | ||
|
|
e5b4fb123f | ||
|
|
116893c1b4 | ||
|
|
592254a9a3 | ||
|
|
f52c6e24fe | ||
|
|
70ae1e24fc | ||
|
|
c491595fc8 | ||
|
|
6875135ede | ||
|
|
662049d878 | ||
|
|
1764d7f9ba | ||
|
|
07933b56b1 | ||
|
|
f6ba4db62d | ||
|
|
5bd36f91d9 | ||
|
|
c199c3019e | ||
|
|
facbc8e76e | ||
|
|
c3c6f6bab3 | ||
|
|
f5e216fedc | ||
|
|
c40a923388 | ||
|
|
a86f4fd9b7 | ||
|
|
59f6d022cf | ||
|
|
b7beeee260 | ||
|
|
31c861cbe4 | ||
|
|
6a9408b7b4 | ||
|
|
a9fd264a01 | ||
|
|
6c77bbfe75 | ||
|
|
890c3d2b1f | ||
|
|
3ede441f54 | ||
|
|
2433ad5957 | ||
|
|
fffd907d63 | ||
|
|
51edc339cd | ||
|
|
bf307abe9a | ||
|
|
08a97707b7 | ||
|
|
d99fecd46e | ||
|
|
6c59c0ec9a | ||
|
|
32c75d1fc3 | ||
|
|
7f0f81c334 | ||
|
|
a882f07f57 | ||
|
|
74b81a5363 | ||
|
|
9b75764717 | ||
|
|
c1468112b0 | ||
|
|
950b74789c | ||
|
|
7e4f12e41a | ||
|
|
d75c1d7c0c | ||
|
|
7eacbedf59 | ||
|
|
2e5947279f | ||
|
|
ef5ac7d626 | ||
|
|
658c37cc4d | ||
|
|
ffc8e4b23e | ||
|
|
3fb96fc792 | ||
|
|
71016ba5e0 | ||
|
|
cd1a6fdaf6 | ||
|
|
c2afeed448 | ||
|
|
37fd7bbeda | ||
|
|
71fbde0099 | ||
|
|
ca6cb60d78 | ||
|
|
df1c0ea66c | ||
|
|
46279efc2c | ||
|
|
aca2848406 | ||
|
|
eaec3cf607 | ||
|
|
ad25d73f26 | ||
|
|
481b9ac1e0 | ||
|
|
4cd3791feb | ||
|
|
7de8699437 | ||
|
|
4c162676b1 | ||
|
|
ca390bdfa6 | ||
|
|
22d064fd38 | ||
|
|
444c9bbfdf | ||
|
|
b289686d42 | ||
|
|
eeb878b340 | ||
|
|
7894d69cc9 | ||
|
|
b992d7db24 | ||
|
|
50b72f4d85 | ||
|
|
44639f66b0 | ||
|
|
ae740efced | ||
|
|
66c020d903 | ||
|
|
562a7d6fc8 | ||
|
|
090c17c2c0 | ||
|
|
70637d6143 | ||
|
|
f38e8705de | ||
|
|
ba06ff72a0 | ||
|
|
82312a1e0d | ||
|
|
1a8cf11a3e | ||
|
|
3deacbabc3 | ||
|
|
c28805add9 | ||
|
|
80fb320ae4 | ||
|
|
76c9c1fa53 | ||
|
|
f38d83dbe0 | ||
|
|
ee9411729d | ||
|
|
f6f7c6cecd | ||
|
|
a45fc0dd60 | ||
|
|
348f7810b9 | ||
|
|
45bec128a9 | ||
|
|
4da307315c | ||
|
|
c87aa415a9 | ||
|
|
b1786ec16a | ||
|
|
6ad35e979e | ||
|
|
9d996d5cb7 | ||
|
|
008e3e8783 | ||
|
|
8458906b0b | ||
|
|
6933aa309b | ||
|
|
067ea68d3b | ||
|
|
e4e8a0b721 | ||
|
|
dc392adc1d | ||
|
|
741efff1fa | ||
|
|
93b0d00ab0 | ||
|
|
d731951159 | ||
|
|
24bf08c14f | ||
|
|
c03819f5a9 | ||
|
|
4521d16891 | ||
|
|
f77525cd8b | ||
|
|
38b7311e66 | ||
|
|
c934f8ccd6 | ||
|
|
4e128e5a5a | ||
|
|
e0ccf049a6 | ||
|
|
b202d78059 | ||
|
|
e15136ce51 | ||
|
|
39c9211167 | ||
|
|
64864e3f54 | ||
|
|
af0bef6d29 | ||
|
|
130704b32f | ||
|
|
5b139b66a9 | ||
|
|
f9c5041af1 | ||
|
|
1e58b35db5 | ||
|
|
245bf8046b | ||
|
|
94eb5f9efe | ||
|
|
044128f36a | ||
|
|
594383fe14 | ||
|
|
90a585367e | ||
|
|
0b8382df00 | ||
|
|
a54c7e7fd7 | ||
|
|
45a2c88e90 | ||
|
|
e4526f026b | ||
|
|
11a2f6b8ec | ||
|
|
3bf35166fc | ||
|
|
cd50c8cf20 | ||
|
|
055ef3f209 | ||
|
|
964ec0d422 | ||
|
|
ec0e291604 | ||
|
|
836e952ba0 | ||
|
|
c5f39e2a24 | ||
|
|
498bd18630 | ||
|
|
56dd060531 | ||
|
|
960fbdb0eb | ||
|
|
b530ef882a | ||
|
|
f8145feebc | ||
|
|
df38d15611 | ||
|
|
bb69518674 | ||
|
|
fee53fb020 | ||
|
|
d086e60ae5 | ||
|
|
51150d4e47 | ||
|
|
1d3cd02f9d | ||
|
|
850d14064a | ||
|
|
d03515efc4 | ||
|
|
30e6e4b3fd | ||
|
|
cbdd729e95 | ||
|
|
ac82209d52 | ||
|
|
d5cc6f6758 | ||
|
|
3aa211dc1a | ||
|
|
33d5eecbbf | ||
|
|
1d6c108ba4 | ||
|
|
79da0f285d | ||
|
|
e8eef32958 | ||
|
|
dc1fab5250 | ||
|
|
3e185ef796 | ||
|
|
8cf5adb273 | ||
|
|
803faed97b | ||
|
|
660eaeb714 | ||
|
|
273ada837b | ||
|
|
79126700c4 | ||
|
|
6355c02485 | ||
|
|
bce028877b | ||
|
|
e47211a8c0 | ||
|
|
4a4c9eaa2d | ||
|
|
0a5653f53d | ||
|
|
5a90a0f484 | ||
|
|
a3a398bd92 | ||
|
|
7d70162137 | ||
|
|
e4c62c4eeb | ||
|
|
3eadca01fa | ||
|
|
659b7fe0f4 | ||
|
|
ade6694427 | ||
|
|
f8160665a4 | ||
|
|
5778bdecae | ||
|
|
9122c71c90 | ||
|
|
2c2065aca8 | ||
|
|
b1dd4df3c0 | ||
|
|
64fd3580ac | ||
|
|
01273777b7 | ||
|
|
23eaa1ae81 | ||
|
|
3fd0613407 | ||
|
|
d45566257f | ||
|
|
e9eb59aa7a | ||
|
|
9e3ba2cf5c | ||
|
|
81afc92c14 | ||
|
|
d3bf9e9e9b | ||
|
|
18e25f6cd9 | ||
|
|
a6d97d587f | ||
|
|
ff9a495775 | ||
|
|
b29c7d5c7f | ||
|
|
fddeba8d44 | ||
|
|
c564b5741c | ||
|
|
179c3c5dfe | ||
|
|
221f71c061 | ||
|
|
a26e65f76f | ||
|
|
9ba05d23fd | ||
|
|
0f67013005 | ||
|
|
65d5c7f337 | ||
|
|
a7dd47f1b1 | ||
|
|
350212c02f | ||
|
|
d9126d6701 | ||
|
|
51c3a0f71a | ||
|
|
a4bf9ff915 | ||
|
|
28a2a113e2 | ||
|
|
07382822bd | ||
|
|
06677d923c | ||
|
|
534dc3d3f5 | ||
|
|
a69682cebf | ||
|
|
2ae7e8ced2 | ||
|
|
e20b75cfc1 | ||
|
|
37b73e36ca | ||
|
|
cc6b64d140 | ||
|
|
4b765a8e1d | ||
|
|
4a7108e4f6 | ||
|
|
66bf577676 | ||
|
|
13e2d92ce6 | ||
|
|
4b9f22a5bc | ||
|
|
77c61c89a2 | ||
|
|
d04b79001a | ||
|
|
e14b2a2a17 | ||
|
|
6afa97a76b | ||
|
|
3a28b1cdd7 | ||
|
|
23b48838ae | ||
|
|
bddebf2758 | ||
|
|
5a68e18c48 | ||
|
|
280b812e5b | ||
|
|
082f0569e2 | ||
|
|
b01618c8ca | ||
|
|
7b0f0d5b9f | ||
|
|
1381a65c0b | ||
|
|
961ec2b2ba | ||
|
|
022cb20b64 | ||
|
|
7b4fa4fe23 | ||
|
|
c85c21ef8a | ||
|
|
09fb01862d | ||
|
|
f6ce1b45fb | ||
|
|
8fd2d4210e | ||
|
|
0f7f1f1ba2 | ||
|
|
8693ef29f6 | ||
|
|
dc67d2c23d | ||
|
|
761925fced | ||
|
|
c240eb133d | ||
|
|
9daed4a5ed | ||
|
|
ee18f82fa9 | ||
|
|
4085f8259d | ||
|
|
98f84f6cc8 | ||
|
|
da9363f588 | ||
|
|
5afb60a687 | ||
|
|
a18733ad79 | ||
|
|
400e132f11 | ||
|
|
c56830b7be | ||
|
|
f988c25491 | ||
|
|
53bd13e03b | ||
|
|
68edecae18 | ||
|
|
b421c0f1a9 | ||
|
|
3858746d64 | ||
|
|
41e8aa18a7 | ||
|
|
fb8915e9db | ||
|
|
ba2bb07aa9 | ||
|
|
74083d0606 | ||
|
|
0f2862c52e | ||
|
|
61e11b7105 | ||
|
|
d7de8648e9 | ||
|
|
c279fd6525 | ||
|
|
3599fbee21 | ||
|
|
9ad51a1d6a | ||
|
|
03e22dbf33 | ||
|
|
5693fa230b | ||
|
|
8cd90911a8 | ||
|
|
7cf820d205 | ||
|
|
4d01547fd5 | ||
|
|
6597f67f0a | ||
|
|
14ce91350e | ||
|
|
7a5f75ea79 | ||
|
|
a44cb7168f | ||
|
|
1c6b056d6c | ||
|
|
cf6759ce79 | ||
|
|
d422f46f47 | ||
|
|
85f94c8ffa | ||
|
|
91da331ce6 | ||
|
|
3db6946198 | ||
|
|
94c8caa3e6 | ||
|
|
ed1120927c | ||
|
|
79b4c38587 | ||
|
|
2cc5517bc1 | ||
|
|
eddf70f102 | ||
|
|
b6774519f9 | ||
|
|
d6bb83f997 | ||
|
|
55e8042b0a | ||
|
|
d5bc2df20f | ||
|
|
c623e868f7 | ||
|
|
174b9b9941 | ||
|
|
0eb247ae0c | ||
|
|
f96df699e4 | ||
|
|
dcaca98aec | ||
|
|
d70f4dddb6 | ||
|
|
db34c4248c | ||
|
|
1d39db6d7d | ||
|
|
654dadce93 | ||
|
|
e578981791 | ||
|
|
c4297a260e | ||
|
|
ecc453957c | ||
|
|
498e0c6be6 | ||
|
|
b4d73b2b44 | ||
|
|
f14c98505a | ||
|
|
3f23bdd372 | ||
|
|
440d253124 | ||
|
|
2b6bf7bcaa | ||
|
|
cb6d9e9b9b | ||
|
|
ebeeedc96c | ||
|
|
5cecf3390b | ||
|
|
9890d568fd | ||
|
|
06bcae64be | ||
|
|
fb017c4bcd | ||
|
|
01d3edd262 | ||
|
|
41a025715e | ||
|
|
306768fbd2 | ||
|
|
d82da53258 | ||
|
|
673245df02 | ||
|
|
42549e5792 | ||
|
|
493731a6af | ||
|
|
cc8b4b7157 | ||
|
|
4cbd7e74f7 | ||
|
|
b300a5bb39 | ||
|
|
5422fefc54 | ||
|
|
248b0cac0b | ||
|
|
a388a7c345 | ||
|
|
fc564ca37b | ||
|
|
37b2e4c97a | ||
|
|
a3bc9817e7 | ||
|
|
8a216e6c0d | ||
|
|
613b1cfe07 | ||
|
|
ee77534e35 | ||
|
|
737b33db1e | ||
|
|
ac214d0467 | ||
|
|
f8b21e7a93 | ||
|
|
7e6df13b71 | ||
|
|
3e584cec1a | ||
|
|
bc75dd109f | ||
|
|
6b3abed782 | ||
|
|
f163958cd0 | ||
|
|
ecd461b96c | ||
|
|
1371c2c09a | ||
|
|
3121f35158 | ||
|
|
3c0db9473c | ||
|
|
4f2b72dd4d | ||
|
|
7d9af1db95 | ||
|
|
0e38783560 | ||
|
|
6880ae35a8 | ||
|
|
9a07c67b69 | ||
|
|
33bfdeef15 | ||
|
|
a404247604 | ||
|
|
c48579daa7 | ||
|
|
509f8f9a07 | ||
|
|
5b85d5b277 | ||
|
|
bbf3170398 | ||
|
|
c172a7c55c | ||
|
|
90f3e0066b | ||
|
|
b6e028a2c0 | ||
|
|
11384192ba | ||
|
|
9df63265fb | ||
|
|
ff6a8ce755 | ||
|
|
60c1aa253a | ||
|
|
ad2fec0d66 | ||
|
|
0451a6cae0 | ||
|
|
0d5f8981fe | ||
|
|
cfd8be0eee | ||
|
|
86e5ef6ef5 | ||
|
|
1af47c8425 | ||
|
|
f0395cb2c7 | ||
|
|
598aa2975f | ||
|
|
0742291216 | ||
|
|
94eeceeaf9 | ||
|
|
970fbecb7b | ||
|
|
361e082930 | ||
|
|
3f9b6eb5a9 | ||
|
|
2265bee61f | ||
|
|
6390706d8d | ||
|
|
37e03c1026 | ||
|
|
c6fa6c710d | ||
|
|
2e921e30b6 | ||
|
|
686a773d34 | ||
|
|
b03e1b8f9d | ||
|
|
f1ceae794f | ||
|
|
5a8bcb8f9a | ||
|
|
ce70da7d8e | ||
|
|
db03dfa641 | ||
|
|
7e70a5e111 | ||
|
|
f5f18f1630 | ||
|
|
fa6856c649 | ||
|
|
3b4946e47b | ||
|
|
05fe68d458 | ||
|
|
2f3cdca821 | ||
|
|
4d8a54a6c5 | ||
|
|
4fe5bdaad8 | ||
|
|
b95ede5273 | ||
|
|
5b2684a9f4 | ||
|
|
d779720f26 | ||
|
|
3cf8029978 | ||
|
|
0bb857a942 | ||
|
|
103d32edcb | ||
|
|
eca6ad374b | ||
|
|
d2efe88266 | ||
|
|
450e5ec7f7 | ||
|
|
3bbbda91b8 | ||
|
|
a48db306bb | ||
|
|
2a60b30c75 | ||
|
|
19cfa9d27d | ||
|
|
0fc12c957e | ||
|
|
6fd332f8b4 | ||
|
|
39f7ccecaa | ||
|
|
3bd306809b | ||
|
|
04e9d13206 | ||
|
|
b24739d61b | ||
|
|
9dda211605 | ||
|
|
93708a6875 | ||
|
|
e78f6f6df4 | ||
|
|
ccaaca75ba | ||
|
|
dedff36b35 | ||
|
|
4d561ca19e | ||
|
|
f70e9e2a3f | ||
|
|
e4a3ce405e | ||
|
|
c3be2a1fef | ||
|
|
8b2f7f1e95 | ||
|
|
55465a035f | ||
|
|
d30105299c | ||
|
|
78b038da13 | ||
|
|
d230ad2655 | ||
|
|
367c824918 | ||
|
|
5f8affb1bb | ||
|
|
3bd23f83cf | ||
|
|
ab1dbecab2 | ||
|
|
44c6aa9b0b | ||
|
|
ad922c16c7 | ||
|
|
87ee2e881e | ||
|
|
3659a1de55 | ||
|
|
cc49a2fd4e | ||
|
|
babd43e710 | ||
|
|
8dde38edf1 | ||
|
|
6c16d00031 | ||
|
|
99f444a8a1 | ||
|
|
f0149f6fd8 | ||
|
|
bdb7d9be8f | ||
|
|
27dcd7fefd | ||
|
|
a2467ef1a6 | ||
|
|
824b0a5407 | ||
|
|
ac8195a52d | ||
|
|
e0920bf5d8 | ||
|
|
4a5f030d87 | ||
|
|
ba839b0fc8 | ||
|
|
27130fd223 | ||
|
|
19ba9e3091 | ||
|
|
8c64a89f08 | ||
|
|
1c3d2d5c9a | ||
|
|
b36df32b19 | ||
|
|
155a3eee14 | ||
|
|
7bb46782a3 | ||
|
|
40961936d0 | ||
|
|
db310b0d73 | ||
|
|
f0e9c6b3e4 | ||
|
|
28fd98c0db | ||
|
|
97bd375de3 | ||
|
|
e2d450f9aa | ||
|
|
add01cb02a | ||
|
|
cea1653805 | ||
|
|
9d81fce956 | ||
|
|
79e3762b36 | ||
|
|
aa04a957ed | ||
|
|
d31910aefa | ||
|
|
96a4a2e7fa | ||
|
|
e8aaab99f9 | ||
|
|
c61be7c35b | ||
|
|
0ee85f53da | ||
|
|
748e2196da | ||
|
|
58199a934a | ||
|
|
b04d2abbab | ||
|
|
7eec822a65 | ||
|
|
6c21028523 | ||
|
|
099a3cc46d | ||
|
|
5adaffc684 | ||
|
|
d588caf6d4 | ||
|
|
5930fd940d | ||
|
|
741f66b019 | ||
|
|
0bf07c2703 | ||
|
|
de26c2d719 | ||
|
|
881ac12b03 | ||
|
|
1932dda6d6 | ||
|
|
46159b7214 | ||
|
|
c57c15a4cb | ||
|
|
724fa7c27e | ||
|
|
2762c3e170 | ||
|
|
10a0c60056 | ||
|
|
036c9b8d4a | ||
|
|
1f50368259 | ||
|
|
6d5e07dac7 | ||
|
|
3656175f11 | ||
|
|
febb50c3cd | ||
|
|
f8ba0d357b | ||
|
|
74bfd39abc | ||
|
|
5a25b2d874 | ||
|
|
3115ff3e2a | ||
|
|
d1e7b9a2ab | ||
|
|
adbf1fe1e0 | ||
|
|
60140885e0 | ||
|
|
fe2f3c5f7c | ||
|
|
a06723ff3f | ||
|
|
387154c07b | ||
|
|
12a6a84450 | ||
|
|
1f350fc459 | ||
|
|
d9fd2a5531 | ||
|
|
cd4fd6e3de | ||
|
|
49cd30de96 | ||
|
|
a24ebe25a8 | ||
|
|
3e664bf677 | ||
|
|
6161e975fb | ||
|
|
c2a45c230b | ||
|
|
a629aa0bff | ||
|
|
899678d98a | ||
|
|
076a0f54c5 | ||
|
|
a94e67cd7c | ||
|
|
e78a3991f0 | ||
|
|
593a8499f4 | ||
|
|
70d643a5e2 | ||
|
|
f2b1a54695 | ||
|
|
4353a4f732 | ||
|
|
34c99edf1a | ||
|
|
91f3bdb299 | ||
|
|
276fc974fa | ||
|
|
081e11f9af | ||
|
|
4f056cc9d3 | ||
|
|
d9da25e908 | ||
|
|
99b3436c8c | ||
|
|
452d4d7a1d | ||
|
|
ed613c3424 | ||
|
|
bf70fa0b11 | ||
|
|
8684197d49 | ||
|
|
648d1224b3 | ||
|
|
126b6ad355 | ||
|
|
0512f27e2e | ||
|
|
2c61630017 | ||
|
|
72c2b2dde6 | ||
|
|
29eb78c960 | ||
|
|
aa9ba05dde | ||
|
|
365b074f62 | ||
|
|
af6cb4c17d | ||
|
|
6bcad91b0f | ||
|
|
1aa2b003b2 | ||
|
|
1a27e9addf | ||
|
|
c2891a0858 | ||
|
|
c1b226db96 | ||
|
|
8a01efee4c | ||
|
|
eafb26d462 | ||
|
|
62e1a1bd9b | ||
|
|
58eb71c52d | ||
|
|
c1e6f2b5ab | ||
|
|
ba0c18b591 | ||
|
|
c3a902dcc7 | ||
|
|
5ece3c0694 | ||
|
|
1053540410 | ||
|
|
24773759c2 | ||
|
|
7a6de70169 | ||
|
|
04b2c7c4da | ||
|
|
d11171e91c | ||
|
|
a4a78cb96c | ||
|
|
dfdcde8fcc | ||
|
|
5e1b84d2e6 | ||
|
|
f7606bce35 | ||
|
|
e643d96797 | ||
|
|
1ff4160ca4 | ||
|
|
62f66bf524 | ||
|
|
de3bc37fa5 | ||
|
|
ea660384c3 | ||
|
|
210cb5e754 | ||
|
|
ed45bfef84 | ||
|
|
a8e77e93f7 | ||
|
|
969071bb28 | ||
|
|
76491a77f1 | ||
|
|
9a51eed2bc | ||
|
|
a675a3c8fd | ||
|
|
fea30930eb | ||
|
|
7373b086e4 | ||
|
|
2fc1eda761 | ||
|
|
2c100ad399 | ||
|
|
1cefe01aee | ||
|
|
f1cf395761 | ||
|
|
4af3d8f78b | ||
|
|
cf21ac7405 | ||
|
|
5f11e92c0c | ||
|
|
c3ffda4dd2 | ||
|
|
c929b6b7a9 | ||
|
|
eb2abd3e6d | ||
|
|
1c4c2e87b4 | ||
|
|
4fda5b4531 | ||
|
|
df61e143af | ||
|
|
3a55c35445 | ||
|
|
f08758fd71 | ||
|
|
08476ba3d6 | ||
|
|
db9ae71d67 | ||
|
|
39806e3ad6 | ||
|
|
7882c61b81 | ||
|
|
b8f886902f | ||
|
|
17660dae21 | ||
|
|
7584667643 | ||
|
|
b47d1cff07 | ||
|
|
b99d8e6d6e | ||
|
|
8b15bceac8 | ||
|
|
86b0e90948 | ||
|
|
3bc57a999c | ||
|
|
27730c89e8 | ||
|
|
82eeffa6e4 | ||
|
|
53147caebc | ||
|
|
6004522d5c | ||
|
|
266c2463c9 | ||
|
|
1b215f53e7 | ||
|
|
73b2ba6abd | ||
|
|
c04fd80a38 | ||
|
|
61b123cb61 | ||
|
|
08e805569f | ||
|
|
375aa0b7e7 | ||
|
|
b94e0fa052 | ||
|
|
b6752549ed | ||
|
|
51505af6d8 | ||
|
|
0e5cffa1fa | ||
|
|
d3b1a37a7c | ||
|
|
885b5287b4 | ||
|
|
5348c6620a | ||
|
|
73c219fa4d | ||
|
|
f4090c0c61 | ||
|
|
cd2be94d29 | ||
|
|
878dca3cd6 | ||
|
|
90cba26904 | ||
|
|
c5c001f030 | ||
|
|
a3ba4e26c3 | ||
|
|
dd95862d5a | ||
|
|
5d3de41e63 | ||
|
|
5cad0a96cd | ||
|
|
e74fd79efc | ||
|
|
6f49fc9126 | ||
|
|
bbf3cc2f44 | ||
|
|
7bf196ef75 | ||
|
|
4b5b3aff49 | ||
|
|
15b0cb132c | ||
|
|
e67f91ea90 | ||
|
|
59350f02fb | ||
|
|
32cde66d64 | ||
|
|
7ba111a275 | ||
|
|
8d3fa6cef8 | ||
|
|
ef7aede146 | ||
|
|
11ae58aad3 | ||
|
|
05e30a5d19 | ||
|
|
64773cafec | ||
|
|
c6be39ffc3 | ||
|
|
376d4293c2 | ||
|
|
67b3752297 | ||
|
|
f463ffc8ed | ||
|
|
6e2aac51b4 | ||
|
|
a9a3c25dfd | ||
|
|
645898ab14 | ||
|
|
3ad1c46455 | ||
|
|
d3d33c85d9 | ||
|
|
c2796f66fb | ||
|
|
f57fcd25c9 | ||
|
|
9bda7dee7f | ||
|
|
7e1f21b02b | ||
|
|
a83acabf1a | ||
|
|
fb99c76b0c | ||
|
|
cc0ff1f5e7 | ||
|
|
afbfd9ed43 | ||
|
|
fcad04461f | ||
|
|
ca98b5a210 | ||
|
|
bd72ef84ed | ||
|
|
cb3c62fb6c | ||
|
|
2976220e4d | ||
|
|
192371c07b | ||
|
|
708a3f4ad9 | ||
|
|
4718c653f7 | ||
|
|
fbcdfa97cf | ||
|
|
4e39762b46 | ||
|
|
4239e8d5fb | ||
|
|
c8ee455865 | ||
|
|
d5be7b8c57 | ||
|
|
bc2f4f1ef3 | ||
|
|
e72baf3c1f | ||
|
|
b2b52b6af7 | ||
|
|
4f417ccf0f | ||
|
|
780c3f5f6d | ||
|
|
f9eff12f94 | ||
|
|
e912a8d4e0 | ||
|
|
224af292d6 | ||
|
|
e07a4457ec | ||
|
|
58cd0495e6 | ||
|
|
6b9b2afbef | ||
|
|
c55a8d09a2 | ||
|
|
71b06f92ec | ||
|
|
692ec8ecbf | ||
|
|
523ea7f314 | ||
|
|
f48444f21d | ||
|
|
b4c5b3cbe6 | ||
|
|
ed88ae013d | ||
|
|
0fe7937808 | ||
|
|
564f57b879 | ||
|
|
a47dc8e767 | ||
|
|
41e192acb0 | ||
|
|
ff4fb191b8 | ||
|
|
2feee80168 | ||
|
|
59fed8a9cc | ||
|
|
7cfca0a650 | ||
|
|
6f1a5fd3a9 | ||
|
|
cd6869778a | ||
|
|
2e8d84430e | ||
|
|
b8f32a6d8d | ||
|
|
11ab37baf1 | ||
|
|
9dbde43a44 | ||
|
|
2a24d71ee3 | ||
|
|
134444c20a | ||
|
|
44b87f178e | ||
|
|
666636d554 | ||
|
|
5ec9d59382 | ||
|
|
addc7622ab | ||
|
|
1c7c3a4777 | ||
|
|
2a46b02487 | ||
|
|
5974515362 | ||
|
|
3fab1d9c6f | ||
|
|
f9b8437981 | ||
|
|
b0c73bc63e | ||
|
|
a283f2ee5d | ||
|
|
934823fad4 | ||
|
|
351ed795ca | ||
|
|
a335d02156 | ||
|
|
3e0747164a | ||
|
|
8ad396d3ba | ||
|
|
26b54f0c00 | ||
|
|
de5b58fff6 | ||
|
|
b9b329dde4 | ||
|
|
2581500307 | ||
|
|
ef5c20a9f2 | ||
|
|
01aee7c028 | ||
|
|
569c7f4b48 | ||
|
|
bb849f175c | ||
|
|
ede0eea944 | ||
|
|
47150cca41 | ||
|
|
86df98a6a4 | ||
|
|
8dd5e90f8b | ||
|
|
f31023e2ed | ||
|
|
740d2e5a6c | ||
|
|
20d37e1ac9 | ||
|
|
f3f79ed499 | ||
|
|
c581222022 | ||
|
|
a3fd738b46 | ||
|
|
3f16e973eb | ||
|
|
cb1013762f | ||
|
|
74e275c5d0 | ||
|
|
dbeb513f65 | ||
|
|
269246659b | ||
|
|
6f4561a38f | ||
|
|
12507190d8 | ||
|
|
85dac65030 | ||
|
|
4f1708619e | ||
|
|
84b545c37d | ||
|
|
b24086e679 | ||
|
|
8308376f53 | ||
|
|
137f371528 | ||
|
|
5707039002 | ||
|
|
d4fabf0fe9 | ||
|
|
a4a0630102 | ||
|
|
439ac7eac6 | ||
|
|
ef1b82f426 | ||
|
|
51fe5d133e | ||
|
|
e7b9f3f121 | ||
|
|
14b4d10be3 | ||
|
|
36a1123535 | ||
|
|
03750e3145 | ||
|
|
ff4879a984 | ||
|
|
7a6a543df5 | ||
|
|
edda20940b | ||
|
|
987a4bd04c | ||
|
|
35d20bd42c | ||
|
|
97983c423f | ||
|
|
68050e3a35 | ||
|
|
4f2801d901 | ||
|
|
aaa3dfc304 | ||
|
|
7a9bebe035 | ||
|
|
ce86510d8e | ||
|
|
9d15b035c5 | ||
|
|
156a05f6ed | ||
|
|
22de053cd3 | ||
|
|
13845adbd5 | ||
|
|
ef68f2da16 | ||
|
|
9114908521 | ||
|
|
f565edc655 | ||
|
|
b0e26b8ae2 | ||
|
|
b35d8406a3 | ||
|
|
3910486b17 | ||
|
|
60ba77a06f | ||
|
|
f86ea04a02 | ||
|
|
9e185fa33c | ||
|
|
5142a8c62d | ||
|
|
db4b5f0452 | ||
|
|
042ad8465e | ||
|
|
ebfd9d3af7 | ||
|
|
c0ae0c7ffa | ||
|
|
861a065849 | ||
|
|
3884909965 | ||
|
|
6a0788c2a1 | ||
|
|
bd60f91734 | ||
|
|
b58c3fd467 | ||
|
|
9d431af0b9 | ||
|
|
cb1ebc6a72 | ||
|
|
a198bce3e2 | ||
|
|
e5c50a0dbb | ||
|
|
caa1dfaf34 | ||
|
|
eb01a0b17d | ||
|
|
1add575d26 | ||
|
|
4a52d327ce | ||
|
|
ef68fe1c85 | ||
|
|
83fa9c9ebd | ||
|
|
1a774bfcfa | ||
|
|
db9ec6bfd5 | ||
|
|
5736306f8c | ||
|
|
67cb508944 | ||
|
|
cbcfb8c063 | ||
|
|
da846dcbbb | ||
|
|
e7280bf017 | ||
|
|
9692f57663 | ||
|
|
f0655893eb | ||
|
|
f9e59905b7 | ||
|
|
c115c7c757 | ||
|
|
4edcf807ad | ||
|
|
416d8c823c | ||
|
|
bea158b8f2 | ||
|
|
39c6560a2d | ||
|
|
509b004014 | ||
|
|
f1b7e1164b | ||
|
|
3a2f626cb9 | ||
|
|
7cde6fd73c | ||
|
|
06d6eecaf0 | ||
|
|
c9ed29261d | ||
|
|
87fe5cdd6f | ||
|
|
f74819f50a | ||
|
|
98c8af5b9c | ||
|
|
ca17f1cfc9 | ||
|
|
74e2582ff9 | ||
|
|
8bbbf9b3d8 | ||
|
|
048629dd32 | ||
|
|
fe03da1d97 | ||
|
|
0a6622159f | ||
|
|
7a5cdefcfa | ||
|
|
0a3d89d0e2 | ||
|
|
882b97da5e | ||
|
|
fa405752da | ||
|
|
2d7571af5a | ||
|
|
07a57969ac | ||
|
|
01b7b1cab8 | ||
|
|
fee3d8f672 | ||
|
|
f1628e46fa | ||
|
|
17cb4479cb | ||
|
|
b52511113b | ||
|
|
95cc15ec4c | ||
|
|
1a4b4655c8 | ||
|
|
9fc69ba7e6 | ||
|
|
8ce31d3d80 | ||
|
|
d097f9a741 | ||
|
|
8686a35405 | ||
|
|
a92d90db4a | ||
|
|
bfa0e0e144 | ||
|
|
d3b2e1f24b | ||
|
|
bc90da4cf2 | ||
|
|
61afc35ac8 | ||
|
|
00e9396590 | ||
|
|
bbedb13f3e | ||
|
|
55cfbae8a7 | ||
|
|
bfa19eec0c | ||
|
|
07851ccde2 | ||
|
|
ad5776cfba | ||
|
|
a13478fa64 | ||
|
|
85a1c10d26 | ||
|
|
6918282e31 | ||
|
|
fd778c25e6 | ||
|
|
af344c8ae2 | ||
|
|
c505f8cf86 | ||
|
|
52320e0cd9 | ||
|
|
214db474c2 | ||
|
|
1bffabc94d | ||
|
|
7c8a5e02f2 | ||
|
|
a49c76c188 | ||
|
|
1f16123865 | ||
|
|
e85bbb7bd9 | ||
|
|
34fc3afb1e | ||
|
|
67479a9c1e | ||
|
|
2a8cedca58 | ||
|
|
57fc4aa06b | ||
|
|
39f3f73896 | ||
|
|
f4e907de36 | ||
|
|
72f7e1fb27 | ||
|
|
3dc109c3dd | ||
|
|
83df89c115 | ||
|
|
4070409a59 | ||
|
|
bb732d7606 | ||
|
|
70963c06b8 | ||
|
|
a1a84b6928 | ||
|
|
eefaf5fe50 | ||
|
|
9500b2d678 | ||
|
|
221a49bb4a | ||
|
|
3c1ceb4995 | ||
|
|
babe17838b | ||
|
|
f5d11f9ba6 | ||
|
|
e52c7c97cf | ||
|
|
ebdc5ef566 | ||
|
|
939d917b7b | ||
|
|
2afb868edc | ||
|
|
a5fe4a345b | ||
|
|
629bf7f51f | ||
|
|
350854ab6b | ||
|
|
cc980758fd | ||
|
|
0f1ce43cc5 | ||
|
|
5e1a9cdc20 | ||
|
|
550ce10c92 | ||
|
|
6080c59bd7 | ||
|
|
273a35d42f | ||
|
|
2f195fe02e | ||
|
|
0715ca9d85 | ||
|
|
a8d564d74d | ||
|
|
89ba3e0fa4 | ||
|
|
eebcfe94d9 | ||
|
|
ea9b0370c4 | ||
|
|
e264511a6e | ||
|
|
1ec10fbcfc | ||
|
|
53d154fa59 | ||
|
|
274620d17b | ||
|
|
06db7d2360 | ||
|
|
2b3549e4df | ||
|
|
d16478792c | ||
|
|
7ba2ef6420 | ||
|
|
b5d98796b7 | ||
|
|
97c17cfe84 | ||
|
|
4b4e200090 | ||
|
|
232d38440d | ||
|
|
341bd9bef0 | ||
|
|
2f4d04c722 | ||
|
|
148ae40e1a | ||
|
|
9cb0ced4f6 | ||
|
|
9a3b70de43 | ||
|
|
742b8f7eae | ||
|
|
abc58941e3 | ||
|
|
aac200bfd3 | ||
|
|
27d3297d61 | ||
|
|
72431b2c82 | ||
|
|
3ca48e5848 | ||
|
|
14784ec80f | ||
|
|
127741eb27 | ||
|
|
2ed987fe10 | ||
|
|
7aaa1876c8 | ||
|
|
390ffb83a2 | ||
|
|
d63e51b8cb | ||
|
|
7bd8d64649 | ||
|
|
f9e6f49ede | ||
|
|
b701d2863f | ||
|
|
cf47093227 | ||
|
|
81d0aea66c | ||
|
|
506ca35be8 | ||
|
|
1cd6718489 | ||
|
|
153c5db4b5 | ||
|
|
ac9367a715 | ||
|
|
081dae0b90 | ||
|
|
e286390aec | ||
|
|
cf88eb2124 | ||
|
|
3226e4fd85 | ||
|
|
cab94eb63d | ||
|
|
bbd5d6805e | ||
|
|
2904ba3085 | ||
|
|
ee609061ed | ||
|
|
6c98a4e899 | ||
|
|
eef9ee7f3c | ||
|
|
2cb4575db1 | ||
|
|
e7bcad80cc | ||
|
|
353839a1dc | ||
|
|
e6d30744ba | ||
|
|
b67932f448 | ||
|
|
c185c42b80 | ||
|
|
4648ace84e | ||
|
|
ccdf50dbe0 | ||
|
|
852a3dc8bb | ||
|
|
d49b7bd201 | ||
|
|
07f2ceed6e | ||
|
|
751c7b5787 | ||
|
|
e82384dff8 | ||
|
|
92d3a85147 | ||
|
|
c5762a0baf | ||
|
|
dc40b04a18 | ||
|
|
1cca5a3085 | ||
|
|
4fe4ca638c | ||
|
|
b8c7110c63 | ||
|
|
288365b74b | ||
|
|
b57e17c01c | ||
|
|
711ab6a556 | ||
|
|
7a12f54587 | ||
|
|
056f4b3e80 | ||
|
|
730a7a4e15 | ||
|
|
a4b4dc885c | ||
|
|
8562a4bb46 | ||
|
|
d3b56432e0 | ||
|
|
527c5be42c | ||
|
|
6c1d0c712a | ||
|
|
fd0ec5fd31 | ||
|
|
f5f19c9b52 | ||
|
|
642ef4f5b7 | ||
|
|
66b65e600b | ||
|
|
01a130ef8c | ||
|
|
c3bccb5412 | ||
|
|
b32e6e71f8 | ||
|
|
3b5875e283 | ||
|
|
da42fe97ae | ||
|
|
56bb7d36eb | ||
|
|
d86b7345d7 | ||
|
|
c1c17160ff | ||
|
|
d400c843e4 | ||
|
|
4e67ab50fe | ||
|
|
74c9e21d55 | ||
|
|
fefcdf6ffc | ||
|
|
0c14d71fa8 | ||
|
|
5170e4eee2 | ||
|
|
8ebfd781c3 | ||
|
|
4e8988bf57 | ||
|
|
c26734751f | ||
|
|
8c209f5a9e | ||
|
|
995a2a5cef | ||
|
|
71fd7b20a6 | ||
|
|
d8e73eef72 | ||
|
|
ca6b4e7d82 | ||
|
|
cb4a94d036 | ||
|
|
300ddb810f | ||
|
|
0b687310fd | ||
|
|
61209f4c6c | ||
|
|
3e741ac2f9 | ||
|
|
c4507dd06b | ||
|
|
f65b23ca26 | ||
|
|
773e8b8dbb | ||
|
|
91976d2355 | ||
|
|
92527e5847 | ||
|
|
77042224a1 | ||
|
|
0b64908eab | ||
|
|
fc3f134bd5 | ||
|
|
d6c2c66fdd | ||
|
|
db9b8d2ba1 | ||
|
|
4866447f33 | ||
|
|
d5853e6fc3 | ||
|
|
765c3c19fa | ||
|
|
2b345ff840 | ||
|
|
f13991b88b | ||
|
|
47ddac3529 | ||
|
|
e51feaa1fe | ||
|
|
d224a44e54 | ||
|
|
f5c0694e32 | ||
|
|
dec13e8b5f | ||
|
|
dd536dc7d0 | ||
|
|
b6ce00519f | ||
|
|
7495955420 | ||
|
|
d17951af30 | ||
|
|
42c6f03f28 | ||
|
|
a9d6bc2246 | ||
|
|
428bb3b7c3 | ||
|
|
ac0cdd28cf | ||
|
|
37250f8a11 | ||
|
|
0102480954 | ||
|
|
f3a1c74a45 | ||
|
|
2544651ff1 | ||
|
|
93297b972a | ||
|
|
7165ed82f5 | ||
|
|
17422427a8 | ||
|
|
9ef2bc9ee2 | ||
|
|
12ec1bad0f | ||
|
|
fd552cedec | ||
|
|
5784378073 | ||
|
|
e0448cd4fa | ||
|
|
9a4bd79d75 | ||
|
|
9ae0334249 | ||
|
|
eb355f562b | ||
|
|
92dc13185b | ||
|
|
65ec4e78ad | ||
|
|
0c4b8143b2 | ||
|
|
b47bf437a9 | ||
|
|
1cc4cf93cb | ||
|
|
944370134a | ||
|
|
c44149050c | ||
|
|
0576123a77 | ||
|
|
c1621f64e6 | ||
|
|
e8c06d0836 | ||
|
|
a6295a6e56 | ||
|
|
47ae52c881 | ||
|
|
f2f0b55596 | ||
|
|
0c415de598 | ||
|
|
411941d9f7 | ||
|
|
3c54f2287c | ||
|
|
dfa121fe57 | ||
|
|
52b17730ef | ||
|
|
346d69124c | ||
|
|
7f35074acd | ||
|
|
b9e9694d61 | ||
|
|
ee4a787b81 | ||
|
|
d801680846 | ||
|
|
d84ab89a9b | ||
|
|
26d38f4ca2 | ||
|
|
745e519eec | ||
|
|
45b3e739d7 | ||
|
|
5ce50db001 | ||
|
|
a8c7b60c1b | ||
|
|
16860bc7b8 | ||
|
|
57f20ae1c0 | ||
|
|
d3b0c614ff | ||
|
|
e28f929ffc | ||
|
|
9f8f9a0992 | ||
|
|
60857638ba | ||
|
|
8041151243 | ||
|
|
7faed512b8 | ||
|
|
5d99a4a9c8 | ||
|
|
25c50d5387 | ||
|
|
5719dcca79 | ||
|
|
db1e81a249 | ||
|
|
ecd3395453 | ||
|
|
4327b2e348 | ||
|
|
06e8140be4 | ||
|
|
14e9330651 | ||
|
|
636910a78e | ||
|
|
e4f9152de8 | ||
|
|
e0b9df9ba5 | ||
|
|
a20bd407a8 | ||
|
|
5630dd45a7 | ||
|
|
20220dd8c9 | ||
|
|
db4b8ec0ee | ||
|
|
2ee9ae16fb | ||
|
|
e50a55403d | ||
|
|
23ae72738d | ||
|
|
8619021f30 | ||
|
|
6c579178c0 | ||
|
|
01e19632a9 | ||
|
|
79ff8fd71d | ||
|
|
8700fd837d | ||
|
|
cdc8eb5bf6 | ||
|
|
95200e234f | ||
|
|
d94527e31a | ||
|
|
903066e6fa | ||
|
|
8c8015fe4a | ||
|
|
a002ac779c | ||
|
|
5c9bfbbdfe | ||
|
|
4a1e6d1318 | ||
|
|
90dc7c738a | ||
|
|
777028f3a7 | ||
|
|
c5a0ff4021 | ||
|
|
bf9f42e94d | ||
|
|
5556de2ef5 | ||
|
|
1b3508a9ef | ||
|
|
3992a9be94 | ||
|
|
eaf875be06 | ||
|
|
e62897e729 | ||
|
|
4c58fe2040 | ||
|
|
298068207e | ||
|
|
d0abe615cb | ||
|
|
87c65e4bea | ||
|
|
be49cb9d6d | ||
|
|
2b73087b7e | ||
|
|
d11dff2987 | ||
|
|
8ac82b1214 | ||
|
|
c46fa19bbb | ||
|
|
2fa2f56bac | ||
|
|
ef50aaa417 | ||
|
|
a1d7275fcd | ||
|
|
080acc9ed6 | ||
|
|
30b888f3a0 | ||
|
|
df11a77971 | ||
|
|
03e660527d | ||
|
|
f7bd439df5 | ||
|
|
2a55c03788 | ||
|
|
f712532793 | ||
|
|
cbdeaf8cc9 | ||
|
|
6bb353fd1f | ||
|
|
523dd43b4d | ||
|
|
bd0216fc39 | ||
|
|
50c40bfec1 | ||
|
|
0e2b6ee180 | ||
|
|
359d86e17b | ||
|
|
0b19c87a50 | ||
|
|
4a35eab7c6 | ||
|
|
b178a3c91e | ||
|
|
9fb0fecba4 | ||
|
|
cfc7408a52 | ||
|
|
aed0b8a3db | ||
|
|
910bd801ac | ||
|
|
22dad33b2e | ||
|
|
4d3ed0232e | ||
|
|
6d83f05b13 | ||
|
|
66cc2e1f2f | ||
|
|
f773fc6e3c | ||
|
|
dc83f21f5e | ||
|
|
0186c48d59 | ||
|
|
43f20508ae | ||
|
|
f678dd02cb | ||
|
|
5c2d6cddcb | ||
|
|
1bbf64fe83 | ||
|
|
296f43101e | ||
|
|
ed6f44fd62 | ||
|
|
776b1a7b43 | ||
|
|
06cdb660b3 | ||
|
|
db9268e1af | ||
|
|
83ce3a1c21 | ||
|
|
bda31de345 | ||
|
|
cdfc952a3f | ||
|
|
9434315775 | ||
|
|
47bb4cac38 | ||
|
|
11d3e6c696 | ||
|
|
0173727bc5 | ||
|
|
3cfa2639e5 | ||
|
|
502400ee56 | ||
|
|
4a3276f4e3 | ||
|
|
750e48ca2e | ||
|
|
823e6a9e2f | ||
|
|
642ef334f6 | ||
|
|
939a67b2c5 | ||
|
|
e1f8481aa2 | ||
|
|
b8e8cd127a | ||
|
|
876a2d78d5 | ||
|
|
16f287fdc0 | ||
|
|
d396a4f71d | ||
|
|
6bee566d8d | ||
|
|
79acd8ae13 | ||
|
|
01666b49cf | ||
|
|
a20ebbf08f | ||
|
|
26cf9c5310 | ||
|
|
3ad58ee52b | ||
|
|
2b67af6097 | ||
|
|
f61dae35ff | ||
|
|
dead3088d6 | ||
|
|
45d65cfbe2 | ||
|
|
6d2010facb | ||
|
|
48d3166049 | ||
|
|
82992d324d | ||
|
|
9a549fa35d | ||
|
|
93a891c8b6 | ||
|
|
2ede742b10 | ||
|
|
c43690faa8 | ||
|
|
a482a92d34 | ||
|
|
1d429cbe36 | ||
|
|
0a36550907 | ||
|
|
09610112fc | ||
|
|
a14454227c | ||
|
|
0d8767ee98 | ||
|
|
f463dc7af7 | ||
|
|
407b01565a | ||
|
|
6c565a144a | ||
|
|
141351de49 | ||
|
|
edecf3ea23 | ||
|
|
e708dad596 | ||
|
|
48012c9343 | ||
|
|
009a24e943 | ||
|
|
04a3251968 | ||
|
|
82694a24f2 | ||
|
|
748cc042dd | ||
|
|
6b8bca0802 | ||
|
|
fed1cff0cb | ||
|
|
b2eba6409f | ||
|
|
81af6a6c61 | ||
|
|
c326849b8d | ||
|
|
a92188d8e2 | ||
|
|
b5dbc69188 | ||
|
|
4055574785 | ||
|
|
9b295f784a | ||
|
|
bfae7f236c | ||
|
|
fe907116e2 | ||
|
|
ade39c2f46 | ||
|
|
31baefcce3 | ||
|
|
59eb4eb9c7 | ||
|
|
3b93f96479 | ||
|
|
dbff436bc2 | ||
|
|
c3cf9c891e | ||
|
|
4e7c8c63bb | ||
|
|
db4494c463 | ||
|
|
d208a90107 | ||
|
|
450a971ca9 | ||
|
|
5ba1f79f3b | ||
|
|
f81f72a147 | ||
|
|
d255d5bfb2 | ||
|
|
f5e835cbc4 | ||
|
|
b40eb7119d | ||
|
|
f56faf3afd | ||
|
|
5fa60d8135 | ||
|
|
695345d911 | ||
|
|
3f60509254 | ||
|
|
6bd9ac0b8f | ||
|
|
2022df000e | ||
|
|
ec6e037422 | ||
|
|
4b11fe77e0 | ||
|
|
d42e11c5c1 | ||
|
|
fe37f3e021 | ||
|
|
95558d1407 | ||
|
|
73978ebce4 | ||
|
|
96ae95a97c | ||
|
|
356d2e81c5 |
@@ -178,6 +178,60 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"a11y"
|
"a11y"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Govinda04",
|
||||||
|
"name": "Govinda Kocharekar",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/50038172?v=4",
|
||||||
|
"profile": "https://github.com/Govinda04",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "crediblebilal",
|
||||||
|
"name": "Bilal Ahmad Bhat",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/55330484?v=4",
|
||||||
|
"profile": "https://bilal.cc",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "VikramNagwal",
|
||||||
|
"name": "Vikram",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/123088024?v=4",
|
||||||
|
"profile": "https://github.com/VikramNagwal",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ugoconsonni",
|
||||||
|
"name": "ugoconsonni",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/13661702?v=4",
|
||||||
|
"profile": "https://github.com/ugoconsonni",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "daniel-mutwiri",
|
||||||
|
"name": "Daniel Mutwiri",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
|
||||||
|
"profile": "https://github.com/daniel-mutwiri",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Zathiel",
|
||||||
|
"name": "Zathiel",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
|
||||||
|
"profile": "https://github.com/Zathiel",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
# Frontend config ************************************************************************************************************************************************************************************************
|
# Frontend config ************************************************************************************************************************************************************************************************
|
||||||
# Set CI to false while running the app locally
|
|
||||||
CI=false
|
|
||||||
# Set it to the URL form where the app home page will be accessed
|
# Set it to the URL form where the app home page will be accessed
|
||||||
PUBLIC_URL=https://app.opensignlabs.com/
|
PUBLIC_URL=https://app.opensignlabs.com/
|
||||||
# Set it to true if you want to generate the Sourcemap for debugging
|
# Set it to true if you want to generate the Sourcemap for debugging
|
||||||
@@ -8,13 +7,13 @@ GENERATE_SOURCEMAP=false
|
|||||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||||
REACT_APP_SERVERURL=https://app.opensignlabs.com/api/app
|
REACT_APP_SERVERURL=https://app.opensignlabs.com/api/app
|
||||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||||
REACT_APP_APPID=aDsADsqweqwe
|
REACT_APP_APPID=opensign
|
||||||
# Google tag manager container id for configuring GTM on all pages of the app
|
# Google tag manager container id for configuring GTM on all pages of the app
|
||||||
REACT_APP_GTM=GTM-123N7LD
|
REACT_APP_GTM=GTM-123N7LD
|
||||||
|
|
||||||
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
||||||
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
||||||
APP_ID=aDsADsqweqwe
|
APP_ID=opensign
|
||||||
# Name of the app. It will be visible in the verification emails sent out.
|
# Name of the app. It will be visible in the verification emails sent out.
|
||||||
appName=open_sign_server
|
appName=open_sign_server
|
||||||
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
||||||
@@ -39,11 +38,18 @@ DO_ACCESS_KEY_ID=
|
|||||||
DO_SECRET_ACCESS_KEY=
|
DO_SECRET_ACCESS_KEY=
|
||||||
# Digital ocean spaces region or AWS s3 region
|
# Digital ocean spaces region or AWS s3 region
|
||||||
DO_REGION=us-west
|
DO_REGION=us-west
|
||||||
|
# Set this to true id you are using local storage instead of s3 compatible storage
|
||||||
|
# USE_LOCAL=TRUE
|
||||||
|
|
||||||
# Email mailgun config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||||
MAILGUN_API_KEY=
|
MAILGUN_API_KEY=XXXXX
|
||||||
MAILGUN_DOMAIN=mail.yourdomain.com
|
MAILGUN_DOMAIN=mail.yourdomain.com
|
||||||
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
||||||
|
SMTP_ENABLE=
|
||||||
|
SMTP_HOST=
|
||||||
|
SMTP_PORT=
|
||||||
|
SMTP_USER_EMAIL=
|
||||||
|
SMTP_PASS=
|
||||||
|
|
||||||
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
||||||
PFX_BASE64=
|
PFX_BASE64=
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
# When used as it is, this env config will connect to our hosted UAT backend
|
# When used as it is, this env config will connect to our hosted UAT backend
|
||||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||||
|
|
||||||
# Set CI to false while running the app locally
|
|
||||||
CI=false
|
|
||||||
# Set it to the URL form where the app home page will be accessed
|
# Set it to the URL form where the app home page will be accessed
|
||||||
PUBLIC_URL=http://localhost:3000
|
PUBLIC_URL=http://localhost:3000
|
||||||
# Set it to true if you want to generate the Sourcemap for debugging
|
# Set it to true if you want to generate the Sourcemap for debugging
|
||||||
@@ -11,4 +9,4 @@ GENERATE_SOURCEMAP=false
|
|||||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||||
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
|
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
|
||||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||||
REACT_APP_APPID=opensignstgn
|
REACT_APP_APPID=opensign
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
# When used as it is, this env config will connect to our hosted UAT backend
|
# When used as it is, this env config will connect to our hosted UAT backend
|
||||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||||
|
|
||||||
# Set CI to false while running the app locally
|
|
||||||
CI=false
|
|
||||||
# Set it to the URL form where the app home page will be accessed
|
# Set it to the URL form where the app home page will be accessed
|
||||||
PUBLIC_URL=http://localhost:3000
|
PUBLIC_URL=http://localhost:3000
|
||||||
# Set it to true if you want to generate the Sourcemap for debugging
|
# Set it to true if you want to generate the Sourcemap for debugging
|
||||||
@@ -11,18 +9,18 @@ GENERATE_SOURCEMAP=false
|
|||||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||||
REACT_APP_SERVERURL=http://localhost:8080/app
|
REACT_APP_SERVERURL=http://localhost:8080/app
|
||||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||||
REACT_APP_APPID=opensignstgn
|
REACT_APP_APPID=opensign
|
||||||
|
|
||||||
|
|
||||||
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
||||||
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
||||||
APP_ID=opensignstgn
|
APP_ID=opensign
|
||||||
# Name of the app. It will be visible in the verification emails sent out.
|
# Name of the app. It will be visible in the verification emails sent out.
|
||||||
appName=open_sign_server
|
appName=open_sign_server
|
||||||
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
||||||
MASTER_KEY=XnAadwKxxByMr
|
MASTER_KEY=XnAadwKxxByMr
|
||||||
# Mongodb URI to connect to
|
# Mongodb URI to connect to
|
||||||
MONGODB_URI=mongodb://host.docker.internal:27017/OpenSignDB
|
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
|
||||||
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
|
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
|
||||||
PARSE_MOUNT=/app
|
PARSE_MOUNT=/app
|
||||||
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
|
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||||
@@ -42,15 +40,74 @@ DO_SECRET_ACCESS_KEY=
|
|||||||
# Digital ocean spaces region or AWS s3 region
|
# Digital ocean spaces region or AWS s3 region
|
||||||
DO_REGION=us-west
|
DO_REGION=us-west
|
||||||
# local storage
|
# local storage
|
||||||
USE_LOCAL=TRUE
|
USE_LOCAL=true
|
||||||
|
|
||||||
# Email mailgun config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||||
MAILGUN_API_KEY=XXXXX
|
MAILGUN_API_KEY=
|
||||||
MAILGUN_DOMAIN=mail.yourdomain.com
|
MAILGUN_DOMAIN=mail.yourdomain.com
|
||||||
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
||||||
|
SMTP_ENABLE=
|
||||||
|
SMTP_HOST=smtp.yourhost.com
|
||||||
|
SMTP_PORT=443
|
||||||
|
SMTP_USER_EMAIL=mailer@yourdomain.com
|
||||||
|
SMTP_PASS=password # if your password includes spaces then write password in single quotes ('asdf pasd asdf bgds').
|
||||||
|
|
||||||
|
|
||||||
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
||||||
PFX_BASE64=
|
PFX_BASE64='MIIKCQIBAzCCCc8GCSqGSIb3DQEHAaCCCcAEggm8MIIJuDCCBG8GCSqGSIb3DQEH
|
||||||
|
BqCCBGAwggRcAgEAMIIEVQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIpumu
|
||||||
|
bFabBWACAggAgIIEKDqHS7Icd5MzIBk1dBokGK2s+5a2fA4308WA1QzEWmczqVYI
|
||||||
|
z6lYmW8qsiZIw4PFkRzdIx1+zCmP8jgWiiqh5RKnbHYmh1JPNyx9SqmUDILDXmjg
|
||||||
|
KxO9agRw9LXge4hgRL7YW0TxYZYw4EeXV9Yr7kcOq9DoO2MoRikJ+2Tuvvq/hfuV
|
||||||
|
9zFHsz4jpf7W95k1IeH6bZ92klz5R1e/EK8JuHy592i/u+BxxW1vTkiPpTaHD6s9
|
||||||
|
1lDSkT1j0LIDU6l9gVvJvFBV8j88vet/Z4QkTbHPMYvBnKNRstKuOvDasEJE4RkA
|
||||||
|
PV4TfBwFF6uvREZI1vBboM/18pzeyjoRX+mlJNfcH3kh1tkck+Jg+M2bLXAH1EdD
|
||||||
|
GRwENvnTNW1CFIn37VVja7SFBQNXA+E6rejmqtUmZ5XktSINDBUyjow9XuYTUtI/
|
||||||
|
sgcRSC0aB0EKKpphbFPp1niKCLm1ef33e+bWyWbtR2L2Kc3ETmhjNADpyapAGmzc
|
||||||
|
LP2BafGaKj18KiLAG4GL6+kXk0GUz7Fw5q8H8RY1T33o+xjWvC0+aJgvr65qWdKw
|
||||||
|
+ug6wuu4Cr2kFEuXxPKsnYqmE5NT/x+lfunlZ5iEQN3w2XO5J2u2eMXVRUsPH2T6
|
||||||
|
0sJKomjY0RB1JsqZBnyleWm+tYo0RpVAyJ3NGBp9dVb7a3Qw6jNZBG/xMW+gvtUJ
|
||||||
|
q6hfK/cD3cagD0NQFVD5STMOuGOSqq/bbwAKjZ/lrPmCCYATKwkjAFybYfHJVrrW
|
||||||
|
Y2tQgCQ/zrZLIxQjp7F17hU0+gT54x0MpF6AoqIM/qqNnHrQ0fi50ECmLvx3SqYa
|
||||||
|
nvjvt/HZhMZfqRXiXjeicIwpnNoG7+uQknySk+7cc9GNhHZR7cfPfkTpgvuuebrQ
|
||||||
|
l5YW+k9tG+xrX8g72nvtFMnammlBixlFwECJaEszEQk5tc2ti8uG2z+kGnFoDoBV
|
||||||
|
M6ZVMpKDJzXPxLdzT6ChIPyatjIkqOKe6vDpnmzcoEhbBhmtkVjWFA2dfX846ugV
|
||||||
|
y1QiecWgk8pMNWhh3IDd05v/wzbFwNcq+Gi+1dZsPmFH+egIcrQKZu2r3jCJtGHh
|
||||||
|
DNG/nyf/anqvKXF90bi4hRT3vdDXXuWgY7EUwfn9h5jYabCO6HsQxQOBjFYNmBrh
|
||||||
|
MfaiBZe72E8fzNAfIlwDcJ4AWY1SOM7hi8bqfRWw1NtNxuMUiRFoR31O6XkQauye
|
||||||
|
9hAmpdlZHZXtJ6LM0QaWkPJmG3058GMtIn/qS60V9nINh+zXhwza/1pnFB+e9MWc
|
||||||
|
hAPgo0nn55FusM//g1nM31PKe+nOm3Jyp0nEQ4m8aDtJS63o2fvp3e2KJbPWQMHE
|
||||||
|
a83W975diWg4NTdevV9lFpNllHclVoPoavUHIzHd7HF9TcPpjbXJSUZ7cITHHefr
|
||||||
|
kQTa/2G9cWuRhN9GKIHBdr13nfEvMIIFQQYJKoZIhvcNAQcBoIIFMgSCBS4wggUq
|
||||||
|
MIIFJgYLKoZIhvcNAQwKAQKgggTuMIIE6jAcBgoqhkiG9w0BDAEDMA4ECDgnjfOf
|
||||||
|
AjArAgIIAASCBMgkbVuptroBdyMY5gl/eBQj5+iAV02YSaF969ihStLSWI4x5FAf
|
||||||
|
HuDgqCyEMSl4RHG8ZvNaO9JoNfIpMK/TPQBoQRCzE2Vjf66VTERaQtU6h7tZrbQt
|
||||||
|
K96n9eqvohm/vXbXe08fsodYp2s79kAvKgpuGG5iX77qYVzJHBPed87cvczo4ToP
|
||||||
|
CpurDpByt8fGEmjtcikal8o+H9uoHjcFwqMigX1Q7IEjuKXrb5e6wTvxMRob8yfv
|
||||||
|
LVl6ahteKSkWCMb6rLZ5f12jDoCGX/YXRrSsU9t/lXe0Nxv5i7c8flb2EsNtwygD
|
||||||
|
fexVvn5u6ble6RDYNpuQkPgF41HyZh+JvGAF6i1r4tJPL9Pf4HnjEB8Y8IHgon+w
|
||||||
|
T5I+8LTgcrWyhptzblXMjKySTEp9OEa2cCVwZkl4z43PWJ5oIAQ2IDTOQ603/5o/
|
||||||
|
d9KA76/zMPQ5O0gv17OA7kwFv98v6sTcR4vgkQZqnyDpJSEPHyNxhw2MSBqS3jq3
|
||||||
|
YPhRz+3Ei1oFp9uYFhzn39f9gzNi++X9pOjSrDb8v3mcDXZKNrxQLuoRMSf0eQNI
|
||||||
|
dgJRqj/fuqNZ5ac3kd4P5BALCsnA1tM27zYrgUdpOnrc2D9FBthEjpRlQKMMzhQf
|
||||||
|
cNMFWmoNjakDilvvLcsQeQ3P6cXbN1ODoNnjgK3VzcmeKyzW2PrTXEPHARUM3j4V
|
||||||
|
sZzGaUOVKLqaaDrXWQKC0vrTvxP2WMfjQ1dTdXQP/kSZTXRC5rtB9tky38flBor0
|
||||||
|
N9rhUDIGMs5qBvyrkV2hKXiW39G69p1KWY+Cw28AzY+CNSjePRi60TsnUxgcOS7i
|
||||||
|
0AT25Gx3A4YqxmTY38MPB4wPcBPcw4hjNoQamaC9mqs+KVHcP5YHUlqykMeH0YVi
|
||||||
|
J+ehytJE9xuDcfBOjxjadxp3/q/Lku4rky89gdJGFQWuuz75VulDywPRZRwmQTel
|
||||||
|
sjEGMHR1zTOlPJdTDWhqaKmWvL4GhbCgEPIKwmqd1QKRTl/3Baa1nYllJeYVwiTT
|
||||||
|
qLCkbA6qXrLGj1ENZxrKn9yqr8HwOoApjwhARu4LF/BRMgdkzelK2kOXHnZ26sZ2
|
||||||
|
NY/MMtVmq4c59y6iQwmsbHr9tWGz3ahyXcZZufjNXU/AVnO1c92g7umAWZF4RohL
|
||||||
|
gMmh57cAhN9PEbEv6j/6BT3XXp8jB0ywcRriYxfxwidviCKH/76Q4sytYJQaDFI1
|
||||||
|
fnzmv0aDotbG2BdpWSiYPDOPm+3cmfjlULsn1FYWL4NDCX1f+C4lNtKVX9LU8c5M
|
||||||
|
m8dA1DUlDfnuNO5/BShuoGXG2z4O/XwxIaRFizMxWS2sWurbAEFJ6oNw3kC7WSah
|
||||||
|
NIe8aUC1umpc1Gk3X2f4Ytzj9OEqn0y55qoqLINJuejXMffF/gjopxHWadLVaVaj
|
||||||
|
Q1SewrECuEXdSbBR/a10po24wmRtkmlvRJXJl7sG95xE5ZCqp+m6sRPWGdf1yKbV
|
||||||
|
vyWNUe4Qvkxe4a0VAJpTyIGr980CKz/jkvtNQMobGl7AfhxKJ450wg454WcexMct
|
||||||
|
BYXvZMSdUDCgMLlh0nHJHl5btrFWqE0Z/fqWmIknZL2jZ4J+2hdVl/xB/sUt6kcu
|
||||||
|
txw+RfL+vNE8PxhTNaOdJFwD8yeN1mJ3yMUA8HHYpw9yljgxJTAjBgkqhkiG9w0B
|
||||||
|
CRUxFgQUDYlgGVxSxuOknhQc256x3++7BDwwMTAhMAkGBSsOAwIaBQAEFFjASdYl
|
||||||
|
3pXAXxZuvVvv9tsb4bdrBAhyb+KCIjp/gAICCAA='
|
||||||
|
|
||||||
# Provide Pass pharse of above PFX or p12 document
|
# Provide Pass pharse of above PFX or p12 document
|
||||||
PASS_PHRASE=
|
PASS_PHRASE=opensign
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
name: "Bug Report"
|
||||||
|
labels: ["bug","up-for-grabs"]
|
||||||
|
title: "[Bug]: "
|
||||||
|
assignees:
|
||||||
|
- andrew-opensignlabs
|
||||||
|
description: Create a bug report to help us make OpenSign™ the world's best document e-signature platform
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Issue Description
|
||||||
|
description: Please provide a clear & concise description of the problem.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Expected Behavior
|
||||||
|
description: Describe what you expected to happen.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Current Behavior
|
||||||
|
description: Describe what is happening instead.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Please provide step-by-step instructions to reproduce the issue. Include code snippets, URLs, error messages and any other relevant information.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Screenshots of the issue(optional)
|
||||||
|
description: Add screenshots to better explain the issue.
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: Operating System [e.g. MacOS Sonoma 14.1, Windows 11]
|
||||||
|
- type: dropdown
|
||||||
|
id: browsers
|
||||||
|
attributes:
|
||||||
|
label: What browsers are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Chrome
|
||||||
|
- Firefox
|
||||||
|
- Safari
|
||||||
|
- Microsoft Edge
|
||||||
|
default: 0
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: What version of OpenSign™ are you seeing this issue on? [e.g. 1.0.6]
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: What environment are you seeing the problem on?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- Production (app.opensignlabs.com)
|
||||||
|
- Staging (staging-app.opensignlabs.com)
|
||||||
|
- Hosted (app.yourdomain.com)
|
||||||
|
- Dev (localhost or vercel)
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Please check the boxes that apply to this issue report.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
||||||
|
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||||
|
required: true
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: Suggest an idea for OpenSign™
|
||||||
|
title: "Feature Request: [Short Description]"
|
||||||
|
labels: [feature request, needs triage]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
## Thanks for taking the time to fill out this feature request!
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: title
|
||||||
|
attributes:
|
||||||
|
label: Feature Title
|
||||||
|
description: What would you call this feature?
|
||||||
|
placeholder: e.g., Advanced Signature Verification
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: feature-description
|
||||||
|
attributes:
|
||||||
|
label: Feature Description
|
||||||
|
description: |
|
||||||
|
Please provide a detailed description of the feature you're proposing.
|
||||||
|
placeholder: |
|
||||||
|
e.g., I'm proposing an advanced signature verification feature that...
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: featuretype
|
||||||
|
attributes:
|
||||||
|
label: What type of feature are you requesting?
|
||||||
|
multiple: true
|
||||||
|
options:
|
||||||
|
- UI/UX Improvement
|
||||||
|
- Security / Compliance
|
||||||
|
- Performance / Optimization
|
||||||
|
- 3rd party integration
|
||||||
|
- Other
|
||||||
|
- type: dropdown
|
||||||
|
id: importance
|
||||||
|
attributes:
|
||||||
|
label: Importance
|
||||||
|
description: How important is this feature to you?
|
||||||
|
options:
|
||||||
|
- Critical
|
||||||
|
- High
|
||||||
|
- Medium
|
||||||
|
- Low
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: Any other context or screenshots about the feature request here.
|
||||||
|
placeholder: Additional details, examples, or screenshots...
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: terms
|
||||||
|
attributes:
|
||||||
|
label: Code of Conduct
|
||||||
|
description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com)
|
||||||
|
options:
|
||||||
|
- label: I agree to follow this project's Code of Conduct
|
||||||
|
required: true
|
||||||
|
- label: I have searched the existing issues & discussions to make sure that this is not a duplicate.
|
||||||
|
required: true
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# maintain dependencies for frontend
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "/apps/OpenSign" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
# maintain dependencies for server
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "/apps/OpenSignServer" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
# maintain dependencies for microfrontends
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "microfrontends/SignDocuments" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
@@ -11,9 +11,9 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- image: amolshejole/OpenSign
|
- image: amolshejole/OpenSign
|
||||||
dockerfile: apps/OpenSign/Dockerfile
|
dockerfile: apps/OpenSign/Dockerhubfile
|
||||||
- image: amolshejole/OpenSignServer
|
- image: amolshejole/OpenSignServer
|
||||||
dockerfile: apps/OpenSignServer/Dockerfile
|
dockerfile: apps/OpenSignServer/Dockerhubfile
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
@@ -31,8 +31,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
images: ${{ matrix.image }}
|
images: ${{ matrix.image }}
|
||||||
-
|
-
|
||||||
name: Debug - List files
|
name: Debug - List files opensign
|
||||||
run: ls -R apps/OpenSign/ # Adjust the path as needed
|
run: ls -R apps/OpenSign/ # Adjust the path as needed
|
||||||
|
-
|
||||||
|
name: Debug - List files opensignserver
|
||||||
|
run: ls -R apps/OpenSignServer/ # Adjust the path as needed
|
||||||
-
|
-
|
||||||
name: Build and push
|
name: Build and push
|
||||||
uses: docker/build-push-action@v4
|
uses: docker/build-push-action@v4
|
||||||
|
|||||||
@@ -22,3 +22,6 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
apps/OpenSign/public/mfbuild/*
|
||||||
|
microfrontends/SignDocuments/build/*
|
||||||
|
apps/OpenSignServer/files/files/*
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
npx eslint '**/*.{js,jsx}'
|
npm run lint-staged-changes
|
||||||
npx pretty-quick --staged '**/*.{js,jsx}'
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"*.js": "prettier --write"
|
||||||
|
}
|
||||||
@@ -1,92 +1,29 @@
|
|||||||
|
|
||||||
# INSTALLATON INSTRUCTIONS
|
# INSTALLATON INSTRUCTIONS HAS A NEW HOME
|
||||||
|
|
||||||
You can use our app as a cloud version from [OpenSignLabs](https://www.opensignlabs.com)
|
We've updated our installation instructions and moved them to our dedicated documentation portal to provide you with a better experience and updated guidance. Whether you're looking to self-host OpenSign™ or contribute to its development, you can find detailed instructions tailored to your needs at [docs.opensignlabs.com](https://docs.opensignlabs.com).
|
||||||
|
|
||||||
or follow below instructions to install it on your own infrastructure.
|
## Self Hosting
|
||||||
|
|
||||||
- Localhost(Frontend only)
|
If you're interested in self-hosting OpenSign™, our new documentation portal provides comprehensive, step-by-step instructions to help you set up OpenSign™ in your own environment. This section is designed for system administrators and those looking to deploy OpenSign™ within their organization or for personal use.
|
||||||
- Digital Ocean
|
|
||||||
- Localhost(Docker)
|
|
||||||
|
|
||||||
## Localhost(Frontend only)
|
- **Access Self Hosting Instructions:** [Self Hosting Guide](https://docs.opensignlabs.com/docs/category/docker)
|
||||||
|
|
||||||
This is the easiest way to run the frontend application for development or testing purpose without the hassle of installing backend & DB. All the features including document upload, signing, email notifications will work seamlessly from staging backend.
|
## Contributors
|
||||||
|
|
||||||
**Warning:** All data, including user accounts and documents, is stored in the staging backend. This data will be erased every time there is a merge to the main branch. As a result, you may need to recreate user accounts repeatedly. If you find that login attempts are failing, it is likely because the data has been cleared due to a recent merge.
|
For developers and contributors who are looking to build upon or contribute to OpenSign™, we've prepared a separate section that covers the setup, development environment configuration, and guidelines for contributing to the OpenSign™ project.
|
||||||
|
|
||||||
Below are the steps to follow -
|
- **Access Contributor Instructions:** [Contributors Guide](https://docs.opensignlabs.com/docs/contribute/INSTALLATION)
|
||||||
- [Clone the repository](https://help.github.com/articles/cloning-a-repository/) to your local machine using below command -
|
|
||||||
```
|
|
||||||
git clone https://github.com/OpenSignLabs/OpenSign.git
|
|
||||||
```
|
|
||||||
- Copy the .env.frontend_dev file to apps/OpenSign/.env using below command(on mac & linux). For windows use COPY command instead.
|
|
||||||
```
|
|
||||||
cp .env.frontend_dev apps/OpenSign/.env
|
|
||||||
```
|
|
||||||
- CD to /apps/OpenSign directory
|
|
||||||
- Install NPM packages using
|
|
||||||
```
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
- Run the project locally using
|
|
||||||
```
|
|
||||||
npm run start
|
|
||||||
```
|
|
||||||
You should be able to access the application from http://localhost:3000 after this.
|
|
||||||
Create an account by signing-up and start contributing.
|
|
||||||
|
|
||||||
|
## Additional Resources
|
||||||
|
|
||||||
## Digital Ocean
|
Should you need further assistance or have any questions, please feel free to reach out to us through our support channels:
|
||||||
|
|
||||||
OpenSign application consistes of 3 components -
|
- **Twitter:** [OpenSignHQ on Twitter](https://twitter.com/OpenSignHQ)
|
||||||
- ReactJS frontend
|
- **Facebook:** [OpenSign on Facebook](https://www.facebook.com/profile.php?id=61551030403669)
|
||||||
- NodeJS API
|
- **LinkedIn:** [OpenSign™ on LinkedIn](https://www.linkedin.com/company/opensign%E2%84%A2/)
|
||||||
- MongoDB database
|
- **Discord:** [Join our Discord Community](https://discord.com/invite/opensign)
|
||||||
|
- **YouTube:** [OpenSignHQ on YouTube](https://www.youtube.com/@opensignhq)
|
||||||
|
|
||||||
You can install all 3 components on digital ocean using the button below -
|
We are committed to providing you with the support you need to successfully install and use OpenSign™. Visit our website at [www.opensignlabs.com](https://www.opensignlabs.com) for more information about our project and its features.
|
||||||
|
|
||||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/OpenSignLabs/Deploy-OpenSign-to-Digital-Ocean/tree/main&refcode=30db1c901ab0)
|
|
||||||
|
|
||||||
## You will need to create an AWS S3 bucket or digital ocean space in order to store your uploaded documents
|
|
||||||
|
|
||||||
### AWS S3 -
|
|
||||||
- Step 1 : Create a S3 bucket
|
|
||||||
- Login to AWS console
|
|
||||||
- Navigate to S3 under services
|
|
||||||
- Hit "Create Bucket" button on upper right corner
|
|
||||||
- Remove the check from "block all public access" checkbox(we need this in order to provide access to not-logged in users after OTP verification)
|
|
||||||
- Set bucket versioning and tags as per your requirements
|
|
||||||
- Hit "Create bucket" button
|
|
||||||
- Step 2 : Create IAM user and provide access to AWS bucket
|
|
||||||
- Search for "IAM" on the search bar in AWS console
|
|
||||||
- On IAM dashboard, click the number of users(count) under IAM resources table
|
|
||||||
- Hit "create user" button on the upper right corner of the page
|
|
||||||
- Enter the user name & click next
|
|
||||||
- Click create policy, search for S3 and provide the Read, Write & list permissions
|
|
||||||
- Click next and click "Create user"
|
|
||||||
- Step 3 : Generate Credentials
|
|
||||||
- Go to IAM/Users in AWS console
|
|
||||||
- Hit the hyperlink for the user created in the previous step
|
|
||||||
- Click the "Security credentials" tab
|
|
||||||
- Scroll down to "Access keys" and hit "Create access key"
|
|
||||||
- In the next step select "Application running outside AWS"
|
|
||||||
- Add a description tag if needed & hit "Create access key"
|
|
||||||
- In the next step you will see "Access key" and "Secret Access key". Copy both the values.
|
|
||||||
- Set the value of "Access key" to "DO_ACCESS_KEY_ID" environment variable
|
|
||||||
- Set the value of "Secret Access key" to "DO_SECRET_ACCESS_KEY" environment variable
|
|
||||||
- Step 3 : Copy bucket credentials
|
|
||||||
- Visit "Amazon S3 -> Buckets" in aws console
|
|
||||||
- Click the bucket created in previous steps & visit the properties tab
|
|
||||||
- Under "Bucket overview" you will find the value of AWS region(for ex. ap-south-1). Set that value to env variable "DO_REGION"
|
|
||||||
- You can create the value for "DO_ENDPOINT" env variable by appending the region value to amazonaws.com (for ex. s3.ap-south-1.amazonaws.com)
|
|
||||||
- You can create the value for "DO_BASEURL" by adding the bucketname in front of the endpoint value(for ex. https://bucketname.s3.ap-south-1.amazonaws.com)
|
|
||||||
|
|
||||||
Visit below link if you face any issues while following the above instructions -
|
|
||||||
- https://repost.aws/knowledge-center/create-access-key
|
|
||||||
|
|
||||||
|
|
||||||
## Localhost(Docker)
|
|
||||||
|
|
||||||
Below are the steps to follow -
|
|
||||||
- Execute `make build`
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
build:
|
build:
|
||||||
|
cp .env.local_dev .env
|
||||||
|
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
|
||||||
|
docker compose up --build --force-recreate
|
||||||
|
|
||||||
|
run:
|
||||||
cp .env.local_dev .env
|
cp .env.local_dev .env
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
<h1 align="center"><a href='https://www.opensignlabs.com'>OpenSign™</a></h1>
|
<h1 align="center"><a href='https://www.opensignlabs.com'><img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/e518cc9c-5de3-47da-950b-f93336b9f14e></a>
|
||||||
|
</h1><div align="center">
|
||||||
|
|
||||||
<div align="center">
|
[The free and open source alternative to DocuSign](https://www.opensignlabs.com)
|
||||||
|
|
||||||
The free and open source alternative to DocuSign
|
|
||||||
|
|
||||||
[](http://isitmaintained.com/project/opensignlabs/opensign "Average time to resolve an issue")
|
[](http://isitmaintained.com/project/opensignlabs/opensign "Average time to resolve an issue")
|
||||||
[](#contributors)
|
[](#contributors)
|
||||||
@@ -23,7 +22,7 @@ The free and open source alternative to DocuSign
|
|||||||
<a href="https://www.linkedin.com/company/opensign%E2%84%A2/about/">LinkedIn</a>
|
<a href="https://www.linkedin.com/company/opensign%E2%84%A2/about/">LinkedIn</a>
|
||||||
|
|
||||||
|
|
||||||
## An open-source document e-signing solution
|
## The open-source document e-signing solution
|
||||||
|
|
||||||
---
|
---
|
||||||
</div>
|
</div>
|
||||||
@@ -39,23 +38,28 @@ The free and open source alternative to DocuSign
|
|||||||
7. [Acknowledgments](#acknowledgments)
|
7. [Acknowledgments](#acknowledgments)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
Please star ⭐ the repo to support us! 😀
|
||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
Welcome to OpenSign, an open-source document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
Welcome to OpenSign, the premier open source docusign alternative - document e-signing solution designed to provide a secure, reliable and free alternative to commercial esign platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- **Secure Signing**: Utilizes state-of-the-art cryptographic algorithms to ensure the security & integrity of your documents.
|
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
|
||||||
- **User-Friendly Interface**: Designed with usability in mind, making it easy for both technical and non-technical users.
|
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that allows hand drawn signatures, uploaded images, typed signatures & saved signatures for the simplest signing experience ever.
|
||||||
- **Multi-Platform Support**: Compatible with various browsers and devices.
|
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "Templates", "One click signatures" and "OpenSign™ Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
|
||||||
- **Invite & collaborate users**: Bring multiple people from your team into the signing process, all within your own infrastructure.
|
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players in e-signature space.
|
||||||
- **Secure**: Allows for the easy, secure and seamless organization of your documents using 'OpenSigDrive'.
|
- **Email Unique Code(OTP) verification support for guest signers:** With OpenSign™, your documents are fully secure even when being signed by guest users. Guest signers can only sign the document after entering a unique code sent to their email address.
|
||||||
- **Audit Trails**: Keeps a detailed log of all activities related to the document signing process including IP addresses and access timings.
|
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign. Not just this, OpenSign™ also allows signers to reject signing a document.
|
||||||
- **Completion Certificate**: Generate secure completion certificate as soon as a document is signed by all participants.
|
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
|
||||||
- **API Support**: Provides a robust API for integration into other software and services.
|
- **PDF Template Creation:** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time & collect e-signatures seamlessly.
|
||||||
|
- **OpenSign™ Drive:** It is a centralised secure vault for your digital documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
|
||||||
|
- **Audit Trails & completion certificate:** Being a security focused solution, OpenSign™ makes it a top priority to save detailed logs for tracking document activities along with time-stamps, IP addresses, email IDs & phone numbers. A completion certificate is generated as soon as document is completed which contains all the document related logs for added safety.
|
||||||
|
- **API Support:** OpenSign™ API allows seamless integration into existing systems and software. You can generate an API key from the app and refer the [docs](https://docs.opensignlabs.com) to start integrating it in your existing applications.
|
||||||
|
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is available. We also have a Zapier integration that allows you to integrate it with virtually any application.
|
||||||
|
|
||||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'>
|
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'>
|
||||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'>
|
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'>
|
||||||
@@ -72,7 +76,7 @@ Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructi
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
|
For comprehensive guidelines on how to use OpenSign™, please consult our [User Manual](USAGE.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -125,6 +129,14 @@ We would like to thank all our contributors and users for their support and feed
|
|||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rishabjasrotia"><img src="https://avatars.githubusercontent.com/u/33950743?v=4?s=100" width="100px;" alt="Rishab"/><br /><sub><b>Rishab</b></sub></a><br /><a href="#code-rishabjasrotia" title="Code">💻</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://session.it"><img src="https://avatars.githubusercontent.com/u/327285?v=4?s=100" width="100px;" alt="Maurizio Pillitu"/><br /><sub><b>Maurizio Pillitu</b></sub></a><br /><a href="#bug-maoo" title="Bug reports">🐛</a></td>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://luisparra.dev"><img src="https://avatars.githubusercontent.com/u/16653744?v=4?s=100" width="100px;" alt="Luis Parra"/><br /><sub><b>Luis Parra</b></sub></a><br /><a href="#a11y-lsprr" title="Accessibility">️️️️♿️</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://luisparra.dev"><img src="https://avatars.githubusercontent.com/u/16653744?v=4?s=100" width="100px;" alt="Luis Parra"/><br /><sub><b>Luis Parra</b></sub></a><br /><a href="#a11y-lsprr" title="Accessibility">️️️️♿️</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Govinda04"><img src="https://avatars.githubusercontent.com/u/50038172?v=4?s=100" width="100px;" alt="Govinda Kocharekar"/><br /><sub><b>Govinda Kocharekar</b></sub></a><br /><a href="#code-Govinda04" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://bilal.cc"><img src="https://avatars.githubusercontent.com/u/55330484?v=4?s=100" width="100px;" alt="Bilal Ahmad Bhat"/><br /><sub><b>Bilal Ahmad Bhat</b></sub></a><br /><a href="#code-crediblebilal" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a><a href="#security-Zathiel" title="Security">🛡️</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
/mfbuild
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Use an official Node runtime as the base image
|
# Use an official Node runtime as the base image
|
||||||
FROM node:16
|
FROM node:18
|
||||||
|
|
||||||
# Set the working directory inside the container
|
# Set the working directory inside the container
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
@@ -21,5 +21,5 @@ EXPOSE 3000
|
|||||||
# ENV NODE_ENV production
|
# ENV NODE_ENV production
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
CMD ["npm", "start"]
|
ENTRYPOINT npm run start
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Use an official Node runtime as the base image
|
||||||
|
FROM node:18
|
||||||
|
|
||||||
|
# Set the working directory inside the container
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json first to leverage Docker cache
|
||||||
|
COPY apps/OpenSign/package*.json ./
|
||||||
|
|
||||||
|
# Install application dependencies
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy the current directory contents into the container
|
||||||
|
COPY apps/OpenSign/ .
|
||||||
|
COPY apps/OpenSign/.husky .
|
||||||
|
|
||||||
|
# build
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Make port 3000 available to the world outside this container
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Define environment variables if needed
|
||||||
|
# ENV NODE_ENV production
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
CMD ["npm", "start"]
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
const { dependencies } = require("./package.json");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
name: "shell_app", // change me
|
|
||||||
filename: "remoteEntry.js",
|
|
||||||
exposes: {},
|
|
||||||
remotes: {},
|
|
||||||
shared: {
|
|
||||||
...dependencies,
|
|
||||||
react: {
|
|
||||||
singleton: true,
|
|
||||||
import: "react", // fallback is also react
|
|
||||||
shareScope: "default",
|
|
||||||
requiredVersion: dependencies.react
|
|
||||||
},
|
|
||||||
"react-dom": {
|
|
||||||
singleton: true,
|
|
||||||
requiredVersion: dependencies["react-dom"]
|
|
||||||
},
|
|
||||||
"react-router-dom": {
|
|
||||||
singleton: true,
|
|
||||||
requiredVersion: dependencies["react-router-dom"]
|
|
||||||
},
|
|
||||||
"rjsf-conditionals": {
|
|
||||||
singleton: true,
|
|
||||||
requiredVersion: dependencies["rjsf-conditionals"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -3,47 +3,58 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^1.9.7",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@rjsf/core": "^5.13.0",
|
"@radix-ui/themes": "^3.0.5",
|
||||||
"@rjsf/utils": "^5.13.0",
|
"@react-pdf/renderer": "^3.4.4",
|
||||||
"@rjsf/validator-ajv8": "^5.13.0",
|
"@reduxjs/toolkit": "^2.2.5",
|
||||||
"axios": "^1.5.1",
|
"axios": "^1.7.2",
|
||||||
"daisyui": "^3.9.2",
|
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html-react-parser": "^4.2.2",
|
"jwt-decode": "^4.0.0",
|
||||||
"json-rules-engine-simplified": "^0.1.17",
|
"moment": "^2.30.1",
|
||||||
"jwt-decode": "^3.1.2",
|
"nth-check": "^2.1.1",
|
||||||
"mf-cra": "^0.0.5",
|
"parse": "^5.1.0",
|
||||||
"moment": "^2.29.4",
|
"pdf-lib": "^1.17.1",
|
||||||
"parse": "^4.2.0",
|
"print-js": "^1.6.0",
|
||||||
|
"radix-ui": "^1.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.9.0",
|
"react-bootstrap": "^2.10.2",
|
||||||
|
"react-confetti": "^6.1.0",
|
||||||
|
"react-cookie": "^7.1.4",
|
||||||
|
"react-datepicker": "^6.9.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dnd-multi-backend": "^8.0.3",
|
"react-dnd-multi-backend": "^8.0.3",
|
||||||
"react-dnd-touch-backend": "^16.0.1",
|
"react-dnd-touch-backend": "^16.0.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-drag-listview": "^2.0.0",
|
|
||||||
"react-gtm-module": "^2.0.11",
|
"react-gtm-module": "^2.0.11",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-modal": "^3.16.1",
|
"react-konva": "^18.2.10",
|
||||||
"react-redux": "^8.1.3",
|
"react-pdf": "^9.0.0",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-quill": "^2.0.0",
|
||||||
|
"react-redux": "^9.1.2",
|
||||||
|
"react-rnd": "^10.4.11",
|
||||||
|
"react-router-dom": "^6.22.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-select": "^5.7.7",
|
"react-scrollbars-custom": "^4.1.1",
|
||||||
"reactour": "^1.19.2",
|
"react-select": "^5.8.0",
|
||||||
"redux": "^4.2.1",
|
"react-signature-canvas": "^1.0.6",
|
||||||
"redux-thunk": "^2.4.2",
|
"react-tooltip": "^5.26.4",
|
||||||
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
|
"react-web-share": "^2.0.2",
|
||||||
"serve": "^14.2.1",
|
"reactour": "^1.19.4",
|
||||||
|
"redux": "^5.0.1",
|
||||||
|
"redux-thunk": "^3.1.0",
|
||||||
|
"regex-parser": "^2.3.0",
|
||||||
|
"serve": "^14.2.3",
|
||||||
"styled-components": "^4.4.1",
|
"styled-components": "^4.4.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^4.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "serve -s build",
|
"start": "serve -s build",
|
||||||
"start-dev": "mf-cra start",
|
"start-dev": "react-scripts start",
|
||||||
"build": "rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
|
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||||
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
|
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||||
|
"build": "npm run version && react-scripts build",
|
||||||
|
"build-win": "npm run version-win && react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
@@ -73,16 +84,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/runtime-corejs2": "^7.23.2",
|
"@babel/runtime-corejs2": "^7.24.7",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.19",
|
||||||
"commitizen": "^4.3.0",
|
"commitizen": "^4.3.0",
|
||||||
"eslint": "^8.51.0",
|
"daisyui": "^4.12.2",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"lint-staged": "^15.0.1",
|
"lint-staged": "^15.2.5",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.8",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.3.1",
|
||||||
"tailwindcss": "^3.3.3"
|
"tailwindcss": "^3.4.4"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"nth-check": "$nth-check"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en" data-theme="opensigncss">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<!-- <link rel="icon" type="image/png"
|
<!-- <link rel="icon" type="image/png"
|
||||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
|
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
|
||||||
sizes="40x40" /> -->
|
sizes="40x40" /> -->
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
||||||
|
|
||||||
<title>Open sign</title>
|
<title>OpenSign™</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,21 +1,45 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect, lazy } from "react";
|
||||||
import { Routes, Route, BrowserRouter } from "react-router-dom";
|
import { Routes, Route, BrowserRouter } from "react-router-dom";
|
||||||
import Pgsignup from "./routes/Pgsignup";
|
import { pdfjs } from "react-pdf";
|
||||||
import Login from "./routes/Login";
|
import Login from "./pages/Login";
|
||||||
import Microapp from "./routes/RemoteApp";
|
import Form from "./pages/Form";
|
||||||
import Signup from "./routes/Signup";
|
import Report from "./pages/Report";
|
||||||
import Form from "./routes/Form";
|
import Dashboard from "./pages/Dashboard";
|
||||||
import Report from "./routes/Report";
|
|
||||||
import Dashboard from "./routes/Dashboard";
|
|
||||||
import PlanSubscriptions from "./routes/PlanSubscriptions";
|
|
||||||
import HomeLayout from "./layout/HomeLayout";
|
import HomeLayout from "./layout/HomeLayout";
|
||||||
import UserProfile from "./routes/UserProfile";
|
import PageNotFound from "./pages/PageNotFound";
|
||||||
import PageNotFound from "./routes/PageNotFound";
|
import ValidateRoute from "./primitives/ValidateRoute";
|
||||||
import ForgetPassword from "./routes/ForgetPassword";
|
import Validate from "./primitives/Validate";
|
||||||
import ChangePassword from "./routes/ChangePassword";
|
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
|
||||||
import ReportMicroapp from "./components/ReportMicroapp";
|
import SignYourSelf from "./pages/SignyourselfPdf";
|
||||||
import LoadMf from "./routes/LoadMf";
|
import DraftDocument from "./components/pdf/DraftDocument";
|
||||||
|
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
||||||
|
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
||||||
|
import LazyPage from "./primitives/LazyPage";
|
||||||
|
import { isEnableSubscription } from "./constant/const";
|
||||||
|
import SSOVerify from "./pages/SSOVerify";
|
||||||
|
import Loader from "./primitives/Loader";
|
||||||
|
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||||
|
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||||
|
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||||
|
const Pgsignup = lazy(() => import("./pages/Pgsignup"));
|
||||||
|
const Subscriptions = lazy(() => import("./pages/PlanSubscriptions"));
|
||||||
|
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
||||||
|
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
||||||
|
const Signup = lazy(() => import("./pages/Signup"));
|
||||||
|
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
||||||
|
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||||
|
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||||
|
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||||
|
|
||||||
|
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||||
|
|
||||||
|
const AppLoader = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center items-center h-[100vh]">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
function App() {
|
function App() {
|
||||||
const [isloading, setIsLoading] = useState(true);
|
const [isloading, setIsLoading] = useState(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -23,12 +47,16 @@ function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCredentials = () => {
|
const handleCredentials = () => {
|
||||||
const appId = process.env.REACT_APP_APPID;
|
const appId = process.env.REACT_APP_APPID
|
||||||
const baseurl = process.env.REACT_APP_SERVERURL;
|
? process.env.REACT_APP_APPID
|
||||||
|
: "opensign";
|
||||||
|
const baseurl = process.env.REACT_APP_SERVERURL
|
||||||
|
? process.env.REACT_APP_SERVERURL
|
||||||
|
: window.location.origin + "/api/app";
|
||||||
const appName = "contracts";
|
const appName = "contracts";
|
||||||
try {
|
try {
|
||||||
localStorage.setItem("BaseUrl12", `${baseurl}/`);
|
localStorage.setItem("baseUrl", `${baseurl}/`);
|
||||||
localStorage.setItem("AppID12", appId);
|
localStorage.setItem("parseAppId", appId);
|
||||||
localStorage.setItem("domain", appName);
|
localStorage.setItem("domain", appName);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -37,109 +65,114 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#eef1f5]">
|
<div className="bg-base-200">
|
||||||
{isloading ? (
|
{isloading ? (
|
||||||
<div
|
<AppLoader />
|
||||||
style={{
|
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route exact path="/" element={<Login />} />
|
<Route element={<ValidateRoute />}>
|
||||||
<Route exact path="/signup" element={<Signup />} />
|
<Route exact path="/" element={<Login />} />
|
||||||
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
|
<Route path="/signup" element={<LazyPage Page={Signup} />} />
|
||||||
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
|
</Route>
|
||||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
<Route element={<Validate />}>
|
||||||
|
<Route
|
||||||
|
path="/load/template/:templateId"
|
||||||
|
element={<TemplatePlaceholder />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/load/placeholdersign/:docId"
|
||||||
|
element={<PlaceHolderSign />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path="/load/recipientSignPdf/:docId/:contactBookId"
|
||||||
|
element={<PdfRequestFiles />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
<Route
|
||||||
|
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||||
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||||
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/login/:base64url"
|
||||||
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
|
/>
|
||||||
|
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||||
|
<Route
|
||||||
|
path="/forgetpassword"
|
||||||
|
element={<LazyPage Page={ForgetPassword} />}
|
||||||
|
/>
|
||||||
|
{isEnableSubscription && (
|
||||||
<>
|
<>
|
||||||
<Route exact path="/pgsignup" element={<Pgsignup />} />
|
|
||||||
<Route
|
<Route
|
||||||
exact
|
path="/pgsignup"
|
||||||
|
element={<LazyPage Page={Pgsignup} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
path="/subscription"
|
path="/subscription"
|
||||||
element={<PlanSubscriptions />}
|
element={<LazyPage Page={Subscriptions} />}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Route
|
<Route element={<HomeLayout />}>
|
||||||
exact
|
<Route
|
||||||
path="/changepassword"
|
path="/changepassword"
|
||||||
element={
|
element={<LazyPage Page={ChangePassword} />}
|
||||||
<HomeLayout>
|
/>
|
||||||
<ChangePassword />
|
<Route path="/form/:id" element={<Form />} />
|
||||||
</HomeLayout>
|
<Route path="/report/:id" element={<Report />} />
|
||||||
}
|
<Route path="/dashboard/:id" element={<Dashboard />} />
|
||||||
/>
|
<Route
|
||||||
<Route
|
path="/profile"
|
||||||
path="/mf/:remoteApp/*"
|
element={<LazyPage Page={UserProfile} />}
|
||||||
element={
|
/>
|
||||||
<HomeLayout>
|
<Route
|
||||||
<Microapp />
|
path="/opensigndrive"
|
||||||
</HomeLayout>
|
element={<LazyPage Page={Opensigndrive} />}
|
||||||
}
|
/>
|
||||||
/>
|
<Route
|
||||||
<Route
|
path="/managesign"
|
||||||
path="/asmf/:remoteApp/*"
|
element={<LazyPage Page={ManageSign} />}
|
||||||
element={
|
/>
|
||||||
<HomeLayout>
|
<Route
|
||||||
<Microapp />
|
path="/generatetoken"
|
||||||
</HomeLayout>
|
element={<LazyPage Page={GenerateToken} />}
|
||||||
}
|
/>
|
||||||
/>
|
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
|
||||||
<Route
|
<Route
|
||||||
path="/rpmf/:remoteApp/*"
|
path="/template/:templateId"
|
||||||
element={
|
element={<TemplatePlaceholder />}
|
||||||
<HomeLayout>
|
/>
|
||||||
<ReportMicroapp />
|
{/* signyouself route with no rowlevel data using docId from url */}
|
||||||
</HomeLayout>
|
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
||||||
}
|
{/* draft document route to handle and navigate route page accordiing to document status */}
|
||||||
/>
|
<Route path="/draftDocument" element={<DraftDocument />} />
|
||||||
<Route
|
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
||||||
path="/form/:id"
|
<Route
|
||||||
element={
|
path="/placeHolderSign/:docId"
|
||||||
<HomeLayout>
|
element={<PlaceHolderSign />}
|
||||||
<Form />
|
/>
|
||||||
</HomeLayout>
|
{/* for user signature (need your sign route) with row level data */}
|
||||||
}
|
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
|
||||||
/>
|
{/* for user signature (need your sign route) with no row level data */}
|
||||||
<Route
|
<Route
|
||||||
path="/report/:id"
|
path="/pdfRequestFiles/:docId"
|
||||||
element={
|
element={<PdfRequestFiles />}
|
||||||
<HomeLayout>
|
/>
|
||||||
<Report />
|
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||||
</HomeLayout>
|
<Route
|
||||||
}
|
path="/recipientSignPdf/:docId/:contactBookId"
|
||||||
/>
|
element={<PdfRequestFiles />}
|
||||||
<Route
|
/>
|
||||||
path="/dashboard/:id"
|
</Route>
|
||||||
element={
|
<Route path="/sso" element={<SSOVerify />} />
|
||||||
<HomeLayout>
|
|
||||||
<Dashboard />
|
|
||||||
</HomeLayout>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Route
|
|
||||||
path="/profile"
|
|
||||||
element={
|
|
||||||
<HomeLayout>
|
|
||||||
<UserProfile />
|
|
||||||
</HomeLayout>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route path="*" element={<PageNotFound />} />
|
<Route path="*" element={<PageNotFound />} />
|
||||||
{/* <Route exact path="/ForgotPassword" element={<ForgotPassword />} /> */}
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
import "./index.css";
|
|
||||||
import App from "./App";
|
|
||||||
import { Provider } from "react-redux";
|
|
||||||
import { store } from "./redux/store";
|
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
|
||||||
import { TouchBackend } from "react-dnd-touch-backend";
|
|
||||||
import {
|
|
||||||
DndProvider,
|
|
||||||
TouchTransition,
|
|
||||||
MouseTransition,
|
|
||||||
Preview
|
|
||||||
} from "react-dnd-multi-backend";
|
|
||||||
import DragElement from "./microappComponent/DragElement";
|
|
||||||
import TagManager from "react-gtm-module";
|
|
||||||
|
|
||||||
const HTML5toTouch = {
|
|
||||||
backends: [
|
|
||||||
{
|
|
||||||
id: "html5",
|
|
||||||
backend: HTML5Backend,
|
|
||||||
transition: MouseTransition
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "touch",
|
|
||||||
backend: TouchBackend,
|
|
||||||
options: { enableMouseEvents: true },
|
|
||||||
preview: true,
|
|
||||||
transition: TouchTransition
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
const generatePreview = (props) => {
|
|
||||||
const { item, style } = props;
|
|
||||||
const newStyle = {
|
|
||||||
...style
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={newStyle}>
|
|
||||||
<DragElement {...item} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.env.REACT_APP_GTM) {
|
|
||||||
const tagManagerArgs = {
|
|
||||||
gtmId: process.env.REACT_APP_GTM
|
|
||||||
};
|
|
||||||
TagManager.initialize(tagManagerArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
|
||||||
root.render(
|
|
||||||
<Provider store={store}>
|
|
||||||
<DndProvider options={HTML5toTouch}>
|
|
||||||
<Preview>{generatePreview}</Preview>
|
|
||||||
<App />
|
|
||||||
</DndProvider>
|
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
|
After Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,310 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import axios from "axios";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
|
const AddSigner = (props) => {
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [phone, setPhone] = useState("");
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [addYourself, setAddYourself] = useState(false);
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [isUserExist, setIsUserExist] = useState(false);
|
||||||
|
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||||
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkUserExist();
|
||||||
|
}, []);
|
||||||
|
// Load user details from localStorage when the component mounts
|
||||||
|
useEffect(() => {
|
||||||
|
const savedUserDetails = JSON.parse(
|
||||||
|
localStorage.getItem("UserInformation")
|
||||||
|
);
|
||||||
|
if (savedUserDetails && addYourself) {
|
||||||
|
setName(savedUserDetails.name);
|
||||||
|
setPhone(savedUserDetails?.phone || "");
|
||||||
|
setEmail(savedUserDetails.email);
|
||||||
|
}
|
||||||
|
}, [addYourself]);
|
||||||
|
|
||||||
|
const checkUserExist = async () => {
|
||||||
|
const user = Parse.User.current();
|
||||||
|
try {
|
||||||
|
const query = new Parse.Query("contracts_Contactbook");
|
||||||
|
query.equalTo("CreatedBy", user);
|
||||||
|
query.notEqualTo("IsDeleted", true);
|
||||||
|
query.equalTo("Email", user.getEmail());
|
||||||
|
const res = await query.first();
|
||||||
|
// console.log(res);
|
||||||
|
if (!res) {
|
||||||
|
setIsUserExist(true);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Define a function to handle form submission
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsLoader(true);
|
||||||
|
if (localStorage.getItem("TenantId")) {
|
||||||
|
try {
|
||||||
|
const user = Parse.User.current();
|
||||||
|
const query = new Parse.Query("contracts_Contactbook");
|
||||||
|
query.equalTo("CreatedBy", user);
|
||||||
|
query.notEqualTo("IsDeleted", true);
|
||||||
|
query.equalTo("Email", email);
|
||||||
|
const res = await query.first();
|
||||||
|
// console.log(res);
|
||||||
|
if (!res) {
|
||||||
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
|
contactQuery.set("Name", name);
|
||||||
|
if (phone) {
|
||||||
|
contactQuery.set("Phone", phone);
|
||||||
|
}
|
||||||
|
contactQuery.set("Email", email);
|
||||||
|
contactQuery.set("UserRole", "contracts_Guest");
|
||||||
|
|
||||||
|
contactQuery.set("TenantId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "partners_Tenant",
|
||||||
|
objectId: localStorage.getItem("TenantId")
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const _users = Parse.Object.extend("User");
|
||||||
|
const _user = new _users();
|
||||||
|
_user.set("name", name);
|
||||||
|
_user.set("username", email);
|
||||||
|
_user.set("email", email);
|
||||||
|
_user.set("password", email);
|
||||||
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
const user = await _user.save();
|
||||||
|
if (user) {
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_Guest",
|
||||||
|
userId: user.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", user);
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoader(false);
|
||||||
|
// Reset the form fields
|
||||||
|
setAddYourself(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err ", err);
|
||||||
|
if (err.code === 202) {
|
||||||
|
const params = { email: email };
|
||||||
|
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_Guest",
|
||||||
|
userId: userRes.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "_User",
|
||||||
|
objectId: userRes.id
|
||||||
|
});
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
setIsLoader(false);
|
||||||
|
// Reset the form fields
|
||||||
|
setAddYourself(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Contact already exist!");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in fetch contact", err);
|
||||||
|
setIsLoader(false);
|
||||||
|
alert("something went wrong, please try again later");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsLoader(false);
|
||||||
|
alert("something went wrong, please try again later");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define a function to handle the "add yourself" checkbox
|
||||||
|
const handleAddYourselfChange = () => {
|
||||||
|
if (addYourself) {
|
||||||
|
setAddYourself(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
} else {
|
||||||
|
setAddYourself(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleReset = () => {
|
||||||
|
setAddYourself(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full p-[20px]">
|
||||||
|
{isLoader && (
|
||||||
|
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full mx-auto p-2 text-base-content">
|
||||||
|
{isUserExist && (
|
||||||
|
<div className="mb-3 flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="addYourself"
|
||||||
|
checked={addYourself}
|
||||||
|
onChange={handleAddYourselfChange}
|
||||||
|
className="op-checkbox op-checkbox-sm"
|
||||||
|
/>
|
||||||
|
<label htmlFor="addYourself" className="ml-2 mb-0">
|
||||||
|
Add Yourself
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="name" className="block text-xs font-semibold">
|
||||||
|
Name
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
required
|
||||||
|
disabled={addYourself}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="email" className="block text-xs font-semibold">
|
||||||
|
Email
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||||
|
required
|
||||||
|
disabled={addYourself}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="phone" className="block text-xs font-semibold">
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="phone"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
disabled={addYourself}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex gap-x-2 justify-start">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleReset()}
|
||||||
|
className="op-btn op-btn-secondary"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddSigner;
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import axios from "axios";
|
||||||
|
import Title from "./Title";
|
||||||
|
import Alert from "../primitives/Alert";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
|
const AddUser = (props) => {
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [phone, setPhone] = useState("");
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [isUserExist, setIsUserExist] = useState(false);
|
||||||
|
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||||
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
|
|
||||||
|
const checkUserExist = async () => {
|
||||||
|
try {
|
||||||
|
const res = await Parse.Cloud.run("getUserDetails", {
|
||||||
|
email: email,
|
||||||
|
userId: Parse.User.current().id
|
||||||
|
});
|
||||||
|
if (res) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Define a function to handle form submission
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsLoader(true);
|
||||||
|
const res = await checkUserExist();
|
||||||
|
if (res) {
|
||||||
|
setIsUserExist(true);
|
||||||
|
setIsLoader(false);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsUserExist(false);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const contactQuery = new Parse.Object("contracts_Users");
|
||||||
|
contactQuery.set("Name", name);
|
||||||
|
contactQuery.set("Phone", phone);
|
||||||
|
contactQuery.set("Email", email);
|
||||||
|
contactQuery.set("UserRole", "contracts_User");
|
||||||
|
|
||||||
|
if (localStorage.getItem("TenantId")) {
|
||||||
|
contactQuery.set("TenantId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "partners_Tenant",
|
||||||
|
objectId: localStorage.getItem("TenantId")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const _users = Parse.Object.extend("User");
|
||||||
|
const _user = new _users();
|
||||||
|
_user.set("name", name);
|
||||||
|
_user.set("username", email);
|
||||||
|
_user.set("email", email);
|
||||||
|
_user.set("password", email);
|
||||||
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await _user.save();
|
||||||
|
if (user) {
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_User",
|
||||||
|
userId: user.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", user);
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoader(false);
|
||||||
|
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err ", err);
|
||||||
|
if (err.code === 202) {
|
||||||
|
const params = { email: email };
|
||||||
|
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_User",
|
||||||
|
userId: userRes.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "_User",
|
||||||
|
objectId: userRes.id
|
||||||
|
});
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
setIsLoader(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// console.log("err", err);
|
||||||
|
setIsLoader(false);
|
||||||
|
alert("something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define a function to handle the "add yourself" checkbox
|
||||||
|
const handleReset = () => {
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||||
|
<Title title={"Add User"} />
|
||||||
|
{isUserExist && <Alert type="danger">User already exists!</Alert>}
|
||||||
|
{isLoader && (
|
||||||
|
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="w-full mx-auto">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<h1 className="text-[20px] font-semibold mb-4">Add User</h1>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="name"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Phone
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="phone"
|
||||||
|
value={phone}
|
||||||
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center mt-3 gap-2 text-white">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleReset()}
|
||||||
|
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddUser;
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import SuggestionInput from "./shared/fields/SuggestionInput";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
|
const BulkSendUi = (props) => {
|
||||||
|
const [forms, setForms] = useState([]);
|
||||||
|
const [formId, setFormId] = useState(2);
|
||||||
|
const formRef = useRef(null);
|
||||||
|
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
|
||||||
|
const [isSubmit, setIsSubmit] = useState(false);
|
||||||
|
const [allowedForm, setAllowedForm] = useState(0);
|
||||||
|
const [isSignatureExist, setIsSignatureExist] = useState();
|
||||||
|
const allowedSigners = 50;
|
||||||
|
useEffect(() => {
|
||||||
|
signatureExist();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//function to check atleast one signature field exist
|
||||||
|
const signatureExist = () => {
|
||||||
|
const getPlaceholder = props.item?.Placeholders;
|
||||||
|
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||||
|
placeholderObj?.placeHolder?.some((holder) =>
|
||||||
|
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setIsSignatureExist(checkIsSignatureExistt);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollOnNextUpdate && formRef.current) {
|
||||||
|
formRef.current.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "end",
|
||||||
|
inline: "nearest"
|
||||||
|
});
|
||||||
|
setScrollOnNextUpdate(false);
|
||||||
|
}
|
||||||
|
}, [forms, scrollOnNextUpdate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(() => {
|
||||||
|
if (props?.Placeholders?.length > 0) {
|
||||||
|
let users = [];
|
||||||
|
props?.Placeholders?.forEach((element) => {
|
||||||
|
if (!element.signerObjId) {
|
||||||
|
users = [
|
||||||
|
...users,
|
||||||
|
{
|
||||||
|
fieldId: element.Id,
|
||||||
|
email: "",
|
||||||
|
label: element.Role,
|
||||||
|
signer: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
|
||||||
|
const totalForms = Math.floor(allowedSigners / users?.length);
|
||||||
|
setAllowedForm(totalForms);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
const handleInputChange = (index, signer, fieldIndex) => {
|
||||||
|
const newForms = [...forms];
|
||||||
|
newForms[index].fields[fieldIndex].email = signer?.Email
|
||||||
|
? signer?.Email
|
||||||
|
: signer || "";
|
||||||
|
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
|
||||||
|
setForms(newForms);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddForm = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Check if the quick send limit has been reached
|
||||||
|
if (forms?.length < allowedForm) {
|
||||||
|
if (props?.Placeholders.length > 0) {
|
||||||
|
let newForm = [];
|
||||||
|
props?.Placeholders?.forEach((element) => {
|
||||||
|
if (!element.signerObjId) {
|
||||||
|
newForm = [
|
||||||
|
...newForm,
|
||||||
|
{
|
||||||
|
fieldId: element.Id,
|
||||||
|
email: "",
|
||||||
|
label: element.Role,
|
||||||
|
signer: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setForms([...forms, { Id: formId, fields: newForm }]);
|
||||||
|
}
|
||||||
|
setFormId(formId + 1);
|
||||||
|
setScrollOnNextUpdate(true);
|
||||||
|
} else {
|
||||||
|
// If the limit has been reached, throw an error with the appropriate message
|
||||||
|
alert("Quick send reached limit.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveForm = (index) => {
|
||||||
|
const updatedForms = forms.filter((_, i) => i !== index);
|
||||||
|
setForms(updatedForms);
|
||||||
|
};
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsSubmit(true);
|
||||||
|
|
||||||
|
// Create a copy of Placeholders array from props.item
|
||||||
|
let Placeholders = [...props.item.Placeholders];
|
||||||
|
// Initialize an empty array to store updated documents
|
||||||
|
let Documents = [];
|
||||||
|
|
||||||
|
// Loop through each form
|
||||||
|
forms.forEach((form) => {
|
||||||
|
// Map through the copied Placeholders array to update email values
|
||||||
|
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||||
|
// Find the field in the current form that matches the placeholder Id
|
||||||
|
const field = form.fields.find(
|
||||||
|
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||||
|
);
|
||||||
|
// If a matching field is found, update the email value in the placeholder
|
||||||
|
const signer = field?.signer?.objectId ? field.signer : {};
|
||||||
|
if (field) {
|
||||||
|
return {
|
||||||
|
...placeholder,
|
||||||
|
email: field.email,
|
||||||
|
signerObjId: field?.signer?.objectId || "",
|
||||||
|
signerPtr: signer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// If no matching field is found, keep the placeholder as is
|
||||||
|
return placeholder;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Push a new document object with updated Placeholders into the Documents array
|
||||||
|
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
|
||||||
|
});
|
||||||
|
// console.log("Documents ", Documents);
|
||||||
|
await batchQuery(Documents);
|
||||||
|
};
|
||||||
|
|
||||||
|
const batchQuery = async (Documents) => {
|
||||||
|
const serverUrl = localStorage.getItem("baseUrl");
|
||||||
|
const functionsUrl = `${serverUrl}functions/batchdocuments`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
Documents: JSON.stringify(Documents)
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const res = await axios.post(functionsUrl, params, { headers: headers });
|
||||||
|
// console.log("res ", res);
|
||||||
|
if (res.data && res.data.result) {
|
||||||
|
props.handleClose("success", Documents?.length);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err ", err);
|
||||||
|
props.handleClose("error", 0);
|
||||||
|
} finally {
|
||||||
|
setIsSubmit(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isSubmit && (
|
||||||
|
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.Placeholders?.length > 0 ? (
|
||||||
|
isSignatureExist ? (
|
||||||
|
<>
|
||||||
|
{props.Placeholders?.some((x) => !x.signerObjId) ? (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="min-h-max max-h-[250px] overflow-y-auto">
|
||||||
|
{forms?.map((form, index) => (
|
||||||
|
<div
|
||||||
|
key={form.Id}
|
||||||
|
className="p-3 op-card border-[1px] border-gray-400 mt-3 mx-4 mb-4 bg-base-200 text-base-content grid grid-cols-1 md:grid-cols-2 gap-2 relative"
|
||||||
|
>
|
||||||
|
{form?.fields?.map((field, fieldIndex) => (
|
||||||
|
<div className="flex flex-col" key={field.fieldId}>
|
||||||
|
<label>{field.label}</label>
|
||||||
|
<SuggestionInput
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
value={field.value}
|
||||||
|
index={fieldIndex}
|
||||||
|
onChange={(signer) =>
|
||||||
|
handleInputChange(index, signer, fieldIndex)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{forms?.length > 1 && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveForm(index)}
|
||||||
|
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<div ref={formRef}></div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col mx-4 mb-4 gap-3">
|
||||||
|
<button
|
||||||
|
onClick={handleAddForm}
|
||||||
|
className="op-btn op-btn-primary focus:outline-none"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i> <span>Add new</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="op-btn op-btn-secondary focus:outline-none"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-paper-plane"></i>{" "}
|
||||||
|
<span>Send</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
All roles in this document are currently linked to contacts. To
|
||||||
|
'quick send' copies of this template to multiple
|
||||||
|
signers, please ensure that at least one role is not linked to
|
||||||
|
any contact.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
Please ensure there's at least one signature widget added for
|
||||||
|
all recipients.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
Please add at least one role to this template in order to 'quick
|
||||||
|
send' copies of it to multiple signers.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BulkSendUi;
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
export class ErrorBoundary extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = { hasError: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
static getDerivedStateFromError() {
|
|
||||||
return {
|
|
||||||
hasError: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidCatch(error, info) {
|
|
||||||
console.log(error);
|
|
||||||
console.log(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
if (this.state.hasError) {
|
|
||||||
return (
|
|
||||||
<div className="p-2">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
|
|
||||||
flexFlow: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h1
|
|
||||||
style={{
|
|
||||||
fontSize: "2em",
|
|
||||||
fontFamily: "sans-serif",
|
|
||||||
textShadow: "0 0 20px black"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Something went wrong.
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.props.children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBoundary;
|
|
||||||
@@ -1,8 +1,21 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Package from "../../package.json";
|
import Package from "../../package.json";
|
||||||
|
import axios from "axios";
|
||||||
|
import { openInNewTab } from "../constant/Utils";
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
const [showButton, setShowButton] = useState(false);
|
const [showButton, setShowButton] = useState(false);
|
||||||
|
const [version, setVersion] = useState("");
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get("/version.txt")
|
||||||
|
.then((response) => {
|
||||||
|
setVersion(response.data); // Set the retrieved data to the state variable
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error reading the file:", error);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (window.pageYOffset >= 50) {
|
if (window.pageYOffset >= 50) {
|
||||||
setShowButton(true);
|
setShowButton(true);
|
||||||
@@ -25,17 +38,23 @@ const Footer = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const appName = "OpenSign™";
|
const appName = "OpenSign™";
|
||||||
|
const openUrl = () => {
|
||||||
|
openInNewTab(
|
||||||
|
"https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
|
<footer className="op-footer op-footer-center py-3 bg-base-300 text-base-content text-center text-[13px]">
|
||||||
All Rights Reserved © {new Date().getFullYear()}
|
<aside>
|
||||||
{appName}{" "}
|
<p>
|
||||||
(version:{" "}
|
All Rights Reserved © {new Date().getFullYear()}
|
||||||
{localStorage.getItem("appVersion") &&
|
<span onClick={openUrl} className="hover:underline cursor-pointer">
|
||||||
`${Package.version}.${localStorage.getItem("appVersion")}`}
|
{appName} ( version: {version ? version : `${Package.version} `})
|
||||||
)
|
</span>
|
||||||
</div>
|
</p>
|
||||||
|
</aside>
|
||||||
|
</footer>
|
||||||
<button
|
<button
|
||||||
className={`${
|
className={`${
|
||||||
showButton ? "block" : "hidden"
|
showButton ? "block" : "hidden"
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const FullScreenButton = () => {
|
|||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button
|
<button
|
||||||
onClick={toggleFullScreen}
|
onClick={toggleFullScreen}
|
||||||
className="text-black p-2 text-sm focus:outline-none"
|
className="text-base-content p-2 text-sm focus:outline-none"
|
||||||
>
|
>
|
||||||
{isFullScreen ? (
|
{isFullScreen ? (
|
||||||
<i className="fa-solid fa-compress"></i>
|
<i className="fa-solid fa-compress"></i>
|
||||||
|
|||||||
@@ -4,26 +4,53 @@ import FullScreenButton from "./FullScreenButton";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import { useWindowSize } from "../hook/useWindowSize";
|
import { useWindowSize } from "../hook/useWindowSize";
|
||||||
|
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
|
||||||
const Header = ({ showSidebar }) => {
|
const Header = ({ showSidebar }) => {
|
||||||
const navigation = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
let applogo = localStorage.getItem("appLogo") || "";
|
|
||||||
let username = localStorage.getItem("username");
|
let username = localStorage.getItem("username");
|
||||||
const image = localStorage.getItem("profileImg") || dp;
|
const image = localStorage.getItem("profileImg") || dp;
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||||
|
const [isPro, setIsPro] = useState(false);
|
||||||
|
const [applogo, setAppLogo] = useState(
|
||||||
|
localStorage.getItem("appLogo") || " "
|
||||||
|
);
|
||||||
|
|
||||||
const toggleDropdown = () => {
|
const toggleDropdown = () => {
|
||||||
setIsOpen(!isOpen);
|
setIsOpen(!isOpen);
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
checkSubscription();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
async function checkSubscription() {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
if (getIsSubscribe) {
|
||||||
|
const applogo = await getAppLogo();
|
||||||
|
if (applogo) {
|
||||||
|
setAppLogo(applogo);
|
||||||
|
} else {
|
||||||
|
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setIsPro(getIsSubscribe);
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const closeDropdown = () => {
|
const closeDropdown = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
Parse.serverURL = parseBaseUrl;
|
if (Parse?.User?.current()) {
|
||||||
Parse.initialize(parseAppId);
|
try {
|
||||||
Parse.User.logOut();
|
Parse.User.logOut();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
let appdata = localStorage.getItem("userSettings");
|
let appdata = localStorage.getItem("userSettings");
|
||||||
let applogo = localStorage.getItem("appLogo");
|
let applogo = localStorage.getItem("appLogo");
|
||||||
let appName = localStorage.getItem("appName");
|
let appName = localStorage.getItem("appName");
|
||||||
@@ -31,8 +58,8 @@ const Header = ({ showSidebar }) => {
|
|||||||
let PageLanding = localStorage.getItem("PageLanding");
|
let PageLanding = localStorage.getItem("PageLanding");
|
||||||
let domain = localStorage.getItem("domain");
|
let domain = localStorage.getItem("domain");
|
||||||
let _appName = localStorage.getItem("_appName");
|
let _appName = localStorage.getItem("_appName");
|
||||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
let baseUrl = localStorage.getItem("baseUrl");
|
||||||
let appid = localStorage.getItem("AppID12");
|
let appid = localStorage.getItem("parseAppId");
|
||||||
|
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
|
|
||||||
@@ -43,10 +70,10 @@ const Header = ({ showSidebar }) => {
|
|||||||
localStorage.setItem("PageLanding", PageLanding);
|
localStorage.setItem("PageLanding", PageLanding);
|
||||||
localStorage.setItem("domain", domain);
|
localStorage.setItem("domain", domain);
|
||||||
localStorage.setItem("userSettings", appdata);
|
localStorage.setItem("userSettings", appdata);
|
||||||
localStorage.setItem("BaseUrl12", baseUrl);
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
localStorage.setItem("AppID12", appid);
|
localStorage.setItem("parseAppId", appid);
|
||||||
|
|
||||||
navigation("/");
|
navigate("/");
|
||||||
};
|
};
|
||||||
|
|
||||||
//handle to close profile drop down menu onclick screen
|
//handle to close profile drop down menu onclick screen
|
||||||
@@ -66,14 +93,16 @@ const Header = ({ showSidebar }) => {
|
|||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row justify-between items-center gap-x-3 md:gap-x-4 bg-white md:px-4 px-2 shadow h-[50px] md:w-full ">
|
<div className="op-navbar bg-base-100 shadow">
|
||||||
<button
|
<div className="flex-none">
|
||||||
className="cursor-pointer focus:outline-none"
|
<button
|
||||||
onClick={showSidebar}
|
className="op-btn op-btn-square op-btn-ghost focus:outline-none"
|
||||||
>
|
onClick={showSidebar}
|
||||||
<i className={`fa-solid fa-bars text-xl `}></i>
|
>
|
||||||
</button>
|
<i className="fa-solid fa-bars text-xl text-base-content"></i>
|
||||||
<div className="flex-1">
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 ml-2">
|
||||||
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
||||||
<img
|
<img
|
||||||
className="object-contain h-full w-auto"
|
className="object-contain h-full w-auto"
|
||||||
@@ -82,10 +111,22 @@ const Header = ({ showSidebar }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div id="profile-menu" className="flex-none gap-2">
|
||||||
id="profile-menu"
|
{!isSubscribe && (
|
||||||
className="flex justify-between items-center gap-x-3"
|
<div>
|
||||||
>
|
<button
|
||||||
|
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
|
||||||
|
onClick={() => navigate("/subscription")}
|
||||||
|
>
|
||||||
|
Upgrade Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isPro && (
|
||||||
|
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||||
|
PRO
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
<div>
|
||||||
<FullScreenButton />
|
<FullScreenButton />
|
||||||
</div>
|
</div>
|
||||||
@@ -102,46 +143,66 @@ const Header = ({ showSidebar }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{width >= 768 && (
|
{width >= 768 && (
|
||||||
<div onClick={toggleDropdown} className="cursor-pointer text-sm">
|
<div
|
||||||
|
onClick={toggleDropdown}
|
||||||
|
className="cursor-pointer text-base-content text-sm"
|
||||||
|
>
|
||||||
{username && username}
|
{username && username}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="relative">
|
<div className="op-dropdown op-dropdown-end" id="profile-menu">
|
||||||
<div className="cursor-pointer">
|
<div tabIndex={0} role="button" className="op-btn op-btn-ghost">
|
||||||
<i onClick={toggleDropdown} className="fa-solid fa-angle-down"></i>
|
<i
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
onClick={toggleDropdown}
|
||||||
|
className="fa-solid fa-angle-down text-base-content"
|
||||||
|
></i>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<ul
|
||||||
className={`dropdown absolute text-sm text-gray-700 font-light right-0 mt-4 p-1 w-48 bg-white border rounded-lg shadow-lg z-10 ${
|
tabIndex={0}
|
||||||
isOpen ? "block" : "hidden"
|
className="mt-3 z-[1] p-2 shadow op-menu op-menu-sm op-dropdown-content text-base-content bg-base-100 rounded-box w-52"
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<ul>
|
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
|
||||||
<li
|
<span>
|
||||||
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
|
<i className="fa-solid fa-book"></i> Docs
|
||||||
onClick={() => {
|
</span>
|
||||||
setIsOpen(false);
|
</li>
|
||||||
navigation("/profile");
|
<li
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
setIsOpen(false);
|
||||||
|
navigate("/profile");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
<i className="fa-regular fa-user"></i> Profile
|
<i className="fa-regular fa-user"></i> Profile
|
||||||
</li>
|
</span>
|
||||||
<li
|
</li>
|
||||||
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
|
<li
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
navigation("/changepassword");
|
navigate("/changepassword");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<span>
|
||||||
<i className="fa-solid fa-lock"></i> Change Password
|
<i className="fa-solid fa-lock"></i> Change Password
|
||||||
</li>
|
</span>
|
||||||
<li
|
</li>
|
||||||
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
|
<li
|
||||||
onClick={closeDropdown}
|
onClick={() => {
|
||||||
>
|
window.open("https://console.opensignlabs.com/");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<i className="fa-regular fa-id-card"></i> Console
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li onClick={closeDropdown}>
|
||||||
|
<span>
|
||||||
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
|
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
|
||||||
</li>
|
</span>
|
||||||
</ul>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
// import FacebookLogin from "react-facebook-login";
|
// import FacebookLogin from "react-facebook-login";
|
||||||
|
|
||||||
const LoginFacebook = ({
|
const LoginFacebook = ({
|
||||||
@@ -27,14 +28,14 @@ const LoginFacebook = ({
|
|||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
const details = {
|
const details = {
|
||||||
Email: response.email,
|
Email: response.email,
|
||||||
Name: response.name,
|
Name: response.name
|
||||||
};
|
};
|
||||||
setUserDetails({ ...userDetails, ...details });
|
setUserDetails({ ...userDetails, ...details });
|
||||||
const fDetails = {
|
const fDetails = {
|
||||||
Id: response.userID,
|
Id: response.userID,
|
||||||
AccessToken: response.accessToken,
|
AccessToken: response.accessToken,
|
||||||
Name: response.name,
|
Name: response.name,
|
||||||
Email: response.email,
|
Email: response.email
|
||||||
};
|
};
|
||||||
setFBDetails({ ...fBDetails, ...fDetails });
|
setFBDetails({ ...fBDetails, ...fDetails });
|
||||||
const res = await checkExtUser(fDetails);
|
const res = await checkExtUser(fDetails);
|
||||||
@@ -42,12 +43,14 @@ const LoginFacebook = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const checkExtUser = async (details) => {
|
const checkExtUser = async (details) => {
|
||||||
const extUser = new Parse.Query("contracts_Users");
|
// const extUser = new Parse.Query("contracts_Users");
|
||||||
extUser.equalTo("Email", details.Email);
|
// extUser.equalTo("Email", details.Email);
|
||||||
const extRes = await extUser.first();
|
// const extRes = await extUser.first();
|
||||||
|
const params = { email: details.Email };
|
||||||
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
// console.log("extRes ", extRes);
|
// console.log("extRes ", extRes);
|
||||||
if (extRes) {
|
if (extRes) {
|
||||||
const params = { ...details, Phone: extRes.get("Phone") };
|
const params = { ...details, Phone: extRes?.get("Phone") || "" };
|
||||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
if (payload && payload.sessiontoken) {
|
if (payload && payload.sessiontoken) {
|
||||||
@@ -57,7 +60,7 @@ const LoginFacebook = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: details.Name,
|
name: details.Name,
|
||||||
email: details.Email,
|
email: details.Email,
|
||||||
phone: extRes.get("Phone"),
|
phone: extRes?.get("Phone") || "",
|
||||||
company: extRes.get("Company")
|
company: extRes.get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
@@ -73,7 +76,7 @@ const LoginFacebook = ({
|
|||||||
const handleSubmitbtn = async () => {
|
const handleSubmitbtn = async () => {
|
||||||
if (userDetails.Phone && userDetails.Company) {
|
if (userDetails.Phone && userDetails.Company) {
|
||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
const params = { ...fBDetails, Phone: userDetails.Phone };
|
const params = { ...fBDetails, Phone: userDetails?.Phone || "" };
|
||||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
|
|
||||||
@@ -83,8 +86,8 @@ const LoginFacebook = ({
|
|||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
// "passsword":userDetails.Phone,
|
// "passsword":userDetails.Phone,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
role: "contracts_Admin",
|
role: "contracts_User",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -94,7 +97,7 @@ const LoginFacebook = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
@@ -110,29 +113,10 @@ const LoginFacebook = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "relative" }}>
|
<div className="relative">
|
||||||
{thirdpartyLoader && (
|
{thirdpartyLoader && (
|
||||||
<div
|
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
|
||||||
style={{
|
<Loader />
|
||||||
position: "fixed",
|
|
||||||
width: "100vw",
|
|
||||||
height: "100vh",
|
|
||||||
backgroundColor: "rgba(0,0,0,0.2)",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 2
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: "fixed",
|
|
||||||
fontSize: "50px",
|
|
||||||
color: "#3ac9d6",
|
|
||||||
top: "50%",
|
|
||||||
left: "45%"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{/* <FacebookLogin
|
{/* <FacebookLogin
|
||||||
@@ -144,11 +128,10 @@ const LoginFacebook = ({
|
|||||||
/> */}
|
/> */}
|
||||||
{isModal && (
|
{isModal && (
|
||||||
<div
|
<div
|
||||||
className="modal fade show"
|
className="modal fade show block z-[1]"
|
||||||
id="exampleModal"
|
id="exampleModal"
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
style={{ display: "block", zIndex: 1 }}
|
|
||||||
>
|
>
|
||||||
<div className="modal-dialog" role="document">
|
<div className="modal-dialog" role="document">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
@@ -161,13 +144,8 @@ const LoginFacebook = ({
|
|||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
<form>
|
<form>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label
|
<label htmlFor="Phone" className="col-form-label">
|
||||||
htmlFor="Phone"
|
Phone <span className="text-[red] text-[13px]">*</span>
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="col-form-label"
|
|
||||||
>
|
|
||||||
Phone{" "}
|
|
||||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
@@ -184,13 +162,8 @@ const LoginFacebook = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label
|
<label htmlFor="Company" className="col-form-label">
|
||||||
htmlFor="Company"
|
Company <span className="text-[red] text-[13px]">*</span>
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="col-form-label"
|
|
||||||
>
|
|
||||||
Company{" "}
|
|
||||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -209,18 +182,17 @@ const LoginFacebook = ({
|
|||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
onClick={() => handleSubmitbtn()}
|
||||||
onClick={() => setIsModal(false)}
|
className="btn btn-info mr-[10px]"
|
||||||
style={{ marginRight: 10, width: 90 }}
|
|
||||||
>
|
>
|
||||||
Cancel
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleSubmitbtn()}
|
className="btn btn-secondary w-[90px]"
|
||||||
className="btn btn-info"
|
onClick={() => setIsModal(false)}
|
||||||
>
|
>
|
||||||
Sign up
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useState, useRef } from "react";
|
import React, { useState, useRef } from "react";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import jwtDecode from "jwt-decode";
|
import { jwtDecode } from "jwt-decode";
|
||||||
import { useScript } from "../hook/useScript";
|
import { useScript } from "../hook/useScript";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* `GoogleSignInBtn`as it's name indicates it render google sign in button
|
* `GoogleSignInBtn`as it's name indicates it render google sign in button
|
||||||
@@ -42,7 +44,42 @@ const GoogleSignInBtn = ({
|
|||||||
width: "187px"
|
width: "187px"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
const clearStorage = async () => {
|
||||||
|
if (Parse.User.current()) {
|
||||||
|
await Parse.User.logOut();
|
||||||
|
}
|
||||||
|
let baseUrl = localStorage.getItem("baseUrl");
|
||||||
|
let appid = localStorage.getItem("parseAppId");
|
||||||
|
let applogo = localStorage.getItem("appLogo");
|
||||||
|
let domain = localStorage.getItem("domain");
|
||||||
|
let appversion = localStorage.getItem("appVersion");
|
||||||
|
let appTitle = localStorage.getItem("appTitle");
|
||||||
|
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||||
|
let PageLanding = localStorage.getItem("PageLanding");
|
||||||
|
let _appName = localStorage.getItem("_appName");
|
||||||
|
let _app_objectId = localStorage.getItem("_app_objectId");
|
||||||
|
let appName = localStorage.getItem("appName");
|
||||||
|
let userSettings = localStorage.getItem("userSettings");
|
||||||
|
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
|
localStorage.setItem("parseAppId", appid);
|
||||||
|
localStorage.setItem("appLogo", applogo);
|
||||||
|
localStorage.setItem("domain", domain);
|
||||||
|
localStorage.setItem("appversion", appversion);
|
||||||
|
localStorage.setItem("appTitle", appTitle);
|
||||||
|
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||||
|
localStorage.setItem("PageLanding", PageLanding);
|
||||||
|
localStorage.setItem("_appName", _appName);
|
||||||
|
localStorage.setItem("_app_objectId", _app_objectId);
|
||||||
|
localStorage.setItem("appName", appName);
|
||||||
|
localStorage.setItem("userSettings", userSettings);
|
||||||
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
|
localStorage.setItem("parseAppId", appid);
|
||||||
|
};
|
||||||
const responseGoogle = async (response) => {
|
const responseGoogle = async (response) => {
|
||||||
|
clearStorage();
|
||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
// console.log("response ", response);
|
// console.log("response ", response);
|
||||||
if (response.credential) {
|
if (response.credential) {
|
||||||
@@ -66,27 +103,29 @@ const GoogleSignInBtn = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const checkExtUser = async (details) => {
|
const checkExtUser = async (details) => {
|
||||||
const extUser = new Parse.Query("contracts_Users");
|
// const extUser = new Parse.Query("contracts_Users");
|
||||||
extUser.equalTo("Email", details.Gmail);
|
// extUser.equalTo("Email", details.Gmail);
|
||||||
const extRes = await extUser.first();
|
// const extRes = await extUser.first();
|
||||||
|
const params = { email: details.Gmail };
|
||||||
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
// console.log("extRes ", extRes);
|
// console.log("extRes ", extRes);
|
||||||
if (extRes) {
|
if (extRes) {
|
||||||
const params = { ...details, Phone: extRes.get("Phone") };
|
const params = { ...details, Phone: extRes?.get("Phone") || "" };
|
||||||
const payload = await Parse.Cloud.run("googlesign", params);
|
const payload = await Parse.Cloud.run("googlesign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
if (payload && payload.sessiontoken) {
|
if (payload && payload.sessiontoken) {
|
||||||
// setThirdpartyLoader(true);
|
// setThirdpartyLoader(true);
|
||||||
const billingDate =
|
// const billingDate =
|
||||||
extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
|
// extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
|
||||||
// console.log("billingDate expired", billingDate > new Date());
|
// console.log("billingDate expired", billingDate > new Date());
|
||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: details.Name,
|
name: details.Name,
|
||||||
email: details.Gmail,
|
email: details.Gmail,
|
||||||
phone: extRes.get("Phone"),
|
phone: extRes?.get("Phone") || "",
|
||||||
company: extRes.get("Company")
|
company: extRes.get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
thirdpartyLoginfn(payload.sessiontoken, billingDate);
|
thirdpartyLoginfn(payload.sessiontoken);
|
||||||
}
|
}
|
||||||
return { msg: "exist" };
|
return { msg: "exist" };
|
||||||
} else {
|
} else {
|
||||||
@@ -100,7 +139,7 @@ const GoogleSignInBtn = ({
|
|||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
// e.preventDefault()
|
// e.preventDefault()
|
||||||
// console.log("handelSubmit", userDetails);
|
// console.log("handelSubmit", userDetails);
|
||||||
const params = { ...googleDetails, Phone: userDetails.Phone };
|
const params = { ...googleDetails, Phone: userDetails?.Phone };
|
||||||
const payload = await Parse.Cloud.run("googlesign", params);
|
const payload = await Parse.Cloud.run("googlesign", params);
|
||||||
|
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
@@ -110,8 +149,8 @@ const GoogleSignInBtn = ({
|
|||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
// "passsword":userDetails.Phone,
|
// "passsword":userDetails.Phone,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
role: "contracts_Admin",
|
role: "contracts_User",
|
||||||
company: userDetails.Company,
|
company: userDetails.Company,
|
||||||
jobTitle: userDetails.Destination
|
jobTitle: userDetails.Destination
|
||||||
}
|
}
|
||||||
@@ -122,7 +161,7 @@ const GoogleSignInBtn = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
// jobTitle: userDetails.JobTitle
|
// jobTitle: userDetails.JobTitle
|
||||||
};
|
};
|
||||||
@@ -141,144 +180,114 @@ const GoogleSignInBtn = ({
|
|||||||
alert("Please fill required details!");
|
alert("Please fill required details!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModal(false);
|
||||||
|
Parse.User.logOut();
|
||||||
|
|
||||||
|
let appdata = localStorage.getItem("userSettings");
|
||||||
|
let applogo = localStorage.getItem("appLogo");
|
||||||
|
let appName = localStorage.getItem("appName");
|
||||||
|
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||||
|
let PageLanding = localStorage.getItem("PageLanding");
|
||||||
|
let domain = localStorage.getItem("domain");
|
||||||
|
let _appName = localStorage.getItem("_appName");
|
||||||
|
let baseUrl = localStorage.getItem("baseUrl");
|
||||||
|
let appid = localStorage.getItem("parseAppId");
|
||||||
|
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
localStorage.setItem("appLogo", applogo);
|
||||||
|
localStorage.setItem("appName", appName);
|
||||||
|
localStorage.setItem("_appName", _appName);
|
||||||
|
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||||
|
localStorage.setItem("PageLanding", PageLanding);
|
||||||
|
localStorage.setItem("domain", domain);
|
||||||
|
localStorage.setItem("userSettings", appdata);
|
||||||
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
|
localStorage.setItem("parseAppId", appid);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "relative" }}>
|
<div className="relative">
|
||||||
{thirdpartyLoader && (
|
{thirdpartyLoader && (
|
||||||
<div
|
<div className="fixed flex justify-center items-center inset-0 bg-black bg-opacity-25 z-20 ">
|
||||||
style={{
|
<Loader />
|
||||||
position: "fixed",
|
|
||||||
width: "100vw",
|
|
||||||
height: "100vh",
|
|
||||||
backgroundColor: "rgba(0,0,0,0.2)",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
zIndex: 2
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: "fixed",
|
|
||||||
fontSize: "50px",
|
|
||||||
color: "#3ac9d6",
|
|
||||||
top: "50%",
|
|
||||||
left: "45%"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div ref={googleBtn} className="text-sm"></div>
|
<div ref={googleBtn} className="text-sm"></div>
|
||||||
|
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
|
||||||
{isModal && (
|
<form className="px-4 py-3 text-base-content">
|
||||||
<div
|
<div className="mb-3">
|
||||||
className="modal fade show"
|
<label htmlFor="Phone" className="block text-xs font-semibold">
|
||||||
id="exampleModal"
|
Phone <span className="text-[13px] text-[red]">*</span>
|
||||||
tabIndex="-1"
|
</label>
|
||||||
role="dialog"
|
<input
|
||||||
style={{ display: "block", zIndex: 1 }}
|
type="tel"
|
||||||
>
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
<div className="modal-dialog" role="document">
|
id="Phone"
|
||||||
<div className="modal-content">
|
value={userDetails.Phone}
|
||||||
<div className="modal-header">
|
onChange={(e) =>
|
||||||
<h5 className="modal-title">Sign up form</h5>
|
setUserDetails({
|
||||||
<span>
|
...userDetails,
|
||||||
<span></span>
|
Phone: e.target.value
|
||||||
</span>
|
})
|
||||||
</div>
|
}
|
||||||
<div className="modal-body">
|
required
|
||||||
<form>
|
/>
|
||||||
<div className="form-group">
|
|
||||||
<label
|
|
||||||
htmlFor="Phone"
|
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="col-form-label"
|
|
||||||
>
|
|
||||||
Phone{" "}
|
|
||||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
className="form-control"
|
|
||||||
id="Phone"
|
|
||||||
value={userDetails.Phone}
|
|
||||||
onChange={(e) =>
|
|
||||||
setUserDetails({
|
|
||||||
...userDetails,
|
|
||||||
Phone: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label
|
|
||||||
htmlFor="Company"
|
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="col-form-label"
|
|
||||||
>
|
|
||||||
Company{" "}
|
|
||||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
id="Company"
|
|
||||||
value={userDetails.Company}
|
|
||||||
onChange={(e) =>
|
|
||||||
setUserDetails({
|
|
||||||
...userDetails,
|
|
||||||
Company: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<label
|
|
||||||
htmlFor="Destination"
|
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="col-form-label"
|
|
||||||
>
|
|
||||||
Destination{" "}
|
|
||||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="form-control"
|
|
||||||
id="Destination"
|
|
||||||
value={userDetails.Destination}
|
|
||||||
onChange={(e) =>
|
|
||||||
setUserDetails({
|
|
||||||
...userDetails,
|
|
||||||
Destination: e.target.value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn btn-secondary"
|
|
||||||
onClick={() => setIsModal(false)}
|
|
||||||
style={{ marginRight: 10, width: 90 }}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => handleSubmitbtn()}
|
|
||||||
className="btn btn-info"
|
|
||||||
>
|
|
||||||
Sign up
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mb-3">
|
||||||
)}
|
<label htmlFor="Company" className="block text-xs font-semibold">
|
||||||
|
Company <span className="text-[13px] text-[red]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
id="Company"
|
||||||
|
value={userDetails.Company}
|
||||||
|
onChange={(e) =>
|
||||||
|
setUserDetails({
|
||||||
|
...userDetails,
|
||||||
|
Company: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="JobTitle" className="block text-xs font-semibold">
|
||||||
|
Job Title <span className="text-[13px] text-[red]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
id="JobTitle"
|
||||||
|
value={userDetails.Destination}
|
||||||
|
onChange={(e) =>
|
||||||
|
setUserDetails({
|
||||||
|
...userDetails,
|
||||||
|
Destination: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row gap-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
onClick={() => handleSubmitbtn()}
|
||||||
|
>
|
||||||
|
Sign up
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost"
|
||||||
|
onClick={handleCloseModal}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalUi>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Document, Page } from "react-pdf";
|
||||||
|
import { Stage, Layer, Rect } from "react-konva";
|
||||||
|
const RenderDebugPdf = (props) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
|
||||||
|
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="relative flex-1 cursor-crosshair border-[1px] border-[gray] overflow-auto"
|
||||||
|
onMouseMove={props.handleMouseMoveDiv}
|
||||||
|
>
|
||||||
|
<Document
|
||||||
|
onLoadError={() => {
|
||||||
|
const load = {
|
||||||
|
status: false,
|
||||||
|
type: "failed"
|
||||||
|
};
|
||||||
|
props.setPdfLoadFail(load);
|
||||||
|
}}
|
||||||
|
loading={"Loading Document.."}
|
||||||
|
onLoadSuccess={props.pageDetails}
|
||||||
|
ref={props.pdfRef}
|
||||||
|
file={props.pdfUrl}
|
||||||
|
>
|
||||||
|
<Page
|
||||||
|
onLoadSuccess={props.handlePageLoadSuccess}
|
||||||
|
pageNumber={props.pageNumber}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
onGetAnnotationsError={(error) => {
|
||||||
|
console.log("annotation error", error);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Document>
|
||||||
|
|
||||||
|
<Stage
|
||||||
|
onMouseMove={props.handleMouseMove}
|
||||||
|
onMouseDown={props.handleMouseDown}
|
||||||
|
onMouseUp={props.handleMouseUp}
|
||||||
|
width={props.pdfDimension.width}
|
||||||
|
height={props.pdfDimension.height}
|
||||||
|
className="absolute top-0 right-0"
|
||||||
|
>
|
||||||
|
<Layer>
|
||||||
|
{props.annotations
|
||||||
|
.filter((value) => value.page === props.pageNumber)
|
||||||
|
.map((value) => {
|
||||||
|
return (
|
||||||
|
<Rect
|
||||||
|
key={value.key}
|
||||||
|
x={value.x}
|
||||||
|
y={value.y}
|
||||||
|
width={value.width}
|
||||||
|
height={value.height}
|
||||||
|
fill="transparent"
|
||||||
|
stroke="black"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Layer>
|
||||||
|
</Stage>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RenderDebugPdf;
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import useFederatedComponent from "mf-cra";
|
|
||||||
import React, { Suspense, useEffect } from "react";
|
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
|
||||||
|
|
||||||
const MicroappModal = () => {
|
|
||||||
const { remoteApp } = useParams();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => localStorage.removeItem("rowlevel");
|
|
||||||
}, []);
|
|
||||||
let paramString = remoteApp;
|
|
||||||
let queryString = new URLSearchParams(paramString);
|
|
||||||
let obj = {};
|
|
||||||
for (const [key, value] of queryString.entries()) {
|
|
||||||
// console.log("value ", key + ":" + value);
|
|
||||||
|
|
||||||
if (key === "remoteUrl") {
|
|
||||||
const url = window.location.origin + "/mfbuild/remoteEntry.js";
|
|
||||||
// console.log("atob(value) ", atob(value))
|
|
||||||
obj = { ...obj, [key]: url };
|
|
||||||
} else {
|
|
||||||
if (key === "moduleToLoad") {
|
|
||||||
obj = { ...obj, [key]: "./" + value };
|
|
||||||
} else {
|
|
||||||
obj = { ...obj, [key]: value };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// console.log("app ", remoteApp);
|
|
||||||
const { Component: RemoteComponent } = useFederatedComponent(obj);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
// console.log("RemoteComponent ", RemoteComponent);
|
|
||||||
return (
|
|
||||||
<div className="w-full h-full flex items-center justify-center ">
|
|
||||||
{/* <!-- Modal Container --> */}
|
|
||||||
<div className="bg-white rounded-lg shadow-lg w-full">
|
|
||||||
{/* <!-- Modal Header --> */}
|
|
||||||
<div className="flex w-full items-center border-b-2 mb-2 p-2">
|
|
||||||
<button
|
|
||||||
className="focus:outline-none bg-blue-500 text-white text-sm shadow px-2.5 py-1 rounded"
|
|
||||||
id="closeModal"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
// setIsModal(false);
|
|
||||||
navigate(-1);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
{localStorage.getItem("domain") && (
|
|
||||||
<p className="flex-1 w-full text-sm md:text-base text-black text-center">
|
|
||||||
{localStorage.getItem("domain") === "contracts" ? "Document" : ""}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* <!-- Modal Body --> */}
|
|
||||||
<div className="w-full">
|
|
||||||
<Suspense
|
|
||||||
fallback={
|
|
||||||
<div style={{ height: "300px" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
backgroundColor: "#ffffff",
|
|
||||||
width: "100%",
|
|
||||||
overflow: "hidden",
|
|
||||||
minHeight: "50vh"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{RemoteComponent && <RemoteComponent />}
|
|
||||||
</div>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MicroappModal;
|
|
||||||
@@ -1,21 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
function Title(props) {
|
function Title({ title, drive }) {
|
||||||
return (
|
return (
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{`${props.title} - OpenSign™`}</title>
|
<title>{drive ? title : `${title} - OpenSign™`}</title>
|
||||||
{/* <title>
|
<meta name="description" content={`${title} - OpenSign™`} />
|
||||||
{`${localStorage.getItem("appTitle") ? localStorage.getItem("appTitle") : ""} - ${props.title}`}
|
|
||||||
</title> */}
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content={`${props.title} - OpenSign™`}
|
|
||||||
/>
|
|
||||||
{/* <meta
|
|
||||||
name="description"
|
|
||||||
content={`${localStorage.getItem("appTitle")} - ${props.title}`}
|
|
||||||
/> */}
|
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
|
|||||||
@@ -1,391 +0,0 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import Form from "@rjsf/core";
|
|
||||||
import validator from "@rjsf/validator-ajv8";
|
|
||||||
import Parse from "parse";
|
|
||||||
import "../styles/loader.css";
|
|
||||||
import LayoutField from "./fields/Rjsf-layout";
|
|
||||||
import TimeWidget from "./fields/TimeWidget";
|
|
||||||
import Level1Dropdown from "./fields/Level1Dropdown";
|
|
||||||
import HiddenField from "./fields/HiddenField";
|
|
||||||
import MultiSelectField from "./fields/MultiSelectField";
|
|
||||||
import TreeWidget from "../components/TreeWidget";
|
|
||||||
import { formJson } from "../json/FormJson";
|
|
||||||
const TreeEditForm = (props) => {
|
|
||||||
const widget = {
|
|
||||||
TimeWidget: TimeWidget
|
|
||||||
};
|
|
||||||
const fields = {
|
|
||||||
layout: LayoutField,
|
|
||||||
Level1Dropdown: Level1Dropdown,
|
|
||||||
HiddenField: HiddenField,
|
|
||||||
MultiSelectField: MultiSelectField,
|
|
||||||
FolderComponent: TreeWidget
|
|
||||||
};
|
|
||||||
|
|
||||||
const [schema, setschema] = useState({});
|
|
||||||
const [ui_schema, setui_schema] = useState({});
|
|
||||||
const [title, settitle] = useState("");
|
|
||||||
const [schemaState, setschemaState] = useState({});
|
|
||||||
const [formData, setformData] = useState({});
|
|
||||||
const [active, setactive] = useState(true);
|
|
||||||
const [loading, setloading] = useState(false);
|
|
||||||
const [_validate, set_validate] = useState(null);
|
|
||||||
const [noValidate, setnoValidate] = useState(false);
|
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
|
|
||||||
const getForm = async (id) => {
|
|
||||||
setloading(true);
|
|
||||||
try {
|
|
||||||
//get json form data (schema ,uischema)
|
|
||||||
const results = formJson(id);
|
|
||||||
try {
|
|
||||||
let _record = Parse.Object.extend(results.class);
|
|
||||||
var query1 = new Parse.Query(_record);
|
|
||||||
await query1.get(props.objectId).then(
|
|
||||||
(x) => {
|
|
||||||
try {
|
|
||||||
if (x) {
|
|
||||||
let result = x.toJSON();
|
|
||||||
let new_result = result;
|
|
||||||
for (let [key, value] of Object.entries(result)) {
|
|
||||||
if (value["__type"] === "Date") {
|
|
||||||
let todayTime1 = new Date(value.iso);
|
|
||||||
var month = String(todayTime1.getMonth() + 1);
|
|
||||||
var day = String(todayTime1.getDate());
|
|
||||||
var year = String(todayTime1.getFullYear());
|
|
||||||
if (month.length < 2) month = "0" + month;
|
|
||||||
if (day.length < 2) day = "0" + day;
|
|
||||||
let date1 = year + "-" + month + "-" + day;
|
|
||||||
let bindVar = date1;
|
|
||||||
if (!results.jsonSchema.properties[key].format) {
|
|
||||||
bindVar = todayTime1.toISOString();
|
|
||||||
}
|
|
||||||
new_result[key] = bindVar;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setschemaState(results.jsonSchema);
|
|
||||||
setformData(new_result);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
//alert(error.message);
|
|
||||||
|
|
||||||
setloading(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.log("err", error);
|
|
||||||
setloading(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setschema(results.jsonSchema);
|
|
||||||
setui_schema(results.uiSchema);
|
|
||||||
set_validate(results.validFunction);
|
|
||||||
setnoValidate(results.noValidate);
|
|
||||||
settitle(results.class);
|
|
||||||
setloading(false);
|
|
||||||
setactive(true);
|
|
||||||
} catch (error) {
|
|
||||||
setloading(false);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
setloading(false);
|
|
||||||
console.error("Problem", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrap = (s) => "{ return " + s + " };";
|
|
||||||
|
|
||||||
const dynamicValidate = (formData, errors) => {
|
|
||||||
try {
|
|
||||||
let body = atob(_validate);
|
|
||||||
let res = new Function(wrap(body))
|
|
||||||
.call(null)
|
|
||||||
.call(null, formData, errors);
|
|
||||||
return res;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async ({ formData }) => {
|
|
||||||
setactive(false);
|
|
||||||
setloading(true);
|
|
||||||
let RowData = formData;
|
|
||||||
RowData &&
|
|
||||||
Object.entries(RowData).forEach(([key, value]) => {
|
|
||||||
if (typeof value === "string") {
|
|
||||||
RowData[key] = value.trim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let _scanData = schemaState;
|
|
||||||
if (_scanData.dependencies) {
|
|
||||||
Object.keys(_scanData.dependencies).forEach((key) => {
|
|
||||||
if (_scanData.dependencies[key].oneOf) {
|
|
||||||
_scanData.dependencies[key].oneOf.forEach((val) => {
|
|
||||||
Object.keys(val.properties).forEach((k) => {
|
|
||||||
if (typeof val.properties[k] === "object") {
|
|
||||||
if (val.properties[k].format === "date") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newdate = new Date(RowData[k]);
|
|
||||||
RowData[k] = newdate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val.properties[k].component === "HtmlEditor") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newHtml = RowData[k]
|
|
||||||
.replace(/<p[^>]*>/g, "")
|
|
||||||
.replace(/<\/p>/g, " ");
|
|
||||||
RowData[k] = newHtml;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val.properties[k].component === "DateTime") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newDate11 = new Date(RowData[k]);
|
|
||||||
RowData[k] = newDate11;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val.properties[k].component === "CurrencyInput") {
|
|
||||||
if (val.properties[k].currencyColumn) {
|
|
||||||
RowData[`${val.properties[k].currencyColumn}`] =
|
|
||||||
val.properties[k].defaultcurrency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (val.properties[k].type === "string") {
|
|
||||||
if (typeof RowData[k] === "string")
|
|
||||||
RowData[k] = RowData[k].trim();
|
|
||||||
}
|
|
||||||
if (val.properties[k].data !== undefined) {
|
|
||||||
if (val.properties[k].data.isPointer) {
|
|
||||||
let pointer = undefined;
|
|
||||||
if (val.properties[k].data.class) {
|
|
||||||
if (RowData[k]) {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: val.properties[k].data.class,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (RowData[k]) {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: localStorage.getItem("extended_class"),
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RowData[k] = pointer;
|
|
||||||
}
|
|
||||||
if (val.properties[k].data.FolderTypeValue) {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let obj = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: val.properties[k].data.ClassName,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
RowData[k] = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let temp = [];
|
|
||||||
|
|
||||||
let _dd = _scanData.properties;
|
|
||||||
let allowed = [];
|
|
||||||
if (_dd) {
|
|
||||||
Object.keys(_dd).forEach(function (k) {
|
|
||||||
allowed.push(k);
|
|
||||||
if (RowData[k]) {
|
|
||||||
let pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].class,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
RowData[k] = pointer;
|
|
||||||
}
|
|
||||||
if (_dd[k].format === "date") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newdate = new Date(RowData[k]);
|
|
||||||
if (!isNaN(newdate.getTime())) {
|
|
||||||
RowData[k] = newdate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].component === "HtmlEditor") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newHtml = RowData[k]
|
|
||||||
.replace(/<p[^>]*>/g, "")
|
|
||||||
.replace(/<\/p>/g, " ");
|
|
||||||
RowData[k] = newHtml;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].component === "DateTime") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let newdate = new Date(RowData[k]);
|
|
||||||
if (!isNaN(newdate.getTime())) {
|
|
||||||
RowData[k] = newdate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].format === "date-time") {
|
|
||||||
let newdate = new Date(RowData[k]);
|
|
||||||
RowData[k] = newdate;
|
|
||||||
}
|
|
||||||
if (_dd[k].component === "CurrencyInput") {
|
|
||||||
if (_dd[k].currencyColumn) {
|
|
||||||
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].data !== undefined) {
|
|
||||||
if (_dd[k].data.isPointer) {
|
|
||||||
let pointer = undefined;
|
|
||||||
if (typeof RowData[k] === "object") {
|
|
||||||
if (RowData[k]) {
|
|
||||||
if (RowData[k].objectId !== "Select") {
|
|
||||||
let obj = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: RowData[k].className,
|
|
||||||
objectId: RowData[k].objectId
|
|
||||||
};
|
|
||||||
pointer = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (_dd[k].data.class) {
|
|
||||||
if (RowData[k]) {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].data.class,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (RowData[k]) {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: localStorage.getItem("extended_class"),
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowData[k] = pointer;
|
|
||||||
}
|
|
||||||
if (_dd[k].data.FolderTypeValue) {
|
|
||||||
if (RowData[k]) {
|
|
||||||
let obj = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].data.ClassName,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
RowData[k] = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].type === "string") {
|
|
||||||
if (typeof RowData[k] === "string") RowData[k] = RowData[k].trim();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const filtered = Object.keys(RowData)
|
|
||||||
.filter((key) => allowed.includes(key))
|
|
||||||
.reduce((obj, key) => {
|
|
||||||
obj[key] = RowData[key];
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
try {
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
if (temp.length !== 0) {
|
|
||||||
let userPointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "_User",
|
|
||||||
objectId: currentUser.id
|
|
||||||
};
|
|
||||||
filtered[[temp[0]]] = userPointer;
|
|
||||||
}
|
|
||||||
var data = Parse.Object.extend(title);
|
|
||||||
var query = new Parse.Query(data);
|
|
||||||
query.get(props.objectId).then(async (object) => {
|
|
||||||
object.save(filtered).then(
|
|
||||||
() => {
|
|
||||||
try {
|
|
||||||
alert("Record updated successfully");
|
|
||||||
props.HideView(false);
|
|
||||||
setloading(false);
|
|
||||||
setactive(true);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("err", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.log("err", error);
|
|
||||||
setloading(false);
|
|
||||||
setactive(true);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log("err", error);
|
|
||||||
|
|
||||||
alert(error.message);
|
|
||||||
setloading(false);
|
|
||||||
setactive(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.FormId) {
|
|
||||||
getForm(props.FormId);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [props.FormId]);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="loader-01"
|
|
||||||
style={{
|
|
||||||
marginTop: "50px",
|
|
||||||
marginLeft: "50%",
|
|
||||||
color: "rgb(0,28,28)",
|
|
||||||
fontSize: "35px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Form
|
|
||||||
schema={schema}
|
|
||||||
uiSchema={ui_schema}
|
|
||||||
formData={formData}
|
|
||||||
showErrorList={false}
|
|
||||||
widgets={widget}
|
|
||||||
fields={fields}
|
|
||||||
validate={noValidate && dynamicValidate}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
validator={validator}
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
{active ? (
|
|
||||||
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit">
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit" disabled>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TreeEditForm;
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import Form from "@rjsf/core";
|
|
||||||
import validator from "@rjsf/validator-ajv8";
|
|
||||||
import LayoutField from "./fields/Rjsf-layout";
|
|
||||||
import Level1Dropdown from "./fields/Level1Dropdown";
|
|
||||||
import HiddenField from "./fields/HiddenField";
|
|
||||||
import MultiSelectField from "./fields/MultiSelectField";
|
|
||||||
import "../styles/loader.css";
|
|
||||||
import "../styles/form.css";
|
|
||||||
import FileUpload from "./fields/FileUpload";
|
|
||||||
import parse from "html-react-parser";
|
|
||||||
import TimeWidget from "./fields/TimeWidget";
|
|
||||||
import { formJson } from "../json/FormJson";
|
|
||||||
function TreeFormComponent(props) {
|
|
||||||
const widget = {
|
|
||||||
TimeWidget: TimeWidget
|
|
||||||
};
|
|
||||||
|
|
||||||
const fields = {
|
|
||||||
layout: LayoutField,
|
|
||||||
FileUpload: FileUpload,
|
|
||||||
Level1Dropdown: Level1Dropdown,
|
|
||||||
HiddenField: HiddenField,
|
|
||||||
MultiSelectField: MultiSelectField
|
|
||||||
};
|
|
||||||
|
|
||||||
const [link, setLink] = useState("");
|
|
||||||
const [help, setHelp] = useState("");
|
|
||||||
const toastDescription = "";
|
|
||||||
const toastColor = "#5cb85c";
|
|
||||||
const [loading, setLoding] = useState(true);
|
|
||||||
const [schemaState, setSchemaState] = useState({});
|
|
||||||
const [formData, setFormData] = useState({});
|
|
||||||
const [ui_schema, setUi_schema] = useState({});
|
|
||||||
const active = true;
|
|
||||||
|
|
||||||
console.log("new form", props);
|
|
||||||
|
|
||||||
const getForm = async (id) => {
|
|
||||||
setLoding(true);
|
|
||||||
try {
|
|
||||||
// get json from data(jsonschema, uischema)
|
|
||||||
const results = formJson(id);
|
|
||||||
// console.log("result", results);
|
|
||||||
if (results) {
|
|
||||||
let parentObject = {},
|
|
||||||
childObject = {};
|
|
||||||
const resultjson = results;
|
|
||||||
for (let [key, value] of Object.entries(
|
|
||||||
resultjson.jsonSchema.properties
|
|
||||||
)) {
|
|
||||||
if (key === props.ParentField) {
|
|
||||||
if (props.ParentValue[key]) {
|
|
||||||
parentObject = { [key]: props.ParentValue[key] };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (props.ParentValue && typeof props.ParentValue === "object") {
|
|
||||||
Object.entries(props.ParentValue).forEach(([k, value]) => {
|
|
||||||
if (k === key) {
|
|
||||||
let newO = { [k]: value };
|
|
||||||
parentObject = { ...parentObject, ...newO };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (props.ChildField) {
|
|
||||||
Object.entries(props.ChildField).forEach(([k, value]) => {
|
|
||||||
if (k === key) {
|
|
||||||
let newO = { [k]: value };
|
|
||||||
childObject = { ...childObject, ...newO };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === "object") {
|
|
||||||
for (let [k, v] of Object.entries(value)) {
|
|
||||||
if (k === "format" && v === "date") {
|
|
||||||
let today = new Date();
|
|
||||||
let date =
|
|
||||||
today.getFullYear() +
|
|
||||||
"-" +
|
|
||||||
("0" + (today.getMonth() + 1)).slice(-2) +
|
|
||||||
"-" +
|
|
||||||
("0" + today.getDate()).slice(-2);
|
|
||||||
value.default = date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (props.ParentField) {
|
|
||||||
setFormData({ ...parentObject, ...childObject });
|
|
||||||
}
|
|
||||||
|
|
||||||
let txt, link;
|
|
||||||
if (resultjson.help) {
|
|
||||||
if (resultjson.help.htmlbody) {
|
|
||||||
txt = resultjson.help.htmlbody;
|
|
||||||
}
|
|
||||||
if (resultjson.help.link) {
|
|
||||||
link = resultjson.help.link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setHelp(txt);
|
|
||||||
setLink(link);
|
|
||||||
|
|
||||||
props.ClassName(resultjson.class);
|
|
||||||
props.SchemaState(resultjson.jsonSchema);
|
|
||||||
setSchemaState(resultjson.jsonSchema);
|
|
||||||
setUi_schema(resultjson.uiSchema);
|
|
||||||
setLoding(false);
|
|
||||||
} else {
|
|
||||||
alert("form not found");
|
|
||||||
setLoding(false);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Problem", e);
|
|
||||||
setLoding(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!props.IsEdit) {
|
|
||||||
getForm(props.Id);
|
|
||||||
}
|
|
||||||
// console.log("props tree", props);
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
let formView = (
|
|
||||||
<React.Fragment>
|
|
||||||
<Form
|
|
||||||
schema={schemaState}
|
|
||||||
uiSchema={ui_schema}
|
|
||||||
showErrorList={false}
|
|
||||||
widgets={widget}
|
|
||||||
fields={fields}
|
|
||||||
formData={formData}
|
|
||||||
onSubmit={props.handleSubmit}
|
|
||||||
validator={validator}
|
|
||||||
>
|
|
||||||
{active ? (
|
|
||||||
<button className="btn btn-info submiBtn" type="submit">
|
|
||||||
{props.IsEdit ? "update folder" : "create folder"}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button className="btn btn-info submiBtn " type="submit" disabled>
|
|
||||||
submitting...
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
formView = (
|
|
||||||
<div style={{ height: "300px" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-md-12">
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-body no-padding height-9">
|
|
||||||
{help ? (
|
|
||||||
<div className="dropdown" style={{ marginTop: "-30px" }}>
|
|
||||||
<i
|
|
||||||
className="far fa-question-circle dropdown-toggle hovereffect"
|
|
||||||
aria-hidden="true"
|
|
||||||
id="dropdownMenuButton"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
style={{
|
|
||||||
fontSize: "18px",
|
|
||||||
color: "purple",
|
|
||||||
cursor: "pointer !important",
|
|
||||||
position: "relative",
|
|
||||||
top: "40px",
|
|
||||||
left: "98%"
|
|
||||||
}}
|
|
||||||
></i>
|
|
||||||
<div
|
|
||||||
className="dropdown-menu"
|
|
||||||
aria-labelledby="dropdownMenuButton"
|
|
||||||
style={{
|
|
||||||
marginleft: "-121px",
|
|
||||||
margintop: "-14px",
|
|
||||||
position: "absolute",
|
|
||||||
padding: "10px",
|
|
||||||
width: "300px",
|
|
||||||
top: "102px!important"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{parse(`
|
|
||||||
${help}
|
|
||||||
`)}
|
|
||||||
<br />
|
|
||||||
{link ? (
|
|
||||||
<a
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
window.location.href = link;
|
|
||||||
}}
|
|
||||||
target="_blank"
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
>
|
|
||||||
Read more..
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div style={{ fontSize: "13px" }}>{formView}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="snackbar" style={{ backgroundColor: toastColor }}>
|
|
||||||
{toastDescription}
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TreeFormComponent;
|
|
||||||
@@ -1,894 +0,0 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import LabelField from "./fields/LabelField";
|
|
||||||
import Parse from "parse";
|
|
||||||
import axios from "axios";
|
|
||||||
import "../styles/spinner.css";
|
|
||||||
import TreeFormComponent from "./TreeFormComponent";
|
|
||||||
import TreeEditForm from "./TreeEditForm";
|
|
||||||
import "../styles/modal.css";
|
|
||||||
import Modal from "react-modal";
|
|
||||||
|
|
||||||
const TreeWidget = (props) => {
|
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
const [isAddField, setIsAddField] = useState(false);
|
|
||||||
const [folderList, setFolderList] = useState([]);
|
|
||||||
const [tabList, setTabList] = useState([]);
|
|
||||||
const [appId, setAppId] = useState(undefined);
|
|
||||||
const [selectedFolder, setSelectedFolder] = useState(undefined);
|
|
||||||
const [loader, setLoader] = useState(false);
|
|
||||||
const [className, setClassName] = useState("");
|
|
||||||
const [schemaState, setSchemaState] = useState({});
|
|
||||||
const [TabURL, setTabURL] = useState("");
|
|
||||||
const [editable, setEditable] = useState(false);
|
|
||||||
const [editId, setEditId] = useState("");
|
|
||||||
const [defaultState, setDefaultState] = useState(false);
|
|
||||||
const [isShowModal, setIsShowModal] = useState(false);
|
|
||||||
const selectFolderHandle = async () => {
|
|
||||||
setIsShowModal(true);
|
|
||||||
setIsAddField(false);
|
|
||||||
setDefaultState(false);
|
|
||||||
setFolderList([]);
|
|
||||||
setLoader(true);
|
|
||||||
|
|
||||||
if (tabList.length > 0) {
|
|
||||||
let len = tabList.length - 1;
|
|
||||||
selectedItemList(tabList[len]);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
var test;
|
|
||||||
if (props.schema.parent) {
|
|
||||||
try {
|
|
||||||
if (props.parent[props.schema.parent]) {
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
let res;
|
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
|
||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
res = data[0];
|
|
||||||
} else {
|
|
||||||
var emp = Parse.Object.extend(
|
|
||||||
localStorage.getItem("extended_class")
|
|
||||||
);
|
|
||||||
var q = new Parse.Query(emp);
|
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
res = await q.first();
|
|
||||||
if (res) res = res.toJSON();
|
|
||||||
}
|
|
||||||
if (res) {
|
|
||||||
let json = res;
|
|
||||||
setAppId(json.AppId);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
let reg = /(\#.*?\#)/gi;
|
|
||||||
let str = props.schema.data.Query;
|
|
||||||
if (str.includes("#")) {
|
|
||||||
test = str.replace(reg, props.parent[props.schema.parent]);
|
|
||||||
} else {
|
|
||||||
test = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (props.child[props.schema.parent]) {
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
let res;
|
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
|
||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
res = data[0];
|
|
||||||
} else {
|
|
||||||
emp = Parse.Object.extend(
|
|
||||||
localStorage.getItem("extended_class")
|
|
||||||
);
|
|
||||||
q = new Parse.Query(emp);
|
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
res = await q.first();
|
|
||||||
if (res) res = res.toJSON();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
let json = res;
|
|
||||||
setAppId(json.AppId);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
let reg = /(\#.*?\#)/gi;
|
|
||||||
let str = props.schema.data.Query;
|
|
||||||
if (str.includes("#")) {
|
|
||||||
test = str.replace(reg, props.child[props.schema.parent]);
|
|
||||||
} else {
|
|
||||||
test = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert(`Please select ${props.schema.parent}`);
|
|
||||||
setLoader(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("error ", error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
let reg = /(\#.*?\#)/gi;
|
|
||||||
let str = props.schema.data.Query;
|
|
||||||
let res;
|
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
|
||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
res = data[0];
|
|
||||||
} else {
|
|
||||||
emp = Parse.Object.extend(localStorage.getItem("extended_class"));
|
|
||||||
q = new Parse.Query(emp);
|
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
res = await q.first();
|
|
||||||
if (res) res = res.toJSON();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
let json = res;
|
|
||||||
setAppId(json.AppId);
|
|
||||||
}
|
|
||||||
if (str.includes("#")) {
|
|
||||||
if (res) {
|
|
||||||
let json = res.toJSON();
|
|
||||||
let output = str.match(reg);
|
|
||||||
if (output.length === 1) {
|
|
||||||
output = output.join();
|
|
||||||
output = output.substring(1, output.length - 1);
|
|
||||||
output = output.split(".");
|
|
||||||
if (output.length === 1) {
|
|
||||||
let out = output[0];
|
|
||||||
if (json[out]) {
|
|
||||||
if (typeof json[out] === "object") {
|
|
||||||
test = str.replace(reg, JSON.stringify(json[out]));
|
|
||||||
} else {
|
|
||||||
test = str.replace(reg, json[out]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
test = str.replace(reg, currentUser.id);
|
|
||||||
}
|
|
||||||
} else if (output.length === 2) {
|
|
||||||
let out1 = json[output[0]][output[1]];
|
|
||||||
if (out1) {
|
|
||||||
test = str.replace(reg, out1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
test = str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?${test}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
await axios
|
|
||||||
.get(url, { headers: headers })
|
|
||||||
.then((res) => {
|
|
||||||
if (res) {
|
|
||||||
setFolderList(res.data.results);
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
setLoader(false);
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedItemList = async (folder) => {
|
|
||||||
setLoader(true);
|
|
||||||
setFolderList([]);
|
|
||||||
try {
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${folder.objectId}"}}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
await axios.get(url, { headers: headers }).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
setFolderList(result.data.results);
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeTabListItem = async (e, i) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setEditable(false);
|
|
||||||
setLoader(true);
|
|
||||||
setFolderList([]);
|
|
||||||
let list = tabList.filter((itm, j) => {
|
|
||||||
if (j <= i) {
|
|
||||||
return itm;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setTabList(list);
|
|
||||||
setIsAddField(false);
|
|
||||||
try {
|
|
||||||
let _len = list.length - 1;
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${list[_len].objectId}"}}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
await axios.get(url, { headers: headers }).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
setFolderList(result.data.results);
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
setLoader(false);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDeleteFolder = async (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (folderList.length > 0) {
|
|
||||||
alert(
|
|
||||||
"Folder should not delete. To delete first delete sub items in it."
|
|
||||||
);
|
|
||||||
} else if (window.confirm(`Are you sure you want to delete this record`)) {
|
|
||||||
try {
|
|
||||||
setLoader(true);
|
|
||||||
let len = tabList.length - 1;
|
|
||||||
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
var deleted = Parse.Object.extend(props.schema.data.ClassName);
|
|
||||||
var query = new Parse.Query(deleted);
|
|
||||||
await query.get(tabList[len].objectId).then(
|
|
||||||
(del) => {
|
|
||||||
del.destroy().then(
|
|
||||||
(y) => {
|
|
||||||
if (y) {
|
|
||||||
setLoader(false);
|
|
||||||
let list = tabList.filter((itm, j) => {
|
|
||||||
if (j !== len) return itm;
|
|
||||||
});
|
|
||||||
setTabList(list);
|
|
||||||
if (list.length > 0) {
|
|
||||||
let len = list.length - 1;
|
|
||||||
selectedItemList(list[len]);
|
|
||||||
} else {
|
|
||||||
selectFolderHandle();
|
|
||||||
}
|
|
||||||
alert("Folder deleted successfully.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
alert(error.message);
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.log("error ", error);
|
|
||||||
// The object was not retrieved successfully.
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
setLoader(false);
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSubmitResult = async (e) => {
|
|
||||||
setIsShowModal(false);
|
|
||||||
e.preventDefault();
|
|
||||||
//console.log("tabList ", tabList);
|
|
||||||
if (tabList.length > 0) {
|
|
||||||
try {
|
|
||||||
let len = tabList.length - 1;
|
|
||||||
setSelectedFolder({
|
|
||||||
Topic: tabList[len][`${props.schema.data.FolderNameField}`],
|
|
||||||
description: tabList[len][`${props.schema.data.FolderDescription}`]
|
|
||||||
});
|
|
||||||
props.onChange(tabList[len]["objectId"]);
|
|
||||||
let url = "Folders";
|
|
||||||
tabList.forEach((t) => {
|
|
||||||
url = url + " / " + t[`${props.schema.data.FolderNameField}`];
|
|
||||||
});
|
|
||||||
setTabURL(url);
|
|
||||||
setIsAddField(false);
|
|
||||||
if (!props.formData) {
|
|
||||||
setTabList([]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setIsAddField(false);
|
|
||||||
alert(error.message);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setIsAddField(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const editFolderDisplay = async (formData) => {
|
|
||||||
try {
|
|
||||||
let objectId = formData.objectId;
|
|
||||||
let className = formData.className;
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const fldr = Parse.Object.extend(className);
|
|
||||||
let query = new Parse.Query(fldr);
|
|
||||||
query.equalTo("objectId", objectId);
|
|
||||||
const result = await query.first();
|
|
||||||
let resultJson = result.toJSON();
|
|
||||||
let CustomTabList = [];
|
|
||||||
if (resultJson) {
|
|
||||||
CustomTabList.push(resultJson);
|
|
||||||
if (resultJson[`${props.schema.data.ParentFolderField}`]) {
|
|
||||||
let tabsRes = resultJson[`${props.schema.data.ParentFolderField}`];
|
|
||||||
let looping = false;
|
|
||||||
do {
|
|
||||||
let fldttr = Parse.Object.extend(tabsRes.className);
|
|
||||||
let query2 = new Parse.Query(fldttr);
|
|
||||||
query2.equalTo("objectId", tabsRes.objectId);
|
|
||||||
let custome_res = await query2.first();
|
|
||||||
if (custome_res) {
|
|
||||||
let tabsData = custome_res.toJSON();
|
|
||||||
CustomTabList.push(tabsData);
|
|
||||||
if (tabsData[`${props.schema.data.ParentFolderField}`]) {
|
|
||||||
tabsRes = tabsData[`${props.schema.data.ParentFolderField}`];
|
|
||||||
looping = true;
|
|
||||||
} else {
|
|
||||||
looping = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (looping);
|
|
||||||
}
|
|
||||||
let url = "Folders";
|
|
||||||
CustomTabList.length > 0 &&
|
|
||||||
CustomTabList.reverse().forEach(
|
|
||||||
(t) => (url = url + " / " + t[props.schema.data.FolderNameField])
|
|
||||||
);
|
|
||||||
setTabURL(url);
|
|
||||||
setSelectedFolder({
|
|
||||||
Topic: resultJson[`${props.schema.data.FolderNameField}`],
|
|
||||||
description: resultJson[`${props.schema.data.FolderDescription}`]
|
|
||||||
});
|
|
||||||
setTabList(CustomTabList);
|
|
||||||
props.onChange(resultJson.objectId);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async ({ formData }) => {
|
|
||||||
setLoader(true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
let RowData = formData;
|
|
||||||
let _scanData = schemaState;
|
|
||||||
Object.keys(_scanData).forEach(function (key) {
|
|
||||||
let _dd = _scanData[key];
|
|
||||||
Object.keys(_dd).forEach(function (k) {
|
|
||||||
if (_dd[k].component === "AutoSuggest" && _dd[k].isPointer) {
|
|
||||||
let pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].class,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
RowData[k] = pointer;
|
|
||||||
}
|
|
||||||
if (_dd[k].format === "date") {
|
|
||||||
let newdate = new Date(RowData[k]);
|
|
||||||
RowData[k] = newdate;
|
|
||||||
}
|
|
||||||
if (_dd[k].component === "DateTime") {
|
|
||||||
let newDate11;
|
|
||||||
if (!RowData[k]) {
|
|
||||||
newDate11 = new Date();
|
|
||||||
} else {
|
|
||||||
newDate11 = new Date(RowData[k]);
|
|
||||||
}
|
|
||||||
RowData[k] = newDate11;
|
|
||||||
}
|
|
||||||
if (_dd[k].component === "CurrencyInput") {
|
|
||||||
if (_dd[k].currencyColumn) {
|
|
||||||
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].data !== undefined) {
|
|
||||||
if (_dd[k].data.isPointer) {
|
|
||||||
let pointer = {};
|
|
||||||
if (RowData[k] && RowData[k] !== "Select") {
|
|
||||||
if (_dd[k].data.class) {
|
|
||||||
if (RowData[k]) {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].data.class,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pointer = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: localStorage.getItem("extended_class"),
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
RowData[k] = pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].data.FolderTypeValue) {
|
|
||||||
let obj = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _dd[k].data.ClassName,
|
|
||||||
objectId: RowData[k]
|
|
||||||
};
|
|
||||||
RowData[k] = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_dd[k].type === "string") {
|
|
||||||
let d = RowData[k];
|
|
||||||
if (typeof d === "string") {
|
|
||||||
RowData[k] = d.trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
|
|
||||||
var forms = Parse.Object.extend(className);
|
|
||||||
var folder = new forms();
|
|
||||||
folder.set(
|
|
||||||
props.schema.data.FolderNameField,
|
|
||||||
formData[props.schema.data.FolderNameField]
|
|
||||||
);
|
|
||||||
if (props.schema.data.FolderDescription) {
|
|
||||||
folder.set(
|
|
||||||
props.schema.data.FolderDescription,
|
|
||||||
formData[props.schema.data.FolderDescription]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
folder.set("AppId", appId);
|
|
||||||
folder.set(
|
|
||||||
props.schema.data.FolderTypeField,
|
|
||||||
props.schema.data.FolderTypeValue
|
|
||||||
);
|
|
||||||
if (tabList.length > 0) {
|
|
||||||
let len = tabList.length - 1;
|
|
||||||
folder.set(props.schema.data.ParentFolderField, {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: props.schema.data.ClassName,
|
|
||||||
objectId: tabList[len]["objectId"]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
folder.save(RowData).then(
|
|
||||||
(result) => {
|
|
||||||
RowData = {};
|
|
||||||
if (result) {
|
|
||||||
setIsAddField(false);
|
|
||||||
setLoader(false);
|
|
||||||
if (tabList.length > 0) {
|
|
||||||
let len = tabList.length - 1;
|
|
||||||
selectedItemList(tabList[len]);
|
|
||||||
} else {
|
|
||||||
selectFolderHandle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
console.log("error ", error);
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
setLoader(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.formData) {
|
|
||||||
editFolderDisplay(props.formData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [props.formData]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (defaultState) {
|
|
||||||
selectFolderHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [defaultState]);
|
|
||||||
const customStyles = {
|
|
||||||
content: {
|
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
right: "auto",
|
|
||||||
bottom: "auto",
|
|
||||||
marginRight: "-50%",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
backgroundColor: "white",
|
|
||||||
padding: 0
|
|
||||||
},
|
|
||||||
overlay: {
|
|
||||||
width:"100%",
|
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
||||||
zIndex: 50
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<LabelField
|
|
||||||
Title={props.schema.title}
|
|
||||||
Name={props.name}
|
|
||||||
Required={props.required}
|
|
||||||
HelpBody={props.schema.helpbody}
|
|
||||||
HelpLink={props.schema.helplink}
|
|
||||||
/>
|
|
||||||
<br />
|
|
||||||
<div className="block max-w-sm bg-white border border-gray-200 shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
color: "black",
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: "13px",
|
|
||||||
padding: "2%",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ width: "20%" }}>
|
|
||||||
<div
|
|
||||||
className="pull-left"
|
|
||||||
title="Select Folder"
|
|
||||||
style={{
|
|
||||||
color: "#33bbff",
|
|
||||||
margin: "auto",
|
|
||||||
padding: "10px 10px",
|
|
||||||
textAlign: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="far fa-folder-open"
|
|
||||||
style={{ fontSize: "40px" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ margin: "0px 10px 0 20px", width: "80%" }}>
|
|
||||||
<div>
|
|
||||||
{selectedFolder && selectedFolder.Topic
|
|
||||||
? selectedFolder.Topic
|
|
||||||
: "Root"}
|
|
||||||
<i
|
|
||||||
className="fa fa-pencil"
|
|
||||||
title="Select Folder"
|
|
||||||
style={{ fontSize: 17, cursor: "pointer", marginLeft: 12 }}
|
|
||||||
onClick={selectFolderHandle}
|
|
||||||
></i>
|
|
||||||
{selectedFolder && (
|
|
||||||
<a
|
|
||||||
className="pull-right"
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedFolder(undefined);
|
|
||||||
setTabURL(undefined);
|
|
||||||
props.onChange(undefined);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa fa-times-circle-o"
|
|
||||||
aria-hidden="true"
|
|
||||||
style={{ fontSize: 17 }}
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{TabURL && (
|
|
||||||
<div
|
|
||||||
style={{ fontSize: "10px", color: "gray", fontWeight: "bold" }}
|
|
||||||
>
|
|
||||||
({TabURL})
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* save document in folder and add new folder modal */}
|
|
||||||
<Modal
|
|
||||||
isOpen={isShowModal}
|
|
||||||
onRequestClose={() => setIsShowModal(false)}
|
|
||||||
shouldCloseOnOverlayClick={false}
|
|
||||||
contentLabel="Modal"
|
|
||||||
style={customStyles}
|
|
||||||
>
|
|
||||||
<div className="w-full min-w-[300px] md:min-w-[500px]">
|
|
||||||
{/* header */}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: "rgba(203, 200, 200, 0.09)"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ padding: "10px" }}>
|
|
||||||
<h4 className=" text-black">Select Folder</h4>
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "10px" }}>
|
|
||||||
<button
|
|
||||||
className="btn btn-sm pull-right"
|
|
||||||
data-dismiss="modal"
|
|
||||||
title="Select Folder"
|
|
||||||
style={{
|
|
||||||
width: "auto",
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: "1px solid gray",
|
|
||||||
boxshadow: "rgba(100, 100, 111, 0.2) 0px 7px 29px 0px"
|
|
||||||
}}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsAddField(false);
|
|
||||||
setFolderList([]);
|
|
||||||
setEditable(false);
|
|
||||||
if (!props.formData) {
|
|
||||||
setTabList([]);
|
|
||||||
}
|
|
||||||
setIsShowModal(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{/* body */}
|
|
||||||
<div className="p-3 space-y-6">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: "#ac4848",
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: "14px",
|
|
||||||
fontWeight: "500"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
setTabList([]);
|
|
||||||
setIsAddField(false);
|
|
||||||
setDefaultState(true);
|
|
||||||
setEditable(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Folders
|
|
||||||
</a>
|
|
||||||
{" / "}
|
|
||||||
|
|
||||||
{tabList &&
|
|
||||||
tabList.map((tab, i) => (
|
|
||||||
<React.Fragment key={`${tab.objectId}-${i}`}>
|
|
||||||
<a
|
|
||||||
title={tab[`${props.schema.data.FolderNameField}`]}
|
|
||||||
onClick={(e) => removeTabListItem(e, i)}
|
|
||||||
>
|
|
||||||
{tab[`${props.schema.data.FolderNameField}`]}
|
|
||||||
</a>
|
|
||||||
{" / "}
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
<hr />
|
|
||||||
</div>
|
|
||||||
{editable && (
|
|
||||||
<TreeEditForm
|
|
||||||
FormId={props.schema.data.FormId}
|
|
||||||
objectId={editId}
|
|
||||||
HideView={(cl) => {
|
|
||||||
setEditable(cl);
|
|
||||||
selectFolderHandle();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isAddField && !loader && !editable && (
|
|
||||||
<TreeFormComponent
|
|
||||||
Id={props.schema.data.FormId}
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
ParentField={props.schema.parent && props.schema.parent}
|
|
||||||
ParentValue={props.schema.parent && props.parent}
|
|
||||||
ChildField={props.child && props.child}
|
|
||||||
ClassName={(cls) => setClassName(cls)}
|
|
||||||
SchemaState={(cls) => setSchemaState(cls)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{loader && (
|
|
||||||
<div
|
|
||||||
className="loader-01"
|
|
||||||
style={{
|
|
||||||
marginTop: "50px",
|
|
||||||
marginLeft: "50%",
|
|
||||||
color: "rgb(0,28,28)",
|
|
||||||
fontSize: "35px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isAddField && !editable && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
maxHeight: "280px",
|
|
||||||
overflowY: "auto",
|
|
||||||
marginTop: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ul>
|
|
||||||
{folderList &&
|
|
||||||
folderList.map(
|
|
||||||
(fldr) =>
|
|
||||||
fldr[props.schema.data.FolderTypeField] ===
|
|
||||||
props.schema.data.FolderTypeValue && (
|
|
||||||
<li
|
|
||||||
key={`${fldr.objectId}`}
|
|
||||||
style={{
|
|
||||||
listStyle: "none",
|
|
||||||
padding: "10px 20px",
|
|
||||||
textTransform: "capitalize",
|
|
||||||
fontWeight: 300,
|
|
||||||
border: "1px solid gray",
|
|
||||||
borderRadius: "1px",
|
|
||||||
marginTop: "8px",
|
|
||||||
transition: "all 0.75s ease",
|
|
||||||
WebkitTransition: "all 0.5s ease",
|
|
||||||
MozTransition: "all 0.5s ease",
|
|
||||||
MsTransition: "all 0.5s ease",
|
|
||||||
OTransition: "all 0.5 ease",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className={"fa fa-folder"}
|
|
||||||
style={{
|
|
||||||
fontSize: "1.4rem",
|
|
||||||
color: "skyblue"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
<a
|
|
||||||
style={{
|
|
||||||
fontWeight: "bold",
|
|
||||||
fontSize: "12px",
|
|
||||||
marginLeft: "4px",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setTabList((tabs) => tabs.concat(fldr));
|
|
||||||
selectedItemList(fldr);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{fldr[`${props.schema.data.FolderNameField}`]}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{fldr[props.schema.data.FolderTypeField] ===
|
|
||||||
props.schema.data.FolderTypeValue && (
|
|
||||||
<a
|
|
||||||
className="float-right"
|
|
||||||
onClick={() => {
|
|
||||||
setEditable(true);
|
|
||||||
setEditId(fldr.objectId);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className={"fa fa-pencil"}
|
|
||||||
title={"Edit Folder"}
|
|
||||||
style={{
|
|
||||||
fontSize: "0.9rem",
|
|
||||||
color: "purple",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* footer */}
|
|
||||||
|
|
||||||
<div className="flex items-center p-3 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600 justify-between">
|
|
||||||
<button
|
|
||||||
className="btn btn-sm float-left createFolder"
|
|
||||||
title="Create New Folder"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsAddField(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
<i
|
|
||||||
className="fa fa-plus"
|
|
||||||
style={{ fontSize: "1.5rem" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
{!isAddField && !editable && (
|
|
||||||
<div className="flex">
|
|
||||||
{tabList.length > 0 && (
|
|
||||||
<button
|
|
||||||
className="btn btn-sm float-right deleteFolder"
|
|
||||||
title="Delete Folder"
|
|
||||||
onClick={onDeleteFolder}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
<i
|
|
||||||
className="fa fa-trash"
|
|
||||||
style={{ fontSize: "1rem" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
className="btn btn-sm float-right saveFolder"
|
|
||||||
title="Save Here"
|
|
||||||
onClick={onSubmitResult}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
{/* <i
|
|
||||||
className="fa fa-floppy-o"
|
|
||||||
style={{ fontSize: "2rem" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i> */}
|
|
||||||
<i
|
|
||||||
className="fas fa-save"
|
|
||||||
aria-hidden="true"
|
|
||||||
style={{ fontSize: "20px" }}
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
parent: state.Level1_Dropdown,
|
|
||||||
child: state.Level2_Dropdown
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps)(TreeWidget);
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import React from "react";
|
||||||
|
import "../../styles/loader.css";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { openInNewTab } from "../../constant/Utils";
|
||||||
|
|
||||||
|
const DashboardButton = (props) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
function openReport() {
|
||||||
|
if (props.Data && props.Data.Redirect_type) {
|
||||||
|
const Redirect_type = props.Data.Redirect_type;
|
||||||
|
const id = props.Data.Redirect_id;
|
||||||
|
if (Redirect_type === "Form") {
|
||||||
|
navigate(`/form/${id}`);
|
||||||
|
} else if (Redirect_type === "Report") {
|
||||||
|
navigate(`/report/${id}`);
|
||||||
|
} else if (Redirect_type === "Url") {
|
||||||
|
openInNewTab(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={() => openReport()}
|
||||||
|
className={`${
|
||||||
|
props.Data && props.Data.Redirect_type
|
||||||
|
? "cursor-pointer"
|
||||||
|
: "cursor-default"
|
||||||
|
} w-full shadow-md px-3 py-2 op-card bg-base-100`}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row items-center">
|
||||||
|
<span className="rounded-full bg-base-content bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||||
|
<i
|
||||||
|
className={`${
|
||||||
|
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||||
|
} text-[25px] lg:text-[30px]`}
|
||||||
|
></i>
|
||||||
|
</span>
|
||||||
|
<div className="op-card-title text-lg ml-3 text-base-content">
|
||||||
|
{props.Label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DashboardButton;
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import onSearchFilter from "../../constant/searchQuery";
|
|
||||||
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||||
import "../../styles/loader.css";
|
import "../../styles/loader.css";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import Tooltip from "../../primitives/Tooltip";
|
||||||
|
|
||||||
const DashboardCard = (props) => {
|
const DashboardCard = (props) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -25,9 +25,6 @@ const DashboardCard = (props) => {
|
|||||||
};
|
};
|
||||||
let body = {};
|
let body = {};
|
||||||
let currentUser;
|
let currentUser;
|
||||||
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
let currentUser1 = Parse.User.current();
|
let currentUser1 = Parse.User.current();
|
||||||
currentUser = currentUser1.id;
|
currentUser = currentUser1.id;
|
||||||
let res;
|
let res;
|
||||||
@@ -35,14 +32,9 @@ const DashboardCard = (props) => {
|
|||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
res = data[0];
|
res = data[0];
|
||||||
} else {
|
} else {
|
||||||
var emp = Parse.Object.extend(localStorage.getItem("extended_class"));
|
res = await Parse.Cloud.run("getUserDetails", {
|
||||||
var q = new Parse.Query(emp);
|
email: currentUser.get("email")
|
||||||
q.equalTo("UserId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "_User",
|
|
||||||
objectId: currentUser
|
|
||||||
});
|
});
|
||||||
res = await q.first();
|
|
||||||
if (res) res = res.toJSON();
|
if (res) res = res.toJSON();
|
||||||
}
|
}
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -50,12 +42,7 @@ const DashboardCard = (props) => {
|
|||||||
var reg = /(\#.*?\#)/gi; // eslint-disable-line
|
var reg = /(\#.*?\#)/gi; // eslint-disable-line
|
||||||
var str = props.Data.query;
|
var str = props.Data.query;
|
||||||
var test = "";
|
var test = "";
|
||||||
if (str.includes("#filterCondition#")) {
|
|
||||||
str = str.replace(
|
|
||||||
"#filterCondition#",
|
|
||||||
onSearchFilter(props.DefaultQuery)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (props.Data.extendkey) {
|
if (props.Data.extendkey) {
|
||||||
let a = props.Data.extendkey.split(".");
|
let a = props.Data.extendkey.split(".");
|
||||||
if (a.length > 0) {
|
if (a.length > 0) {
|
||||||
@@ -93,8 +80,6 @@ const DashboardCard = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
const currentUser = Parse.User.current();
|
||||||
let reg1 = /(\#.*?\#)/gi; // eslint-disable-line
|
let reg1 = /(\#.*?\#)/gi; // eslint-disable-line
|
||||||
let _query = props.Data.query;
|
let _query = props.Data.query;
|
||||||
@@ -110,13 +95,10 @@ const DashboardCard = (props) => {
|
|||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
resr = data[0];
|
resr = data[0];
|
||||||
} else {
|
} else {
|
||||||
let emp = Parse.Object.extend(
|
resr = await Parse.Cloud.run("getUserDetails", {
|
||||||
localStorage.getItem("extended_class")
|
email: currentUser.get("email")
|
||||||
);
|
});
|
||||||
let q = new Parse.Query(emp);
|
if (resr) resr = resr.toJSON();
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
let t = await q.first();
|
|
||||||
resr = t.toJSON();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let json = resr;
|
let json = resr;
|
||||||
@@ -150,47 +132,65 @@ const DashboardCard = (props) => {
|
|||||||
"X-Parse-Application-Id": parseAppId,
|
"X-Parse-Application-Id": parseAppId,
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
};
|
};
|
||||||
await axios.get(url, { headers: headers }).then((res) => {
|
// handle need your sign report count
|
||||||
if (res.data.results.length > 0) {
|
if (props.Data.Redirect_id === "4Hhwbp482K") {
|
||||||
setLoading(false);
|
const params = {
|
||||||
if (props.Data.key !== "count") {
|
reportId: props.Data.Redirect_id,
|
||||||
setresponse(res.data.results[0][props.Data.key]);
|
skip: 0,
|
||||||
} else {
|
limit: 200
|
||||||
if (props.Label === "Need your Signature") {
|
};
|
||||||
const listData = res.data?.results.filter(
|
const url = `${parseBaseUrl}/functions/getReport`;
|
||||||
(x) => x.Signers.length > 0
|
await axios
|
||||||
|
.post(url, params, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessiontoken: localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const listData = res.data?.result?.filter(
|
||||||
|
(x) => x.Signers.length > 0
|
||||||
|
);
|
||||||
|
let arr = [];
|
||||||
|
for (const obj of listData) {
|
||||||
|
const isSigner = obj.Signers.some(
|
||||||
|
(item) => item.UserId.objectId === currentUser.id
|
||||||
);
|
);
|
||||||
let arr = [];
|
if (isSigner) {
|
||||||
for (const obj of listData) {
|
let isRecord;
|
||||||
const isSigner = obj.Signers.some(
|
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
||||||
(item) => item.UserId.objectId === currentUser.id
|
isRecord = obj?.AuditTrail.some(
|
||||||
);
|
(item) =>
|
||||||
if (isSigner) {
|
item?.UserPtr?.UserId?.objectId === currentUser.id &&
|
||||||
let isRecord;
|
item.Activity === "Signed"
|
||||||
if (obj?.AuditTrail && obj?.AuditTrail.length > 0) {
|
);
|
||||||
isRecord = obj?.AuditTrail.some(
|
} else {
|
||||||
(item) =>
|
isRecord = false;
|
||||||
item?.UserPtr?.UserId?.objectId === currentUser.id &&
|
}
|
||||||
item.Activity === "Signed"
|
if (isRecord === false) {
|
||||||
);
|
arr.push(obj);
|
||||||
} else {
|
|
||||||
isRecord = false;
|
|
||||||
}
|
|
||||||
if (isRecord === false) {
|
|
||||||
arr.push(obj);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setresponse(arr.length);
|
}
|
||||||
|
setresponse(arr.length);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await axios.get(url, { headers: headers }).then((res) => {
|
||||||
|
if (res.data.results.length > 0) {
|
||||||
|
setLoading(false);
|
||||||
|
if (props.Data.key !== "count") {
|
||||||
|
setresponse(res.data.results[0][props.Data.key]);
|
||||||
} else {
|
} else {
|
||||||
setresponse(res.data[props.Data.key]);
|
setresponse(res.data[props.Data.key]);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setresponse(0);
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
setresponse(0);
|
}
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Problem", e);
|
console.error("Problem", e);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -219,21 +219,16 @@ const DashboardCard = (props) => {
|
|||||||
|
|
||||||
if (restr.includes("#")) {
|
if (restr.includes("#")) {
|
||||||
try {
|
try {
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
const currentUser = Parse.User.current();
|
||||||
let res;
|
let res;
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
if (localStorage.getItem("Extand_Class")) {
|
||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
res = data[0];
|
res = data[0];
|
||||||
} else {
|
} else {
|
||||||
var emp = Parse.Object.extend(
|
let resr = await Parse.Cloud.run("getUserDetails", {
|
||||||
localStorage.getItem("extended_class")
|
email: currentUser.get("email")
|
||||||
);
|
});
|
||||||
var q = new Parse.Query(emp);
|
if (res) res = resr.toJSON();
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
let resr = await q.first();
|
|
||||||
res = resr.toJSON();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let json = res;
|
let json = res;
|
||||||
@@ -341,23 +336,30 @@ const DashboardCard = (props) => {
|
|||||||
props.Data && props.Data.Redirect_type
|
props.Data && props.Data.Redirect_type
|
||||||
? "cursor-pointer"
|
? "cursor-pointer"
|
||||||
: "cursor-default"
|
: "cursor-default"
|
||||||
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-start gap-5">
|
<div className="flex items-center justify-start gap-5 text-white">
|
||||||
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||||
<i
|
<i
|
||||||
className={`${
|
className={`${
|
||||||
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||||
} text-[25px] lg:text-[30px]`}
|
} text-[25px] lg:text-[30px]`}
|
||||||
></i>
|
></i>
|
||||||
</span>
|
</span>
|
||||||
<div className="">
|
<div className="font-medium">
|
||||||
<div className="text-base lg:text-lg"> {props.Label}</div>
|
<div className="text-base lg:text-lg"> {props.Label}</div>
|
||||||
<div className="text-2xl font-light">
|
<div className="text-2xl font-light">
|
||||||
{loading ? <div className="loader-01"></div> : setFormat(response)}
|
{loading ? <div className="loader-01"></div> : setFormat(response)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-xs absolute top-3 right-2">
|
||||||
|
<Tooltip
|
||||||
|
id={props.Label}
|
||||||
|
iconColor={"white"}
|
||||||
|
message={props?.Data?.tourMessage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,45 +3,65 @@ import Parse from "parse";
|
|||||||
import ReportTable from "../../primitives/GetReportDisplay";
|
import ReportTable from "../../primitives/GetReportDisplay";
|
||||||
import reportJson from "../../json/ReportJson";
|
import reportJson from "../../json/ReportJson";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import Loader from "../../primitives/Loader";
|
||||||
function DashboardReport(props) {
|
function DashboardReport(props) {
|
||||||
// console.log("props ", props)
|
// console.log("props ", props)
|
||||||
const [List, setList] = useState([]);
|
const [List, setList] = useState([]);
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
const [reportName, setReportName] = useState("");
|
const [reportName, setReportName] = useState("");
|
||||||
const [actions, setActions] = useState([]);
|
const [actions, setActions] = useState([]);
|
||||||
|
const [heading, setHeading] = useState([]);
|
||||||
|
const [isNextRecord, setIsNextRecord] = useState(false);
|
||||||
|
const [isMoreDocs, setIsMoreDocs] = useState(true);
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const docPerPage = 5;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setReportName("");
|
setReportName("");
|
||||||
getReportData(props.Record.reportId);
|
getReportData(props.Record.reportId);
|
||||||
|
|
||||||
|
// Function returned from useEffect is called on unmount
|
||||||
|
return () => {
|
||||||
|
setIsLoader(true);
|
||||||
|
setList([]);
|
||||||
|
setIsNextRecord(false);
|
||||||
|
// Here it'll abort the fetch
|
||||||
|
abortController.abort();
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line
|
||||||
}, [props.Record.reportId]);
|
}, [props.Record.reportId]);
|
||||||
|
|
||||||
const getReportData = async (id) => {
|
// below useEffect call when isNextRecord state is true and fetch next record
|
||||||
|
useEffect(() => {
|
||||||
|
if (isNextRecord) {
|
||||||
|
getReportData(props.Record.reportId, List.length, 200);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [isNextRecord]);
|
||||||
|
|
||||||
|
const getReportData = async (id, skipUserRecord = 0, limit = 200) => {
|
||||||
setIsLoader(true);
|
setIsLoader(true);
|
||||||
const json = reportJson(id);
|
const json = reportJson(id);
|
||||||
if (json) {
|
if (json) {
|
||||||
setActions(json.actions);
|
setActions(json.actions);
|
||||||
setReportName(json.reportName);
|
setReportName(json.reportName);
|
||||||
const { className, params, keys, orderBy } = json;
|
setHeading(json.heading);
|
||||||
Parse.serverURL = localStorage.getItem("BaseUrl12");
|
const currentUser = Parse.User.current().id;
|
||||||
Parse.initialize(localStorage.getItem("AppID12"));
|
|
||||||
const serverURL =
|
|
||||||
localStorage.getItem("BaseUrl12") + "classes/" + className;
|
|
||||||
|
|
||||||
const strParams = JSON.stringify(params);
|
|
||||||
const strKeys = keys.join();
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
sessiontoken: localStorage.getItem("accesstoken")
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`;
|
const params = { reportId: id, skip: skipUserRecord, limit: limit };
|
||||||
const res = await axios.get(url, { headers: headers });
|
const url = `${localStorage.getItem("baseUrl")}/functions/getReport`;
|
||||||
// console.log("res ", res.data?.results);
|
const res = await axios.post(url, params, {
|
||||||
|
headers: headers,
|
||||||
|
signal: abortController.signal // is used to cancel fetch query
|
||||||
|
});
|
||||||
if (id === "5Go51Q7T8r") {
|
if (id === "5Go51Q7T8r") {
|
||||||
const currentUser = Parse.User.current().id;
|
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
||||||
const listData = res.data?.results.filter(
|
|
||||||
(x) => x.Signers.length > 0
|
|
||||||
);
|
|
||||||
let arr = [];
|
let arr = [];
|
||||||
for (const obj of listData) {
|
for (const obj of listData) {
|
||||||
const isSigner = obj.Signers.some(
|
const isSigner = obj.Signers.some(
|
||||||
@@ -63,13 +83,36 @@ function DashboardReport(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setList(arr);
|
if (arr.length === docPerPage) {
|
||||||
|
setIsMoreDocs(true);
|
||||||
|
} else {
|
||||||
|
setIsMoreDocs(false);
|
||||||
|
}
|
||||||
|
setList((prevRecord) =>
|
||||||
|
prevRecord.length > 0 ? [...prevRecord, ...arr] : arr
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
setList(res.data?.results);
|
if (res.data.result.length === docPerPage) {
|
||||||
|
setIsMoreDocs(true);
|
||||||
|
} else {
|
||||||
|
setIsMoreDocs(false);
|
||||||
|
}
|
||||||
|
setIsNextRecord(false);
|
||||||
|
if (!res.data.result.error) {
|
||||||
|
setList((prevRecord) =>
|
||||||
|
prevRecord.length > 0
|
||||||
|
? [...prevRecord, ...res.data.result]
|
||||||
|
: res.data.result
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
const isCancel = axios.isCancel(err);
|
||||||
|
if (!isCancel) {
|
||||||
|
console.log("err ", err);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -79,21 +122,8 @@ function DashboardReport(props) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="h-[250px] flex justify-center items-center">
|
||||||
style={{
|
<Loader />
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -101,8 +131,12 @@ function DashboardReport(props) {
|
|||||||
<ReportTable
|
<ReportTable
|
||||||
ReportName={reportName}
|
ReportName={reportName}
|
||||||
List={List}
|
List={List}
|
||||||
act={"Sign"}
|
setList={setList}
|
||||||
actions={actions}
|
actions={actions}
|
||||||
|
heading={heading}
|
||||||
|
setIsNextRecord={setIsNextRecord}
|
||||||
|
isMoreDocs={isMoreDocs}
|
||||||
|
docPerPage={docPerPage}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
|
<div className="flex items-center justify-center h-[100px] w-full bg-white rounded">
|
||||||
|
|||||||
@@ -1,337 +1,69 @@
|
|||||||
import React, { useState, Suspense, lazy } from "react";
|
import React, { Suspense, lazy } from "react";
|
||||||
import moment from "moment";
|
const DashboardButton = lazy(() => import("./DashboardButton"));
|
||||||
import ErrorBoundary from "../ErrorBoundary";
|
|
||||||
|
|
||||||
const DashboardCard = lazy(() => import("./DashboardCard"));
|
const DashboardCard = lazy(() => import("./DashboardCard"));
|
||||||
|
|
||||||
const DashboardReport = lazy(() => import("./DashboardReport"));
|
const DashboardReport = lazy(() => import("./DashboardReport"));
|
||||||
|
const buttonList = [
|
||||||
|
{
|
||||||
|
label: "Sign yourself",
|
||||||
|
redirectId: "sHAnZphf69",
|
||||||
|
redirectType: "Form",
|
||||||
|
icon: "fas fa-pen-nib"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Request signature",
|
||||||
|
redirectId: "8mZzFxbG1z",
|
||||||
|
redirectType: "Form",
|
||||||
|
icon: "fa-solid fa-paper-plane"
|
||||||
|
}
|
||||||
|
];
|
||||||
const GetDashboard = (props) => {
|
const GetDashboard = (props) => {
|
||||||
const [filter, setfilter] = useState("");
|
const Button = ({ label, redirectId, redirectType, icon }) => (
|
||||||
const [option, setoption] = useState("");
|
<DashboardButton
|
||||||
|
Icon={icon}
|
||||||
const onSearchFilter = (e) => {
|
Label={label}
|
||||||
let _search = e.target.value;
|
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
|
||||||
localStorage.setItem("DashboardDefaultFilter", _search);
|
/>
|
||||||
switch (_search) {
|
);
|
||||||
case "Today": {
|
|
||||||
let d = new Date();
|
|
||||||
let formDate = new Date(
|
|
||||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
let Todate = new Date(
|
|
||||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let Query = `'$gte':'${formDate}','$lte':'${Todate}'`;
|
|
||||||
setfilter(Query);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Yesterday": {
|
|
||||||
let d1 = new Date();
|
|
||||||
d1.setDate(d1.getDate() - 1);
|
|
||||||
let yesFrDate = new Date(
|
|
||||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
|
|
||||||
let YesTodate = new Date(
|
|
||||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let Query1 = `'$gte':'${yesFrDate}','$lte':'${YesTodate}'`;
|
|
||||||
setfilter(Query1);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "This week": {
|
|
||||||
let oneDay = 24 * 60 * 60 * 1000;
|
|
||||||
let curr = new Date(); // get current date
|
|
||||||
let first = curr.getDate() - curr.getDay(); // First day is the day of the month - the day of the week
|
|
||||||
|
|
||||||
let firstday = new Date(
|
|
||||||
new Date(
|
|
||||||
Date.UTC(
|
|
||||||
curr.getFullYear(),
|
|
||||||
curr.getMonth(),
|
|
||||||
curr.getDate(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
).setDate(first)
|
|
||||||
).toISOString();
|
|
||||||
let diffDays = Math.round(
|
|
||||||
Math.abs(
|
|
||||||
(new Date(
|
|
||||||
Date.UTC(
|
|
||||||
curr.getFullYear(),
|
|
||||||
curr.getMonth(),
|
|
||||||
curr.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
) -
|
|
||||||
new Date(firstday)) /
|
|
||||||
oneDay
|
|
||||||
)
|
|
||||||
); //Days since last week
|
|
||||||
let last = first + diffDays;
|
|
||||||
let lastday = new Date(
|
|
||||||
new Date(
|
|
||||||
Date.UTC(
|
|
||||||
curr.getFullYear(),
|
|
||||||
curr.getMonth(),
|
|
||||||
curr.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).setDate(last)
|
|
||||||
).toISOString();
|
|
||||||
let Query2 = `'$gte':'${firstday}','$lte':'${lastday}'`;
|
|
||||||
setfilter(Query2);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last 7 days": {
|
|
||||||
let d7 = new Date();
|
|
||||||
let d7l = new Date();
|
|
||||||
d7.setDate(d7.getDate() - 7);
|
|
||||||
let l7t = new Date(
|
|
||||||
Date.UTC(d7l.getFullYear(), d7l.getMonth(), d7l.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let date7 = new Date(
|
|
||||||
Date.UTC(d7.getFullYear(), d7.getMonth(), d7.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
let Query7 = `'$gte':'${date7}','$lte':'${l7t}'`;
|
|
||||||
setfilter(Query7);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last 14 days": {
|
|
||||||
let d14 = new Date();
|
|
||||||
let d14l = new Date();
|
|
||||||
let l14t = new Date(
|
|
||||||
Date.UTC(
|
|
||||||
d14l.getFullYear(),
|
|
||||||
d14l.getMonth(),
|
|
||||||
d14l.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).toISOString();
|
|
||||||
d14.setDate(d14.getDate() - 14);
|
|
||||||
let date14 = new Date(
|
|
||||||
Date.UTC(d14.getFullYear(), d14.getMonth(), d14.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
|
|
||||||
let Query14 = `'$gte':'${date14}','$lte':'${l14t}'`;
|
|
||||||
setfilter(Query14);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last 30 days": {
|
|
||||||
let d30 = new Date();
|
|
||||||
let d30l = new Date();
|
|
||||||
let l30t = new Date(
|
|
||||||
Date.UTC(
|
|
||||||
d30l.getFullYear(),
|
|
||||||
d30l.getMonth(),
|
|
||||||
d30l.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).toISOString();
|
|
||||||
d30.setDate(d30.getDate() - 30);
|
|
||||||
let date30 = new Date(
|
|
||||||
Date.UTC(d30.getFullYear(), d30.getMonth(), d30.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
let Query30 = `'$gte':'${date30}','$lte':'${l30t}'`;
|
|
||||||
setfilter(Query30);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last week": {
|
|
||||||
const from_date = moment().startOf("week").subtract(7, "days");
|
|
||||||
const to_date = moment().endOf("week").subtract(7, "days");
|
|
||||||
let Queryl = `'$gte':'${from_date.toISOString()}','$lte':'${to_date.toISOString()}'`;
|
|
||||||
setfilter(Queryl);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "This month": {
|
|
||||||
const tdate = new Date();
|
|
||||||
let tfirstDay = new Date(
|
|
||||||
tdate.getFullYear(),
|
|
||||||
tdate.getMonth(),
|
|
||||||
1
|
|
||||||
).toISOString();
|
|
||||||
let tlastDay = new Date(
|
|
||||||
tdate.getFullYear(),
|
|
||||||
tdate.getMonth() + 1,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let Querylt = `'$gte':'${tfirstDay}','$lte':'${tlastDay}'`;
|
|
||||||
setfilter(Querylt);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last month": {
|
|
||||||
const ldate = new Date();
|
|
||||||
var lfirstDay = new Date(
|
|
||||||
ldate.getFullYear(),
|
|
||||||
ldate.getMonth() - 1,
|
|
||||||
2
|
|
||||||
).toISOString();
|
|
||||||
var llastDay = new Date(
|
|
||||||
ldate.getFullYear(),
|
|
||||||
ldate.getMonth(),
|
|
||||||
1
|
|
||||||
).toISOString();
|
|
||||||
let Queryltl = `'$gte':'${lfirstDay}','$lte':'${llastDay}'`;
|
|
||||||
setfilter(Queryltl);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "All": {
|
|
||||||
let fall = new Date(
|
|
||||||
new Date().getFullYear() - 100,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let fall_year = `'$gte':'${fall}'`;
|
|
||||||
setfilter(fall_year);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "This year": {
|
|
||||||
let fcy = new Date(
|
|
||||||
new Date().getFullYear(),
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let lcy = new Date(
|
|
||||||
new Date().getFullYear(),
|
|
||||||
11,
|
|
||||||
31,
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
).toISOString();
|
|
||||||
let cur_year = `'$gte':'${fcy}','$lte':'${lcy}'`;
|
|
||||||
setfilter(cur_year);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "Last year": {
|
|
||||||
let fly = new Date(
|
|
||||||
new Date().getFullYear() - 1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let lly = new Date(
|
|
||||||
new Date().getFullYear() - 1,
|
|
||||||
11,
|
|
||||||
31,
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
).toISOString();
|
|
||||||
let last_year = `'$gte':'${fly}','$lte':'${lly}'`;
|
|
||||||
setfilter(last_year);
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
setoption(_search);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const renderSwitchWithTour = (col) => {
|
const renderSwitchWithTour = (col) => {
|
||||||
switch (col.widget.type) {
|
switch (col.widget.type) {
|
||||||
case "Card":
|
case "Card":
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
|
className={`${
|
||||||
|
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
|
||||||
|
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
|
||||||
data-tut={col.widget.data.tourSection}
|
data-tut={col.widget.data.tourSection}
|
||||||
style={{ background: col.widget.bgColor }}
|
|
||||||
>
|
>
|
||||||
<Suspense fallback={<div>please wait</div>}>
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="h-[150px] w-full flex justify-center items-center">
|
||||||
|
loading...
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
<DashboardCard
|
<DashboardCard
|
||||||
Icon={col.widget.icon}
|
Icon={col.widget.icon}
|
||||||
Label={col.widget.label}
|
Label={col.widget.label}
|
||||||
Filter={filter}
|
|
||||||
Format={col.widget.format && col.widget.format}
|
Format={col.widget.format && col.widget.format}
|
||||||
Data={col.widget.data}
|
Data={col.widget.data}
|
||||||
FilterData={col.widget.filter}
|
FilterData={col.widget.filter}
|
||||||
DefaultQuery={props.DefaultQuery}
|
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "customeHtml":
|
|
||||||
return (
|
|
||||||
<div data-tut={col.widget.data.tourSection}>
|
|
||||||
<select
|
|
||||||
id="searchFiltr"
|
|
||||||
className="form-control"
|
|
||||||
value={
|
|
||||||
option ||
|
|
||||||
localStorage.getItem("DashboardDefaultFilter") ||
|
|
||||||
col.widget.data[0].default
|
|
||||||
}
|
|
||||||
onChange={onSearchFilter}
|
|
||||||
>
|
|
||||||
{col.widget.data[0].options.map((opt) => (
|
|
||||||
<option key={opt} value={opt}>
|
|
||||||
{opt}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
case "customeTag":
|
|
||||||
return (
|
|
||||||
<h3
|
|
||||||
data-tut={col.widget.data.tourSection}
|
|
||||||
style={{
|
|
||||||
marginBottom: "4px",
|
|
||||||
color: "currentColor",
|
|
||||||
marginLeft: "4px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{col.widget.label}
|
|
||||||
</h3>
|
|
||||||
);
|
|
||||||
case "report": {
|
case "report": {
|
||||||
let _filter = `${col.widget.filterKey}:${filter}`;
|
|
||||||
return (
|
return (
|
||||||
<div data-tut={col.widget.data.tourSection}>
|
<div data-tut={col.widget.data.tourSection}>
|
||||||
<Suspense fallback={<div>please wait</div>}>
|
<Suspense fallback={<div>please wait</div>}>
|
||||||
<div className="mb-4 md:mb-0">
|
<div className="mb-3 md:mb-0">
|
||||||
<DashboardReport Filter={_filter} Record={col.widget} />
|
<DashboardReport Record={col.widget} />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const renderSwitch = (col) => {
|
const renderSwitch = (col) => {
|
||||||
@@ -339,92 +71,66 @@ const GetDashboard = (props) => {
|
|||||||
case "Card":
|
case "Card":
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
|
className={`${
|
||||||
style={{ background: col.widget.bgColor }}
|
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
|
||||||
|
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
|
||||||
>
|
>
|
||||||
<Suspense fallback={<div>please wait</div>}>
|
<Suspense fallback={<div>please wait</div>}>
|
||||||
<DashboardCard
|
<DashboardCard
|
||||||
Icon={col.widget.icon}
|
Icon={col.widget.icon}
|
||||||
Label={col.widget.label}
|
Label={col.widget.label}
|
||||||
Filter={filter}
|
|
||||||
Format={col.widget.format && col.widget.format}
|
Format={col.widget.format && col.widget.format}
|
||||||
Data={col.widget.data}
|
Data={col.widget.data}
|
||||||
FilterData={col.widget.filter}
|
FilterData={col.widget.filter}
|
||||||
DefaultQuery={props.DefaultQuery}
|
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case "customeHtml":
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<select
|
|
||||||
id="searchFiltr"
|
|
||||||
className="form-control"
|
|
||||||
value={
|
|
||||||
option ||
|
|
||||||
localStorage.getItem("DashboardDefaultFilter") ||
|
|
||||||
col.widget.data[0].default
|
|
||||||
}
|
|
||||||
onChange={onSearchFilter}
|
|
||||||
>
|
|
||||||
{col.widget.data[0].options.map((opt) => (
|
|
||||||
<option key={opt} value={opt}>
|
|
||||||
{opt}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
case "customeTag":
|
|
||||||
return (
|
|
||||||
<h3
|
|
||||||
style={{
|
|
||||||
marginBottom: "4px",
|
|
||||||
color: "currentColor",
|
|
||||||
marginLeft: "4px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{col.widget.label}
|
|
||||||
</h3>
|
|
||||||
);
|
|
||||||
case "report": {
|
case "report": {
|
||||||
let _filter = `${col.widget.filterKey}:${filter}`;
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<div>please wait</div>}>
|
<Suspense fallback={<div>please wait</div>}>
|
||||||
<div className="mb-4 md:mb-0">
|
<div className="mb-3 md:mb-0">
|
||||||
<DashboardReport Filter={_filter} Record={col.widget} />
|
<DashboardReport Record={col.widget} />
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<div>
|
||||||
<div>
|
<div className="mb-3">
|
||||||
{props.dashboard.map((val, key) => (
|
<div
|
||||||
<div key={"a" + key} className="row">
|
data-tut={"tourbutton"}
|
||||||
{val.columns.map((col, i) =>
|
className="flex flex-col md:flex-row gap-4"
|
||||||
col.widget.data && col.widget.data.tourSection ? (
|
>
|
||||||
<div key={i} className={props.classnameArray[key][i]}>
|
{buttonList.map((btn) => (
|
||||||
{renderSwitchWithTour(col)}
|
<Button
|
||||||
</div>
|
key={btn.label}
|
||||||
) : (
|
label={btn.label}
|
||||||
<div key={i} className={props.classnameArray[key][i]}>
|
redirectType={btn.redirectType}
|
||||||
{renderSwitch(col)}
|
redirectId={btn.redirectId}
|
||||||
</div>
|
icon={btn.icon}
|
||||||
)
|
/>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</ErrorBoundary>
|
<div className="grid grid-cols-12 w-full gap-x-4">
|
||||||
|
{props?.dashboard?.columns?.map((col, i) =>
|
||||||
|
col.widget.data && col.widget.data.tourSection ? (
|
||||||
|
<div key={i} className={col?.colsize}>
|
||||||
|
{renderSwitchWithTour(col)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div key={i} className={col?.colsize}>
|
||||||
|
{renderSwitch(col)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { SaveFileSize } from "../../constant/saveFileSize";
|
|
||||||
import Parse from "parse";
|
|
||||||
import sanitizeFileName from "../../primitives/sanitizeFileName";
|
|
||||||
|
|
||||||
const FileUpload = (props) => {
|
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
const [_fileupload, setFileUpload] = useState("");
|
|
||||||
const [fileload, setfileload] = useState(false);
|
|
||||||
|
|
||||||
const [localValue, setLocalValue] = useState("");
|
|
||||||
const [Message] = useState(false);
|
|
||||||
const [percentage, setpercentage] = useState(0);
|
|
||||||
|
|
||||||
const REQUIRED_FIELD_SYMBOL = "*";
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!props.formData) {
|
|
||||||
setFileUpload([]);
|
|
||||||
props.onChange(undefined);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [props.formData]);
|
|
||||||
const onChange = (e) => {
|
|
||||||
try {
|
|
||||||
let files = e.target.files;
|
|
||||||
setLocalValue(e.target.files);
|
|
||||||
if (typeof files[0] !== "undefined") {
|
|
||||||
if (props.schema.filetypes && props.schema.filetypes.length > 0) {
|
|
||||||
var fileName = files[0].name;
|
|
||||||
var fileNameExt = fileName
|
|
||||||
.substr(fileName.lastIndexOf(".") + 1)
|
|
||||||
.toLowerCase();
|
|
||||||
let Extensions = props.schema.filetypes.map((x) => x.toLowerCase());
|
|
||||||
let arr = Extensions.filter((x) => x === fileNameExt);
|
|
||||||
if (arr.length > 0) {
|
|
||||||
console.log("multiple type");
|
|
||||||
} else {
|
|
||||||
alert(
|
|
||||||
"Only these file types are accepted : " + Extensions.join(", ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (props.schema.maxfilesizeKB && props.schema.maxfilesizeKB !== "") {
|
|
||||||
// console.log(Math.round(files[0].size / 1024));
|
|
||||||
if (
|
|
||||||
Math.round(Number(files[0].size) / 1024) >=
|
|
||||||
props.schema.maxfilesizeKB
|
|
||||||
) {
|
|
||||||
alert(
|
|
||||||
`The selected file size is too large. Please select a file less than ${Math.round(
|
|
||||||
props.schema.maxfilesizeKB / 1024
|
|
||||||
)} MB`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleFileUpload(files[0]);
|
|
||||||
} else {
|
|
||||||
alert("Please select file.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert(error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFileUpload = async (file) => {
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
setfileload(true);
|
|
||||||
const size = file.size;
|
|
||||||
const fileName = file.name;
|
|
||||||
const name = sanitizeFileName(fileName);
|
|
||||||
const pdfFile = file;
|
|
||||||
const parseFile = new Parse.File(name, pdfFile);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await parseFile.save({
|
|
||||||
progress: (progressValue, loaded, total, { type }) => {
|
|
||||||
if (type === "upload" && progressValue !== null) {
|
|
||||||
const percentCompleted = Math.round((loaded * 100) / total);
|
|
||||||
// console.log("percentCompleted ", percentCompleted);
|
|
||||||
setpercentage(percentCompleted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setFileUpload(response.url());
|
|
||||||
props.onChange(response.url());
|
|
||||||
setfileload(false);
|
|
||||||
// The response object will contain information about the uploaded file
|
|
||||||
// console.log("File uploaded:", response);
|
|
||||||
|
|
||||||
// You can access the URL of the uploaded file using response.url()
|
|
||||||
// console.log("File URL:", response.url());
|
|
||||||
if (response.url()) {
|
|
||||||
SaveFileSize(size, response.url());
|
|
||||||
return response.url();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setfileload(false);
|
|
||||||
setpercentage(0);
|
|
||||||
console.error("Error uploading file:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let fileView =
|
|
||||||
props.formData &&
|
|
||||||
props.schema.uploadtype === "s3viajw" ? null : props.formData &&
|
|
||||||
props.formData ? (
|
|
||||||
<React.Fragment>
|
|
||||||
<a
|
|
||||||
href={props.formData}
|
|
||||||
title={props.formData}
|
|
||||||
style={{ paddingBottom: "10px", color: "blue" }}
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
</React.Fragment>
|
|
||||||
) : fileload === false ? null : (
|
|
||||||
<React.Fragment>
|
|
||||||
<a
|
|
||||||
href={_fileupload}
|
|
||||||
title={_fileupload}
|
|
||||||
style={{ paddingBottom: "10px" }}
|
|
||||||
>
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div style={{ display: "inline-block" }}>
|
|
||||||
<label htmlFor={props.name}>
|
|
||||||
{props.schema.title}
|
|
||||||
{props.required && (
|
|
||||||
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
|
|
||||||
)}
|
|
||||||
{fileload ? (
|
|
||||||
<div className="flex items-center gap-x-2">
|
|
||||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
|
||||||
<div
|
|
||||||
className="h-2 rounded-full bg-blue-500"
|
|
||||||
style={{ width: `${percentage}%` }}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<span className="text-black text-sm">{percentage}%</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
Message && (
|
|
||||||
<span style={{ fontSize: "9px", marginLeft: "4px" }}>
|
|
||||||
Processing will take 5-10 mins....
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
{props.schema.helpbody ? (
|
|
||||||
<div className="dropdown pull-right">
|
|
||||||
<i
|
|
||||||
className="far fa-question-circle dropdown-toggle hovereffect"
|
|
||||||
aria-hidden="true"
|
|
||||||
id="dropdownMenuButton"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
style={{
|
|
||||||
fontSize: "12px",
|
|
||||||
color: "purple",
|
|
||||||
cursor: "pointer !important",
|
|
||||||
position: "relative",
|
|
||||||
bottom: "0px",
|
|
||||||
left: "0px",
|
|
||||||
paddingBottom: "4px",
|
|
||||||
paddingLeft: "4px"
|
|
||||||
}}
|
|
||||||
></i>
|
|
||||||
<div
|
|
||||||
className="dropdown-menu"
|
|
||||||
aria-labelledby="dropdownMenuButton"
|
|
||||||
style={{
|
|
||||||
marginleft: "-121px",
|
|
||||||
margintop: "-14px",
|
|
||||||
position: "absolute",
|
|
||||||
padding: "10px",
|
|
||||||
top: "102px!important"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<br />
|
|
||||||
{props.schema.helplink ? (
|
|
||||||
<a
|
|
||||||
href={props.schema.helplink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
>
|
|
||||||
Read more..
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<span style={{ marginLeft: "10px" }}>{fileView}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<>
|
|
||||||
{localValue ? (
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="hashfile"
|
|
||||||
style={{
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
color: "gray",
|
|
||||||
backgroundColor: "white",
|
|
||||||
padding: "5px 10px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
fontSize: "13px",
|
|
||||||
width: "100%",
|
|
||||||
fontWeight: "bold"
|
|
||||||
}}
|
|
||||||
accept="application/pdf,application/vnd.ms-excel"
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
) : props.formData ? (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
color: "gray",
|
|
||||||
backgroundColor: "white",
|
|
||||||
padding: "5px 10px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
fontSize: "13px",
|
|
||||||
width: "100%",
|
|
||||||
fontWeight: "bold"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
file selected : {props.formData.split("/")[3]}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="hashfile"
|
|
||||||
style={{
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
color: "gray",
|
|
||||||
backgroundColor: "white",
|
|
||||||
padding: "5px 10px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
fontSize: "13px",
|
|
||||||
width: "100%",
|
|
||||||
fontWeight: "bold"
|
|
||||||
}}
|
|
||||||
accept="application/pdf,application/vnd.ms-excel"
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FileUpload;
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import Parse from "parse";
|
|
||||||
import axios from "axios";
|
|
||||||
|
|
||||||
export class HiddenField extends Component {
|
|
||||||
state = {
|
|
||||||
baseUrl: localStorage.getItem("baseUrl"),
|
|
||||||
parseAppId: localStorage.getItem("parseAppId"),
|
|
||||||
_fields: ""
|
|
||||||
};
|
|
||||||
loadData = async () => {
|
|
||||||
if (this.props.schema.default) {
|
|
||||||
this.setState({ _fields: this.props.schema.default });
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Parse.serverURL = this.state.baseUrl;
|
|
||||||
Parse.initialize(this.state.parseAppId);
|
|
||||||
let currentUser = await Parse.User.current();
|
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
|
||||||
let _rdata = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
let custome_Filed = "";
|
|
||||||
let splitArray = this.props.schema.data.valueKey.split(".");
|
|
||||||
if (splitArray.length > 1) {
|
|
||||||
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
|
|
||||||
} else {
|
|
||||||
let _Filed = _rdata[0][splitArray[0]];
|
|
||||||
if (typeof _Filed === "object") {
|
|
||||||
custome_Filed = {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: _Filed["className"],
|
|
||||||
objectId: _Filed["objectId"]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
custome_Filed = _rdata[0][splitArray[0]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.setState({ _fields: custome_Filed });
|
|
||||||
} else {
|
|
||||||
if (currentUser) {
|
|
||||||
let url = `${this.state.baseUrl}classes/${localStorage.getItem(
|
|
||||||
"extended_class"
|
|
||||||
)}?where={"UserId":{"__type":"Pointer","className":"_User","objectId":"${
|
|
||||||
currentUser.id
|
|
||||||
}"}}&keys=${this.props.schema.data.valueKey}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": this.state.parseAppId
|
|
||||||
};
|
|
||||||
await axios.get(url, { headers: headers }).then((res) => {
|
|
||||||
let custome_Filed = "";
|
|
||||||
let _rdata = res.data.results;
|
|
||||||
let splitArray = this.props.schema.data.valueKey.split(".");
|
|
||||||
if (splitArray.length > 1) {
|
|
||||||
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
|
|
||||||
} else {
|
|
||||||
console.log(_rdata[0][splitArray[0]]);
|
|
||||||
custome_Filed = _rdata[0][splitArray[0]];
|
|
||||||
}
|
|
||||||
this.setState({ _fields: custome_Filed });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('err', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
componentDidMount() {
|
|
||||||
this.loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
value={this.state._fields}
|
|
||||||
onChange={this.props.onChange(this.state._fields)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default HiddenField;
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import parse from "html-react-parser";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
const LabelField = (props) => {
|
|
||||||
const { Title, Name, Required, HelpBody, HelpLink } = props;
|
|
||||||
const REQUIRED_FIELD_SYMBOL = "*";
|
|
||||||
return (
|
|
||||||
<div style={{ display: "inline-block" }}>
|
|
||||||
<label htmlFor={Name}>
|
|
||||||
{Title}
|
|
||||||
{Required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
|
|
||||||
{HelpBody ? (
|
|
||||||
<div className="dropdown pull-right">
|
|
||||||
<i
|
|
||||||
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
|
|
||||||
aria-hidden="true"
|
|
||||||
id="dropdownMenuButton"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
style={{
|
|
||||||
fontSize: "12px",
|
|
||||||
color: "purple",
|
|
||||||
cursor: "pointer !important",
|
|
||||||
position: "relative",
|
|
||||||
bottom: "0px",
|
|
||||||
left: "0px",
|
|
||||||
paddingBottom: "4px",
|
|
||||||
paddingLeft: "4px"
|
|
||||||
}}
|
|
||||||
></i>
|
|
||||||
<div
|
|
||||||
className="dropdown-menu"
|
|
||||||
aria-labelledby="dropdownMenuButton"
|
|
||||||
style={{
|
|
||||||
marginleft: "-121px",
|
|
||||||
margintop: "-14px",
|
|
||||||
position: "absolute",
|
|
||||||
padding: "10px",
|
|
||||||
top: "102px!important"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{parse(`
|
|
||||||
${HelpBody}
|
|
||||||
`)}
|
|
||||||
<br />
|
|
||||||
{HelpLink ? (
|
|
||||||
<a
|
|
||||||
href={HelpLink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
>
|
|
||||||
Read more..
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
LabelField.propTypes = {
|
|
||||||
Name: PropTypes.string.isRequired,
|
|
||||||
Title: PropTypes.string.isRequired
|
|
||||||
};
|
|
||||||
export default LabelField;
|
|
||||||
@@ -1,484 +0,0 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { onChangeLevel1Dropdown } from "../../redux/actions/index";
|
|
||||||
import axios from "axios";
|
|
||||||
import Parse from "parse";
|
|
||||||
import parse from "html-react-parser";
|
|
||||||
|
|
||||||
const Level1Dropdown = (props) => {
|
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
const [Level1_DD, setLevel1_DD] = useState([]);
|
|
||||||
const [toastColor] = useState("#d9534f");
|
|
||||||
const [toastDescription, setToastDescription] = useState("");
|
|
||||||
const [active, setActive] = useState(false);
|
|
||||||
|
|
||||||
const getParams = (offset) => {
|
|
||||||
const params = {
|
|
||||||
skip: offset
|
|
||||||
};
|
|
||||||
return params;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Level1DropdownData = async () => {
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
props.formData &&
|
|
||||||
props.uiSchema["ui:disabled"] &&
|
|
||||||
props.formData !== "Select"
|
|
||||||
) {
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData}`;
|
|
||||||
if (typeof props.formData === "object") {
|
|
||||||
url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData.objectId}`;
|
|
||||||
}
|
|
||||||
let headers1 = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
let data = [];
|
|
||||||
let response = [];
|
|
||||||
await axios.get(url, { headers: headers1 }).then((res) => {
|
|
||||||
if (res) {
|
|
||||||
data.push(res.data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.length > 0) {
|
|
||||||
let temp = [];
|
|
||||||
data.forEach((x) => {
|
|
||||||
let opt = "";
|
|
||||||
if (Array.isArray(x[props.schema.data.valueKey])) {
|
|
||||||
x[props.schema.data.valueKey].forEach((tt) => {
|
|
||||||
if (typeof tt === "object") {
|
|
||||||
opt = {
|
|
||||||
objectId: tt[props.schema.data.displayKey],
|
|
||||||
option: tt[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: tt,
|
|
||||||
option: tt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
temp.push(opt);
|
|
||||||
});
|
|
||||||
} else if (props.schema.data.isPointer) {
|
|
||||||
if (props.schema.data.valueKey.includes(".")) {
|
|
||||||
let newArr = props.schema.data.valueKey.split(".");
|
|
||||||
if (newArr.length === 2) {
|
|
||||||
opt = {
|
|
||||||
objectId: x[newArr[0]][newArr[1]],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[newArr[0]],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[props.schema.data.valueKey],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
temp.push(opt);
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[props.schema.data.valueKey],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
temp.push(opt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response = temp;
|
|
||||||
setLevel1_DD(response);
|
|
||||||
setActive(false);
|
|
||||||
if (response.length === 1) {
|
|
||||||
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
|
|
||||||
props.onChange(response[0].objectId);
|
|
||||||
} else if (response.length === 0) {
|
|
||||||
props.onChangeLevel1Dropdown("", props.name);
|
|
||||||
props.onChange("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let response = [];
|
|
||||||
setActive(true);
|
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
var reg = /(\#.*?\#)/gi;
|
|
||||||
let str = props.schema.data.query;
|
|
||||||
var test;
|
|
||||||
if (str.includes("#")) {
|
|
||||||
let res;
|
|
||||||
if (localStorage.getItem("Extand_Class")) {
|
|
||||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
res = data[0];
|
|
||||||
} else {
|
|
||||||
var emp = Parse.Object.extend(
|
|
||||||
localStorage.getItem("extended_class")
|
|
||||||
);
|
|
||||||
var q = new Parse.Query(emp);
|
|
||||||
q.equalTo("UserId", currentUser);
|
|
||||||
res = await q.first();
|
|
||||||
if (res) res = res.toJSON();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
let json = res;
|
|
||||||
let output = str.match(reg);
|
|
||||||
if (output.length === 1) {
|
|
||||||
output = output.join();
|
|
||||||
output = output.substring(1, output.length - 1);
|
|
||||||
output = output.split(".");
|
|
||||||
if (output.length === 1) {
|
|
||||||
let out = output[0];
|
|
||||||
if (json[out]) {
|
|
||||||
if (typeof json[out] === "object") {
|
|
||||||
test = str.replace(reg, JSON.stringify(json[out]));
|
|
||||||
} else {
|
|
||||||
test = str.replace(reg, json[out]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
test = str.replace(reg, currentUser.id);
|
|
||||||
}
|
|
||||||
} else if (output.length === 2) {
|
|
||||||
let out1 = json[output[0]][output[1]];
|
|
||||||
if (out1) {
|
|
||||||
test = str.replace(reg, out1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (output.length === 2) {
|
|
||||||
let output1 = output[0];
|
|
||||||
output1 = output1.substring(1, output1.length - 1);
|
|
||||||
output1 = output1.split(".");
|
|
||||||
if (output1.length === 1) {
|
|
||||||
let out = output1[0];
|
|
||||||
if (json[out]) {
|
|
||||||
if (typeof json[out] === "object") {
|
|
||||||
str = str.replace(output[0], JSON.stringify(json[out]));
|
|
||||||
} else {
|
|
||||||
str = str.replace(output[0], json[out]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str = str.replace(output[0], currentUser.id);
|
|
||||||
}
|
|
||||||
} else if (output1.length === 2) {
|
|
||||||
let out1 = json[output1[0]][output1[1]];
|
|
||||||
if (out1) {
|
|
||||||
str = str.replace(output[0], out1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let output2 = output[1];
|
|
||||||
output2 = output2.substring(1, output2.length - 1);
|
|
||||||
output2 = output2.split(".");
|
|
||||||
if (output2.length === 1) {
|
|
||||||
let out = output2[0];
|
|
||||||
if (json[out]) {
|
|
||||||
if (typeof json[out] === "object") {
|
|
||||||
str = str.replace(output[1], JSON.stringify(json[out]));
|
|
||||||
} else {
|
|
||||||
str = str.replace(output[1], json[out]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
str = str.replace(output[1], currentUser.id);
|
|
||||||
}
|
|
||||||
} else if (output2.length === 2) {
|
|
||||||
let out1 = json[output2[0]][output2[1]];
|
|
||||||
if (out1) {
|
|
||||||
str = str.replace(output[1], out1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test = str;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setToastDescription(
|
|
||||||
`User not found in ${localStorage.getItem("extended_class")}`
|
|
||||||
);
|
|
||||||
setActive(false);
|
|
||||||
var x = document.getElementById("Level_1");
|
|
||||||
x.className = "show";
|
|
||||||
setTimeout(function () {
|
|
||||||
x.className = x.className.replace("show", "");
|
|
||||||
}, 5000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
test = str;
|
|
||||||
}
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${test}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
let fetchCount = 0;
|
|
||||||
let offset = 0;
|
|
||||||
let data = [];
|
|
||||||
/* eslint-disable no-constant-condition */
|
|
||||||
while (true) {
|
|
||||||
const params = getParams(offset);
|
|
||||||
await axios
|
|
||||||
.get(url, { params: params, headers: headers })
|
|
||||||
.then((response) => {
|
|
||||||
fetchCount = response.data.results.length;
|
|
||||||
offset += response.data.results.length;
|
|
||||||
|
|
||||||
if (fetchCount > 0) {
|
|
||||||
response.data.results.forEach((x) => {
|
|
||||||
data.push(x);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Problem", err.response);
|
|
||||||
setToastDescription(err.response.data.message);
|
|
||||||
setActive(false);
|
|
||||||
var x = document.getElementById("Level_1");
|
|
||||||
x.className = "show";
|
|
||||||
setTimeout(function () {
|
|
||||||
x.className = x.className.replace("show", "");
|
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
if (fetchCount === 0 || fetchCount < 100) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.length > 0) {
|
|
||||||
let temp = [];
|
|
||||||
data.forEach((x) => {
|
|
||||||
let opt = "";
|
|
||||||
if (Array.isArray(x[props.schema.data.valueKey])) {
|
|
||||||
x[props.schema.data.valueKey].forEach((tt) => {
|
|
||||||
if (typeof tt === "object") {
|
|
||||||
if (props.schema.data.isPointer) {
|
|
||||||
opt = {
|
|
||||||
objectId: tt["objectId"],
|
|
||||||
option: tt[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: tt[props.schema.data.displayKey],
|
|
||||||
option: tt[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: tt,
|
|
||||||
option: tt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
temp.push(opt);
|
|
||||||
});
|
|
||||||
} else if (props.schema.data.isPointer) {
|
|
||||||
if (props.schema.data.valueKey.includes(".")) {
|
|
||||||
let newArr = props.schema.data.valueKey.split(".");
|
|
||||||
if (newArr.length === 2) {
|
|
||||||
let _dis = props.schema.data.displayKey.split(".");
|
|
||||||
if (_dis.length === 2) {
|
|
||||||
opt = {
|
|
||||||
objectId: x[newArr[0]][newArr[1]],
|
|
||||||
option: x[_dis[0]][_dis[1]]
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[newArr[0]][newArr[1]],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[newArr[0]],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[props.schema.data.valueKey],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
temp.push(opt);
|
|
||||||
} else {
|
|
||||||
opt = {
|
|
||||||
objectId: x[props.schema.data.valueKey],
|
|
||||||
option: x[props.schema.data.displayKey]
|
|
||||||
};
|
|
||||||
temp.push(opt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
response = temp;
|
|
||||||
setLevel1_DD(response);
|
|
||||||
setActive(false);
|
|
||||||
if (!props.formData) {
|
|
||||||
props.onChangeLevel1Dropdown("", props.name);
|
|
||||||
}
|
|
||||||
if (response.length === 1) {
|
|
||||||
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
|
|
||||||
props.onChange(response[0].objectId);
|
|
||||||
} else if (response.length === 0) {
|
|
||||||
props.onChangeLevel1Dropdown("", props.name);
|
|
||||||
props.onChange("");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
try {
|
|
||||||
setToastDescription(e.message);
|
|
||||||
setActive(false);
|
|
||||||
x = document.getElementById("Level_1");
|
|
||||||
x.className = "show";
|
|
||||||
setTimeout(function () {
|
|
||||||
x.className = x.className.replace("show", "");
|
|
||||||
}, 5000);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Err", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
Level1DropdownData();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.formData && props.formData[`objectId`]) {
|
|
||||||
props.onChangeLevel1Dropdown(props.formData.objectId, props.name);
|
|
||||||
} else if (props.formData) {
|
|
||||||
props.onChangeLevel1Dropdown(props.formData, props.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [props.formData]);
|
|
||||||
|
|
||||||
const selectDefault = () => {
|
|
||||||
props.onChange(Level1_DD[0].objectId);
|
|
||||||
props.onChangeLevel1Dropdown(Level1_DD[0].objectId, props.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
const REQUIRED_FIELD_SYMBOL = "*";
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<div style={{ display: "inline-block" }}>
|
|
||||||
<label htmlFor={props.name}>
|
|
||||||
{props.schema.title}
|
|
||||||
{props.required && (
|
|
||||||
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
|
|
||||||
)}
|
|
||||||
{props.schema.data.helpbody ? (
|
|
||||||
<div className="dropdown pull-right">
|
|
||||||
<i
|
|
||||||
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
|
|
||||||
aria-hidden="true"
|
|
||||||
id="dropdownMenuButton"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"
|
|
||||||
style={{
|
|
||||||
fontSize: "12px",
|
|
||||||
color: "purple",
|
|
||||||
cursor: "pointer !important",
|
|
||||||
position: "relative",
|
|
||||||
bottom: "0px",
|
|
||||||
left: "0px",
|
|
||||||
paddingBottom: "4px",
|
|
||||||
paddingLeft: "4px"
|
|
||||||
}}
|
|
||||||
></i>
|
|
||||||
<div
|
|
||||||
className="dropdown-menu"
|
|
||||||
aria-labelledby="dropdownMenuButton"
|
|
||||||
style={{
|
|
||||||
marginleft: "-121px",
|
|
||||||
margintop: "-14px",
|
|
||||||
position: "absolute",
|
|
||||||
padding: "10px",
|
|
||||||
top: "102px!important"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{parse(`
|
|
||||||
${props.schema.data.helpbody}
|
|
||||||
`)}
|
|
||||||
<br />
|
|
||||||
{props.schema.data.helplink ? (
|
|
||||||
<a
|
|
||||||
href={props.schema.data.helplink}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="btn btn-xs btn-primary"
|
|
||||||
>
|
|
||||||
Read more..
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{Level1_DD && (
|
|
||||||
<select
|
|
||||||
className="form-control"
|
|
||||||
name={props.name}
|
|
||||||
value={props.value}
|
|
||||||
required={props.required}
|
|
||||||
disabled={props.uiSchema["ui:disabled"]}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (!e.target.value) {
|
|
||||||
if (props.schema.data.isPointer) {
|
|
||||||
localStorage.setItem("Level1", undefined);
|
|
||||||
props.onChangeLevel1Dropdown(undefined, props.name);
|
|
||||||
return props.onChange(undefined);
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("Level1", e.target.value);
|
|
||||||
props.onChangeLevel1Dropdown(e.target.value, props.name);
|
|
||||||
return props.onChange(e.target.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localStorage.setItem("Level1", e.target.value);
|
|
||||||
props.onChangeLevel1Dropdown(e.target.value, props.name);
|
|
||||||
return props.onChange(e.target.value);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
readOnly={props.uiSchema["ui:disabled"] ? true : false}
|
|
||||||
defaultValue={Level1_DD.length === 1 && selectDefault}
|
|
||||||
>
|
|
||||||
<option value="">{!active ? "Select" : "loading..."}</option>
|
|
||||||
{Level1_DD &&
|
|
||||||
Level1_DD.map((x, i) => (
|
|
||||||
<React.Fragment key={i}>
|
|
||||||
{props.formData && props.formData.objectId ? (
|
|
||||||
props.formData.objectId === x.objectId ? (
|
|
||||||
<option value={x.objectId} selected>
|
|
||||||
{x.option}
|
|
||||||
</option>
|
|
||||||
) : (
|
|
||||||
<option value={x.objectId}>{x.option}</option>
|
|
||||||
)
|
|
||||||
) : props.formData && props.formData === x.objectId ? (
|
|
||||||
<option value={x.objectId} selected>
|
|
||||||
{x.option}
|
|
||||||
</option>
|
|
||||||
) : (
|
|
||||||
<option value={x.objectId}>{x.option}</option>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
)}
|
|
||||||
<div id="Level_1" style={{ backgroundColor: toastColor }}>
|
|
||||||
{toastDescription}
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(null, { onChangeLevel1Dropdown })(Level1Dropdown);
|
|
||||||
@@ -1,709 +0,0 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import Select from "react-select";
|
|
||||||
import axios from "axios";
|
|
||||||
import LabelField from "./LabelField";
|
|
||||||
import ReactDragListView from "react-drag-listview";
|
|
||||||
import AppendFormInForm from "../AppendFormInForm";
|
|
||||||
import Modal from "react-modal";
|
|
||||||
function arrayMove(array, from, to) {
|
|
||||||
array = array.slice();
|
|
||||||
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* react-sortable-hoc is depcreated not usable from react 18.x.x
|
|
||||||
* need to replace it with @dnd-kit
|
|
||||||
* code changes required
|
|
||||||
*/
|
|
||||||
|
|
||||||
const MultiSelectField = (props) => {
|
|
||||||
Modal.setAppElement("body");
|
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
|
||||||
const [state, setState] = useState(undefined);
|
|
||||||
// const [editFormData, setEditFormData] = useState([]);
|
|
||||||
const [selected, setSelected] = React.useState([]);
|
|
||||||
const [isModal, setIsModel] = useState(false);
|
|
||||||
const onChange = (selectedOptions) => setSelected(selectedOptions);
|
|
||||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const onSortEnd = ({ oldIndex, newIndex }) => {
|
|
||||||
const newValue = arrayMove(selected, oldIndex, newIndex);
|
|
||||||
setSelected(newValue);
|
|
||||||
};
|
|
||||||
// console.log("props",props)
|
|
||||||
const GetSelectListData = async () => {
|
|
||||||
try {
|
|
||||||
let response = [];
|
|
||||||
let str = props.schema.data.query;
|
|
||||||
let _query;
|
|
||||||
/* eslint-disable no-useless-escape */
|
|
||||||
let reg = /(\#.*?\#)/gi;
|
|
||||||
let extclass =
|
|
||||||
localStorage.getItem("Extand_Class") &&
|
|
||||||
JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
let extRow = extclass.length && extclass[0];
|
|
||||||
if (str.includes("#")) {
|
|
||||||
let output = str.match(reg);
|
|
||||||
output = output.join();
|
|
||||||
output = output.substring(1, output.length - 1);
|
|
||||||
output = output.split(".");
|
|
||||||
|
|
||||||
if (output.length === 2) {
|
|
||||||
let out1 = extRow[output[0]][output[1]];
|
|
||||||
_query = str.replace(reg, out1 && out1);
|
|
||||||
} else {
|
|
||||||
let out1 = extRow[output[0]];
|
|
||||||
_query = str.replace(reg, out1 && out1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_query = str;
|
|
||||||
}
|
|
||||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
await axios.get(url, { headers: headers }).then((res) => {
|
|
||||||
let temp = [];
|
|
||||||
let formArray = [];
|
|
||||||
let _selected = [];
|
|
||||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
|
||||||
if (props.formData) {
|
|
||||||
props.formData.forEach((x) => {
|
|
||||||
if (typeof x === "object") {
|
|
||||||
formArray.push(x.objectId);
|
|
||||||
} else {
|
|
||||||
formArray.push(x);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
res.data.results.forEach((x) => {
|
|
||||||
let obj = {};
|
|
||||||
if (formArray.includes(x[props.schema.data.valueKey])) {
|
|
||||||
obj = {
|
|
||||||
name: x[props.schema.data.displayKey],
|
|
||||||
value: x[props.schema.data.valueKey],
|
|
||||||
isChecked: true
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
if (props.schema.selectAll) {
|
|
||||||
obj = {
|
|
||||||
name: x[props.schema.data.displayKey],
|
|
||||||
value: x[props.schema.data.valueKey],
|
|
||||||
isChecked: true
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
obj = {
|
|
||||||
name: x[props.schema.data.displayKey],
|
|
||||||
value: x[props.schema.data.valueKey],
|
|
||||||
isChecked: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
temp.push(obj);
|
|
||||||
});
|
|
||||||
response = temp;
|
|
||||||
if (props.formData) {
|
|
||||||
let checkData = [];
|
|
||||||
response.forEach((x) => {
|
|
||||||
if (x.isChecked) {
|
|
||||||
checkData.push(x.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setSelected(checkData);
|
|
||||||
}
|
|
||||||
setState({ [`${props.name}_DD`]: response });
|
|
||||||
} else {
|
|
||||||
res.data.results.forEach((x) => {
|
|
||||||
let obj = {};
|
|
||||||
if (props.formData) {
|
|
||||||
props.formData.forEach((x) => {
|
|
||||||
if (typeof x === "object") {
|
|
||||||
formArray.push(x.objectId);
|
|
||||||
} else {
|
|
||||||
formArray.push(x);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = {
|
|
||||||
label: x[props.schema.data.displayKey],
|
|
||||||
value: x[props.schema.data.valueKey]
|
|
||||||
};
|
|
||||||
if (formArray.includes(x[props.schema.data.valueKey])) {
|
|
||||||
_selected.push(obj);
|
|
||||||
}
|
|
||||||
if (props.schema.selectAll) {
|
|
||||||
_selected.push(obj);
|
|
||||||
}
|
|
||||||
temp.push(obj);
|
|
||||||
});
|
|
||||||
response = temp;
|
|
||||||
setState({ [`${props.name}_DD`]: response });
|
|
||||||
setSelected(_selected);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log("err", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCheckChieldElement = (event) => {
|
|
||||||
let SelectLists = state[`${props.name}_DD`];
|
|
||||||
let List = [];
|
|
||||||
SelectLists.forEach((select) => {
|
|
||||||
if (select.value === event.target.value) {
|
|
||||||
select.isChecked = event.target.checked;
|
|
||||||
}
|
|
||||||
if (select.isChecked) {
|
|
||||||
List.push(select.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
setSelected(List);
|
|
||||||
setState({ [`${props.name}_DD`]: SelectLists });
|
|
||||||
};
|
|
||||||
|
|
||||||
// const Level1CheckList = async (id) => {
|
|
||||||
// try {
|
|
||||||
// let response = [];
|
|
||||||
// // eslint-disable-next-line
|
|
||||||
// let reg = /(\#.*?\#)/gi;
|
|
||||||
// let _query = props.schema.data.query;
|
|
||||||
// let output = _query.match(reg);
|
|
||||||
// if (output.length === 2) {
|
|
||||||
// let res;
|
|
||||||
// if (localStorage.getItem("Extand_Class")) {
|
|
||||||
// let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
|
||||||
// res = data[0];
|
|
||||||
// }
|
|
||||||
// output = output.filter((x) => x !== "#queryString#");
|
|
||||||
// if (output.length === 1) {
|
|
||||||
// _query = _query.replace("#queryString#", id);
|
|
||||||
// output = output.join();
|
|
||||||
// output = output.substring(1, output.length - 1);
|
|
||||||
// output = output.split(".");
|
|
||||||
// if (output.length > 0) {
|
|
||||||
// _query = _query.replace(reg, res[output[0]][output[1]]);
|
|
||||||
// } else {
|
|
||||||
// _query = _query.replace(reg, res[output[0]]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// _query = props.schema.data.query.replace(reg, id);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
|
|
||||||
// const headers = {
|
|
||||||
// "Content-Type": "application/json",
|
|
||||||
// "X-Parse-Application-Id": parseAppId,
|
|
||||||
// "X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
// };
|
|
||||||
// await axios.get(url, { headers: headers }).then(async (res) => {
|
|
||||||
// let temp = [];
|
|
||||||
// let formArray = [];
|
|
||||||
// let _selected = [];
|
|
||||||
// if (editFormData.length > 0) {
|
|
||||||
// editFormData.forEach((x) => {
|
|
||||||
// if (typeof x === "object") {
|
|
||||||
// formArray.push(x.objectId);
|
|
||||||
// } else {
|
|
||||||
// formArray.push(x);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// } else if (
|
|
||||||
// props.schema.selectedData &&
|
|
||||||
// Object.keys(props.schema.selectedData).length !== 0 &&
|
|
||||||
// props.schema.selectedData.constructor === Object
|
|
||||||
// ) {
|
|
||||||
// try {
|
|
||||||
// let selectedDataQuery = props.schema.selectedData.query.replace(
|
|
||||||
// reg,
|
|
||||||
// id
|
|
||||||
// );
|
|
||||||
// let selectedDataUrl = `${parseBaseUrl}classes/${props.schema.selectedData.class}?${selectedDataQuery}`;
|
|
||||||
|
|
||||||
// await axios.get(selectedDataUrl, { headers }).then((sltres) => {
|
|
||||||
// let sltData = sltres.data.results;
|
|
||||||
// sltData.forEach((x) => {
|
|
||||||
// if (props.schema.selectedData.valueKey.includes(".")) {
|
|
||||||
// let sltArr = props.schema.selectedData.valueKey.split(".");
|
|
||||||
|
|
||||||
// if (Array.isArray(x[sltArr[0]])) {
|
|
||||||
// x[sltArr[0]].forEach((l) => {
|
|
||||||
// formArray.push(l[sltArr[1]]);
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// formArray.push(x[sltArr[0][sltArr[1]]]);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// formArray.push(x[props.schema.selectedData.valueKey]);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// } catch (error) {}
|
|
||||||
// }
|
|
||||||
// res.data.results.forEach((x) => {
|
|
||||||
// let obj = {};
|
|
||||||
// if (props.schema.data.valueKey.includes(".")) {
|
|
||||||
// let newArr = props.schema.data.valueKey.split(".");
|
|
||||||
// if (Array.isArray(x[newArr[0]])) {
|
|
||||||
// if (props.schema.data.displayKey.includes(".")) {
|
|
||||||
// let _dis = props.schema.data.displayKey.split(".");
|
|
||||||
// x[newArr[0]].forEach((l) => {
|
|
||||||
// if (formArray.includes(l[newArr[1]])) {
|
|
||||||
// obj = {
|
|
||||||
// label: l[_dis[1]],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: l[_dis[1]],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: l[_dis[1]],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// temp.push(obj);
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// x[newArr[0]].forEach((l) => {
|
|
||||||
// if (formArray.includes(l[newArr[1]])) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: l[newArr[1]],
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// temp.push(obj);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.data.displayKey.includes(".")) {
|
|
||||||
// let disArr = props.schema.data.displayKey.split(".");
|
|
||||||
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[disArr[0]][disArr[1]],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[disArr[0]][disArr[1]],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: x[disArr[0]][disArr[1]],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[newArr[0]][newArr[1]],
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if (Array.isArray(x[props.schema.data.valueKey])) {
|
|
||||||
// x[props.schema.data.valueKey].forEach((t) => {
|
|
||||||
// if (formArray.includes(t)) {
|
|
||||||
// obj = {
|
|
||||||
// label: t,
|
|
||||||
// value: t,
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: t,
|
|
||||||
// value: t,
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: t,
|
|
||||||
// value: t,
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// temp.push(obj);
|
|
||||||
// });
|
|
||||||
// } else if (formArray.includes(x[props.schema.data.valueKey])) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[props.schema.data.valueKey],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// if (props.schema.selectAll) {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[props.schema.data.valueKey],
|
|
||||||
// isChecked: true
|
|
||||||
// };
|
|
||||||
// _selected.push(obj);
|
|
||||||
// } else {
|
|
||||||
// obj = {
|
|
||||||
// label: x[props.schema.data.displayKey],
|
|
||||||
// value: x[props.schema.data.valueKey],
|
|
||||||
// isChecked: false
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// temp.push(obj);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// response = temp;
|
|
||||||
// if (props.schema.uiLayout === "MultiDropdownList") {
|
|
||||||
// if (editFormData) {
|
|
||||||
// let checkData = [];
|
|
||||||
// response.forEach((x) => {
|
|
||||||
// if (x.isChecked) {
|
|
||||||
// checkData.push(x.value);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// setSelected(checkData);
|
|
||||||
// }
|
|
||||||
// setState({ [`${props.name}_DD`]: response });
|
|
||||||
// } else {
|
|
||||||
// setState({ [`${props.name}_DD`]: response });
|
|
||||||
// setSelected(_selected);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// } catch (error) {}
|
|
||||||
// };
|
|
||||||
|
|
||||||
const dragProps = {
|
|
||||||
onDragEnd(fromIndex, toIndex) {
|
|
||||||
const data = [...state[`${props.name}_DD`]];
|
|
||||||
const item = data.splice(fromIndex, 1)[0];
|
|
||||||
data.splice(toIndex, 0, item);
|
|
||||||
let NewList = [];
|
|
||||||
data.forEach((x) => {
|
|
||||||
if (x.isChecked) {
|
|
||||||
NewList.push(x.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setState({ [`${props.name}_DD`]: data });
|
|
||||||
setSelected(NewList);
|
|
||||||
},
|
|
||||||
nodeSelector: "li",
|
|
||||||
handleSelector: "a"
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!props.schema.parent) GetSelectListData();
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
|
||||||
props.onChange(selected);
|
|
||||||
} else {
|
|
||||||
if (selected && selected.length) {
|
|
||||||
let newData = [];
|
|
||||||
selected.forEach((x) => {
|
|
||||||
newData.push(x.value);
|
|
||||||
});
|
|
||||||
props.onChange(newData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
// useState(() => {
|
|
||||||
// if (props.formData) {
|
|
||||||
// if (props.formData === "Select") {
|
|
||||||
// } else {
|
|
||||||
// setEditFormData(props.formData);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // eslint-disable-next-line
|
|
||||||
// }, [props.formData]);
|
|
||||||
|
|
||||||
const handleModalCloseClick = () => {
|
|
||||||
setIsModel(false);
|
|
||||||
setModalIsOpen(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openModal = () => {
|
|
||||||
setModalIsOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
|
|
||||||
const handleNewDetails = (data) => {
|
|
||||||
setState({ [`${props.name}_DD`]: [...state[`${props.name}_DD`], data] });
|
|
||||||
if (selected.length > 0) {
|
|
||||||
setSelected([...selected, data]);
|
|
||||||
} else {
|
|
||||||
setSelected([data]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<LabelField
|
|
||||||
Title={props.schema.title}
|
|
||||||
Name={props.name}
|
|
||||||
Required={props.required}
|
|
||||||
HelpBody={props.schema.helpbody}
|
|
||||||
HelpLink={props.schema.helplink}
|
|
||||||
/>
|
|
||||||
<ReactDragListView {...dragProps}>
|
|
||||||
<ul
|
|
||||||
style={{
|
|
||||||
listStyleType: "square",
|
|
||||||
listStylePosition: "outside",
|
|
||||||
maxHeight: "250px",
|
|
||||||
overflow: "auto",
|
|
||||||
paddingTop: "10px",
|
|
||||||
paddingBottom: "10px",
|
|
||||||
border: "0px solid #b3b3b3",
|
|
||||||
borderRadius: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{state && state[`${props.name}_DD`].length > 0 ? (
|
|
||||||
state[`${props.name}_DD`].map((x, i) => {
|
|
||||||
return (
|
|
||||||
<li key={i} className="multiSelectList">
|
|
||||||
<input
|
|
||||||
key={x.value}
|
|
||||||
onClick={handleCheckChieldElement}
|
|
||||||
type="checkbox"
|
|
||||||
checked={x.isChecked}
|
|
||||||
style={{ marginTop: "7px" }}
|
|
||||||
value={x.value}
|
|
||||||
disabled={props.disabled}
|
|
||||||
/>{" "}
|
|
||||||
<span style={{ marginTop: "7px" }}>{x.name}</span>
|
|
||||||
{props.schema.isSortable && !props.disabled && (
|
|
||||||
<a
|
|
||||||
onClick={(e) => e.preventDefault()}
|
|
||||||
className="pull-right"
|
|
||||||
title="Sort List"
|
|
||||||
style={{ textDecoration: "none" }}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa fa-bars"
|
|
||||||
style={{
|
|
||||||
fontSize: "30px",
|
|
||||||
color: "skyblue",
|
|
||||||
paddingTop: "11px"
|
|
||||||
}}
|
|
||||||
></i>
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<li className="multiSelectList">No Record Found</li>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</ReactDragListView>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<LabelField
|
|
||||||
Title={props.schema.title}
|
|
||||||
Name={props.name}
|
|
||||||
Required={props.required}
|
|
||||||
HelpBody={props.schema.helpbody}
|
|
||||||
HelpLink={props.schema.helplink}
|
|
||||||
/>
|
|
||||||
<div style={{ display: "flex", gap: 5 }}>
|
|
||||||
<div style={{ flexWrap: "wrap", width: "100%" }}>
|
|
||||||
<Select
|
|
||||||
onSortEnd={onSortEnd}
|
|
||||||
distance={4}
|
|
||||||
isMulti
|
|
||||||
options={(state && state[`${props.name}_DD`]) || []}
|
|
||||||
value={selected}
|
|
||||||
onChange={onChange}
|
|
||||||
closeMenuOnSelect={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{props.schema.data.quickAddFormId && (
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
setIsModel(true);
|
|
||||||
openModal();
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: 4,
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
minHeight: 38,
|
|
||||||
minWidth: 48,
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="fas fa-plus"></i>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* {props.schema.data.quickAddFormId && (
|
|
||||||
<div
|
|
||||||
className="modal fade"
|
|
||||||
id={"multiSelectModal" + props.schema.data.quickAddFormId}
|
|
||||||
tabIndex="-1"
|
|
||||||
role="dialog"
|
|
||||||
aria-labelledby="exampleModalLabel"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<div className="modal-dialog modal-lg" role="document">
|
|
||||||
<div className="modal-content">
|
|
||||||
<div className="modal-header">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
aria-label="Close"
|
|
||||||
onClick={handleModalCloseClick}
|
|
||||||
>
|
|
||||||
<i className="fa fa-times-circle" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="modal-body">
|
|
||||||
{isModal && (
|
|
||||||
<AppendFormInForm
|
|
||||||
id={props.schema.data.quickAddFormId}
|
|
||||||
valueKey={props.schema.data.valueKey}
|
|
||||||
displayKey={props.schema.data.displayKey}
|
|
||||||
details={handleNewDetails}
|
|
||||||
closePopup={handleModalCloseClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
<Modal
|
|
||||||
isOpen={modalIsOpen}
|
|
||||||
onRequestClose={handleModalCloseClick}
|
|
||||||
shouldCloseOnOverlayClick={false}
|
|
||||||
contentLabel="Modal"
|
|
||||||
style={{
|
|
||||||
content: {
|
|
||||||
top: "50%",
|
|
||||||
left: "50%",
|
|
||||||
right: "auto",
|
|
||||||
bottom: "auto",
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
padding: 0
|
|
||||||
},
|
|
||||||
overlay: {
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
|
||||||
zIndex: 50
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="min-w-full md:min-w-[500px]">
|
|
||||||
<div
|
|
||||||
type="button"
|
|
||||||
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
|
|
||||||
>
|
|
||||||
<div className=" text-black text-xl font-semibold pl-3">
|
|
||||||
Add Signer
|
|
||||||
</div>
|
|
||||||
<button onClick={handleModalCloseClick}>
|
|
||||||
<i
|
|
||||||
style={{ fontSize: 25 }}
|
|
||||||
className="fa fa-times-circle"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isModal && (
|
|
||||||
<AppendFormInForm
|
|
||||||
id={props.schema.data.quickAddFormId}
|
|
||||||
valueKey={props.schema.data.valueKey}
|
|
||||||
displayKey={props.schema.data.displayKey}
|
|
||||||
details={handleNewDetails}
|
|
||||||
closePopup={handleModalCloseClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
|
||||||
return {
|
|
||||||
Level1_Dropdown: state.Level1_Dropdown,
|
|
||||||
Level2_Dropdown: state.Level2_Dropdown,
|
|
||||||
Level3_Dropdown: state.Level3_Dropdown
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export default connect(mapStateToProps, null)(MultiSelectField);
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ObjectField from "@rjsf/core/lib/components/fields/ObjectField";
|
|
||||||
import validator from "@rjsf/validator-ajv8";
|
|
||||||
import { Col } from "react-bootstrap";
|
|
||||||
import { retrieveSchema } from "@rjsf/utils";
|
|
||||||
|
|
||||||
class GridField extends ObjectField {
|
|
||||||
state = {};
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
uiSchema,
|
|
||||||
errorSchema,
|
|
||||||
idSchema,
|
|
||||||
|
|
||||||
disabled,
|
|
||||||
readonly,
|
|
||||||
onBlur,
|
|
||||||
formData
|
|
||||||
} = this.props;
|
|
||||||
const { fields, rootSchema } = this.props.registry;
|
|
||||||
|
|
||||||
const { SchemaField } = fields;
|
|
||||||
const schema = retrieveSchema(validator, this.props.schema, rootSchema);
|
|
||||||
const title = schema.title === undefined ? "" : schema.title;
|
|
||||||
const description =
|
|
||||||
schema && schema.description === undefined ? "" : schema.description;
|
|
||||||
// console.log('this.props.schema ', this.props.schema)
|
|
||||||
// console.log("schema ", schema);
|
|
||||||
const layout = uiSchema["ui:layout"];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<fieldset>
|
|
||||||
{title ? <h4>{title}</h4> : null}
|
|
||||||
{description ? <h6>{description}</h6> : null}
|
|
||||||
{layout.map((row, index) => {
|
|
||||||
return (
|
|
||||||
<div className="row" key={index}>
|
|
||||||
{Object.keys(row).map((name, index) => {
|
|
||||||
const { doShow, ...rowProps } = row[name];
|
|
||||||
let style = {};
|
|
||||||
if (doShow && !doShow({ formData })) {
|
|
||||||
style = { display: "none" };
|
|
||||||
}
|
|
||||||
if (schema.properties[name]) {
|
|
||||||
return (
|
|
||||||
<Col {...rowProps} key={index} style={style}>
|
|
||||||
<SchemaField
|
|
||||||
name={name}
|
|
||||||
required={this.isRequired(name)}
|
|
||||||
schema={schema.properties[name]}
|
|
||||||
uiSchema={uiSchema[name]}
|
|
||||||
errorSchema={errorSchema[name]}
|
|
||||||
idSchema={idSchema[name]}
|
|
||||||
formData={formData[name]}
|
|
||||||
onChange={this.onPropertyChange(name)}
|
|
||||||
onBlur={onBlur}
|
|
||||||
registry={this.props.registry}
|
|
||||||
disabled={disabled}
|
|
||||||
readonly={readonly}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const { render, ...rowProps } = row[name];
|
|
||||||
let UIComponent = () => null;
|
|
||||||
|
|
||||||
if (render) {
|
|
||||||
UIComponent = render;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Col {...rowProps} key={index} style={style}>
|
|
||||||
<UIComponent
|
|
||||||
name={name}
|
|
||||||
formData={formData}
|
|
||||||
errorSchema={errorSchema}
|
|
||||||
uiSchema={uiSchema}
|
|
||||||
schema={schema}
|
|
||||||
registry={this.props.registry}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</fieldset>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export default GridField;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
|
|
||||||
function TimeWidget(props) {
|
|
||||||
const {
|
|
||||||
onChange,
|
|
||||||
registry: {
|
|
||||||
widgets: { BaseInput }
|
|
||||||
}
|
|
||||||
} = props;
|
|
||||||
return (
|
|
||||||
<BaseInput
|
|
||||||
type="time"
|
|
||||||
{...props}
|
|
||||||
onChange={(value) => onChange(value || undefined)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== "production") {
|
|
||||||
TimeWidget.propTypes = {
|
|
||||||
value: PropTypes.string
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TimeWidget;
|
|
||||||
@@ -0,0 +1,595 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import folder from "../../assets/images/folder.png";
|
||||||
|
import "../../styles/opensigndrive.css";
|
||||||
|
import pdfLogo from "../../assets/images/pdf3.png";
|
||||||
|
import axios from "axios";
|
||||||
|
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import Table from "react-bootstrap/Table";
|
||||||
|
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import FolderModal from "../shared/fields/FolderModal";
|
||||||
|
|
||||||
|
function DriveBody(props) {
|
||||||
|
const [rename, setRename] = useState("");
|
||||||
|
const [renameValue, setRenameValue] = useState("");
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
const [isOpenMoveModal, setIsOpenMoveModal] = useState(false);
|
||||||
|
const [selectDoc, setSelectDoc] = useState();
|
||||||
|
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
|
||||||
|
const contextMenu = [
|
||||||
|
{ type: "Download", icon: "fa-solid fa-arrow-down" },
|
||||||
|
{ type: "Rename", icon: "fa-solid fa-font" },
|
||||||
|
{ type: "Move", icon: "fa-solid fa-file-export" },
|
||||||
|
{ type: "Delete", icon: "fa-solid fa-trash" }
|
||||||
|
];
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
//to focus input box on press rename to change doc name
|
||||||
|
useEffect(() => {
|
||||||
|
if (rename && inputRef.current) {
|
||||||
|
setTimeout(() => {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}, [rename]);
|
||||||
|
|
||||||
|
//function to handle folder component
|
||||||
|
const handleOnclikFolder = (data) => {
|
||||||
|
const folderData = {
|
||||||
|
name: data.Name,
|
||||||
|
objectId: data.objectId
|
||||||
|
};
|
||||||
|
props.setFolderName((prev) => [...prev, folderData]);
|
||||||
|
props.setIsLoading({
|
||||||
|
isLoad: true,
|
||||||
|
message: "This might take some time"
|
||||||
|
});
|
||||||
|
props.setDocId(data.objectId);
|
||||||
|
props.setPdfData([]);
|
||||||
|
props.setSkip(0);
|
||||||
|
};
|
||||||
|
//function for change doc name and update doc name in _document class
|
||||||
|
const handledRenameDoc = async (data) => {
|
||||||
|
setRename("");
|
||||||
|
const trimmedValue = renameValue.trim();
|
||||||
|
if (trimmedValue.length > 0) {
|
||||||
|
const updateName = {
|
||||||
|
Name: renameValue
|
||||||
|
};
|
||||||
|
const docId = data.objectId;
|
||||||
|
const docData = props.pdfData;
|
||||||
|
const updatedData = docData.map((item) => {
|
||||||
|
if (item.objectId === docId) {
|
||||||
|
// If the item's ID matches the target ID, update the name
|
||||||
|
return { ...item, Name: renameValue };
|
||||||
|
}
|
||||||
|
// If the item's ID doesn't match, keep it unchanged
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
props.setPdfData(updatedData);
|
||||||
|
props.sortingData(null, null, updatedData);
|
||||||
|
await axios
|
||||||
|
.put(
|
||||||
|
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||||
|
"_appName"
|
||||||
|
)}_Document/${docId}`,
|
||||||
|
updateName,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
// const res = result.data;
|
||||||
|
// console.log("res", res);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Err ", err);
|
||||||
|
props.setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for navigate user to microapp-signature component
|
||||||
|
const checkPdfStatus = async (data) => {
|
||||||
|
const signerExist = data.Signers && data.Signers;
|
||||||
|
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||||
|
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||||
|
const signedUrl = data.SignedUrl;
|
||||||
|
//checking if document has completed and request signature flow
|
||||||
|
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//checking if document has completed and signyour-self flow
|
||||||
|
else if (!signerExist && !isPlaceholder) {
|
||||||
|
navigate(`/signaturePdf/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//checking if document has declined by someone
|
||||||
|
else if (isDecline) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
//checking draft type document
|
||||||
|
} else if (
|
||||||
|
signerExist?.length > 0 &&
|
||||||
|
isPlaceholder?.length > 0 &&
|
||||||
|
!signedUrl
|
||||||
|
) {
|
||||||
|
navigate(`/placeHolderSign/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//Inprogress document
|
||||||
|
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
} //placeholder draft document
|
||||||
|
else if (
|
||||||
|
(signerExist?.length > 0 &&
|
||||||
|
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||||
|
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||||
|
) {
|
||||||
|
navigate(`/placeHolderSign/${data.objectId}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuItemClick = (selectType, data) => {
|
||||||
|
switch (selectType) {
|
||||||
|
case "Download": {
|
||||||
|
const pdfName = data && data.Name;
|
||||||
|
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||||
|
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Rename": {
|
||||||
|
setRenameValue(data.Name);
|
||||||
|
setRename(data.objectId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Delete": {
|
||||||
|
setIsDeleteDoc(true);
|
||||||
|
setSelectDoc(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Move": {
|
||||||
|
handleMoveDocument(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function for delete document
|
||||||
|
const handleDeleteDocument = async (docData) => {
|
||||||
|
setIsDeleteDoc(false);
|
||||||
|
const docId = docData.objectId;
|
||||||
|
const data = {
|
||||||
|
IsArchive: true
|
||||||
|
};
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.put(
|
||||||
|
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||||
|
"_appName"
|
||||||
|
)}_Document/${docId}`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((result) => {
|
||||||
|
const res = result.data;
|
||||||
|
if (res) {
|
||||||
|
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
|
||||||
|
props.setPdfData(updatedData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Err ", err);
|
||||||
|
props.setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleMoveDocument = async (docData) => {
|
||||||
|
setIsOpenMoveModal(true);
|
||||||
|
setSelectDoc(docData);
|
||||||
|
};
|
||||||
|
//function for move document from one folder to another folder
|
||||||
|
const handleMoveFolder = async (selectFolderData) => {
|
||||||
|
const selecFolderId = selectDoc?.Folder?.objectId;
|
||||||
|
const moveFolderId = selectFolderData?.ObjectId;
|
||||||
|
let updateDocId = selectDoc?.objectId;
|
||||||
|
let updateData;
|
||||||
|
const checkExist = moveFolderId
|
||||||
|
? selecFolderId === moveFolderId
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
: selecFolderId
|
||||||
|
? false
|
||||||
|
: true;
|
||||||
|
if (!checkExist) {
|
||||||
|
if (moveFolderId) {
|
||||||
|
updateData = {
|
||||||
|
Folder: {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: `${localStorage.getItem("_appName")}_Document`,
|
||||||
|
objectId: moveFolderId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
updateData = {
|
||||||
|
Folder: { __op: "Delete" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.put(
|
||||||
|
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||||
|
"_appName"
|
||||||
|
)}_Document/${updateDocId}`,
|
||||||
|
updateData,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
.then((Listdata) => {
|
||||||
|
// console.log("Listdata ", Listdata);
|
||||||
|
const res = Listdata.data;
|
||||||
|
if (res) {
|
||||||
|
const updatedData = props.pdfData.filter(
|
||||||
|
(x) => x.objectId !== updateDocId
|
||||||
|
);
|
||||||
|
props.setPdfData(updatedData);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("err", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsOpenMoveModal(false);
|
||||||
|
} else {
|
||||||
|
alert("folder already exist!");
|
||||||
|
setIsOpenMoveModal(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitizeFileName = (pdfName) => {
|
||||||
|
// Replace spaces with underscore
|
||||||
|
return pdfName.replace(/ /g, "_");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEnterPress = (e, data) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handledRenameDoc(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//component to handle type of document and render according to type
|
||||||
|
const handleFolderData = (data, ind, listType) => {
|
||||||
|
let createddate, status, isDecline, signerExist, isComplete;
|
||||||
|
if (data.Type !== "Folder") {
|
||||||
|
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
|
||||||
|
const createdDate = data.createdAt && data.createdAt;
|
||||||
|
createddate = new Date(createdDate).toLocaleDateString();
|
||||||
|
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
|
||||||
|
isDecline = data.IsDeclined && data.IsDeclined;
|
||||||
|
signerExist = data.Signers && data.Signers;
|
||||||
|
const signedUrl = data.SignedUrl;
|
||||||
|
|
||||||
|
const expireUpdateDate = new Date(expireDate).getTime();
|
||||||
|
const currDate = new Date().getTime();
|
||||||
|
let isExpire = false;
|
||||||
|
if (currDate > expireUpdateDate) {
|
||||||
|
isExpire = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
status = "Completed";
|
||||||
|
} else if (isDecline) {
|
||||||
|
status = "Declined";
|
||||||
|
} else if (!signedUrl) {
|
||||||
|
status = "Draft";
|
||||||
|
} else if (isExpire) {
|
||||||
|
status = "Expired";
|
||||||
|
} else {
|
||||||
|
status = "In Progress";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const signersName = () => {
|
||||||
|
const getSignersName = signerExist.map((data) => data.Name);
|
||||||
|
const signerName = getSignersName.join(", ");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="statusSpan w-[90%] break-words">{signerName}</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return listType === "table" ? (
|
||||||
|
data.Type === "Folder" ? (
|
||||||
|
<tr onClick={() => handleOnclikFolder(data)}>
|
||||||
|
<td className="cursor-pointer flex items-center">
|
||||||
|
<i
|
||||||
|
className="fa fa-folder mr-[8px] text-[26px] text-[#f0ad26]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||||
|
</td>
|
||||||
|
<td>_</td>
|
||||||
|
<td>Folder</td>
|
||||||
|
<td>_</td>
|
||||||
|
<td>_</td>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
<tr onClick={() => checkPdfStatus(data)}>
|
||||||
|
<td className="cursor-pointer flex items-center">
|
||||||
|
<i className="fa fa-file-pdf mr-[8px] text-[26px] text-[#ed4d0e]"></i>
|
||||||
|
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||||
|
</td>
|
||||||
|
<td>{createddate}</td>
|
||||||
|
<td>Pdf</td>
|
||||||
|
<td>{status}</td>
|
||||||
|
<td>
|
||||||
|
<i
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleMenuItemClick("Download", data);
|
||||||
|
}}
|
||||||
|
className="fa fa-download mr-[8px] text-[#ed280e]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
) : listType === "list" && data.Type === "Folder" ? (
|
||||||
|
<div key={ind} className="icon-container">
|
||||||
|
<ContextMenu.Root>
|
||||||
|
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||||
|
<div data-tut={props.dataTutSeventh}>
|
||||||
|
<img
|
||||||
|
alt="folder"
|
||||||
|
onClick={() => handleOnclikFolder(data)}
|
||||||
|
src={folder}
|
||||||
|
className="w-full h-full"
|
||||||
|
/>
|
||||||
|
{rename === data.objectId ? (
|
||||||
|
<input
|
||||||
|
onFocus={() => {
|
||||||
|
inputRef.current.setSelectionRange(0, 0);
|
||||||
|
const input = inputRef.current;
|
||||||
|
if (input) {
|
||||||
|
input.select();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
autoFocus={true}
|
||||||
|
type="text"
|
||||||
|
onBlur={() => handledRenameDoc(data)}
|
||||||
|
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||||
|
ref={inputRef}
|
||||||
|
defaultValue={renameValue}
|
||||||
|
// value={renameValue}
|
||||||
|
onChange={(e) => setRenameValue(e.target.value)}
|
||||||
|
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="foldName">{data.Name}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ContextMenu.Trigger>
|
||||||
|
|
||||||
|
<ContextMenu.Portal>
|
||||||
|
<ContextMenu.Content
|
||||||
|
className="ContextMenuContent"
|
||||||
|
sideOffset={5}
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
<ContextMenu.Item
|
||||||
|
onClick={() => handleMenuItemClick("Rename", data)}
|
||||||
|
className="ContextMenuItem"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-font mr-[8px]"></i>
|
||||||
|
<span>Rename</span>
|
||||||
|
</ContextMenu.Item>
|
||||||
|
</ContextMenu.Content>
|
||||||
|
</ContextMenu.Portal>
|
||||||
|
</ContextMenu.Root>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<HoverCard.Root openDelay={0} closeDelay={100}>
|
||||||
|
<HoverCard.Trigger asChild>
|
||||||
|
<div>
|
||||||
|
<ContextMenu.Root>
|
||||||
|
<div className="icon-container">
|
||||||
|
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||||
|
<img
|
||||||
|
alt="PDF"
|
||||||
|
className="w-full h-full"
|
||||||
|
src={pdfLogo}
|
||||||
|
onClick={() => checkPdfStatus(data)}
|
||||||
|
data-tut={props.dataTutSixth}
|
||||||
|
/>
|
||||||
|
{rename === data.objectId ? (
|
||||||
|
<input
|
||||||
|
autoFocus={true}
|
||||||
|
type="text"
|
||||||
|
onFocus={() => {
|
||||||
|
inputRef.current.setSelectionRange(0, 0);
|
||||||
|
const input = inputRef.current;
|
||||||
|
if (input) {
|
||||||
|
input.select();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={() => handledRenameDoc(data)}
|
||||||
|
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||||
|
ref={inputRef}
|
||||||
|
defaultValue={renameValue}
|
||||||
|
// value={renameValue}
|
||||||
|
onChange={(e) => setRenameValue(e.target.value)}
|
||||||
|
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="fileName">{data.Name}</span>
|
||||||
|
)}
|
||||||
|
</ContextMenu.Trigger>
|
||||||
|
{status === "Completed" ? (
|
||||||
|
<div className="status-badge completed">
|
||||||
|
<i className="fas fa-check-circle"></i>
|
||||||
|
</div>
|
||||||
|
) : status === "Declined" ? (
|
||||||
|
<div className="status-badge declined">
|
||||||
|
<i className="fa fa-thumbs-down"></i>
|
||||||
|
</div>
|
||||||
|
) : status === "Expired" ? (
|
||||||
|
<div className="status-badge expired">
|
||||||
|
<i className="fa fa-hourglass-end"></i>
|
||||||
|
</div>
|
||||||
|
) : status === "Draft" ? (
|
||||||
|
<div className="status-badge draft">
|
||||||
|
<i className="fa fa-file"></i>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
status === "In Progress" && (
|
||||||
|
<div className="status-badge in-progress">
|
||||||
|
<i className="fa fa-paper-plane"></i>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ContextMenu.Portal>
|
||||||
|
<ContextMenu.Content
|
||||||
|
className="ContextMenuContent"
|
||||||
|
sideOffset={5}
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
|
{contextMenu.map((menu, ind) => {
|
||||||
|
return (
|
||||||
|
<ContextMenu.Item
|
||||||
|
key={ind}
|
||||||
|
onClick={() => handleMenuItemClick(menu.type, data)}
|
||||||
|
className="ContextMenuItem"
|
||||||
|
>
|
||||||
|
<i className={menu.icon}></i>
|
||||||
|
<span className="ml-[8px]">{menu.type}</span>
|
||||||
|
</ContextMenu.Item>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ContextMenu.Content>
|
||||||
|
</ContextMenu.Portal>
|
||||||
|
</ContextMenu.Root>
|
||||||
|
</div>
|
||||||
|
</HoverCard.Trigger>
|
||||||
|
<HoverCard.Portal>
|
||||||
|
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
|
||||||
|
<strong className="text-[13px]">Title: </strong>
|
||||||
|
<span className="statusSpan" style={{ marginBottom: "0px" }}>
|
||||||
|
{" "}
|
||||||
|
{data.Name}
|
||||||
|
</span>
|
||||||
|
<br />
|
||||||
|
<strong className="text-[13px]">Status: </strong>
|
||||||
|
<span className="statusSpan"> {status}</span>
|
||||||
|
<br />
|
||||||
|
<strong className="text-[13px]">Created Date: </strong>
|
||||||
|
<span className="statusSpan">{createddate}</span>
|
||||||
|
<br />
|
||||||
|
{signerExist && (
|
||||||
|
<>
|
||||||
|
<strong className="text-[13px]">Signers: </strong>
|
||||||
|
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
||||||
|
{signersName()}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<HoverCard.Arrow className="HoverCardArrow" />
|
||||||
|
</HoverCard.Content>
|
||||||
|
</HoverCard.Portal>
|
||||||
|
</HoverCard.Root>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//component to handle type of document and render according to type
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.isList ? (
|
||||||
|
<div className="container" style={{ overflowX: "auto" }}>
|
||||||
|
<Table striped bordered hover>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Created Date</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{props.pdfData.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={ind}>
|
||||||
|
{handleFolderData(data, ind, "table")}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
|
||||||
|
{props.pdfData.map((data, ind) => {
|
||||||
|
return <div key={ind}>{handleFolderData(data, ind, "list")}</div>;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isOpenMoveModal && (
|
||||||
|
<FolderModal
|
||||||
|
onSuccess={handleMoveFolder}
|
||||||
|
isOpenModal={isOpenMoveModal}
|
||||||
|
folderCls={"contracts_Document"}
|
||||||
|
setIsOpenMoveModal={setIsOpenMoveModal}
|
||||||
|
setPdfData={props.setPdfData}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ModalUi
|
||||||
|
isOpen={isDeleteDoc}
|
||||||
|
title={"Delete Document"}
|
||||||
|
handleClose={() => setIsDeleteDoc(false)}
|
||||||
|
>
|
||||||
|
<div className="h-full p-[20px] text-base-content">
|
||||||
|
<p>Are you sure you want to delete this document?</p>
|
||||||
|
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteDocument(selectDoc)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary mr-2"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setIsDeleteDoc(false)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-neutral"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DriveBody;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
|
||||||
|
const AddRoleModal = (props) => {
|
||||||
|
return (
|
||||||
|
<ModalUi
|
||||||
|
title={"Add Role"}
|
||||||
|
isOpen={props.isModalRole}
|
||||||
|
handleClose={props.handleCloseRoleModal}
|
||||||
|
>
|
||||||
|
<div className="h-full py-[10px] px-[20px]">
|
||||||
|
<form className="flex flex-col" onSubmit={props.handleAddRole}>
|
||||||
|
<input
|
||||||
|
value={props.roleName}
|
||||||
|
onChange={(e) => props.setRoleName(e.target.value)}
|
||||||
|
placeholder={
|
||||||
|
props.signersdata.length > 0
|
||||||
|
? "User " + (props.signersdata.length + 1)
|
||||||
|
: "User 1"
|
||||||
|
}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
|
||||||
|
/>
|
||||||
|
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
|
||||||
|
e.g: Customer, Hr, Director, Manager, Student, etc...
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Add
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={props.handleCloseRoleModal}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost ml-2"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddRoleModal;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function BorderResize({ right, top }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="borderResize"
|
||||||
|
style={{
|
||||||
|
right: right ? right + "px" : "-1px",
|
||||||
|
bottom: top ? top + "px" : "-1px",
|
||||||
|
borderRight: "3px solid #188ae2",
|
||||||
|
borderBottom: "3px solid #188ae2"
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BorderResize;
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import "./certificate.css";
|
import opensignLogo from "../../assets/images/logo.png";
|
||||||
|
import {
|
||||||
import { Page, Text, View, Document, StyleSheet } from "@react-pdf/renderer";
|
Page,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
Document,
|
||||||
|
StyleSheet,
|
||||||
|
Image
|
||||||
|
} from "@react-pdf/renderer";
|
||||||
|
|
||||||
function Certificate({ pdfData }) {
|
function Certificate({ pdfData }) {
|
||||||
const [isMultiSigners, setIsMultiSigners] = useState();
|
const [isMultiSigners, setIsMultiSigners] = useState();
|
||||||
@@ -10,6 +16,7 @@ function Certificate({ pdfData }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleSignerData();
|
handleSignerData();
|
||||||
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSignerData = () => {
|
const handleSignerData = () => {
|
||||||
@@ -49,70 +56,34 @@ function Certificate({ pdfData }) {
|
|||||||
fontSize: "11px",
|
fontSize: "11px",
|
||||||
marginBottom: "10px",
|
marginBottom: "10px",
|
||||||
color: "gray"
|
color: "gray"
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: "71px",
|
||||||
|
height: "17px"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const generatedDate = () => {
|
const generatedDate = () => {
|
||||||
const newDate = new Date();
|
const newDate = new Date();
|
||||||
const localExpireDate = newDate.toLocaleDateString("en-US", {
|
const utcTime = newDate.toUTCString();
|
||||||
day: "numeric",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric"
|
|
||||||
});
|
|
||||||
|
|
||||||
var currentOffset = newDate.getTimezoneOffset();
|
|
||||||
|
|
||||||
var ISTOffset = 330; // IST offset UTC +5:30
|
|
||||||
|
|
||||||
var ISTTime = new Date(
|
|
||||||
newDate.getTime() + (ISTOffset + currentOffset) * 60000
|
|
||||||
);
|
|
||||||
|
|
||||||
// ISTTime now represents the time in IST coordinates
|
|
||||||
|
|
||||||
var hoursIST = ISTTime.getHours();
|
|
||||||
var minutesIST = ISTTime.getMinutes();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
textAlign: "right",
|
|
||||||
color: "gray",
|
color: "gray",
|
||||||
fontSize: "10px",
|
fontSize: "10px"
|
||||||
marginBottom: "30px"
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Generated On {localExpireDate} {hoursIST}:{minutesIST} IST
|
Generated On {utcTime}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const changeCompletedDate = () => {
|
const changeCompletedDate = () => {
|
||||||
const completedOn = pdfData[0].updatedAt;
|
const completedOn = pdfData[0].updatedAt;
|
||||||
const newDate = new Date(completedOn);
|
const newDate = new Date(completedOn);
|
||||||
const localExpireDate = newDate.toLocaleDateString("en-US", {
|
const utcTime = newDate.toUTCString();
|
||||||
day: "numeric",
|
|
||||||
month: "long",
|
|
||||||
year: "numeric"
|
|
||||||
});
|
|
||||||
|
|
||||||
var currentOffset = newDate.getTimezoneOffset();
|
return <Text style={styles.textStyle2}>{utcTime}</Text>;
|
||||||
|
|
||||||
var ISTOffset = 330; // IST offset UTC +5:30
|
|
||||||
|
|
||||||
var ISTTime = new Date(
|
|
||||||
newDate.getTime() + (ISTOffset + currentOffset) * 60000
|
|
||||||
);
|
|
||||||
|
|
||||||
// ISTTime now represents the time in IST coordinates
|
|
||||||
|
|
||||||
var hoursIST = ISTTime.getHours();
|
|
||||||
var minutesIST = ISTTime.getMinutes();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Text style={styles.textStyle2}>
|
|
||||||
{localExpireDate} {hoursIST}:{minutesIST} IST
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const signerName = (data) => {
|
const signerName = (data) => {
|
||||||
@@ -143,7 +114,19 @@ function Certificate({ pdfData }) {
|
|||||||
{/** Page defines a single page of content. */}
|
{/** Page defines a single page of content. */}
|
||||||
<Page size="A4" style={styles.page}>
|
<Page size="A4" style={styles.page}>
|
||||||
<View style={styles.section1}>
|
<View style={styles.section1}>
|
||||||
{generatedDate()}
|
<View
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: "30px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image src={opensignLogo} style={styles.image} />
|
||||||
|
{generatedDate()}
|
||||||
|
</View>
|
||||||
|
|
||||||
<View style={{ justifyContent: "center" }}>
|
<View style={{ justifyContent: "center" }}>
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
@@ -182,7 +165,9 @@ function Certificate({ pdfData }) {
|
|||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.textStyle}>
|
<Text style={styles.textStyle}>
|
||||||
Organization :
|
Organization :
|
||||||
<Text style={styles.textStyle2}>__</Text>
|
<Text style={styles.textStyle2}>
|
||||||
|
{pdfData[0].ExtUserPtr.Company}
|
||||||
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.textStyle}>
|
<Text style={styles.textStyle}>
|
||||||
Completed on : {changeCompletedDate()}
|
Completed on : {changeCompletedDate()}
|
||||||
@@ -260,6 +245,12 @@ function Certificate({ pdfData }) {
|
|||||||
{pdfData[0].ExtUserPtr.Name}
|
{pdfData[0].ExtUserPtr.Name}
|
||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text style={styles.textStyle}>
|
||||||
|
Email :
|
||||||
|
<Text style={styles.textStyle2}>
|
||||||
|
{pdfData[0].ExtUserPtr.Email}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
<Text style={styles.textStyle}>
|
<Text style={styles.textStyle}>
|
||||||
Accessed from :
|
Accessed from :
|
||||||
<Text style={styles.textStyle2}>
|
<Text style={styles.textStyle2}>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import React from "react";
|
||||||
|
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||||
|
const confirmToaddDefaultSign = () => {
|
||||||
|
if (xyPostion.length > 0) {
|
||||||
|
setDefaultSignAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Are you sure you want to auto sign at requested all locations?"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setDefaultSignAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "please select position!"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-tut="reactourThird">
|
||||||
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
|
Signature
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center mt-[10px] font-semibold">
|
||||||
|
<p className="text-base-content">Your Signature</p>
|
||||||
|
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
|
||||||
|
<img
|
||||||
|
alt="signature"
|
||||||
|
className="w-full h-full object-contain"
|
||||||
|
src={defaultSignImg}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
|
||||||
|
onClick={() => confirmToaddDefaultSign()}
|
||||||
|
>
|
||||||
|
Auto Sign All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DefaultSignature;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
|
||||||
|
import { contractDocument } from "../../constant/Utils";
|
||||||
|
import HandleError from "../../primitives/HandleError";
|
||||||
|
function DraftDocument() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isLoading, setIsLoading] = useState({
|
||||||
|
isLoader: true,
|
||||||
|
message: "This might take some time"
|
||||||
|
});
|
||||||
|
|
||||||
|
const rowLevel =
|
||||||
|
localStorage.getItem("rowlevel") &&
|
||||||
|
JSON.parse(localStorage.getItem("rowlevel"));
|
||||||
|
const docId =
|
||||||
|
rowLevel && rowLevel?.id
|
||||||
|
? rowLevel.id
|
||||||
|
: rowLevel?.objectId && rowLevel.objectId;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDocumentDetails();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
//get document details
|
||||||
|
const getDocumentDetails = async () => {
|
||||||
|
//getting document details
|
||||||
|
const documentData = await contractDocument(docId);
|
||||||
|
if (documentData && documentData.length > 0) {
|
||||||
|
handleDraftDoc(documentData);
|
||||||
|
} else if (
|
||||||
|
documentData === "Error: Something went wrong!" ||
|
||||||
|
(documentData.result && documentData.result.error)
|
||||||
|
) {
|
||||||
|
setIsLoading({
|
||||||
|
isLoader: false,
|
||||||
|
message: "Error: Something went wrong!"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setIsLoading({
|
||||||
|
isLoader: false,
|
||||||
|
message: "No data found!"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//check document type and render on signyour self and placeholder route
|
||||||
|
const handleDraftDoc = (documentData) => {
|
||||||
|
const data = documentData[0];
|
||||||
|
const signerExist = data.Signers && data.Signers;
|
||||||
|
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||||
|
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||||
|
const signedUrl = data.SignedUrl;
|
||||||
|
//checking if document has completed and request signature flow
|
||||||
|
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//checking if document has completed and signyour-self flow
|
||||||
|
else if (!signerExist && !isPlaceholder) {
|
||||||
|
navigate(`/signaturePdf/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//checking if document has declined by someone
|
||||||
|
else if (isDecline) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
//checking draft type document
|
||||||
|
} else if (
|
||||||
|
signerExist?.length > 0 &&
|
||||||
|
isPlaceholder?.length > 0 &&
|
||||||
|
!signedUrl
|
||||||
|
) {
|
||||||
|
navigate(`/placeHolderSign/${data.objectId}`);
|
||||||
|
}
|
||||||
|
//Inprogress document
|
||||||
|
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||||
|
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||||
|
} //placeholder draft document
|
||||||
|
else if (
|
||||||
|
(signerExist?.length > 0 &&
|
||||||
|
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||||
|
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||||
|
) {
|
||||||
|
navigate(`/placeHolderSign/${data.objectId}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isLoading.isLoader ? (
|
||||||
|
<LoaderWithMsg isLoading={isLoading} />
|
||||||
|
) : (
|
||||||
|
<HandleError handleError={isLoading.message} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DraftDocument;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { getWidgetType, widgets } from "../../constant/Utils";
|
||||||
|
|
||||||
|
function DragElement(item) {
|
||||||
|
const getWidgets = widgets;
|
||||||
|
const filterWidgetPreview = getWidgets.filter(
|
||||||
|
(data) => data.type === item?.text
|
||||||
|
);
|
||||||
|
|
||||||
|
return <div>{getWidgetType(filterWidgetPreview[0])}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DragElement;
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import { radioButtonWidget } from "../../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
|
function DropdownWidgetOption(props) {
|
||||||
|
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||||
|
"option-1",
|
||||||
|
"option-2"
|
||||||
|
]);
|
||||||
|
const [minCount, setMinCount] = useState(0);
|
||||||
|
const [maxCount, setMaxCount] = useState(0);
|
||||||
|
const [dropdownName, setDropdownName] = useState(props.type);
|
||||||
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||||
|
const [isHideLabel, setIsHideLabel] = useState(false);
|
||||||
|
const [status, setStatus] = useState("required");
|
||||||
|
const [defaultValue, setDefaultValue] = useState("");
|
||||||
|
const statusArr = ["required", "optional"];
|
||||||
|
const [defaultCheckbox, setDefaultCheckbox] = useState([]);
|
||||||
|
|
||||||
|
const resetState = () => {
|
||||||
|
setDropdownOptionList(["option-1", "option-2"]);
|
||||||
|
setDropdownName(props.type);
|
||||||
|
setIsReadOnly(false);
|
||||||
|
setIsHideLabel(false);
|
||||||
|
setMinCount(0);
|
||||||
|
setMaxCount(0);
|
||||||
|
setDefaultCheckbox([]);
|
||||||
|
setDefaultValue("");
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
props.currWidgetsDetails?.options?.name &&
|
||||||
|
props.currWidgetsDetails?.options?.values
|
||||||
|
) {
|
||||||
|
setDropdownName(props.currWidgetsDetails?.options?.name);
|
||||||
|
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
|
||||||
|
setMinCount(
|
||||||
|
props.currWidgetsDetails?.options?.validation?.minRequiredCount
|
||||||
|
);
|
||||||
|
setMaxCount(
|
||||||
|
props.currWidgetsDetails?.options?.validation?.maxRequiredCount
|
||||||
|
);
|
||||||
|
setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly);
|
||||||
|
setIsHideLabel(props.currWidgetsDetails?.options?.isHideLabel);
|
||||||
|
setStatus(props.currWidgetsDetails?.options?.status || "required");
|
||||||
|
setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || "");
|
||||||
|
setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []);
|
||||||
|
} else {
|
||||||
|
setStatus("required");
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [props.currWidgetsDetails]);
|
||||||
|
const handleInputChange = (index, value) => {
|
||||||
|
setDropdownOptionList((prevInputs) => {
|
||||||
|
const newInputs = [...prevInputs];
|
||||||
|
newInputs[index] = value;
|
||||||
|
return newInputs;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//function add add checkbox option and add width of checkbox
|
||||||
|
const handleAddInput = () => {
|
||||||
|
const deleteOption = false;
|
||||||
|
const addOption = true;
|
||||||
|
setDropdownOptionList((prevInputs) => [...prevInputs, ""]);
|
||||||
|
props.handleSaveWidgetsOptions(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
addOption,
|
||||||
|
deleteOption
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//function add add checkbox option and delete width of checkbox
|
||||||
|
const handleDeleteInput = (ind) => {
|
||||||
|
const deleteOption = true;
|
||||||
|
const addOption = false;
|
||||||
|
const getUpdatedOptions = dropdownOptionList.filter(
|
||||||
|
(data, index) => index !== ind
|
||||||
|
);
|
||||||
|
setDropdownOptionList(getUpdatedOptions);
|
||||||
|
props.handleSaveWidgetsOptions(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
addOption,
|
||||||
|
deleteOption
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveOption = () => {
|
||||||
|
const defaultData =
|
||||||
|
defaultCheckbox && defaultCheckbox.length > 0
|
||||||
|
? defaultCheckbox
|
||||||
|
: defaultValue;
|
||||||
|
|
||||||
|
props.handleSaveWidgetsOptions(
|
||||||
|
dropdownName,
|
||||||
|
dropdownOptionList,
|
||||||
|
minCount,
|
||||||
|
maxCount,
|
||||||
|
isReadOnly,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
status,
|
||||||
|
defaultData,
|
||||||
|
isHideLabel
|
||||||
|
);
|
||||||
|
// props.setShowDropdown(false);
|
||||||
|
setDropdownOptionList(["option-1", "option-2"]);
|
||||||
|
setDropdownName(props.type);
|
||||||
|
// props.setCurrWidgetsDetails({});
|
||||||
|
setIsReadOnly(false);
|
||||||
|
setIsHideLabel(false);
|
||||||
|
setMinCount(0);
|
||||||
|
setMaxCount(0);
|
||||||
|
setDefaultCheckbox([]);
|
||||||
|
setDefaultValue("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSetMinMax = (e) => {
|
||||||
|
const minValue = e.target.value;
|
||||||
|
if (minValue > dropdownOptionList.length) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return minValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDefaultCheck = (index) => {
|
||||||
|
const getDefaultCheck = defaultCheckbox.includes(index);
|
||||||
|
if (getDefaultCheck) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
|
||||||
|
<div className="h-full p-[15px] text-base-content">
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSaveOption();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<label className="text-[13px] font-semibold">
|
||||||
|
Name<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
defaultValue={dropdownName}
|
||||||
|
value={dropdownName}
|
||||||
|
onChange={(e) => setDropdownName(e.target.value)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="text-[13px] font-semibold mt-[5px]">
|
||||||
|
Options
|
||||||
|
</label>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{dropdownOptionList.map((option, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex flex-row mb-[5px] items-center"
|
||||||
|
>
|
||||||
|
{props.type === "checkbox" && !props.isSignYourself && (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={handleDefaultCheck(index)}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
const getDefaultCheck =
|
||||||
|
defaultCheckbox?.includes(index);
|
||||||
|
if (!getDefaultCheck) {
|
||||||
|
setDefaultCheckbox((prev) => [...prev, index]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const removeOption = defaultCheckbox.filter(
|
||||||
|
(data) => data !== index
|
||||||
|
);
|
||||||
|
setDefaultCheckbox(removeOption);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="op-checkbox focus:outline-none hover:border-base-content mr-[5px]"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
type="text"
|
||||||
|
value={option}
|
||||||
|
onChange={(e) => handleInputChange(index, e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<i
|
||||||
|
className="fa-solid fa-rectangle-xmark text-[25px] ml-[10px] text-error"
|
||||||
|
onClick={() => handleDeleteInput(index)}
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<i
|
||||||
|
onClick={handleAddInput}
|
||||||
|
className="fa-solid fa-square-plus text-[25px] ml-[10px] text-primary"
|
||||||
|
></i>
|
||||||
|
<div>
|
||||||
|
{props.type === "checkbox" && !props.isSignYourself && (
|
||||||
|
<>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
!props.isSubscribe ? "text-[gray]" : ""
|
||||||
|
} text-[13px] font-semibold`}
|
||||||
|
>
|
||||||
|
Minimun check
|
||||||
|
</label>
|
||||||
|
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
defaultValue={0}
|
||||||
|
value={minCount}
|
||||||
|
onChange={(e) => {
|
||||||
|
const count = handleSetMinMax(e);
|
||||||
|
setMinCount(count);
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
props.isSubscribe || !isEnableSubscription
|
||||||
|
? ""
|
||||||
|
: "pointer-events-none bg-opacity-50"
|
||||||
|
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
!props.isSubscribe ? "text-[gray]" : ""
|
||||||
|
} text-[13px] font-semibold`}
|
||||||
|
>
|
||||||
|
Maximum check
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
defaultValue={0}
|
||||||
|
value={maxCount}
|
||||||
|
onChange={(e) => {
|
||||||
|
const count = handleSetMinMax(e);
|
||||||
|
setMaxCount(count);
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
props.isSubscribe || !isEnableSubscription
|
||||||
|
? ""
|
||||||
|
: "pointer-events-none bg-opacity-50"
|
||||||
|
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||||
|
<>
|
||||||
|
<label className="text-[13px] font-semibold mt-[5px]">
|
||||||
|
Default value
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
onChange={(e) => setDefaultValue(e.target.value)}
|
||||||
|
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
name="defaultvalue"
|
||||||
|
value={defaultValue}
|
||||||
|
placeholder="select default value"
|
||||||
|
>
|
||||||
|
<option value="" disabled hidden className="text-[13px]">
|
||||||
|
Select...
|
||||||
|
</option>
|
||||||
|
{dropdownOptionList.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<option className="text-[13px]" key={ind} value={data}>
|
||||||
|
{data}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
|
||||||
|
<>
|
||||||
|
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
|
||||||
|
{statusArr.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={ind}
|
||||||
|
className="flex flex-row gap-[5px] items-center"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="op-radio op-radio-xs my-1"
|
||||||
|
type="radio"
|
||||||
|
name="status"
|
||||||
|
onChange={() => setStatus(data.toLowerCase())}
|
||||||
|
checked={status.toLowerCase() === data.toLowerCase()}
|
||||||
|
/>
|
||||||
|
<div className="text-[13px] font-500">{data}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{["checkbox", radioButtonWidget].includes(props.type) && (
|
||||||
|
<div className="flex flex-row gap-5 my-2 items-center text-center">
|
||||||
|
{!props.isSignYourself && (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
id="isreadonly"
|
||||||
|
type="checkbox"
|
||||||
|
checked={isReadOnly}
|
||||||
|
className="op-checkbox op-checkbox-sm"
|
||||||
|
onChange={(e) => setIsReadOnly(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<label className="ml-1 mb-0" htmlFor="isreadonly">
|
||||||
|
Is read only
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
id="ishidelabel"
|
||||||
|
type="checkbox"
|
||||||
|
checked={isHideLabel}
|
||||||
|
className="op-checkbox op-checkbox-sm"
|
||||||
|
onChange={(e) => setIsHideLabel(e.target.checked)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label className="ml-1 mb-0" htmlFor="ishidelabel">
|
||||||
|
Hide labels
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{props.type === "checkbox" &&
|
||||||
|
!props.isSignYourself &&
|
||||||
|
!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Field validations are free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
props.type === "checkbox" && !props.isSignYourself
|
||||||
|
? "mb-[15px]"
|
||||||
|
: "my-[15px]"
|
||||||
|
} w-full h-[1px] bg-[#9f9f9f]`}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
disabled={dropdownOptionList.length === 0 && true}
|
||||||
|
type="submit"
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="op-btn op-btn-ghost ml-1"
|
||||||
|
onClick={() => {
|
||||||
|
props.handleClose && props.handleClose();
|
||||||
|
resetState();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DropdownWidgetOption;
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
|
// import SelectFolder from "../../premitives/SelectFolder";
|
||||||
|
|
||||||
|
const EditTemplate = ({ template, onSuccess }) => {
|
||||||
|
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
Name: template?.Name || "",
|
||||||
|
Note: template?.Note || "",
|
||||||
|
Description: template?.Description || "",
|
||||||
|
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||||
|
AutomaticReminders: template?.AutomaticReminders || false,
|
||||||
|
RemindOnceInEvery: template?.RemindOnceInEvery || 5
|
||||||
|
});
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
fetchSubscription();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
const fetchSubscription = async () => {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleStrInput = (e) => {
|
||||||
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
// const handleFolder = (data) => {
|
||||||
|
// console.log("handleFolder ", data)
|
||||||
|
// setFolder(data);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// Define a function to handle form submission
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||||
|
const AutoReminder = formData?.AutomaticReminders || false;
|
||||||
|
|
||||||
|
let reminderDate = {};
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||||
|
const ReminderDate = new Date(template?.createdAt);
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
reminderDate = { NextReminderDate: ReminderDate };
|
||||||
|
}
|
||||||
|
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
|
||||||
|
onSuccess(data);
|
||||||
|
};
|
||||||
|
const handleAutoReminder = () => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
AutomaticReminders: !formData.AutomaticReminders
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||||
|
<div className="text-base-content">
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
|
File
|
||||||
|
</label>
|
||||||
|
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
|
||||||
|
{getFileName(template.URL)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-section">
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
|
Name
|
||||||
|
<span className="text-[13px] text-[red]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Name"
|
||||||
|
value={formData.Name}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
required
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-section">
|
||||||
|
<label htmlFor="Note" className="text-[13px]">
|
||||||
|
Note
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Note"
|
||||||
|
id="Note"
|
||||||
|
value={formData.Note}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-section">
|
||||||
|
<label htmlFor="Description" className="text-[13px]">
|
||||||
|
Description
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="Description"
|
||||||
|
id="Description"
|
||||||
|
value={formData.Description}
|
||||||
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-section">
|
||||||
|
<label className="text-[13px]">Send In Order</label>
|
||||||
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"true"}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
name="SendinOrder"
|
||||||
|
checked={formData.SendinOrder === "true"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-[12px]">Yes</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"false"}
|
||||||
|
name="SendinOrder"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
|
checked={formData.SendinOrder === "false"}
|
||||||
|
onChange={handleStrInput}
|
||||||
|
/>
|
||||||
|
<div className="text-[12px]">No</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
{!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Disable Auto reminder is free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "font-semibold"
|
||||||
|
: "font-semibold text-gray-300"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Auto reminder{" "}
|
||||||
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</span>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "cursor-pointer "
|
||||||
|
: "pointer-events-none opacity-50"
|
||||||
|
} relative block items-center mb-0`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
checked={formData.AutomaticReminders}
|
||||||
|
onChange={handleAutoReminder}
|
||||||
|
type="checkbox"
|
||||||
|
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{isSubscribe && formData?.AutomaticReminders === true && (
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
Remind once in every (Days)
|
||||||
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.RemindOnceInEvery}
|
||||||
|
name="RemindOnceInEvery"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
onChange={handleStrInput}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="mt-[1rem] flex justify-start">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditTemplate;
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Quill } from "react-quill";
|
||||||
|
|
||||||
|
// Custom Undo button icon component for Quill editor. You can import it directly
|
||||||
|
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
|
||||||
|
// handle them correctly
|
||||||
|
const CustomUndo = () => (
|
||||||
|
<svg viewBox="0 0 18 18">
|
||||||
|
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
|
||||||
|
<path
|
||||||
|
className="ql-stroke"
|
||||||
|
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Redo button icon component for Quill editor
|
||||||
|
const CustomRedo = () => (
|
||||||
|
<svg viewBox="0 0 18 18">
|
||||||
|
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
|
||||||
|
<path
|
||||||
|
className="ql-stroke"
|
||||||
|
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Undo and redo functions for Custom Toolbar
|
||||||
|
function undoChange() {
|
||||||
|
this.quill.history.undo();
|
||||||
|
}
|
||||||
|
function redoChange() {
|
||||||
|
this.quill.history.redo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sizes to whitelist and register them
|
||||||
|
const Size = Quill.import("formats/size");
|
||||||
|
Size.whitelist = ["extra-small", "small", "medium", "large"];
|
||||||
|
Quill.register(Size, true);
|
||||||
|
|
||||||
|
// Add fonts to whitelist and register them
|
||||||
|
const Font = Quill.import("formats/font");
|
||||||
|
Font.whitelist = [
|
||||||
|
"arial",
|
||||||
|
"comic-sans",
|
||||||
|
"courier-new",
|
||||||
|
"georgia",
|
||||||
|
"helvetica",
|
||||||
|
"lucida"
|
||||||
|
];
|
||||||
|
Quill.register(Font, true);
|
||||||
|
|
||||||
|
// Modules object for setting up the Quill editor
|
||||||
|
export const module1 = {
|
||||||
|
toolbar: {
|
||||||
|
container: "#toolbar1",
|
||||||
|
handlers: {
|
||||||
|
undo: undoChange,
|
||||||
|
redo: redoChange
|
||||||
|
}
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
delay: 500,
|
||||||
|
maxStack: 100,
|
||||||
|
userOnly: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Formats objects for setting up the Quill editor
|
||||||
|
export const formats = [
|
||||||
|
"header",
|
||||||
|
"font",
|
||||||
|
"size",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"underline",
|
||||||
|
"align",
|
||||||
|
"strike",
|
||||||
|
"script",
|
||||||
|
"blockquote",
|
||||||
|
"background",
|
||||||
|
"list",
|
||||||
|
"bullet",
|
||||||
|
"indent",
|
||||||
|
"link",
|
||||||
|
"image",
|
||||||
|
"color",
|
||||||
|
"code-block"
|
||||||
|
];
|
||||||
|
// Quill Toolbar component
|
||||||
|
export const QuillToolbar = ({ containerId }) => (
|
||||||
|
<div id={containerId}>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<select className="ql-font" defaultValue="arial">
|
||||||
|
<option value="arial">Arial</option>
|
||||||
|
<option value="comic-sans">Comic Sans</option>
|
||||||
|
<option value="courier-new">Courier New</option>
|
||||||
|
<option value="georgia">Georgia</option>
|
||||||
|
<option value="helvetica">Helvetica</option>
|
||||||
|
<option value="lucida">Lucida</option>
|
||||||
|
</select>
|
||||||
|
<select className="ql-size" defaultValue="medium">
|
||||||
|
<option value="extra-small">Size 1</option>
|
||||||
|
<option value="small">Size 2</option>
|
||||||
|
<option value="medium">Size 3</option>
|
||||||
|
<option value="large">Size 4</option>
|
||||||
|
</select>
|
||||||
|
<select className="ql-header" defaultValue="3">
|
||||||
|
<option value="1">Heading</option>
|
||||||
|
<option value="2">Subheading</option>
|
||||||
|
<option value="3">Normal</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-bold" />
|
||||||
|
<button className="ql-italic" />
|
||||||
|
<button className="ql-underline" />
|
||||||
|
<button className="ql-strike" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-list" value="ordered" />
|
||||||
|
<button className="ql-list" value="bullet" />
|
||||||
|
<button className="ql-indent" value="-1" />
|
||||||
|
<button className="ql-indent" value="+1" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-script" value="super" />
|
||||||
|
<button className="ql-script" value="sub" />
|
||||||
|
<button className="ql-blockquote" />
|
||||||
|
<button className="ql-direction" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<select className="ql-align" />
|
||||||
|
<select className="ql-color" />
|
||||||
|
<select className="ql-background" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-link" />
|
||||||
|
<button className="ql-image" />
|
||||||
|
<button className="ql-video" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-formula" />
|
||||||
|
<button className="ql-code-block" />
|
||||||
|
<button className="ql-clean" />
|
||||||
|
</span>
|
||||||
|
<span className="ql-formats">
|
||||||
|
<button className="ql-undo">
|
||||||
|
<CustomUndo />
|
||||||
|
</button>
|
||||||
|
<button className="ql-redo">
|
||||||
|
<CustomRedo />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default QuillToolbar;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Tooltip from "../../primitives/Tooltip";
|
||||||
|
import ReactQuill from "react-quill";
|
||||||
|
import "react-quill/dist/quill.snow.css";
|
||||||
|
import EditorToolbar, { module1, formats } from "./EditorToolbar";
|
||||||
|
|
||||||
|
export function EmailBody(props) {
|
||||||
|
return (
|
||||||
|
<form className="flex flex-col text-base-content text-lg font-normal">
|
||||||
|
<div className="m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
|
||||||
|
<label className="text-sm ml-2">
|
||||||
|
Subject <Tooltip message={"email subject"} />
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
value={props.requestSubject}
|
||||||
|
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||||
|
placeholder='${senderName} has requested you to sign "${documentName}"'
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
/>
|
||||||
|
<label className="text-sm ml-2 mt-3">
|
||||||
|
Body <Tooltip message={"email body"} />
|
||||||
|
</label>
|
||||||
|
<div className="px-3 py-2 w-full focus:outline-none text-xs">
|
||||||
|
<EditorToolbar containerId="toolbar1" />
|
||||||
|
<ReactQuill
|
||||||
|
theme="snow"
|
||||||
|
value={props.requestBody}
|
||||||
|
// value={typedText}
|
||||||
|
placeholder="add body of email "
|
||||||
|
// readOnly={loading}
|
||||||
|
// onChangeSelection={handleTextSelection} // Listen for text selection
|
||||||
|
ref={props.editorRef}
|
||||||
|
modules={module1}
|
||||||
|
formats={formats}
|
||||||
|
onChange={props.handleOnchangeRequest}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import celebration from "../../assets/images/newCeleb.gif";
|
||||||
|
import axios from "axios";
|
||||||
|
import { getBase64FromUrl } from "../../constant/Utils";
|
||||||
|
import { themeColor } from "../../constant/const";
|
||||||
|
import printModule from "print-js";
|
||||||
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
|
function EmailComponent({
|
||||||
|
isEmail,
|
||||||
|
pdfUrl,
|
||||||
|
setIsEmail,
|
||||||
|
setSuccessEmail,
|
||||||
|
pdfName,
|
||||||
|
sender,
|
||||||
|
setIsAlert,
|
||||||
|
extUserId,
|
||||||
|
activeMailAdapter
|
||||||
|
}) {
|
||||||
|
const [emailList, setEmailList] = useState([]);
|
||||||
|
const [emailValue, setEmailValue] = useState();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isEmailCelebration, setIsEmailCelebration] = useState(false);
|
||||||
|
//function for send email
|
||||||
|
const sendEmail = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
let sendMail;
|
||||||
|
for (let i = 0; i < emailList.length; i++) {
|
||||||
|
try {
|
||||||
|
const imgPng =
|
||||||
|
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
|
||||||
|
|
||||||
|
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||||
|
const themeBGcolor = themeColor;
|
||||||
|
let params = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
extUserId: extUserId,
|
||||||
|
pdfName: pdfName,
|
||||||
|
url: pdfUrl,
|
||||||
|
recipient: emailList[i],
|
||||||
|
subject: `${sender.name} has signed the doc - ${pdfName}`,
|
||||||
|
from: sender.email,
|
||||||
|
html:
|
||||||
|
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
|
||||||
|
imgPng +
|
||||||
|
" height='50' style='padding:20px,width:170px,height:40px'/> </div><div style='padding:2px;font-family:system-ui; background-color:" +
|
||||||
|
themeBGcolor +
|
||||||
|
";'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>" +
|
||||||
|
pdfName +
|
||||||
|
" </strong>is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||||
|
sender.email +
|
||||||
|
" directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||||
|
openSignUrl +
|
||||||
|
" target=_blank>here</a> </p></div></div></body></html>"
|
||||||
|
};
|
||||||
|
sendMail = await axios.post(url, params, { headers: headers });
|
||||||
|
setIsEmailCelebration(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsEmailCelebration(false);
|
||||||
|
}, 3000);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
setIsLoading(false);
|
||||||
|
setIsEmail(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendMail && sendMail.data.result.status === "success") {
|
||||||
|
setSuccessEmail(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setSuccessEmail(false);
|
||||||
|
setIsEmail(false);
|
||||||
|
setEmailValue("");
|
||||||
|
setEmailList([]);
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
} else if (sendMail && sendMail.data.result.status === "error") {
|
||||||
|
setIsLoading(false);
|
||||||
|
setIsEmail(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
setEmailValue("");
|
||||||
|
setEmailList([]);
|
||||||
|
} else {
|
||||||
|
setIsLoading(false);
|
||||||
|
setIsEmail(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
setEmailValue("");
|
||||||
|
setEmailList([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for remove email
|
||||||
|
const removeChip = (index) => {
|
||||||
|
const updateEmailCount = emailList.filter((data, key) => key !== index);
|
||||||
|
setEmailList(updateEmailCount);
|
||||||
|
};
|
||||||
|
//function for get email value
|
||||||
|
const handleEmailValue = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setEmailValue(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for save email in array after press enter
|
||||||
|
const handleEnterPress = (e) => {
|
||||||
|
if (e.key === "Enter" && emailValue) {
|
||||||
|
setEmailList((prev) => [...prev, emailValue]);
|
||||||
|
setEmailValue("");
|
||||||
|
} else if (e === "add" && emailValue) {
|
||||||
|
setEmailList((prev) => [...prev, emailValue]);
|
||||||
|
setEmailValue("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// function for print signed pdf
|
||||||
|
const handleToPrint = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const pdf = await getBase64FromUrl(pdfUrl);
|
||||||
|
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||||
|
const isAppleDevice =
|
||||||
|
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||||
|
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||||
|
!window.MSStream;
|
||||||
|
if (isAndroidDevice || isAppleDevice) {
|
||||||
|
const byteArray = Uint8Array.from(
|
||||||
|
atob(pdf)
|
||||||
|
.split("")
|
||||||
|
.map((char) => char.charCodeAt(0))
|
||||||
|
);
|
||||||
|
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
window.open(blobUrl, "_blank");
|
||||||
|
} else {
|
||||||
|
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle download signed pdf
|
||||||
|
const handleDownloadPdf = () => {
|
||||||
|
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitizeFileName = (pdfName) => {
|
||||||
|
// Replace spaces with underscore
|
||||||
|
return pdfName.replace(/ /g, "_");
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAndroid = /Android/i.test(navigator.userAgent);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* isEmail */}
|
||||||
|
{isEmail && (
|
||||||
|
<div className="modaloverlay">
|
||||||
|
<div className="modalcontainer">
|
||||||
|
{isLoading && (
|
||||||
|
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2] bg-opacity-80">
|
||||||
|
<Loader />
|
||||||
|
<span className="text-[12px] font-bold">
|
||||||
|
This might take some time
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div style={{ background: "#32a3ac" }} className="modalheader">
|
||||||
|
<span className="text-white">Successfully signed!</span>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<div></div>
|
||||||
|
{!isAndroid && (
|
||||||
|
<button
|
||||||
|
onClick={handleToPrint}
|
||||||
|
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||||
|
>
|
||||||
|
<i className="fa fa-print" aria-hidden="true"></i>
|
||||||
|
Print
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||||
|
onClick={() => handleDownloadPdf()}
|
||||||
|
>
|
||||||
|
<i className="fa fa-download" aria-hidden="true"></i>
|
||||||
|
Download
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="h-full p-[20px]">
|
||||||
|
{isEmailCelebration && (
|
||||||
|
<div className="absolute w-[100%] flex justify-center items-center">
|
||||||
|
<img alt="celeb" width={300} height={250} src={celebration} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
|
||||||
|
Recipients added here will get a copy of the signed document.
|
||||||
|
</p>
|
||||||
|
{emailList.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<div className="addEmail">
|
||||||
|
<div className="flex flex-row flex-wrap">
|
||||||
|
{emailList.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex flex-row items-center bg-[#47a3ad] m-[4px] rounded-md py-[5px] px-[10px]"
|
||||||
|
key={ind}
|
||||||
|
>
|
||||||
|
<span className="text-white text-[13px]">
|
||||||
|
{data}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="text-white text-[13px] font-semibold ml-[7px] cursor-pointer"
|
||||||
|
onClick={() => removeChip(ind)}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-xmark"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{emailList.length <= 9 && (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={emailValue}
|
||||||
|
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-white outline-none"
|
||||||
|
onChange={handleEmailValue}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
onBlur={() => {
|
||||||
|
if (emailValue) {
|
||||||
|
handleEnterPress("add");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={emailValue}
|
||||||
|
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-white border-2 border-[#47a3ad]"
|
||||||
|
onChange={handleEmailValue}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
placeholder="Add the email addresses"
|
||||||
|
onBlur={() => {
|
||||||
|
if (emailValue) {
|
||||||
|
handleEnterPress("add");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className={`${
|
||||||
|
emailValue ? "cursor-pointer" : "cursor-default"
|
||||||
|
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
|
||||||
|
onClick={() => {
|
||||||
|
if (emailValue) {
|
||||||
|
handleEnterPress("add");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="fa fa-plus" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
|
||||||
|
<span className="font-bold">Note: </span>
|
||||||
|
<span className="text-[15px]">
|
||||||
|
You can only send to ten recipients at a time.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
onClick={() => emailList.length > 0 && sendEmail()}
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost ml-2"
|
||||||
|
onClick={() => {
|
||||||
|
setIsEmail(false);
|
||||||
|
setEmailValue("");
|
||||||
|
setEmailList([]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EmailComponent;
|
||||||
@@ -0,0 +1,488 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import PrevNext from "./PrevNext";
|
||||||
|
import {
|
||||||
|
handleDownloadCertificate,
|
||||||
|
handleDownloadPdf,
|
||||||
|
handleToPrint
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import "../../styles/signature.css";
|
||||||
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
|
function Header({
|
||||||
|
isPdfRequestFiles,
|
||||||
|
isPlaceholder,
|
||||||
|
setIsDecline,
|
||||||
|
pageNumber,
|
||||||
|
allPages,
|
||||||
|
changePage,
|
||||||
|
pdfUrl,
|
||||||
|
embedWidgetsData,
|
||||||
|
pdfDetails,
|
||||||
|
signerPos,
|
||||||
|
signersdata,
|
||||||
|
isMailSend,
|
||||||
|
alertSendEmail,
|
||||||
|
isCompleted,
|
||||||
|
isShowHeader,
|
||||||
|
decline,
|
||||||
|
currentSigner,
|
||||||
|
dataTut4,
|
||||||
|
alreadySign,
|
||||||
|
isSignYourself,
|
||||||
|
setIsEmail,
|
||||||
|
completeBtnTitle,
|
||||||
|
setIsEditTemplate
|
||||||
|
}) {
|
||||||
|
const filterPrefill =
|
||||||
|
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||||
|
const isMobile = window.innerWidth < 767;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [isDownloading, setIsDownloading] = useState("");
|
||||||
|
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||||
|
|
||||||
|
//function for show decline alert
|
||||||
|
const handleDeclinePdfAlert = async () => {
|
||||||
|
const currentDecline = {
|
||||||
|
currnt: "Sure",
|
||||||
|
isDeclined: true
|
||||||
|
};
|
||||||
|
setIsDecline(currentDecline);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||||
|
{isMobile && isShowHeader ? (
|
||||||
|
<div
|
||||||
|
id="navbar"
|
||||||
|
className={isGuestSigner ? "stickySignerHead" : "stickyHead"}
|
||||||
|
style={{
|
||||||
|
width: isGuestSigner
|
||||||
|
? window.innerWidth
|
||||||
|
: window.innerWidth - 30 + "px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center py-[5px] px-[10px] ">
|
||||||
|
<div onClick={() => navigate(-1)}>
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-left text-base-content"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
<PrevNext
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
allPages={allPages}
|
||||||
|
changePage={changePage}
|
||||||
|
/>
|
||||||
|
{pdfUrl && alreadySign ? (
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild>
|
||||||
|
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
|
||||||
|
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="DropdownMenuContent"
|
||||||
|
sideOffset={5}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="DropdownMenuItem"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-down mr-[2px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
Download
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
{isCompleted && (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="DropdownMenuItem"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadCertificate(
|
||||||
|
pdfDetails,
|
||||||
|
setIsDownloading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="border-none bg-[#fff]">
|
||||||
|
<i
|
||||||
|
className="fa-solid fa-award mr-[2px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
Certificate
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)}
|
||||||
|
{isSignYourself && (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="DropdownMenuItem"
|
||||||
|
onClick={() => setIsEmail(true)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<i
|
||||||
|
className="fa fa-envelope mr-[2px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
Mail
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="DropdownMenuItem"
|
||||||
|
onClick={(e) =>
|
||||||
|
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
<i
|
||||||
|
className="fa fa-print mr-[2px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
Print
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
) : (
|
||||||
|
<div className="flex justify-around items-center">
|
||||||
|
{/* current signer is checking user send request and check status of pdf sign than if current
|
||||||
|
user exist than show finish button else no
|
||||||
|
*/}
|
||||||
|
{currentSigner && (
|
||||||
|
<div className="flex items-center" data-tut="reactourFifth">
|
||||||
|
{decline && (
|
||||||
|
<div
|
||||||
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
|
className="text-[red] border-none font-[650] text-[14px] mr-2"
|
||||||
|
>
|
||||||
|
Decline
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isPlaceholder ? (
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (!isMailSend) {
|
||||||
|
alertSendEmail();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isMailSend ? "" : "op-link-primary"
|
||||||
|
} op-link no-underline font-[650] text-[14px]`}
|
||||||
|
data-tut={dataTut4}
|
||||||
|
>
|
||||||
|
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
data-tut="reactourThird"
|
||||||
|
onClick={() => {
|
||||||
|
if (!pdfUrl) {
|
||||||
|
embedWidgetsData();
|
||||||
|
} else if (isPdfRequestFiles) {
|
||||||
|
embedWidgetsData();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
|
||||||
|
>
|
||||||
|
Finish
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild>
|
||||||
|
<div className="font-[650] text-[18px] px-2 ml-[4px] op-link op-link-info no-underline">
|
||||||
|
<i
|
||||||
|
className="fa fa-ellipsis-v"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="bg-white shadow-md rounded px-2 py-1"
|
||||||
|
sideOffset={5}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-down mr-[5px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-[500]">Download</span>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-wrap justify-between items-center mt-[5px]">
|
||||||
|
<PrevNext
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
allPages={allPages}
|
||||||
|
changePage={changePage}
|
||||||
|
/>
|
||||||
|
{isPlaceholder ? (
|
||||||
|
<>
|
||||||
|
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
|
||||||
|
{!isMailSend &&
|
||||||
|
signersdata.length > 0 &&
|
||||||
|
signersdata.length !== filterPrefill.length && (
|
||||||
|
<div>
|
||||||
|
{filterPrefill.length === 0 ? (
|
||||||
|
<span className="text-[13px] text-[#f5405e]">
|
||||||
|
Add {signersdata.length - filterPrefill.length}{" "}
|
||||||
|
recipients signature
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="text-[13px] text-[#f5405e]">
|
||||||
|
Add {signersdata.length - filterPrefill.length} more
|
||||||
|
recipients signature
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex">
|
||||||
|
{setIsEditTemplate && (
|
||||||
|
<button
|
||||||
|
onClick={() => setIsEditTemplate(true)}
|
||||||
|
className="outline-none border-none text-center mr-[5px]"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-gear fa-lg"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
disabled={isMailSend && true}
|
||||||
|
data-tut="reactourFour"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||||
|
onClick={() => alertSendEmail()}
|
||||||
|
>
|
||||||
|
{completeBtnTitle
|
||||||
|
? completeBtnTitle
|
||||||
|
: isMailSend
|
||||||
|
? "Sent"
|
||||||
|
: "Send"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : isPdfRequestFiles ? (
|
||||||
|
alreadySign ? (
|
||||||
|
<div className="flex flex-row">
|
||||||
|
{isCompleted && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||||
|
}
|
||||||
|
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa-solid fa-award py-[3px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="hidden lg:block">Certificate</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-neutral op-btn-sm mr-[5px] shadow"
|
||||||
|
>
|
||||||
|
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Print</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Download</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex" data-tut="reactourFifth">
|
||||||
|
<button
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
{currentSigner && (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
|
||||||
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
|
>
|
||||||
|
Decline
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||||
|
onClick={() => embedWidgetsData()}
|
||||||
|
>
|
||||||
|
Finish
|
||||||
|
</button>
|
||||||
|
<div className="op-dropdown op-dropdown-end">
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
className="op-btn op-btn-info op-btn-sm shadow"
|
||||||
|
>
|
||||||
|
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
className="op-dropdown-content z-[1] op-menu op-menu-sm p-1 shadow bg-base-100 rounded-box mt-1"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="font-semibold text-[12px]">
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-down"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>{" "}
|
||||||
|
Download
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : isCompleted ? (
|
||||||
|
<div className="flex flex-row">
|
||||||
|
{isCompleted && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||||
|
}
|
||||||
|
className="op-btn op-btn-secondary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-award" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Certificate</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-neutral op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
|
>
|
||||||
|
<i className="fa fa-print" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Print</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="fa fa-download" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Download</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-info op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
|
onClick={() => setIsEmail(true)}
|
||||||
|
>
|
||||||
|
<i className="fa fa-envelope" aria-hidden="true"></i>
|
||||||
|
<span className="hidden lg:block">Mail</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex">
|
||||||
|
<button
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
|
>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||||
|
onClick={() => {
|
||||||
|
if (!pdfUrl) {
|
||||||
|
embedWidgetsData();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Finish
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isDownloading === "pdf" && (
|
||||||
|
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ModalUi
|
||||||
|
isOpen={isDownloading === "certificate"}
|
||||||
|
title={
|
||||||
|
isDownloading === "certificate"
|
||||||
|
? "Generating certificate"
|
||||||
|
: "PDF Download"
|
||||||
|
}
|
||||||
|
handleClose={() => setIsDownloading("")}
|
||||||
|
>
|
||||||
|
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
|
||||||
|
{isDownloading === "certificate"}{" "}
|
||||||
|
<p>
|
||||||
|
Your completion certificate is being generated. Please wait
|
||||||
|
momentarily.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If the download doesn't start shortly, click the button again.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header;
|
||||||
@@ -0,0 +1,759 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import BorderResize from "./BorderResize";
|
||||||
|
import PlaceholderBorder from "./PlaceholderBorder";
|
||||||
|
import { Rnd } from "react-rnd";
|
||||||
|
import {
|
||||||
|
defaultWidthHeight,
|
||||||
|
handleCopyNextToWidget,
|
||||||
|
isMobile,
|
||||||
|
onChangeInput,
|
||||||
|
radioButtonWidget,
|
||||||
|
textInputWidget,
|
||||||
|
textWidget
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import PlaceholderType from "./PlaceholderType";
|
||||||
|
import moment from "moment";
|
||||||
|
import "../../styles/opensigndrive.css";
|
||||||
|
|
||||||
|
const selectFormat = (data) => {
|
||||||
|
switch (data) {
|
||||||
|
case "L":
|
||||||
|
return "MM/dd/yyyy";
|
||||||
|
case "DD-MM-YYYY":
|
||||||
|
return "dd-MM-yyyy";
|
||||||
|
case "DD/MM/YYYY":
|
||||||
|
return "dd/MM/yyyy";
|
||||||
|
case "LL":
|
||||||
|
return "MMMM dd, yyyy";
|
||||||
|
case "DD MMM, YYYY":
|
||||||
|
return "dd MMM, yyyy";
|
||||||
|
case "YYYY-MM-DD":
|
||||||
|
return "yyyy-MM-dd";
|
||||||
|
case "MM-DD-YYYY":
|
||||||
|
return "MM-dd-yyyy";
|
||||||
|
case "MM.DD.YYYY":
|
||||||
|
return "MM.dd.yyyy";
|
||||||
|
case "MMM DD, YYYY":
|
||||||
|
return "MMM dd, yyyy";
|
||||||
|
case "DD MMMM, YYYY":
|
||||||
|
return "dd MMMM, yyyy";
|
||||||
|
default:
|
||||||
|
return "MM/dd/yyyy";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeDateToMomentFormat = (format) => {
|
||||||
|
switch (format) {
|
||||||
|
case "MM/dd/yyyy":
|
||||||
|
return "L";
|
||||||
|
case "dd-MM-yyyy":
|
||||||
|
return "DD-MM-YYYY";
|
||||||
|
case "dd/MM/yyyy":
|
||||||
|
return "DD/MM/YYYY";
|
||||||
|
case "MMMM dd, yyyy":
|
||||||
|
return "LL";
|
||||||
|
case "dd MMM, yyyy":
|
||||||
|
return "DD MMM, YYYY";
|
||||||
|
case "yyyy-MM-dd":
|
||||||
|
return "YYYY-MM-DD";
|
||||||
|
case "MM-dd-yyyy":
|
||||||
|
return "MM-DD-YYYY";
|
||||||
|
case "MM.dd.yyyy":
|
||||||
|
return "MM.DD.YYYY";
|
||||||
|
case "MMM dd, yyyy":
|
||||||
|
return "MMM DD, YYYY";
|
||||||
|
case "dd MMMM, yyyy":
|
||||||
|
return "DD MMMM, YYYY";
|
||||||
|
default:
|
||||||
|
return "L";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function to get default date
|
||||||
|
const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
|
||||||
|
let date;
|
||||||
|
if (format && format === "dd-MM-yyyy") {
|
||||||
|
const newdate = selectedDate
|
||||||
|
? selectedDate
|
||||||
|
: moment(new Date()).format(changeDateToMomentFormat(format));
|
||||||
|
const [day, month, year] = newdate.split("-");
|
||||||
|
date = new Date(`${year}-${month}-${day}`);
|
||||||
|
} else {
|
||||||
|
date = new Date(selectedDate);
|
||||||
|
}
|
||||||
|
const value = date;
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
//function to get default format
|
||||||
|
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||||
|
|
||||||
|
function Placeholder(props) {
|
||||||
|
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
||||||
|
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
|
||||||
|
const [selectDate, setSelectDate] = useState({
|
||||||
|
date:
|
||||||
|
props.pos.type === "date"
|
||||||
|
? moment(
|
||||||
|
getDefaultdate(
|
||||||
|
props?.pos?.options?.response,
|
||||||
|
props.pos?.options?.validation?.format
|
||||||
|
).getTime()
|
||||||
|
).format(
|
||||||
|
changeDateToMomentFormat(props.pos?.options?.validation?.format)
|
||||||
|
)
|
||||||
|
: "",
|
||||||
|
format:
|
||||||
|
props.pos.type === "date"
|
||||||
|
? getDefaultFormat(props.pos?.options?.validation?.format)
|
||||||
|
: ""
|
||||||
|
});
|
||||||
|
const [dateFormat, setDateFormat] = useState([]);
|
||||||
|
const [startDate, setStartDate] = useState(
|
||||||
|
props.pos.type === "date" &&
|
||||||
|
getDefaultdate(
|
||||||
|
props?.pos?.options?.response,
|
||||||
|
props.pos?.options?.validation?.format
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
|
||||||
|
width: null,
|
||||||
|
height: null
|
||||||
|
});
|
||||||
|
const dateFormatArr = [
|
||||||
|
"L",
|
||||||
|
"DD-MM-YYYY",
|
||||||
|
"YYYY-MM-DD",
|
||||||
|
"MM.DD.YYYY",
|
||||||
|
"MM-DD-YYYY",
|
||||||
|
"MMM DD, YYYY",
|
||||||
|
"LL",
|
||||||
|
"DD MMM, YYYY",
|
||||||
|
"DD MMMM, YYYY"
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateWidth = () => {
|
||||||
|
const rndElement = document.getElementById(props.pos.key);
|
||||||
|
if (rndElement) {
|
||||||
|
const { width, height } = rndElement.getBoundingClientRect();
|
||||||
|
setGetCheckboxRenderWidth({ width: width, height: height });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delay to ensure rendering is complete
|
||||||
|
const timer = setTimeout(updateWidth, 0);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [props.pos]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const closeMenuOnOutsideClick = (e) => {
|
||||||
|
if (!isDraggingEnabled && !e.target.closest("#changeIsDragging")) {
|
||||||
|
setDraggingEnabled(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Cleanup the event listener when the component unmounts
|
||||||
|
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||||
|
};
|
||||||
|
}, [isDraggingEnabled]);
|
||||||
|
//function change format array list with selected date and format
|
||||||
|
const changeDateFormat = () => {
|
||||||
|
const updateDate = [];
|
||||||
|
dateFormatArr.map((data) => {
|
||||||
|
let date;
|
||||||
|
if (selectDate && selectDate.format === "dd-MM-yyyy") {
|
||||||
|
const [day, month, year] = selectDate.date.split("-");
|
||||||
|
date = new Date(`${year}-${month}-${day}`);
|
||||||
|
} else {
|
||||||
|
date = new Date(selectDate?.date);
|
||||||
|
}
|
||||||
|
const milliseconds = date.getTime();
|
||||||
|
const newDate = moment(milliseconds).format(data);
|
||||||
|
const dateObj = {
|
||||||
|
date: newDate,
|
||||||
|
format: selectFormat(data)
|
||||||
|
};
|
||||||
|
updateDate.push(dateObj);
|
||||||
|
});
|
||||||
|
setDateFormat(updateDate);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isPlaceholder || props.isSignYourself) {
|
||||||
|
selectDate && changeDateFormat();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [selectDate]);
|
||||||
|
|
||||||
|
//it detect outside click of date dropdown menu
|
||||||
|
useEffect(() => {
|
||||||
|
const closeMenuOnOutsideClick = (e) => {
|
||||||
|
if (isShowDateFormat && !e.target.closest("#menu-container")) {
|
||||||
|
setIsShowDateFormat(!isShowDateFormat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||||
|
return () => {
|
||||||
|
// Cleanup the event listener when the component unmounts
|
||||||
|
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||||
|
};
|
||||||
|
}, [isShowDateFormat]);
|
||||||
|
|
||||||
|
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
|
||||||
|
const handleWidgetIdandPopup = () => {
|
||||||
|
if (props.setSelectWidgetId) {
|
||||||
|
props.setSelectWidgetId(props.pos.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const widgetTypeExist = [
|
||||||
|
textInputWidget,
|
||||||
|
"checkbox",
|
||||||
|
"name",
|
||||||
|
"company",
|
||||||
|
"job title",
|
||||||
|
"date",
|
||||||
|
"email",
|
||||||
|
textWidget
|
||||||
|
].includes(props.pos.type);
|
||||||
|
|
||||||
|
if (widgetTypeExist) {
|
||||||
|
setDraggingEnabled(false);
|
||||||
|
}
|
||||||
|
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
|
||||||
|
if (props.pos.type) {
|
||||||
|
if (
|
||||||
|
props.pos.type === "signature" ||
|
||||||
|
props.pos.type === "stamp" ||
|
||||||
|
props.pos.type === "image"
|
||||||
|
) {
|
||||||
|
props.setIsSignPad(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setIsStamp(props.pos.isStamp);
|
||||||
|
} else if (props.pos.type === "initials") {
|
||||||
|
props.setIsSignPad(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setIsStamp(props.pos.isStamp);
|
||||||
|
props.setIsInitial(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props.setIsSignPad(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
props.isPlaceholder &&
|
||||||
|
!props.isDragging &&
|
||||||
|
props.pos.type !== textWidget
|
||||||
|
) {
|
||||||
|
if (props.pos.key === props.selectWidgetId) {
|
||||||
|
props.handleLinkUser(props.data.Id);
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}
|
||||||
|
} else if (!props.pos.type) {
|
||||||
|
if (
|
||||||
|
!props.pos.type &&
|
||||||
|
props.isNeedSign &&
|
||||||
|
props.data.signerObjId === props.signerObjId
|
||||||
|
) {
|
||||||
|
props.setIsSignPad(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setIsStamp(props.pos.isStamp);
|
||||||
|
} else if (
|
||||||
|
(props.isNeedSign && props.pos.type === "signature") ||
|
||||||
|
props.pos.type === "stamp"
|
||||||
|
) {
|
||||||
|
props.setIsSignPad(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setIsStamp(props.pos.isStamp);
|
||||||
|
} else if (props.isNeedSign && props.pos.type === "dropdown") {
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleOnClickPlaceholder = () => {
|
||||||
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
||||||
|
if (!props.isNeedSign) {
|
||||||
|
props.setWidgetType(props.pos.type);
|
||||||
|
}
|
||||||
|
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
|
||||||
|
handleWidgetIdandPopup();
|
||||||
|
} else if (props.isPlaceholder || props.isSignYourself) {
|
||||||
|
handleWidgetIdandPopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
|
||||||
|
const handleOnClickSettingIcon = () => {
|
||||||
|
if (props.pos.type === radioButtonWidget) {
|
||||||
|
props.setIsRadio(true);
|
||||||
|
} else if (props.pos.type === "dropdown") {
|
||||||
|
props?.setShowDropdown(true);
|
||||||
|
} else if (props.pos.type === "checkbox") {
|
||||||
|
props?.setIsCheckbox(true);
|
||||||
|
} else {
|
||||||
|
props?.handleNameModal(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isPlaceholder && props.type !== textWidget) {
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setWidgetType(props.pos.type);
|
||||||
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
|
};
|
||||||
|
//function to set required state value onclick on widget's copy icon
|
||||||
|
const handleCopyPlaceholder = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
//condition to handle text widget signer obj id and unique id
|
||||||
|
//when user click on copy icon of text widget in that condition text widget does not have any signerObjId
|
||||||
|
//in that case i have to save in tempSignerId as a unique id of previous select signer's unique id
|
||||||
|
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
|
||||||
|
|
||||||
|
if (props.data && props?.pos?.type !== textWidget) {
|
||||||
|
props.setSignerObjId(props?.data?.signerObjId);
|
||||||
|
props.setUniqueId(props?.data?.Id);
|
||||||
|
} else if (props.data && props.pos.type === textWidget) {
|
||||||
|
props.setTempSignerId(props.uniqueId);
|
||||||
|
props.setSignerObjId(props?.data?.signerObjId);
|
||||||
|
props.setUniqueId(props?.data?.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//checking widget's type and open widget copy modal for required widgets
|
||||||
|
if (
|
||||||
|
["signature", textWidget, "stamp", "initial"].includes(props.pos.type)
|
||||||
|
) {
|
||||||
|
props.setIsPageCopy(true);
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
} else {
|
||||||
|
//function to create new widget next to just widget
|
||||||
|
handleCopyNextToWidget(
|
||||||
|
props.pos,
|
||||||
|
props.pos.type,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function to save date and format on local array onchange date and onclick format
|
||||||
|
const handleSaveDate = (data, isDateChange) => {
|
||||||
|
let updateDate = data.date;
|
||||||
|
//check if date change by user
|
||||||
|
if (isDateChange) {
|
||||||
|
//`changeDateToMomentFormat` is used to convert date as per required to moment package
|
||||||
|
updateDate = moment(data.date).format(
|
||||||
|
changeDateToMomentFormat(data.format)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
|
||||||
|
//`getDefaultdate` is used to convert update date in new Date() format
|
||||||
|
const date = moment(
|
||||||
|
getDefaultdate(updateDate, data?.format).getTime()
|
||||||
|
).format(changeDateToMomentFormat(data?.format));
|
||||||
|
|
||||||
|
//`onChangeInput` is used to save data related to date in a placeholder field
|
||||||
|
onChangeInput(
|
||||||
|
date,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data.Id,
|
||||||
|
false,
|
||||||
|
data?.format
|
||||||
|
);
|
||||||
|
setSelectDate({ date: date, format: data?.format });
|
||||||
|
};
|
||||||
|
const PlaceholderIcon = () => {
|
||||||
|
return (
|
||||||
|
props.isShowBorder && (
|
||||||
|
<>
|
||||||
|
{(props.isPlaceholder || props.isSignYourself) && (
|
||||||
|
<>
|
||||||
|
{props.pos.type === "checkbox" && props.isSignYourself ? (
|
||||||
|
<i
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleOnClickSettingIcon();
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleOnClickSettingIcon();
|
||||||
|
}}
|
||||||
|
className="fa-solid fa-gear settingIcon"
|
||||||
|
style={{
|
||||||
|
color: "#188ae2",
|
||||||
|
right: "29px",
|
||||||
|
top: "-19px"
|
||||||
|
}}
|
||||||
|
></i>
|
||||||
|
) : (
|
||||||
|
((!props?.pos?.type && props.pos.isStamp) ||
|
||||||
|
(props?.pos?.type &&
|
||||||
|
!["date", textWidget, "signature"].includes(
|
||||||
|
props.pos.type
|
||||||
|
) &&
|
||||||
|
!props.isSignYourself)) && (
|
||||||
|
<i
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleOnClickSettingIcon();
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleOnClickSettingIcon();
|
||||||
|
}}
|
||||||
|
className="fa-solid fa-gear settingIcon"
|
||||||
|
style={{ color: "#188ae2", right: "47px", top: "-19px" }}
|
||||||
|
></i>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
{props.pos.type !== textWidget && !props.isSignYourself && (
|
||||||
|
<i
|
||||||
|
data-tut="assignSigner"
|
||||||
|
className="fa-regular fa-user signUserIcon"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
props.handleLinkUser(props.data.Id);
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
props.handleLinkUser(props.data.Id);
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}}
|
||||||
|
style={{ color: "#188ae2", right: "32px", top: "-18px" }}
|
||||||
|
></i>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{props.pos.type === "date" && selectDate && (
|
||||||
|
<div
|
||||||
|
id="menu-container"
|
||||||
|
style={{
|
||||||
|
zIndex: "99",
|
||||||
|
top: "-19px",
|
||||||
|
right: props.isPlaceholder ? "60px" : "44px"
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isShowDateFormat
|
||||||
|
? "dropdown signUserIcon show"
|
||||||
|
: "dropdown signUserIcon"
|
||||||
|
}
|
||||||
|
onClick={(e) => {
|
||||||
|
setIsShowDateFormat(!isShowDateFormat);
|
||||||
|
e.stopPropagation();
|
||||||
|
if (props.data) {
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsShowDateFormat(!isShowDateFormat);
|
||||||
|
if (props.data) {
|
||||||
|
props.setSignKey(props.pos.key);
|
||||||
|
props.setUniqueId(props.data.Id);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa-solid fa-gear settingIcon"
|
||||||
|
style={{
|
||||||
|
color: "#188ae2",
|
||||||
|
fontSize: "14px"
|
||||||
|
}}
|
||||||
|
></i>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
|
||||||
|
}
|
||||||
|
aria-labelledby="dropdownMenuButton"
|
||||||
|
aria-expanded={isShowDateFormat ? "true" : "false"}
|
||||||
|
>
|
||||||
|
{dateFormat.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
key={ind}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsShowDateFormat(!isShowDateFormat);
|
||||||
|
setSelectDate(data);
|
||||||
|
handleSaveDate(data);
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setIsShowDateFormat(!isShowDateFormat);
|
||||||
|
setSelectDate(data);
|
||||||
|
handleSaveDate(data);
|
||||||
|
}}
|
||||||
|
className="dropdown-item itemColor"
|
||||||
|
style={{ fontSize: "12px" }}
|
||||||
|
>
|
||||||
|
{data?.date ? data?.date : "nodata"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<i
|
||||||
|
className="fa-regular fa-copy signCopy"
|
||||||
|
onClick={(e) => handleCopyPlaceholder(e)}
|
||||||
|
onTouchEnd={(e) => handleCopyPlaceholder(e)}
|
||||||
|
style={{ color: "#188ae2", right: "12px", top: "-18px" }}
|
||||||
|
></i>
|
||||||
|
<i
|
||||||
|
className="fa-regular fa-circle-xmark signCloseBtn"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (props.data) {
|
||||||
|
props.handleDeleteSign(props.pos.key, props.data.Id);
|
||||||
|
} else {
|
||||||
|
props.handleDeleteSign(props.pos.key);
|
||||||
|
props.setIsStamp(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onTouchEnd={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (props.data) {
|
||||||
|
props.handleDeleteSign(props.pos.key, props.data?.Id);
|
||||||
|
} else {
|
||||||
|
props.handleDeleteSign(props.pos.key);
|
||||||
|
props.setIsStamp(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
|
||||||
|
></i>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Rnd
|
||||||
|
id={props.pos.key}
|
||||||
|
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||||
|
key={props.pos.key}
|
||||||
|
lockAspectRatio={
|
||||||
|
// props.pos.type !== textWidget
|
||||||
|
![
|
||||||
|
textWidget,
|
||||||
|
"email",
|
||||||
|
"name",
|
||||||
|
"company",
|
||||||
|
"job title",
|
||||||
|
textInputWidget
|
||||||
|
].includes(props.pos.type) &&
|
||||||
|
(props.pos.Width
|
||||||
|
? props.pos.Width / props.pos.Height
|
||||||
|
: defaultWidthHeight(props.pos.type).width /
|
||||||
|
defaultWidthHeight(props.pos.type).height)
|
||||||
|
}
|
||||||
|
enableResizing={{
|
||||||
|
top: false,
|
||||||
|
right: false,
|
||||||
|
bottom: false,
|
||||||
|
left: false,
|
||||||
|
topRight: false,
|
||||||
|
bottomRight:
|
||||||
|
props.data && props.isNeedSign
|
||||||
|
? props.data?.signerObjId === props.signerObjId &&
|
||||||
|
props.pos.type !== "checkbox" &&
|
||||||
|
props.pos.type !== radioButtonWidget
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
: props.pos.type !== radioButtonWidget &&
|
||||||
|
props.pos.type !== "checkbox" &&
|
||||||
|
props.pos.key === props.selectWidgetId &&
|
||||||
|
true,
|
||||||
|
bottomLeft: false,
|
||||||
|
topLeft: false
|
||||||
|
}}
|
||||||
|
bounds="parent"
|
||||||
|
className="signYourselfBlock"
|
||||||
|
style={{
|
||||||
|
border: "1px solid #007bff",
|
||||||
|
borderRadius: "2px",
|
||||||
|
textAlign:
|
||||||
|
props.pos.type !== "name" &&
|
||||||
|
props.pos.type !== "company" &&
|
||||||
|
props.pos.type !== "job title" &&
|
||||||
|
"center",
|
||||||
|
cursor:
|
||||||
|
props.data && props.isNeedSign
|
||||||
|
? props.data?.signerObjId === props.signerObjId
|
||||||
|
? "pointer"
|
||||||
|
: "not-allowed"
|
||||||
|
: "all-scroll",
|
||||||
|
zIndex:
|
||||||
|
props.pos.type === "date"
|
||||||
|
? props.pos.key === props.selectWidgetId
|
||||||
|
? 99 + 1
|
||||||
|
: 99
|
||||||
|
: props?.pos?.zIndex
|
||||||
|
? props.pos.zIndex
|
||||||
|
: "5",
|
||||||
|
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
|
||||||
|
}}
|
||||||
|
onDrag={() => {
|
||||||
|
setDraggingEnabled(true);
|
||||||
|
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||||
|
}}
|
||||||
|
size={{
|
||||||
|
width:
|
||||||
|
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||||
|
? "auto"
|
||||||
|
: props.posWidth(props.pos, props.isSignYourself),
|
||||||
|
height:
|
||||||
|
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||||
|
? "auto"
|
||||||
|
: props.posHeight(props.pos, props.isSignYourself)
|
||||||
|
}}
|
||||||
|
onResizeStart={() => {
|
||||||
|
setDraggingEnabled(true);
|
||||||
|
props.setIsResize && props.setIsResize(true);
|
||||||
|
}}
|
||||||
|
onResizeStop={() => {
|
||||||
|
props.setIsResize && props.setIsResize(false);
|
||||||
|
}}
|
||||||
|
disableDragging={
|
||||||
|
props.isNeedSign
|
||||||
|
? true
|
||||||
|
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
|
||||||
|
? false
|
||||||
|
: !isDraggingEnabled
|
||||||
|
}
|
||||||
|
onDragStop={(event, dragElement) =>
|
||||||
|
props.handleStop &&
|
||||||
|
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||||
|
}
|
||||||
|
default={{
|
||||||
|
x: props.xPos(props.pos, props.isSignYourself),
|
||||||
|
y: props.yPos(props.pos, props.isSignYourself)
|
||||||
|
}}
|
||||||
|
onResize={(e, direction, ref) => {
|
||||||
|
props.handleSignYourselfImageResize &&
|
||||||
|
props.handleSignYourselfImageResize(
|
||||||
|
ref,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.index,
|
||||||
|
props.data && props.data.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
onClick={() => handleOnClickPlaceholder()}
|
||||||
|
>
|
||||||
|
{props.isShowBorder &&
|
||||||
|
props.pos.type !== radioButtonWidget &&
|
||||||
|
props.pos.type !== "checkbox" &&
|
||||||
|
props.pos.key === props.selectWidgetId ? (
|
||||||
|
<BorderResize right={-12} top={-11} />
|
||||||
|
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||||
|
props.data?.signerObjId === props.signerObjId &&
|
||||||
|
props.pos.type !== radioButtonWidget &&
|
||||||
|
props.pos.type !== "checkbox" ? (
|
||||||
|
<BorderResize />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
props.pos.type !== radioButtonWidget &&
|
||||||
|
props.pos.type !== "checkbox" &&
|
||||||
|
props.pos.key === props.selectWidgetId && <BorderResize />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
|
||||||
|
<PlaceholderBorder
|
||||||
|
setDraggingEnabled={setDraggingEnabled}
|
||||||
|
pos={props.pos}
|
||||||
|
isPlaceholder={props.isPlaceholder}
|
||||||
|
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isMobile ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
left: props.xPos(props.pos, props.isSignYourself),
|
||||||
|
top: props.yPos(props.pos, props.isSignYourself),
|
||||||
|
width:
|
||||||
|
props.pos.type === radioButtonWidget ||
|
||||||
|
props.pos.type === "checkbox"
|
||||||
|
? "auto"
|
||||||
|
: props.posWidth(props.pos, props.isSignYourself),
|
||||||
|
// "auto", //props.posWidth(props.pos, props.isSignYourself),
|
||||||
|
// height: props.posHeight(props.pos, props.isSignYourself),
|
||||||
|
height:
|
||||||
|
props.pos.type === radioButtonWidget ||
|
||||||
|
props.pos.type === "checkbox"
|
||||||
|
? "auto"
|
||||||
|
: props.posHeight(props.pos, props.isSignYourself),
|
||||||
|
zIndex: "10"
|
||||||
|
}}
|
||||||
|
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||||
|
>
|
||||||
|
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||||
|
|
||||||
|
<PlaceholderType
|
||||||
|
pos={props.pos}
|
||||||
|
xyPostion={props.xyPostion}
|
||||||
|
index={props.index}
|
||||||
|
setXyPostion={props.setXyPostion}
|
||||||
|
data={props.data}
|
||||||
|
setSignKey={props.setSignKey}
|
||||||
|
isShowDropdown={props?.isShowDropdown}
|
||||||
|
isPlaceholder={props.isPlaceholder}
|
||||||
|
isSignYourself={props.isSignYourself}
|
||||||
|
signerObjId={props.signerObjId}
|
||||||
|
handleUserName={props.handleUserName}
|
||||||
|
setDraggingEnabled={setDraggingEnabled}
|
||||||
|
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||||
|
isNeedSign={props.isNeedSign}
|
||||||
|
setSelectDate={setSelectDate}
|
||||||
|
selectDate={selectDate}
|
||||||
|
setValidateAlert={props.setValidateAlert}
|
||||||
|
setStartDate={setStartDate}
|
||||||
|
startDate={startDate}
|
||||||
|
handleSaveDate={handleSaveDate}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||||
|
<PlaceholderType
|
||||||
|
pos={props.pos}
|
||||||
|
xyPostion={props.xyPostion}
|
||||||
|
index={props.index}
|
||||||
|
setXyPostion={props.setXyPostion}
|
||||||
|
data={props.data}
|
||||||
|
setSignKey={props.setSignKey}
|
||||||
|
isShowDropdown={props?.isShowDropdown}
|
||||||
|
isPlaceholder={props.isPlaceholder}
|
||||||
|
isSignYourself={props.isSignYourself}
|
||||||
|
signerObjId={props.signerObjId}
|
||||||
|
handleUserName={props.handleUserName}
|
||||||
|
setDraggingEnabled={setDraggingEnabled}
|
||||||
|
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||||
|
isNeedSign={props.isNeedSign}
|
||||||
|
setSelectDate={setSelectDate}
|
||||||
|
selectDate={selectDate}
|
||||||
|
setValidateAlert={props.setValidateAlert}
|
||||||
|
setStartDate={setStartDate}
|
||||||
|
startDate={startDate}
|
||||||
|
handleSaveDate={handleSaveDate}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Rnd>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Placeholder;
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { themeColor } from "../../constant/const";
|
||||||
|
import {
|
||||||
|
defaultWidthHeight,
|
||||||
|
isMobile,
|
||||||
|
radioButtonWidget,
|
||||||
|
resizeBorderExtraWidth,
|
||||||
|
textWidget
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
function PlaceholderBorder(props) {
|
||||||
|
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
|
||||||
|
const defaultWidth = defaultWidthHeight(props.pos.type).width;
|
||||||
|
const defaultHeight = defaultWidthHeight(props.pos.type).height;
|
||||||
|
|
||||||
|
const handleMinWidth = () => {
|
||||||
|
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||||
|
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
|
||||||
|
} else {
|
||||||
|
return props.pos.Width
|
||||||
|
? props.pos.Width + getResizeBorderExtraWidth
|
||||||
|
: defaultWidth + getResizeBorderExtraWidth;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleMinHeight = () => {
|
||||||
|
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||||
|
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
|
||||||
|
} else {
|
||||||
|
return props.pos.Height
|
||||||
|
? props.pos.Height + getResizeBorderExtraWidth
|
||||||
|
: defaultHeight + getResizeBorderExtraWidth;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(true)}
|
||||||
|
onTouchEnd={() =>
|
||||||
|
props.pos.type === textWidget && props?.setDraggingEnabled(true)
|
||||||
|
}
|
||||||
|
className="borderResize"
|
||||||
|
style={{
|
||||||
|
borderColor: themeColor,
|
||||||
|
borderStyle: "dashed",
|
||||||
|
minWidth: handleMinWidth(),
|
||||||
|
minHeight: handleMinHeight(),
|
||||||
|
borderWidth: "0.2px",
|
||||||
|
overflow: "hidden"
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlaceholderBorder;
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import { randomId, textWidget } from "../../constant/Utils";
|
||||||
|
|
||||||
|
function PlaceholderCopy(props) {
|
||||||
|
const copyType = ["All pages", "All pages but last", "All pages but first"];
|
||||||
|
const [selectCopyType, setSelectCopyType] = useState("");
|
||||||
|
|
||||||
|
//function for get copy placeholder position
|
||||||
|
const getCopyPlaceholderPosition = (
|
||||||
|
type,
|
||||||
|
currentPlaceholder,
|
||||||
|
newPageNumber,
|
||||||
|
existPlaceholderPosition
|
||||||
|
) => {
|
||||||
|
const filterPosition =
|
||||||
|
existPlaceholderPosition &&
|
||||||
|
existPlaceholderPosition.filter(
|
||||||
|
(data) => data.xPosition !== currentPlaceholder.xPosition
|
||||||
|
);
|
||||||
|
//copy all page placeholder at requested position except first page
|
||||||
|
if (newPageNumber === 1 && type === "first") {
|
||||||
|
if (filterPosition && filterPosition.length > 0) {
|
||||||
|
return {
|
||||||
|
pageNumber: newPageNumber,
|
||||||
|
pos: [...filterPosition]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//copy all page placeholder at requested position except last page
|
||||||
|
else if (newPageNumber === props.allPages && type === "last") {
|
||||||
|
if (existPlaceholderPosition) {
|
||||||
|
return {
|
||||||
|
pageNumber: newPageNumber,
|
||||||
|
pos: [...filterPosition]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//copy all page placeholder at requested position with existing placeholder position
|
||||||
|
else if (existPlaceholderPosition) {
|
||||||
|
return {
|
||||||
|
pageNumber: newPageNumber,
|
||||||
|
pos: [...filterPosition, currentPlaceholder]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//copy all page placeholder at requested position
|
||||||
|
else {
|
||||||
|
return {
|
||||||
|
pageNumber: newPageNumber,
|
||||||
|
pos: [currentPlaceholder]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for copy placeholder as per select copy type
|
||||||
|
const copyPlaceholder = (type) => {
|
||||||
|
let newPlaceholderPosition = [];
|
||||||
|
let newPageNumber = 1;
|
||||||
|
const signerPosition = props.xyPostion;
|
||||||
|
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||||
|
|
||||||
|
//handle placeholder array and copy for multiple signers placeholder at requested location
|
||||||
|
if (signerId) {
|
||||||
|
//get current signers data
|
||||||
|
let filterSignerPosition;
|
||||||
|
if (props?.signerObjId) {
|
||||||
|
filterSignerPosition = signerPosition.filter(
|
||||||
|
(data) => data.signerObjId === signerId
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
filterSignerPosition = signerPosition.filter(
|
||||||
|
(item) => item.Id === signerId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//get current pagenumber's all placeholder position data
|
||||||
|
const placeholderPosition = filterSignerPosition[0].placeHolder.filter(
|
||||||
|
(data) => data.pageNumber === props.pageNumber
|
||||||
|
);
|
||||||
|
//get current placeholder position data which user want to copy
|
||||||
|
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||||
|
(position) => position.key === props.signKey
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < props.allPages; i++) {
|
||||||
|
const newId = randomId();
|
||||||
|
currentPlaceholder.key = newId;
|
||||||
|
//get exist placeholder position for particular page
|
||||||
|
const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
|
||||||
|
(data) => data.pageNumber === newPageNumber
|
||||||
|
);
|
||||||
|
const existPlaceholderPosition =
|
||||||
|
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||||
|
|
||||||
|
//function for get copy to requested location of placeholder position
|
||||||
|
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||||
|
type,
|
||||||
|
currentPlaceholder,
|
||||||
|
newPageNumber,
|
||||||
|
existPlaceholderPosition
|
||||||
|
);
|
||||||
|
if (getPlaceholderObj) {
|
||||||
|
newPlaceholderPosition.push(getPlaceholderObj);
|
||||||
|
}
|
||||||
|
newPageNumber++;
|
||||||
|
}
|
||||||
|
let updatedSignerPlaceholder;
|
||||||
|
if (props?.signerObjId) {
|
||||||
|
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||||
|
if (signersData.signerObjId === props.signerObjId) {
|
||||||
|
return {
|
||||||
|
...signersData,
|
||||||
|
placeHolder: newPlaceholderPosition
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return signersData;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||||
|
if (signersData.Id === props.Id) {
|
||||||
|
return {
|
||||||
|
...signersData,
|
||||||
|
placeHolder: newPlaceholderPosition
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return signersData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const signersData = signerPosition;
|
||||||
|
signersData.splice(0, signerPosition.length, ...updatedSignerPlaceholder);
|
||||||
|
props.setXyPostion(signersData);
|
||||||
|
}
|
||||||
|
//handle signyourself array and copy for single signers placeholder at requested location
|
||||||
|
else {
|
||||||
|
const xyPostion = props.xyPostion;
|
||||||
|
|
||||||
|
const placeholderPosition = xyPostion.filter(
|
||||||
|
(data) => data.pageNumber === props.pageNumber
|
||||||
|
);
|
||||||
|
//get current placeholder position data which user want to copy
|
||||||
|
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||||
|
(pos) => pos.key === props.signKey
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i < props.allPages; i++) {
|
||||||
|
//get exist placeholder position for particular page
|
||||||
|
const existPlaceholder = xyPostion.filter(
|
||||||
|
(data) => data.pageNumber === newPageNumber
|
||||||
|
);
|
||||||
|
const existPlaceholderPosition =
|
||||||
|
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||||
|
|
||||||
|
const newId = randomId();
|
||||||
|
currentPlaceholder.key = newId;
|
||||||
|
//function for get copy to requested location of placeholder position
|
||||||
|
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||||
|
type,
|
||||||
|
currentPlaceholder,
|
||||||
|
newPageNumber,
|
||||||
|
existPlaceholderPosition
|
||||||
|
);
|
||||||
|
if (getPlaceholderObj) {
|
||||||
|
newPlaceholderPosition.push(getPlaceholderObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
newPageNumber++;
|
||||||
|
}
|
||||||
|
props.setXyPostion(newPlaceholderPosition);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for getting selected type placeholder copy
|
||||||
|
const handleApplyCopy = () => {
|
||||||
|
if (selectCopyType === "All pages") {
|
||||||
|
copyPlaceholder("all");
|
||||||
|
} else if (selectCopyType === "All pages but last") {
|
||||||
|
copyPlaceholder("last");
|
||||||
|
} else if (selectCopyType === "All pages but first") {
|
||||||
|
copyPlaceholder("first");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleUniqueId = () => {
|
||||||
|
if (props.widgetType === textWidget) {
|
||||||
|
props.setUniqueId(props?.tempSignerId);
|
||||||
|
props.setTempSignerId("");
|
||||||
|
}
|
||||||
|
props.setIsPageCopy(false);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<ModalUi
|
||||||
|
isOpen={props.isPageCopy}
|
||||||
|
title={"Copy to all pages"}
|
||||||
|
handleClose={() => {
|
||||||
|
props.setIsPageCopy(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="h-full p-[20px] text-base-content">
|
||||||
|
{copyType.map((data, key) => {
|
||||||
|
return (
|
||||||
|
<div key={key} className="flex flex-col">
|
||||||
|
<label className="text-[16px] font-medium">
|
||||||
|
<input
|
||||||
|
className="mr-[8px] op-radio op-radio-xs"
|
||||||
|
type="radio"
|
||||||
|
value={data}
|
||||||
|
onChange={() => setSelectCopyType(data)}
|
||||||
|
checked={selectCopyType === data}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{data}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<div className="flex bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
handleApplyCopy();
|
||||||
|
handleUniqueId();
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
disabled={!selectCopyType}
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
>
|
||||||
|
Apply
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost"
|
||||||
|
onClick={() => handleUniqueId()}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlaceholderCopy;
|
||||||
@@ -0,0 +1,837 @@
|
|||||||
|
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||||
|
import {
|
||||||
|
getMonth,
|
||||||
|
getYear,
|
||||||
|
isMobile,
|
||||||
|
onChangeHeightOfTextArea,
|
||||||
|
onChangeInput,
|
||||||
|
radioButtonWidget,
|
||||||
|
range,
|
||||||
|
textInputWidget,
|
||||||
|
textWidget
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import DatePicker from "react-datepicker";
|
||||||
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
|
import "../../styles/signature.css";
|
||||||
|
import RegexParser from "regex-parser";
|
||||||
|
|
||||||
|
function PlaceholderType(props) {
|
||||||
|
const type = props?.pos?.type;
|
||||||
|
const [selectOption, setSelectOption] = useState("");
|
||||||
|
const [validatePlaceholder, setValidatePlaceholder] = useState("");
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
const [textValue, setTextValue] = useState();
|
||||||
|
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||||
|
const years = range(1990, getYear(new Date()) + 16, 1);
|
||||||
|
const months = [
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December"
|
||||||
|
];
|
||||||
|
const validateExpression = (regexValidation) => {
|
||||||
|
if (textValue && regexValidation) {
|
||||||
|
let regexObject = regexValidation;
|
||||||
|
if (props.pos?.options?.validation?.type === "regex") {
|
||||||
|
regexObject = RegexParser(regexValidation);
|
||||||
|
}
|
||||||
|
// new RegExp(regexValidation);
|
||||||
|
let isValidate = regexObject.test(textValue);
|
||||||
|
if (!isValidate) {
|
||||||
|
props?.setValidateAlert(true);
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputBlur = () => {
|
||||||
|
props.setDraggingEnabled(true);
|
||||||
|
const validateType = props.pos?.options?.validation?.type;
|
||||||
|
let regexValidation;
|
||||||
|
if (validateType && validateType !== "text") {
|
||||||
|
switch (validateType) {
|
||||||
|
case "email":
|
||||||
|
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
validateExpression(regexValidation);
|
||||||
|
break;
|
||||||
|
case "number":
|
||||||
|
regexValidation = /^[0-9\s]*$/;
|
||||||
|
validateExpression(regexValidation);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
regexValidation = props.pos?.options?.validation?.pattern || "";
|
||||||
|
validateExpression(regexValidation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTextValid = (e) => {
|
||||||
|
const textInput = e.target.value;
|
||||||
|
setTextValue(textInput);
|
||||||
|
};
|
||||||
|
function checkRegularExpress(validateType) {
|
||||||
|
switch (validateType) {
|
||||||
|
case "email":
|
||||||
|
setValidatePlaceholder("demo@gmail.com");
|
||||||
|
break;
|
||||||
|
case "number":
|
||||||
|
setValidatePlaceholder("12345");
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
setValidatePlaceholder("please enter text");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setValidatePlaceholder("please enter value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type && type === "checkbox" && props.isNeedSign) {
|
||||||
|
const isDefaultValue = props.pos.options?.defaultValue;
|
||||||
|
if (isDefaultValue) {
|
||||||
|
setSelectedCheckbox(isDefaultValue);
|
||||||
|
}
|
||||||
|
} else if (props.pos?.options?.hint) {
|
||||||
|
setValidatePlaceholder(props.pos?.options.hint);
|
||||||
|
} else if (props.pos?.options?.validation?.type) {
|
||||||
|
checkRegularExpress(props.pos?.options?.validation?.type);
|
||||||
|
}
|
||||||
|
setTextValue(
|
||||||
|
props.pos?.options?.response
|
||||||
|
? props.pos?.options?.response
|
||||||
|
: props.pos?.options?.defaultValue
|
||||||
|
? props.pos?.options?.defaultValue
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
setSelectOption(
|
||||||
|
props.pos?.options?.response
|
||||||
|
? props.pos?.options?.response
|
||||||
|
: props.pos?.options?.defaultValue
|
||||||
|
? props.pos?.options?.defaultValue
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
["name", "email", "job title", "company"].includes(props.pos?.type) &&
|
||||||
|
props.isNeedSign &&
|
||||||
|
props.data?.signerObjId === props?.signerObjId
|
||||||
|
) {
|
||||||
|
const defaultData = props.pos?.options?.defaultValue;
|
||||||
|
if (defaultData) {
|
||||||
|
setTextValue(defaultData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [props.pos?.options?.defaultValue]);
|
||||||
|
|
||||||
|
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||||
|
<div
|
||||||
|
className="inputPlaceholder"
|
||||||
|
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
|
||||||
|
onClick={onClick}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
["name", "email", "job title", "company"].includes(type) &&
|
||||||
|
props.isNeedSign &&
|
||||||
|
props.data?.signerObjId === props.signerObjId
|
||||||
|
) {
|
||||||
|
const isDefault = true;
|
||||||
|
const senderUser = localStorage.getItem(`Extand_Class`);
|
||||||
|
const jsonSender = JSON.parse(senderUser);
|
||||||
|
onChangeInput(
|
||||||
|
jsonSender && jsonSender[0],
|
||||||
|
null,
|
||||||
|
props.xyPostion,
|
||||||
|
null,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data.Id,
|
||||||
|
isDefault
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
|
const calculateFontSize = () => {
|
||||||
|
const fontSize = 10 + Math.min(props.pos.Width, props.pos.Height) * 0.1;
|
||||||
|
const size = fontSize ? fontSize : 12;
|
||||||
|
return size + "px";
|
||||||
|
};
|
||||||
|
//function for show checked checkbox
|
||||||
|
const selectCheckbox = (ind) => {
|
||||||
|
const res = props.pos.options?.response;
|
||||||
|
const defaultCheck = props.pos.options?.defaultValue;
|
||||||
|
if (res && res?.length > 0) {
|
||||||
|
const isSelectIndex = res.indexOf(ind);
|
||||||
|
if (isSelectIndex > -1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
} else if (defaultCheck) {
|
||||||
|
const isSelectIndex = defaultCheck.indexOf(ind);
|
||||||
|
if (isSelectIndex > -1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRadioCheck = (data) => {
|
||||||
|
const defaultData = props.pos.options?.defaultValue;
|
||||||
|
if (textValue === data) {
|
||||||
|
return true;
|
||||||
|
} else if (defaultData === data) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function for set checked and unchecked value of checkbox
|
||||||
|
const handleCheckboxValue = (isChecked, ind) => {
|
||||||
|
let updateSelectedCheckbox = [],
|
||||||
|
checkedList;
|
||||||
|
let isDefaultValue, isDefaultEmpty;
|
||||||
|
if (type === "checkbox") {
|
||||||
|
updateSelectedCheckbox = selectedCheckbox ? selectedCheckbox : [];
|
||||||
|
|
||||||
|
if (isChecked) {
|
||||||
|
updateSelectedCheckbox.push(ind);
|
||||||
|
setSelectedCheckbox(updateSelectedCheckbox);
|
||||||
|
} else {
|
||||||
|
checkedList = selectedCheckbox.filter((data) => data !== ind);
|
||||||
|
setSelectedCheckbox(checkedList);
|
||||||
|
}
|
||||||
|
if (props.isNeedSign) {
|
||||||
|
isDefaultValue = props.pos.options?.defaultValue;
|
||||||
|
}
|
||||||
|
if (isDefaultValue && isDefaultValue.length > 0) {
|
||||||
|
isDefaultEmpty = true;
|
||||||
|
}
|
||||||
|
onChangeInput(
|
||||||
|
checkedList ? checkedList : updateSelectedCheckbox,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data.Id,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
isDefaultEmpty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function to handle select radio widget and set value seletced by user
|
||||||
|
const handleCheckRadio = (isChecked, data) => {
|
||||||
|
let isDefaultValue,
|
||||||
|
isDefaultEmpty,
|
||||||
|
isRadio = true;
|
||||||
|
if (props.isNeedSign) {
|
||||||
|
isDefaultValue = props.pos.options?.defaultValue;
|
||||||
|
}
|
||||||
|
if (isDefaultValue) {
|
||||||
|
isDefaultEmpty = true;
|
||||||
|
}
|
||||||
|
if (isChecked) {
|
||||||
|
setTextValue(data);
|
||||||
|
} else {
|
||||||
|
setTextValue("");
|
||||||
|
}
|
||||||
|
onChangeInput(
|
||||||
|
data,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data.Id,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
isDefaultEmpty,
|
||||||
|
isRadio
|
||||||
|
);
|
||||||
|
};
|
||||||
|
//function to set onchange date
|
||||||
|
const handleOnDateChange = (date) => {
|
||||||
|
props.setStartDate(date);
|
||||||
|
const isDateChange = true;
|
||||||
|
const dateObj = {
|
||||||
|
date: date,
|
||||||
|
format: props.selectDate.format
|
||||||
|
};
|
||||||
|
props.handleSaveDate(dateObj, isDateChange);
|
||||||
|
};
|
||||||
|
//handle height on enter press in text area
|
||||||
|
const handleEnterPress = (e) => {
|
||||||
|
const height = 18;
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
//function to save height of text area
|
||||||
|
onChangeHeightOfTextArea(
|
||||||
|
height,
|
||||||
|
props.pos.type,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
switch (type) {
|
||||||
|
case "signature":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
style={{
|
||||||
|
width: "99%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||||
|
{props?.handleUserName &&
|
||||||
|
props?.handleUserName(
|
||||||
|
props?.data?.Id,
|
||||||
|
props?.data?.Role,
|
||||||
|
props?.pos?.type
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "stamp":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
style={{
|
||||||
|
width: "99%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||||
|
{props?.handleUserName &&
|
||||||
|
props?.handleUserName(
|
||||||
|
props?.data?.Id,
|
||||||
|
props?.data?.Role,
|
||||||
|
props?.pos?.type
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "checkbox":
|
||||||
|
return (
|
||||||
|
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
||||||
|
{props.pos.options?.values?.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||||
|
<input
|
||||||
|
className={`${
|
||||||
|
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||||
|
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
disabled={
|
||||||
|
props.isNeedSign &&
|
||||||
|
(props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
type="checkbox"
|
||||||
|
checked={selectCheckbox(ind)}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (e.target.checked) {
|
||||||
|
if (!props.isPlaceholder) {
|
||||||
|
const maxRequired =
|
||||||
|
props.pos.options?.validation?.maxRequiredCount;
|
||||||
|
const maxCountInt =
|
||||||
|
maxRequired && parseInt(maxRequired);
|
||||||
|
|
||||||
|
if (maxCountInt > 0) {
|
||||||
|
if (
|
||||||
|
selectedCheckbox &&
|
||||||
|
selectedCheckbox?.length <= maxCountInt - 1
|
||||||
|
) {
|
||||||
|
handleCheckboxValue(e.target.checked, ind);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleCheckboxValue(e.target.checked, ind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleCheckboxValue(e.target.checked, ind);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!props.pos.options?.isHideLabel && (
|
||||||
|
<label className="text-xs mb-0 text-center">{data}</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case textInputWidget:
|
||||||
|
return props.isSignYourself ||
|
||||||
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={validatePlaceholder || "please enter value"}
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTextValue(e.target.value);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: calculateFontSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{type}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "dropdown":
|
||||||
|
return props.data?.signerObjId === props.signerObjId ? (
|
||||||
|
<select
|
||||||
|
className="inputPlaceholder"
|
||||||
|
id="myDropdown"
|
||||||
|
style={{ fontSize: "12px", color: "initial" }}
|
||||||
|
value={selectOption}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectOption(e.target.value);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Default/Title option */}
|
||||||
|
<option value="" disabled hidden>
|
||||||
|
{props?.pos?.options?.name}
|
||||||
|
</option>
|
||||||
|
|
||||||
|
{props.pos?.options?.values.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<option key={ind} value={data}>
|
||||||
|
{data}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
className="inputPlaceholder"
|
||||||
|
style={{ fontSize: calculateFontSize() }}
|
||||||
|
>
|
||||||
|
{props.pos?.options?.name ? props.pos.options.name : type}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "initials":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
style={{
|
||||||
|
width: "99%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||||
|
{props?.handleUserName &&
|
||||||
|
props?.handleUserName(
|
||||||
|
props?.data?.Id,
|
||||||
|
props?.data?.Role,
|
||||||
|
props?.pos?.type
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "name":
|
||||||
|
return props.isSignYourself ||
|
||||||
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={"name"}
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
const isDefault = false;
|
||||||
|
handleTextValid(e);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
isDefault
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: calculateFontSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{type}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "company":
|
||||||
|
return props.isSignYourself ||
|
||||||
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder="Enter text"
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleTextValid(e);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: calculateFontSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{type}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "job title":
|
||||||
|
return props.isSignYourself ||
|
||||||
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={"job title"}
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleTextValid(e);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: calculateFontSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{type}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "date":
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<DatePicker
|
||||||
|
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||||
|
<div className="flex justify-start ml-2 ">
|
||||||
|
<select
|
||||||
|
className="bg-transparent outline-none"
|
||||||
|
value={months[getMonth(date)]}
|
||||||
|
onChange={({ target: { value } }) =>
|
||||||
|
changeMonth(months.indexOf(value))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{months.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
className="bg-transparent outline-none"
|
||||||
|
value={getYear(date)}
|
||||||
|
onChange={({ target: { value } }) => changeYear(value)}
|
||||||
|
>
|
||||||
|
{years.map((option) => (
|
||||||
|
<option key={option} value={option}>
|
||||||
|
{option}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
disabled={
|
||||||
|
props.isPlaceholder ||
|
||||||
|
(props.isNeedSign &&
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
closeOnScroll={true}
|
||||||
|
className="inputPlaceholder"
|
||||||
|
style={{ outlineColor: "#007bff" }}
|
||||||
|
selected={
|
||||||
|
props?.startDate
|
||||||
|
? props?.startDate
|
||||||
|
: props.pos.options?.response &&
|
||||||
|
new Date(props.pos.options.response)
|
||||||
|
}
|
||||||
|
onChange={(date) => handleOnDateChange(date)}
|
||||||
|
popperPlacement="top-end"
|
||||||
|
customInput={<ExampleCustomInput />}
|
||||||
|
dateFormat={
|
||||||
|
props.selectDate
|
||||||
|
? props.selectDate?.format
|
||||||
|
: props.pos?.options?.validation?.format
|
||||||
|
? props.pos?.options?.validation?.format
|
||||||
|
: "MM/dd/yyyy"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "image":
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<img
|
||||||
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
style={{
|
||||||
|
width: "99%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||||
|
{props?.handleUserName &&
|
||||||
|
props?.handleUserName(
|
||||||
|
props?.data?.Id,
|
||||||
|
props?.data?.Role,
|
||||||
|
props?.pos?.type
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case "email":
|
||||||
|
return props.isSignYourself ||
|
||||||
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
|
<textarea
|
||||||
|
ref={inputRef}
|
||||||
|
placeholder={"enter email"}
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
handleTextValid(e);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
color: "black",
|
||||||
|
fontSize: calculateFontSize()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{type}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case radioButtonWidget:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{props.pos.options?.values.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||||
|
<input
|
||||||
|
className={`${ind === 0 ? "mt-0" : "mt-[5px]"} ${
|
||||||
|
props?.pos?.Width
|
||||||
|
? `w-[${props?.pos?.Width}px] h-[${props?.pos?.Width}px]`
|
||||||
|
: "w-[15px] h-[15px]"
|
||||||
|
} flex justify-center op-radio`}
|
||||||
|
type="radio"
|
||||||
|
disabled={
|
||||||
|
props.isNeedSign &&
|
||||||
|
(props.pos.options?.isReadOnly ||
|
||||||
|
props.data?.signerObjId !== props.signerObjId)
|
||||||
|
}
|
||||||
|
checked={handleRadioCheck(data)}
|
||||||
|
onChange={(e) => {
|
||||||
|
if (!props.isPlaceholder) {
|
||||||
|
handleCheckRadio(e.target.checked, data);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!props.pos.options?.isHideLabel && (
|
||||||
|
<label className="text-xs mb-0">{data}</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case textWidget:
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
placeholder="Enter text"
|
||||||
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
|
value={textValue}
|
||||||
|
onBlur={handleInputBlur}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTextValue(e.target.value);
|
||||||
|
onChangeInput(
|
||||||
|
e.target.value,
|
||||||
|
props.pos.key,
|
||||||
|
props.xyPostion,
|
||||||
|
props.index,
|
||||||
|
props.setXyPostion,
|
||||||
|
props.data && props.data?.Id,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
color: "initial"
|
||||||
|
}}
|
||||||
|
cols="50"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return props.pos.SignUrl ? (
|
||||||
|
<div style={{ pointerEvents: "none" }}>
|
||||||
|
<img
|
||||||
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
|
src={props.pos.SignUrl}
|
||||||
|
style={{
|
||||||
|
width: "99%",
|
||||||
|
height: "100%",
|
||||||
|
objectFit: "contain"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "10px",
|
||||||
|
color: "black",
|
||||||
|
justifyContent: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||||
|
{props?.handleUserName &&
|
||||||
|
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PlaceholderType;
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
function PrevNext({ pageNumber, allPages, changePage }) {
|
||||||
|
//for go to previous page
|
||||||
|
function previousPage() {
|
||||||
|
changePage(-1);
|
||||||
|
}
|
||||||
|
//for go to next page
|
||||||
|
function nextPage() {
|
||||||
|
changePage(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center">
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||||
|
disabled={pageNumber <= 1}
|
||||||
|
onClick={previousPage}
|
||||||
|
>
|
||||||
|
<span className="block lg:hidden">
|
||||||
|
<i className="fa fa-backward" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span className="lg:block hidden">Prev</span>
|
||||||
|
</button>
|
||||||
|
<span className="text-xs text-base-content font-medium mx-2">
|
||||||
|
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||||
|
disabled={pageNumber >= allPages}
|
||||||
|
onClick={nextPage}
|
||||||
|
>
|
||||||
|
<span className="block lg:hidden">
|
||||||
|
<i className="fa fa-forward" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span className="lg:block hidden">Next</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrevNext;
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
|
import {
|
||||||
|
color,
|
||||||
|
darkenColor,
|
||||||
|
getFirstLetter,
|
||||||
|
isMobile,
|
||||||
|
nameColor
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
const cursor =
|
||||||
|
"cursor-[url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC'),_pointer]";
|
||||||
|
const RecipientList = (props) => {
|
||||||
|
const [animationParent] = useAutoAnimate();
|
||||||
|
const [isHover, setIsHover] = useState();
|
||||||
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
|
//function for onhover signer name change background color
|
||||||
|
const inputRef = useRef(null);
|
||||||
|
|
||||||
|
const isWidgetExist = (Id) => {
|
||||||
|
return props.signerPos.some((x) => x.Id === Id);
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle drag start
|
||||||
|
const handleDragStart = (e, id) => {
|
||||||
|
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
|
||||||
|
// The first argument specifies the type of data being set, and the second argument is the actual data you want to transfer.
|
||||||
|
e.dataTransfer.setData("text/plain", id);
|
||||||
|
};
|
||||||
|
|
||||||
|
//handleDragOver prevents the default behavior of the dragover event, which is necessary for the drop event to be triggered.
|
||||||
|
const handleDragOver = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list
|
||||||
|
const handleChangeSequence = (e, ind, isUp, isDown) => {
|
||||||
|
e.preventDefault();
|
||||||
|
let draggedItemId;
|
||||||
|
let index = ind;
|
||||||
|
|
||||||
|
//if isUp true then set item's id and index in mobile view
|
||||||
|
if (isUp) {
|
||||||
|
draggedItemId = index;
|
||||||
|
index = index - 1;
|
||||||
|
}
|
||||||
|
//if isDown true then set item's id and index in mobile view
|
||||||
|
else if (isDown) {
|
||||||
|
draggedItemId = index;
|
||||||
|
index = index + 1;
|
||||||
|
} //else set id on drag element in desktop viiew
|
||||||
|
else {
|
||||||
|
//`e.dataTransfer.getData('text/plain')` is used to get data that you have saved.
|
||||||
|
draggedItemId = e.dataTransfer.getData("text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert string to number
|
||||||
|
const intDragId = parseInt(draggedItemId);
|
||||||
|
//get that item to change position
|
||||||
|
const draggedItem = props.signersdata.filter((_, ind) => ind === intDragId);
|
||||||
|
const remainingItems = props.signersdata.filter(
|
||||||
|
(_, ind) => ind !== intDragId
|
||||||
|
);
|
||||||
|
//splice method is used to replace or add new value in array at specific index
|
||||||
|
remainingItems.splice(index, 0, ...draggedItem);
|
||||||
|
|
||||||
|
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
|
||||||
|
props.setSignersData(remainingItems);
|
||||||
|
props.setSignerObjId(remainingItems[index]?.objectId || "");
|
||||||
|
props.setIsSelectId(index);
|
||||||
|
props.setContractName(remainingItems[index]?.className || "");
|
||||||
|
props.setUniqueId(remainingItems[index]?.Id);
|
||||||
|
props.setRoleName(remainingItems[index]?.Role);
|
||||||
|
props.setBlockColor(remainingItems[index]?.blockColor);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.signersdata.length > 0 &&
|
||||||
|
props.signersdata.map((obj, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={animationParent}
|
||||||
|
key={ind}
|
||||||
|
draggable={props.sendInOrder && !isMobile ? true : false}
|
||||||
|
onDragStart={(e) =>
|
||||||
|
props.sendInOrder && !isMobile && handleDragStart(e, ind)
|
||||||
|
}
|
||||||
|
onDragOver={(e) =>
|
||||||
|
props.sendInOrder && !isMobile && handleDragOver(e)
|
||||||
|
}
|
||||||
|
onDrop={(e) =>
|
||||||
|
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
|
||||||
|
}
|
||||||
|
data-tut="reactourFirst"
|
||||||
|
onMouseEnter={() => setIsHover(ind)}
|
||||||
|
onMouseLeave={() => setIsHover(null)}
|
||||||
|
className={`${
|
||||||
|
props.sendInOrder
|
||||||
|
? props.isMailSend
|
||||||
|
? "pointer-events-none bg-opacity-80"
|
||||||
|
: `text-[12px] font-bold ${cursor}`
|
||||||
|
: props.isMailSend && "pointer-events-none bg-opacity-80"
|
||||||
|
} flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
(!isMobile && isHover === ind) || props.isSelectListId === ind
|
||||||
|
? obj?.blockColor
|
||||||
|
? obj?.blockColor
|
||||||
|
: color[ind % color.length]
|
||||||
|
: "transparent"
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
props.setSignerObjId(obj?.objectId || "");
|
||||||
|
props.setIsSelectId(ind);
|
||||||
|
props.setContractName(obj?.className || "");
|
||||||
|
props.setUniqueId(obj.Id);
|
||||||
|
props.setRoleName(obj.Role);
|
||||||
|
props.setBlockColor(obj?.blockColor);
|
||||||
|
if (props.handleModal) {
|
||||||
|
props.handleModal();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-row items-center w-full">
|
||||||
|
<div
|
||||||
|
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
|
||||||
|
style={{
|
||||||
|
background: obj?.blockColor
|
||||||
|
? darkenColor(obj?.blockColor, 0.4)
|
||||||
|
: nameColor[ind % nameColor.length]
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="text-white uppercase font-bold text-center text-[12px]">
|
||||||
|
{isWidgetExist(obj.Id) ? (
|
||||||
|
<i className="fa-solid fa-check"></i>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{obj.Name
|
||||||
|
? getFirstLetter(obj.Name)
|
||||||
|
: getFirstLetter(obj.Role)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
obj?.Name ? "flex-col" : "flex-row"
|
||||||
|
} flex items-center`}
|
||||||
|
>
|
||||||
|
{obj.Name ? (
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
(!isMobile && isHover === ind) ||
|
||||||
|
props.isSelectListId === ind
|
||||||
|
? "text-[#424242]"
|
||||||
|
: "text-base-content"
|
||||||
|
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
|
||||||
|
>
|
||||||
|
{obj.Name}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span
|
||||||
|
className={`${
|
||||||
|
(!isMobile && isHover === ind) ||
|
||||||
|
props.isSelectListId === ind
|
||||||
|
? "text-[#424242]"
|
||||||
|
: "text-base-content"
|
||||||
|
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
|
||||||
|
onClick={() => {
|
||||||
|
setIsEdit({ [obj.Id]: true });
|
||||||
|
props.setRoleName(obj.Role);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
className="bg-transparent p-[3px"
|
||||||
|
value={obj.Role}
|
||||||
|
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||||
|
onBlur={() => {
|
||||||
|
setIsEdit({});
|
||||||
|
props.handleOnBlur(obj.Role, obj.Id);
|
||||||
|
}}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
inputRef.current.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="p-[3px]">{obj.Role}</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{obj.Name && (
|
||||||
|
<span
|
||||||
|
className={` ${
|
||||||
|
(!isMobile && isHover === ind) ||
|
||||||
|
props.isSelectListId === ind
|
||||||
|
? "text-[#424242]"
|
||||||
|
: "text-base-content"
|
||||||
|
} text-[10px] font-medium w-[100px] whitespace-nowrap overflow-hidden text-ellipsis`}
|
||||||
|
>
|
||||||
|
{obj?.Role || obj?.Email}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isMobile && props.sendInOrder && (
|
||||||
|
<div className="flex flex-row items-center gap-[5px]">
|
||||||
|
<div
|
||||||
|
onClick={(e) => {
|
||||||
|
if (ind !== 0) {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleChangeSequence(e, ind, "up");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={ind === 0 ? "text-[gray]" : "text-black"}
|
||||||
|
>
|
||||||
|
▲
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={(e) => {
|
||||||
|
if (ind !== props.signersdata.length - 1) {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleChangeSequence(e, ind, null, "down");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
ind === props.signersdata.length - 1
|
||||||
|
? "text-[gray]"
|
||||||
|
: "text-black"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
▼
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.handleDeleteUser && (
|
||||||
|
<div
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
props.handleDeleteUser(obj.Id);
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
(!isMobile && isHover === ind) ||
|
||||||
|
props.isSelectListId === ind
|
||||||
|
? "text-[#424242]"
|
||||||
|
: "text-base-content"
|
||||||
|
} cursor-pointer ml-[5px]`}
|
||||||
|
>
|
||||||
|
<i className="fa-regular fa-trash-can"></i>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RecipientList;
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import RSC from "react-scrollbars-custom";
|
||||||
|
import { Document, Page } from "react-pdf";
|
||||||
|
|
||||||
|
function RenderAllPdfPage({
|
||||||
|
signPdfUrl,
|
||||||
|
allPages,
|
||||||
|
setAllPages,
|
||||||
|
setPageNumber,
|
||||||
|
setSignBtnPosition,
|
||||||
|
pageNumber,
|
||||||
|
signerPos,
|
||||||
|
signerObjectId
|
||||||
|
}) {
|
||||||
|
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||||
|
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||||
|
//set all number of pages after load pdf
|
||||||
|
function onDocumentLoad({ numPages }) {
|
||||||
|
setAllPages(numPages);
|
||||||
|
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
|
||||||
|
if (signerPos) {
|
||||||
|
const checkUser = signerPos.filter(
|
||||||
|
(data) => data.signerObjId === signerObjectId
|
||||||
|
);
|
||||||
|
setBookmarkColor(checkUser[0]?.blockColor);
|
||||||
|
let pageNumberArr = [];
|
||||||
|
if (checkUser?.length > 0) {
|
||||||
|
checkUser[0]?.placeHolder?.map((data) => {
|
||||||
|
pageNumberArr.push(data?.pageNumber);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSignPageNumber(pageNumberArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
|
||||||
|
const addSignatureBookmark = (index) => {
|
||||||
|
const ispageNumber = signPageNumber.includes(index + 1);
|
||||||
|
return (
|
||||||
|
ispageNumber && (
|
||||||
|
<div className="absolute z-20 top-[1px] -right-[13px] -translate-x-1/2 -translate-y-1/2">
|
||||||
|
<i
|
||||||
|
style={{ color: bookmarkColor || "red" }}
|
||||||
|
className="fa-solid fa-bookmark"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className=" hidden md:flex flex-row bg-base-100 h-full">
|
||||||
|
<div className="w-[140px]">
|
||||||
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
|
Pages
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<RSC
|
||||||
|
id="RSC-Example"
|
||||||
|
style={{
|
||||||
|
width: "135px",
|
||||||
|
height: window.innerHeight - 130 + "px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Document
|
||||||
|
loading={"Loading Document.."}
|
||||||
|
onLoadSuccess={onDocumentLoad}
|
||||||
|
file={signPdfUrl}
|
||||||
|
>
|
||||||
|
{Array.from(new Array(allPages), (el, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`${
|
||||||
|
pageNumber - 1 === index
|
||||||
|
? "border-[red]"
|
||||||
|
: "border-[#878787]"
|
||||||
|
} border-2 w-[100px] m-[10px] flex justify-center items-center relative`}
|
||||||
|
onClick={() => {
|
||||||
|
setPageNumber(index + 1);
|
||||||
|
if (setSignBtnPosition) {
|
||||||
|
setSignBtnPosition([]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{signerPos && addSignatureBookmark(index)}
|
||||||
|
|
||||||
|
<div className="relative z-[1] overflow-hidden">
|
||||||
|
<Page
|
||||||
|
key={`page_${index + 1}`}
|
||||||
|
pageNumber={index + 1}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
scale={1}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Document>
|
||||||
|
</RSC>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RenderAllPdfPage;
|
||||||
@@ -0,0 +1,675 @@
|
|||||||
|
import React from "react";
|
||||||
|
import RSC from "react-scrollbars-custom";
|
||||||
|
import { Document, Page } from "react-pdf";
|
||||||
|
import {
|
||||||
|
defaultWidthHeight,
|
||||||
|
handleImageResize,
|
||||||
|
handleSignYourselfImageResize
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
import Placeholder from "./Placeholder";
|
||||||
|
import Alert from "../../primitives/Alert";
|
||||||
|
|
||||||
|
function RenderPdf({
|
||||||
|
pageNumber,
|
||||||
|
pdfOriginalWidth,
|
||||||
|
pdfNewWidth,
|
||||||
|
drop,
|
||||||
|
signerPos,
|
||||||
|
successEmail,
|
||||||
|
handleTabDrag,
|
||||||
|
handleStop,
|
||||||
|
isDragging,
|
||||||
|
setIsSignPad,
|
||||||
|
setIsStamp,
|
||||||
|
handleDeleteSign,
|
||||||
|
setSignKey,
|
||||||
|
pdfDetails,
|
||||||
|
xyPostion,
|
||||||
|
pdfUrl,
|
||||||
|
numPages,
|
||||||
|
pageDetails,
|
||||||
|
pdfRequest,
|
||||||
|
setCurrentSigner,
|
||||||
|
signerObjectId,
|
||||||
|
signedSigners,
|
||||||
|
setPdfLoadFail,
|
||||||
|
placeholder,
|
||||||
|
pdfLoadFail,
|
||||||
|
setSignerPos,
|
||||||
|
setXyPostion,
|
||||||
|
index,
|
||||||
|
containerWH,
|
||||||
|
setIsResize,
|
||||||
|
handleLinkUser,
|
||||||
|
setUniqueId,
|
||||||
|
signersdata,
|
||||||
|
setIsPageCopy,
|
||||||
|
setSignerObjId,
|
||||||
|
setShowDropdown,
|
||||||
|
setIsInitial,
|
||||||
|
setIsValidate,
|
||||||
|
setWidgetType,
|
||||||
|
setValidateAlert,
|
||||||
|
setIsRadio,
|
||||||
|
setCurrWidgetsDetails,
|
||||||
|
setSelectWidgetId,
|
||||||
|
selectWidgetId,
|
||||||
|
unSignedWidgetId,
|
||||||
|
setIsCheckbox,
|
||||||
|
handleNameModal,
|
||||||
|
setTempSignerId,
|
||||||
|
uniqueId
|
||||||
|
}) {
|
||||||
|
const isMobile = window.innerWidth < 767;
|
||||||
|
const newWidth = containerWH.width;
|
||||||
|
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
|
||||||
|
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||||
|
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||||
|
|
||||||
|
// handle signature block width and height according to screen
|
||||||
|
const posWidth = (pos, signYourself) => {
|
||||||
|
const defaultWidth = defaultWidthHeight(pos.type).width;
|
||||||
|
const posWidth = pos.Width ? pos.Width : defaultWidth;
|
||||||
|
let width;
|
||||||
|
if (signYourself) {
|
||||||
|
width = posWidth;
|
||||||
|
return width;
|
||||||
|
} else {
|
||||||
|
if (isMobile) {
|
||||||
|
if (!pos.isMobile) {
|
||||||
|
if (pos.IsResize) {
|
||||||
|
width = posWidth ? posWidth : defaultWidth;
|
||||||
|
return width;
|
||||||
|
} else {
|
||||||
|
width = (posWidth || defaultWidth) / scale;
|
||||||
|
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
width = posWidth;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pos.isMobile) {
|
||||||
|
if (pos.IsResize) {
|
||||||
|
width = posWidth ? posWidth : defaultWidth;
|
||||||
|
return width;
|
||||||
|
} else {
|
||||||
|
width = (posWidth || defaultWidth) * pos.scale;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
width = posWidth ? posWidth : defaultWidth;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const posHeight = (pos, signYourself) => {
|
||||||
|
let height;
|
||||||
|
const posHeight = pos.Height;
|
||||||
|
const defaultHeight = defaultWidthHeight(pos.type).height;
|
||||||
|
|
||||||
|
if (signYourself) {
|
||||||
|
height = posHeight ? posHeight : defaultHeight;
|
||||||
|
|
||||||
|
return height;
|
||||||
|
} else {
|
||||||
|
if (isMobile) {
|
||||||
|
if (!pos.isMobile) {
|
||||||
|
if (pos.IsResize) {
|
||||||
|
height = posHeight ? posHeight : defaultHeight;
|
||||||
|
return height;
|
||||||
|
} else {
|
||||||
|
height = (posHeight || defaultHeight) / scale;
|
||||||
|
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
height = posHeight ? posHeight : defaultHeight;
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pos.isMobile) {
|
||||||
|
if (pos.IsResize) {
|
||||||
|
height = posHeight ? posHeight : defaultHeight;
|
||||||
|
return height;
|
||||||
|
} else {
|
||||||
|
height = (posHeight || defaultHeight) * pos.scale;
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
height = posHeight ? posHeight : defaultHeight;
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const xPos = (pos, signYourself) => {
|
||||||
|
const resizePos = pos.xPosition;
|
||||||
|
if (signYourself) {
|
||||||
|
return resizePos;
|
||||||
|
} else {
|
||||||
|
//checking both condition mobile and desktop view
|
||||||
|
if (isMobile) {
|
||||||
|
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||||
|
if (!pos.isMobile) {
|
||||||
|
return resizePos / scale;
|
||||||
|
}
|
||||||
|
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||||
|
else {
|
||||||
|
return resizePos * (pos.scale / scale);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||||
|
|
||||||
|
if (pos.isMobile) {
|
||||||
|
return pos.scale && resizePos * pos.scale;
|
||||||
|
}
|
||||||
|
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||||
|
else {
|
||||||
|
return resizePos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const yPos = (pos, signYourself) => {
|
||||||
|
const resizePos = pos.yPosition;
|
||||||
|
if (signYourself) {
|
||||||
|
return resizePos;
|
||||||
|
} else {
|
||||||
|
//checking both condition mobile and desktop view
|
||||||
|
if (isMobile) {
|
||||||
|
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||||
|
if (!pos.isMobile) {
|
||||||
|
return resizePos / scale;
|
||||||
|
}
|
||||||
|
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||||
|
else {
|
||||||
|
return resizePos * (pos.scale / scale);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||||
|
|
||||||
|
if (pos.isMobile) {
|
||||||
|
return pos.scale && resizePos * pos.scale;
|
||||||
|
}
|
||||||
|
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||||
|
else {
|
||||||
|
return resizePos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function for render placeholder block over pdf document
|
||||||
|
const checkSignedSignes = (data) => {
|
||||||
|
let checkSign = [];
|
||||||
|
//condition to handle quick send flow and using normal request sign flow
|
||||||
|
checkSign = signedSigners.filter(
|
||||||
|
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
|
||||||
|
);
|
||||||
|
if (data.signerObjId === signerObjectId) {
|
||||||
|
setCurrentSigner(true);
|
||||||
|
}
|
||||||
|
const handleAllUserName = (Id, Role, type) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="text-black text-[8px] font-medium">
|
||||||
|
{
|
||||||
|
pdfDetails[0].Signers?.find(
|
||||||
|
(signer) => signer.objectId === data.signerObjId
|
||||||
|
)?.Name
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
|
{Role && (
|
||||||
|
<div className="text-black text-[8px] font-medium">
|
||||||
|
{`(${Role})`}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
checkSign.length === 0 &&
|
||||||
|
data.placeHolder.map((placeData, key) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={key}>
|
||||||
|
{placeData.pageNumber === pageNumber &&
|
||||||
|
placeData.pos.map((pos) => {
|
||||||
|
return (
|
||||||
|
pos && (
|
||||||
|
<React.Fragment key={pos.key}>
|
||||||
|
<Placeholder
|
||||||
|
pos={pos}
|
||||||
|
setSignKey={setSignKey}
|
||||||
|
setIsSignPad={setIsSignPad}
|
||||||
|
setIsStamp={setIsStamp}
|
||||||
|
handleSignYourselfImageResize={handleImageResize}
|
||||||
|
index={pageNumber}
|
||||||
|
xyPostion={signerPos}
|
||||||
|
setXyPostion={setSignerPos}
|
||||||
|
data={data}
|
||||||
|
setIsResize={setIsResize}
|
||||||
|
isShowBorder={false}
|
||||||
|
signerObjId={signerObjectId}
|
||||||
|
isShowDropdown={true}
|
||||||
|
isNeedSign={true}
|
||||||
|
isSignYourself={false}
|
||||||
|
xPos={xPos}
|
||||||
|
yPos={yPos}
|
||||||
|
posWidth={posWidth}
|
||||||
|
posHeight={posHeight}
|
||||||
|
handleUserName={handleAllUserName}
|
||||||
|
isDragging={false}
|
||||||
|
pdfDetails={pdfDetails}
|
||||||
|
setIsInitial={setIsInitial}
|
||||||
|
setValidateAlert={setValidateAlert}
|
||||||
|
unSignedWidgetId={unSignedWidgetId}
|
||||||
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
|
selectWidgetId={selectWidgetId}
|
||||||
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUserName = (Id, Role, type) => {
|
||||||
|
if (Id) {
|
||||||
|
const checkSign = signersdata.find((sign) => sign.Id === Id);
|
||||||
|
if (checkSign?.Name) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="text-black text-[8px] font-medium">
|
||||||
|
{checkSign?.Name}
|
||||||
|
</div>
|
||||||
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
|
{Role && (
|
||||||
|
<div className="text-black text-[8px] font-medium">
|
||||||
|
{`(${Role})`}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
|
<div className="text-black text-[8px] font-medium">{Role}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
|
<div className="text-black text-[8px] font-medium">{Role}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
||||||
|
{isMobile && scale ? (
|
||||||
|
<div
|
||||||
|
data-tut="reactourForth"
|
||||||
|
style={{
|
||||||
|
border: "0.1px solid #ebe8e8",
|
||||||
|
marginTop: isGuestSigner && "30px"
|
||||||
|
}}
|
||||||
|
ref={drop}
|
||||||
|
id="container"
|
||||||
|
>
|
||||||
|
{pdfLoadFail.status &&
|
||||||
|
(pdfRequest
|
||||||
|
? signerPos.map((data, key) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={key}>
|
||||||
|
{checkSignedSignes(data)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: placeholder // placeholder mobile
|
||||||
|
? signerPos.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={ind}>
|
||||||
|
{data.placeHolder.map((placeData, index) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{placeData.pageNumber === pageNumber &&
|
||||||
|
placeData.pos.map((pos) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={pos.key}>
|
||||||
|
<Placeholder
|
||||||
|
pos={pos}
|
||||||
|
setIsPageCopy={setIsPageCopy}
|
||||||
|
setSignKey={setSignKey}
|
||||||
|
handleDeleteSign={handleDeleteSign}
|
||||||
|
setIsStamp={setIsStamp}
|
||||||
|
handleTabDrag={handleTabDrag}
|
||||||
|
handleStop={handleStop}
|
||||||
|
handleSignYourselfImageResize={
|
||||||
|
handleImageResize
|
||||||
|
}
|
||||||
|
index={pageNumber}
|
||||||
|
xyPostion={signerPos}
|
||||||
|
setXyPostion={setSignerPos}
|
||||||
|
setSignerObjId={setSignerObjId}
|
||||||
|
data={data}
|
||||||
|
setIsResize={setIsResize}
|
||||||
|
setShowDropdown={setShowDropdown}
|
||||||
|
isShowBorder={true}
|
||||||
|
isPlaceholder={true}
|
||||||
|
setUniqueId={setUniqueId}
|
||||||
|
handleLinkUser={handleLinkUser}
|
||||||
|
handleUserName={handleUserName}
|
||||||
|
isSignYourself={false}
|
||||||
|
xPos={xPos}
|
||||||
|
yPos={yPos}
|
||||||
|
posWidth={posWidth}
|
||||||
|
posHeight={posHeight}
|
||||||
|
isDragging={isDragging}
|
||||||
|
setIsValidate={setIsValidate}
|
||||||
|
setWidgetType={setWidgetType}
|
||||||
|
setIsRadio={setIsRadio}
|
||||||
|
setIsCheckbox={setIsCheckbox}
|
||||||
|
setCurrWidgetsDetails={
|
||||||
|
setCurrWidgetsDetails
|
||||||
|
}
|
||||||
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
|
selectWidgetId={selectWidgetId}
|
||||||
|
handleNameModal={handleNameModal}
|
||||||
|
setTempSignerId={setTempSignerId}
|
||||||
|
uniqueId={uniqueId}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: xyPostion.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={ind}>
|
||||||
|
{data.pageNumber === pageNumber &&
|
||||||
|
data.pos.map((pos) => {
|
||||||
|
return (
|
||||||
|
pos && (
|
||||||
|
<Placeholder
|
||||||
|
pos={pos}
|
||||||
|
setIsPageCopy={setIsPageCopy}
|
||||||
|
setSignKey={setSignKey}
|
||||||
|
handleDeleteSign={handleDeleteSign}
|
||||||
|
setIsStamp={setIsStamp}
|
||||||
|
handleTabDrag={handleTabDrag}
|
||||||
|
handleStop={handleStop}
|
||||||
|
handleSignYourselfImageResize={
|
||||||
|
handleSignYourselfImageResize
|
||||||
|
}
|
||||||
|
index={index}
|
||||||
|
xyPostion={xyPostion}
|
||||||
|
setXyPostion={setXyPostion}
|
||||||
|
pdfOriginalWidth={pdfOriginalWidth}
|
||||||
|
containerWH={containerWH}
|
||||||
|
setIsSignPad={setIsSignPad}
|
||||||
|
isShowBorder={true}
|
||||||
|
isSignYourself={true}
|
||||||
|
xPos={xPos}
|
||||||
|
yPos={yPos}
|
||||||
|
posWidth={posWidth}
|
||||||
|
posHeight={posHeight}
|
||||||
|
pdfDetails={pdfDetails[0]}
|
||||||
|
isDragging={isDragging}
|
||||||
|
setIsInitial={setIsInitial}
|
||||||
|
setWidgetType={setWidgetType}
|
||||||
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
|
selectWidgetId={selectWidgetId}
|
||||||
|
handleUserName={handleUserName}
|
||||||
|
setIsCheckbox={setIsCheckbox}
|
||||||
|
setValidateAlert={setValidateAlert}
|
||||||
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}))}
|
||||||
|
|
||||||
|
{/* this component for render pdf document is in middle of the component */}
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<Document
|
||||||
|
onLoadError={() => setPdfLoadFail(true)}
|
||||||
|
loading={"Loading Document.."}
|
||||||
|
onLoadSuccess={pageDetails}
|
||||||
|
// ref={pdfRef}'
|
||||||
|
onClick={() => {
|
||||||
|
if (setSelectWidgetId) {
|
||||||
|
setSelectWidgetId("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
file={
|
||||||
|
pdfUrl
|
||||||
|
? pdfUrl
|
||||||
|
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||||
|
? pdfDetails[0].SignedUrl
|
||||||
|
: pdfDetails[0].URL
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Array.from(new Array(numPages), (el, index) => (
|
||||||
|
<Page
|
||||||
|
key={index}
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
width={containerWH.width}
|
||||||
|
height={containerWH.height}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
onGetAnnotationsError={(error) => {
|
||||||
|
console.log("annotation error", error);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Document>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<RSC
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||||
|
width:
|
||||||
|
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
|
||||||
|
height: window.innerHeight - 110 + "px"
|
||||||
|
}}
|
||||||
|
noScrollY={false}
|
||||||
|
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-tut="reactourForth"
|
||||||
|
style={{ border: "0.1px solid #ebe8e8", width: pdfOriginalWidth }}
|
||||||
|
ref={drop}
|
||||||
|
id="container"
|
||||||
|
>
|
||||||
|
{pdfLoadFail.status &&
|
||||||
|
(pdfRequest
|
||||||
|
? signerPos.map((data, key) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={key}>
|
||||||
|
{checkSignedSignes(data)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: placeholder
|
||||||
|
? signerPos.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={ind}>
|
||||||
|
{data.placeHolder.map((placeData, index) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{placeData.pageNumber === pageNumber &&
|
||||||
|
placeData.pos.map((pos) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={pos.key}>
|
||||||
|
<Placeholder
|
||||||
|
pos={pos}
|
||||||
|
setIsPageCopy={setIsPageCopy}
|
||||||
|
setSignKey={setSignKey}
|
||||||
|
handleDeleteSign={handleDeleteSign}
|
||||||
|
setIsStamp={setIsStamp}
|
||||||
|
handleTabDrag={handleTabDrag}
|
||||||
|
handleStop={handleStop}
|
||||||
|
handleSignYourselfImageResize={
|
||||||
|
handleImageResize
|
||||||
|
}
|
||||||
|
index={pageNumber}
|
||||||
|
xyPostion={signerPos}
|
||||||
|
setXyPostion={setSignerPos}
|
||||||
|
setSignerObjId={setSignerObjId}
|
||||||
|
data={data}
|
||||||
|
setIsResize={setIsResize}
|
||||||
|
setShowDropdown={setShowDropdown}
|
||||||
|
isShowBorder={true}
|
||||||
|
isPlaceholder={true}
|
||||||
|
setUniqueId={setUniqueId}
|
||||||
|
handleLinkUser={handleLinkUser}
|
||||||
|
handleUserName={handleUserName}
|
||||||
|
isSignYourself={false}
|
||||||
|
xPos={xPos}
|
||||||
|
yPos={yPos}
|
||||||
|
posWidth={posWidth}
|
||||||
|
posHeight={posHeight}
|
||||||
|
isDragging={isDragging}
|
||||||
|
setIsValidate={setIsValidate}
|
||||||
|
setWidgetType={setWidgetType}
|
||||||
|
setIsRadio={setIsRadio}
|
||||||
|
setIsCheckbox={setIsCheckbox}
|
||||||
|
setCurrWidgetsDetails={
|
||||||
|
setCurrWidgetsDetails
|
||||||
|
}
|
||||||
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
|
selectWidgetId={selectWidgetId}
|
||||||
|
handleNameModal={handleNameModal}
|
||||||
|
setTempSignerId={setTempSignerId}
|
||||||
|
uniqueId={uniqueId}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: xyPostion.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={ind}>
|
||||||
|
{data.pageNumber === pageNumber &&
|
||||||
|
data.pos.map((pos) => {
|
||||||
|
return (
|
||||||
|
pos && (
|
||||||
|
<React.Fragment key={pos.key}>
|
||||||
|
<Placeholder
|
||||||
|
pos={pos}
|
||||||
|
setIsPageCopy={setIsPageCopy}
|
||||||
|
setSignKey={setSignKey}
|
||||||
|
handleDeleteSign={handleDeleteSign}
|
||||||
|
setIsStamp={setIsStamp}
|
||||||
|
handleTabDrag={handleTabDrag}
|
||||||
|
handleStop={(event, dragElement) =>
|
||||||
|
handleStop(event, dragElement, pos.type)
|
||||||
|
}
|
||||||
|
handleSignYourselfImageResize={
|
||||||
|
handleSignYourselfImageResize
|
||||||
|
}
|
||||||
|
index={index}
|
||||||
|
xyPostion={xyPostion}
|
||||||
|
setXyPostion={setXyPostion}
|
||||||
|
pdfOriginalWidth={pdfOriginalWidth}
|
||||||
|
containerWH={containerWH}
|
||||||
|
setIsSignPad={setIsSignPad}
|
||||||
|
isShowBorder={true}
|
||||||
|
isSignYourself={true}
|
||||||
|
xPos={xPos}
|
||||||
|
yPos={yPos}
|
||||||
|
posWidth={posWidth}
|
||||||
|
posHeight={posHeight}
|
||||||
|
pdfDetails={pdfDetails[0]}
|
||||||
|
isDragging={isDragging}
|
||||||
|
setIsInitial={setIsInitial}
|
||||||
|
setWidgetType={setWidgetType}
|
||||||
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
|
selectWidgetId={selectWidgetId}
|
||||||
|
handleUserName={handleUserName}
|
||||||
|
setIsCheckbox={setIsCheckbox}
|
||||||
|
setValidateAlert={setValidateAlert}
|
||||||
|
setCurrWidgetsDetails={
|
||||||
|
setCurrWidgetsDetails
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}))}
|
||||||
|
|
||||||
|
{/* this component for render pdf document is in middle of the component */}
|
||||||
|
<Document
|
||||||
|
onLoadError={() => {
|
||||||
|
const load = {
|
||||||
|
status: false,
|
||||||
|
type: "failed"
|
||||||
|
};
|
||||||
|
setPdfLoadFail(load);
|
||||||
|
}}
|
||||||
|
loading={"Loading Document.."}
|
||||||
|
onLoadSuccess={pageDetails}
|
||||||
|
onClick={() => {
|
||||||
|
if (setSelectWidgetId) {
|
||||||
|
setSelectWidgetId("");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
// ref={pdfRef}
|
||||||
|
file={
|
||||||
|
pdfUrl
|
||||||
|
? pdfUrl
|
||||||
|
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||||
|
? pdfDetails[0].SignedUrl
|
||||||
|
: pdfDetails[0].URL
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{Array.from(new Array(numPages), (el, index) => (
|
||||||
|
<Page
|
||||||
|
key={index}
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
onGetAnnotationsError={(error) => {
|
||||||
|
console.log("annotation error", error);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Document>
|
||||||
|
</div>
|
||||||
|
</RSC>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RenderPdf;
|
||||||
@@ -0,0 +1,599 @@
|
|||||||
|
import React, { useRef, useState, useEffect } from "react";
|
||||||
|
import SignatureCanvas from "react-signature-canvas";
|
||||||
|
|
||||||
|
function SignPad({
|
||||||
|
isSignPad,
|
||||||
|
isStamp,
|
||||||
|
setIsImageSelect,
|
||||||
|
onSaveSign,
|
||||||
|
setIsSignPad,
|
||||||
|
setImage,
|
||||||
|
isImageSelect,
|
||||||
|
imageRef,
|
||||||
|
onImageChange,
|
||||||
|
onSaveImage,
|
||||||
|
image,
|
||||||
|
defaultSign,
|
||||||
|
setSignature,
|
||||||
|
myInitial,
|
||||||
|
isInitial,
|
||||||
|
setIsInitial,
|
||||||
|
setIsStamp,
|
||||||
|
widgetType,
|
||||||
|
currWidgetsDetails,
|
||||||
|
setCurrWidgetsDetails
|
||||||
|
}) {
|
||||||
|
const [penColor, setPenColor] = useState("blue");
|
||||||
|
const allColor = ["blue", "red", "black"];
|
||||||
|
const canvasRef = useRef(null);
|
||||||
|
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
||||||
|
const [isTab, setIsTab] = useState("draw");
|
||||||
|
const [isSignImg, setIsSignImg] = useState("");
|
||||||
|
const [signValue, setSignValue] = useState("");
|
||||||
|
const [textWidth, setTextWidth] = useState(null);
|
||||||
|
const [textHeight, setTextHeight] = useState(null);
|
||||||
|
const [signatureType, setSignatureType] = useState("draw");
|
||||||
|
const fontOptions = [
|
||||||
|
{ value: "Fasthand" },
|
||||||
|
{ value: "Dancing Script" },
|
||||||
|
{ value: "Cedarville Cursive" },
|
||||||
|
{ value: "Delicious Handrawn" }
|
||||||
|
// Add more font options as needed
|
||||||
|
];
|
||||||
|
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
|
||||||
|
|
||||||
|
const senderUser =
|
||||||
|
localStorage.getItem(
|
||||||
|
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||||
|
) &&
|
||||||
|
localStorage.getItem(
|
||||||
|
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||||
|
);
|
||||||
|
const jsonSender = JSON.parse(senderUser);
|
||||||
|
const currentUserName = jsonSender && jsonSender.name;
|
||||||
|
|
||||||
|
//function for clear signature image
|
||||||
|
const handleClear = () => {
|
||||||
|
setCurrWidgetsDetails({});
|
||||||
|
if (isTab === "draw") {
|
||||||
|
if (canvasRef.current) {
|
||||||
|
canvasRef.current.clear();
|
||||||
|
} else if (isStamp) {
|
||||||
|
setImage("");
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSignImg("");
|
||||||
|
} else if (isTab === "uploadImage") {
|
||||||
|
setImage("");
|
||||||
|
}
|
||||||
|
// setIsInitial(false);
|
||||||
|
};
|
||||||
|
//function for set signature url
|
||||||
|
const handleSignatureChange = () => {
|
||||||
|
setSignature(canvasRef.current.toDataURL());
|
||||||
|
setIsSignImg(canvasRef.current.toDataURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
//save button component
|
||||||
|
const SaveBtn = () => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{(isTab === "draw" || isTab === "uploadImage") && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost mr-1 mt-[2px]"
|
||||||
|
onClick={() => handleClear()}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setCurrWidgetsDetails({});
|
||||||
|
if (!image) {
|
||||||
|
if (isTab === "mysignature") {
|
||||||
|
setIsSignImg("");
|
||||||
|
if (isInitial) {
|
||||||
|
onSaveSign(signatureType, "initials");
|
||||||
|
} else {
|
||||||
|
onSaveSign(null, "default");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isTab === "type") {
|
||||||
|
setIsSignImg("");
|
||||||
|
onSaveSign(null, false, textWidth, textHeight);
|
||||||
|
} else {
|
||||||
|
onSaveSign(signatureType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setPenColor("blue");
|
||||||
|
} else {
|
||||||
|
setIsSignImg("");
|
||||||
|
onSaveImage(signatureType);
|
||||||
|
}
|
||||||
|
setIsSignPad(false);
|
||||||
|
setIsInitial(false);
|
||||||
|
setIsImageSelect(false);
|
||||||
|
setIsDefaultSign(false);
|
||||||
|
setImage();
|
||||||
|
setIsTab("draw");
|
||||||
|
setSignValue("");
|
||||||
|
setIsStamp(false);
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
className={`${
|
||||||
|
isSignImg || image || isDefaultSign || textWidth
|
||||||
|
? ""
|
||||||
|
: "pointer-events-none"
|
||||||
|
} op-btn op-btn-primary shadow-lg`}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
|
||||||
|
useEffect(() => {
|
||||||
|
if (currWidgetsDetails && canvasRef.current) {
|
||||||
|
const isWidgetType = currWidgetsDetails?.type;
|
||||||
|
const signatureType = currWidgetsDetails?.signatureType;
|
||||||
|
const url = currWidgetsDetails?.SignUrl;
|
||||||
|
|
||||||
|
//checking widget type and draw type signature url
|
||||||
|
if (isInitial) {
|
||||||
|
if (isWidgetType === "initials" && signatureType === "draw" && url) {
|
||||||
|
canvasRef.current.fromDataURL(url);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
isWidgetType === "signature" &&
|
||||||
|
signatureType === "draw" &&
|
||||||
|
url
|
||||||
|
) {
|
||||||
|
canvasRef.current.fromDataURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmedName = currentUserName.trim();
|
||||||
|
const firstCharacter = trimmedName.charAt(0);
|
||||||
|
const userName = isInitial ? firstCharacter : currentUserName;
|
||||||
|
setSignValue(userName);
|
||||||
|
setFontSelect("Fasthand");
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isSignPad]);
|
||||||
|
useEffect(() => {
|
||||||
|
const loadFont = async () => {
|
||||||
|
try {
|
||||||
|
await document.fonts.load(`20px ${fontSelect}`);
|
||||||
|
const selectFontSTyle = fontOptions.find(
|
||||||
|
(font) => font.value === fontSelect
|
||||||
|
);
|
||||||
|
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading font:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadFont();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [fontSelect]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Load the default signature after the component mounts
|
||||||
|
if (canvasRef.current) {
|
||||||
|
canvasRef.current.fromDataURL(isSignImg);
|
||||||
|
}
|
||||||
|
if (isTab === "type") {
|
||||||
|
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||||
|
const firstCharacter = trimmedName.charAt(0);
|
||||||
|
const userName = isInitial ? firstCharacter : signValue;
|
||||||
|
setSignValue(userName);
|
||||||
|
convertToImg(fontSelect, userName);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isTab]);
|
||||||
|
|
||||||
|
//function for convert input text value in image
|
||||||
|
const convertToImg = async (fontStyle, text, color) => {
|
||||||
|
//get text content to convert in image
|
||||||
|
const textContent = text;
|
||||||
|
const fontfamily = fontStyle
|
||||||
|
? fontStyle
|
||||||
|
: fontSelect
|
||||||
|
? fontSelect
|
||||||
|
: "Fasthand";
|
||||||
|
|
||||||
|
//creating span for getting text content width
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.textContent = textContent;
|
||||||
|
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
||||||
|
span.style.color = color ? color : penColor;
|
||||||
|
span.style.display = "hidden";
|
||||||
|
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
||||||
|
|
||||||
|
//create canvas to render text in canvas and convert in image
|
||||||
|
const canvasElement = document.createElement("canvas");
|
||||||
|
// Draw the text content on the canvas
|
||||||
|
const ctx = canvasElement.getContext("2d");
|
||||||
|
const pixelRatio = window.devicePixelRatio || 1;
|
||||||
|
const width = span.offsetWidth;
|
||||||
|
const height = span.offsetHeight;
|
||||||
|
setTextWidth(width);
|
||||||
|
setTextHeight(height);
|
||||||
|
const font = span.style["font"];
|
||||||
|
// Set the canvas dimensions to match the span
|
||||||
|
canvasElement.width = width * pixelRatio;
|
||||||
|
canvasElement.height = height * pixelRatio;
|
||||||
|
|
||||||
|
// You can customize text styles if needed
|
||||||
|
ctx.font = font;
|
||||||
|
ctx.fillStyle = color ? color : penColor; // Set the text color
|
||||||
|
ctx.textAlign = "center";
|
||||||
|
ctx.textBaseline = "middle";
|
||||||
|
ctx.scale(pixelRatio, pixelRatio);
|
||||||
|
// Draw the content of the span onto the canvas
|
||||||
|
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
|
||||||
|
//remove span tag
|
||||||
|
document.body.removeChild(span);
|
||||||
|
// Convert the canvas to image data
|
||||||
|
const dataUrl = canvasElement.toDataURL("image/png");
|
||||||
|
setSignature(dataUrl);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PenColorComponent = (props) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row items-start">
|
||||||
|
{allColor.map((data, key) => {
|
||||||
|
return (
|
||||||
|
<i
|
||||||
|
style={{
|
||||||
|
margin: "5px",
|
||||||
|
color: data,
|
||||||
|
borderBottom:
|
||||||
|
key === 0 && penColor === "blue"
|
||||||
|
? "2px solid blue"
|
||||||
|
: key === 1 && penColor === "red"
|
||||||
|
? "2px solid red"
|
||||||
|
: key === 2 && penColor === "black"
|
||||||
|
? "2px solid black"
|
||||||
|
: "2px solid white"
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
props?.convertToImg &&
|
||||||
|
props?.convertToImg(fontSelect, signValue, data);
|
||||||
|
if (key === 0) {
|
||||||
|
setPenColor("blue");
|
||||||
|
} else if (key === 1) {
|
||||||
|
setPenColor("red");
|
||||||
|
} else if (key === 2) {
|
||||||
|
setPenColor("black");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
key={key}
|
||||||
|
className="fa solid fa-pen-nib"
|
||||||
|
width={20}
|
||||||
|
height={20}
|
||||||
|
></i>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isSignPad && (
|
||||||
|
<div className="op-modal op-modal-open">
|
||||||
|
<div className="op-modal-box px-[13px] pt-2 pb-0">
|
||||||
|
<div className="flex justify-between text-base-content items-center">
|
||||||
|
<div className="text-[1.2rem]">
|
||||||
|
<div className="flex flex-row justify-between mt-[3px]">
|
||||||
|
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||||
|
{isStamp ? (
|
||||||
|
<span className="op-link-primary op-link">
|
||||||
|
{widgetType === "image"
|
||||||
|
? "Upload image"
|
||||||
|
: "Upload stamp image"}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
setIsDefaultSign(false);
|
||||||
|
setIsImageSelect(false);
|
||||||
|
setIsTab("draw");
|
||||||
|
setImage();
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isTab === "draw"
|
||||||
|
? "op-link-primary"
|
||||||
|
: "no-underline"
|
||||||
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
|
>
|
||||||
|
Draw
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
setIsDefaultSign(false);
|
||||||
|
setIsImageSelect(true);
|
||||||
|
setIsTab("uploadImage");
|
||||||
|
setSignatureType("");
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isTab === "uploadImage"
|
||||||
|
? "op-link-primary"
|
||||||
|
: "no-underline"
|
||||||
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
|
>
|
||||||
|
Upload Image
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
setIsDefaultSign(false);
|
||||||
|
setIsImageSelect(false);
|
||||||
|
setIsTab("type");
|
||||||
|
setSignatureType("");
|
||||||
|
setImage();
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isTab === "type"
|
||||||
|
? "op-link-primary"
|
||||||
|
: "no-underline"
|
||||||
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
|
>
|
||||||
|
Type
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{!isInitial && defaultSign ? (
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
setIsDefaultSign(true);
|
||||||
|
setIsImageSelect(true);
|
||||||
|
setIsTab("mysignature");
|
||||||
|
setSignatureType("");
|
||||||
|
setImage();
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isTab === "mysignature"
|
||||||
|
? "op-link-primary"
|
||||||
|
: "no-underline"
|
||||||
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
|
>
|
||||||
|
My Signature
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
isInitial &&
|
||||||
|
myInitial && (
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
onClick={() => {
|
||||||
|
setIsDefaultSign(true);
|
||||||
|
setIsImageSelect(true);
|
||||||
|
setIsTab("mysignature");
|
||||||
|
setSignatureType("");
|
||||||
|
setImage();
|
||||||
|
}}
|
||||||
|
className={`${
|
||||||
|
isTab === "mysignature"
|
||||||
|
? "op-link-primary"
|
||||||
|
: "no-underline"
|
||||||
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
|
>
|
||||||
|
My Initials
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="text-[1.5rem] cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setPenColor("blue");
|
||||||
|
setIsSignPad(false);
|
||||||
|
setIsInitial(false);
|
||||||
|
setIsImageSelect(false);
|
||||||
|
setIsDefaultSign(false);
|
||||||
|
setImage();
|
||||||
|
setIsTab("draw");
|
||||||
|
setSignatureType("draw");
|
||||||
|
setSignValue("");
|
||||||
|
setIsStamp(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-[20px] h-full">
|
||||||
|
{isDefaultSign ? (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1.3px solid #007bff",
|
||||||
|
borderRadius: "2px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 6,
|
||||||
|
cursor: "pointer"
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="stamp img"
|
||||||
|
className="w-full h-full object-contain bg-white"
|
||||||
|
draggable="false"
|
||||||
|
src={isInitial ? myInitial : defaultSign}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<SaveBtn />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : isImageSelect || isStamp ? (
|
||||||
|
!image ? (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1.3px solid #007bff",
|
||||||
|
borderRadius: "2px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 6,
|
||||||
|
cursor: "pointer"
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||||
|
}
|
||||||
|
onClick={() => imageRef.current.click()}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={onImageChange}
|
||||||
|
className="filetype"
|
||||||
|
accept="image/png,image/jpeg"
|
||||||
|
ref={imageRef}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
|
||||||
|
<div className="uploadImg">Upload</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1.3px solid #007bff",
|
||||||
|
borderRadius: "2px",
|
||||||
|
marginBottom: 6,
|
||||||
|
overflow: "hidden"
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
isInitial
|
||||||
|
? "intialSignatureCanvas"
|
||||||
|
: "signatureCanvas"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt="print img"
|
||||||
|
ref={imageRef}
|
||||||
|
src={image.src}
|
||||||
|
draggable="false"
|
||||||
|
className=" object-contain h-full w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<SaveBtn />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
) : isTab === "type" ? (
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="signatureText">
|
||||||
|
{isInitial ? "Initials" : "Signature"}:
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
maxLength={isInitial ? 3 : 30}
|
||||||
|
style={{ fontFamily: fontSelect, color: penColor }}
|
||||||
|
type="text"
|
||||||
|
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||||
|
placeholder="Your signature"
|
||||||
|
value={signValue}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSignValue(e.target.value);
|
||||||
|
convertToImg(fontSelect, e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="fontOptionContainer">
|
||||||
|
{fontOptions.map((font, ind) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={ind}
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
fontFamily: font.value,
|
||||||
|
backgroundColor:
|
||||||
|
fontSelect === font.value && "rgb(206 225 247)"
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setFontSelect(font.value);
|
||||||
|
convertToImg(font.value, signValue);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: "5px 10px 5px 10px",
|
||||||
|
fontSize: "20px",
|
||||||
|
color: penColor
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{signValue ? signValue : "Your signature"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between mt-[10px]">
|
||||||
|
<PenColorComponent convertToImg={convertToImg} />
|
||||||
|
<SaveBtn />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<SignatureCanvas
|
||||||
|
ref={canvasRef}
|
||||||
|
penColor={penColor}
|
||||||
|
canvasProps={{
|
||||||
|
className: isInitial
|
||||||
|
? "intialSignatureCanvas"
|
||||||
|
: "signatureCanvas",
|
||||||
|
style: {
|
||||||
|
border: "1.6px solid #007bff",
|
||||||
|
borderRadius: "2px"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
backgroundColor="rgb(255, 255, 255)"
|
||||||
|
onEnd={() =>
|
||||||
|
handleSignatureChange(canvasRef.current.toDataURL())
|
||||||
|
}
|
||||||
|
dotSize={1}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row justify-between mt-[10px]">
|
||||||
|
<PenColorComponent />
|
||||||
|
<SaveBtn />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignPad;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import React from "react";
|
||||||
|
import "../../styles/signature.css";
|
||||||
|
function Signedby({ pdfDetails }) {
|
||||||
|
const getFirstLetter = (name) => {
|
||||||
|
const firstLetter = name.charAt(0);
|
||||||
|
return firstLetter;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="hidden md:block w-[180px] h-full bg-base-100">
|
||||||
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
|
Signed By
|
||||||
|
</div>
|
||||||
|
<div className="mt-[2px] bg-base-100">
|
||||||
|
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
|
||||||
|
<div className="bg-[#576081] flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1">
|
||||||
|
<span className="text-[12px] text-center font-bold text-white uppercase">
|
||||||
|
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
|
||||||
|
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Signedby;
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import React from "react";
|
||||||
|
import RecipientList from "./RecipientList";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
|
||||||
|
function SignerListPlace(props) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
|
<span>
|
||||||
|
{props.title ? props.title : "Recipients"}
|
||||||
|
<span className="text-xs mt-1 ml-0.5">
|
||||||
|
{props?.title === "Roles" && (
|
||||||
|
<>
|
||||||
|
<a data-tooltip-id="my-tooltip">
|
||||||
|
<sup>
|
||||||
|
<i className="fa-solid fa-question rounded-full border-[1.5px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
|
||||||
|
</sup>
|
||||||
|
</a>
|
||||||
|
<Tooltip id="my-tooltip" className="z-[100]">
|
||||||
|
<div className="max-w-[450px]">
|
||||||
|
<p className="font-bold">What are template roles?</p>
|
||||||
|
<p>
|
||||||
|
Begin by specifying each role needed for the completion of
|
||||||
|
the document. Think about the parties involved in the
|
||||||
|
signing process and what their responsibilities are.
|
||||||
|
Common roles include HR for internal documents, Customer
|
||||||
|
for agreements or Vendor for business agreements.{" "}
|
||||||
|
</p>
|
||||||
|
<p className="font-bold">
|
||||||
|
Why pre-attach users to some roles?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For roles that consistently involve the same individual
|
||||||
|
(e.g., the CEO's signature on employee offer
|
||||||
|
letters), you can pre-attach a user to a role within the
|
||||||
|
template. This step is optional but recommended for
|
||||||
|
efficiency and consistency across documents.
|
||||||
|
</p>
|
||||||
|
<p className="font-bold">
|
||||||
|
When do i specify the user attached to each role?
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When you create a document from your template, you'll
|
||||||
|
be prompted to attach users to each defined role. If a
|
||||||
|
role already has a user attached, this will be pre-filled,
|
||||||
|
but you can modify it as needed before sending out the
|
||||||
|
document.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-auto hide-scrollbar max-h-[180px]">
|
||||||
|
<RecipientList {...props} />
|
||||||
|
</div>
|
||||||
|
<div className="mx-1">
|
||||||
|
{props.handleAddSigner ? (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
data-tut="reactourAddbtn"
|
||||||
|
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||||
|
disabled={props?.isMailSend ? true : false}
|
||||||
|
onClick={() => props.handleAddSigner()}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i> Add role
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
role="button"
|
||||||
|
data-tut="reactourAddbtn"
|
||||||
|
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||||
|
disabled={props?.isMailSend ? true : false}
|
||||||
|
onClick={() => props.setIsAddSigner(true)}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i> Add recipients
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignerListPlace;
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import React from "react";
|
||||||
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
|
function VerifyEmail(props) {
|
||||||
|
return (
|
||||||
|
<dialog className="op-modal op-modal-open absolute z-[1999]">
|
||||||
|
<div className="md:w-[40%] w-[80%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
|
||||||
|
<h3 className="font-bold text-lg pt-[15px] px-[20px] text-base-content">
|
||||||
|
OTP verification
|
||||||
|
</h3>
|
||||||
|
{props.isVerifyModal ? (
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
props.setIsVerifyModal(false);
|
||||||
|
props.handleVerifyEmail(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="px-6 py-3 text-base-content">
|
||||||
|
<label className="mb-2">Enter OTP</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="tel"
|
||||||
|
pattern="[0-9]{4}"
|
||||||
|
className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||||
|
placeholder="Enter OTP received over email"
|
||||||
|
value={props.otp}
|
||||||
|
onChange={(e) => props.setOtp(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-6 my-3">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Verify
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary ml-2"
|
||||||
|
onClick={(e) => props.handleResend(e)}
|
||||||
|
>
|
||||||
|
Resend
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : props.otpLoader ? (
|
||||||
|
<div className="h-[150px] flex items-center justify-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-6 py-3 text-base-content">
|
||||||
|
<p className="mb-2 text-sm">Please verify your email !</p>
|
||||||
|
<div className="px-0 mt-3">
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
props.handleVerifyBtn();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send OTP
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
@@ -0,0 +1,404 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import RecipientList from "./RecipientList";
|
||||||
|
import { useDrag } from "react-dnd";
|
||||||
|
import WidgetList from "./WidgetList";
|
||||||
|
import {
|
||||||
|
radioButtonWidget,
|
||||||
|
textInputWidget,
|
||||||
|
textWidget,
|
||||||
|
widgets
|
||||||
|
} from "../../constant/Utils";
|
||||||
|
function WidgetComponent({
|
||||||
|
dragSignature,
|
||||||
|
signRef,
|
||||||
|
handleDivClick,
|
||||||
|
handleMouseLeave,
|
||||||
|
dragStamp,
|
||||||
|
isSignYourself,
|
||||||
|
addPositionOfSignature,
|
||||||
|
signersdata,
|
||||||
|
isSelectListId,
|
||||||
|
setSignerObjId,
|
||||||
|
setIsSelectId,
|
||||||
|
setContractName,
|
||||||
|
isSigners,
|
||||||
|
dataTut,
|
||||||
|
isMailSend,
|
||||||
|
handleAddSigner,
|
||||||
|
setUniqueId,
|
||||||
|
setRoleName,
|
||||||
|
handleDeleteUser,
|
||||||
|
signerPos,
|
||||||
|
handleRoleChange,
|
||||||
|
handleOnBlur,
|
||||||
|
title,
|
||||||
|
setSignersData,
|
||||||
|
sendInOrder,
|
||||||
|
isTemplateFlow,
|
||||||
|
setBlockColor,
|
||||||
|
blockColor,
|
||||||
|
setIsAddSigner
|
||||||
|
}) {
|
||||||
|
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||||
|
|
||||||
|
const [, dropdown] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 5,
|
||||||
|
text: "dropdown"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragDropdown: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, checkbox] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 6,
|
||||||
|
text: "checkbox"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragCheck: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, textInput] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 7,
|
||||||
|
text: textInputWidget
|
||||||
|
},
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragTextInput: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, initials] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 8,
|
||||||
|
text: "initials"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragInitial: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, name] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 9,
|
||||||
|
text: "name"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragName: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, company] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 10,
|
||||||
|
text: "company"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragCompany: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, jobTitle] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 11,
|
||||||
|
text: "job title"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragJobtitle: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, date] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 12,
|
||||||
|
text: "date"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragDate: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, image] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 13,
|
||||||
|
text: "image"
|
||||||
|
},
|
||||||
|
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragImage: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, email] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 14,
|
||||||
|
text: "email"
|
||||||
|
},
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragEmail: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, radioButton] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 15,
|
||||||
|
text: radioButtonWidget
|
||||||
|
},
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragRadiotton: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const [, text] = useDrag({
|
||||||
|
type: "BOX",
|
||||||
|
|
||||||
|
item: {
|
||||||
|
type: "BOX",
|
||||||
|
id: 16,
|
||||||
|
text: textWidget
|
||||||
|
},
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragText: !!monitor.isDragging()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const isMobile = window.innerWidth < 767;
|
||||||
|
const scrollContainerRef = useRef(null);
|
||||||
|
const [widget, setWidget] = useState([]);
|
||||||
|
|
||||||
|
const color = [
|
||||||
|
"#93a3db",
|
||||||
|
"#e6c3db",
|
||||||
|
"#c0e3bc",
|
||||||
|
"#bce3db",
|
||||||
|
"#b8ccdb",
|
||||||
|
"#ceb8db",
|
||||||
|
"#ffccff",
|
||||||
|
"#99ffcc",
|
||||||
|
"#cc99ff",
|
||||||
|
"#ffcc99",
|
||||||
|
"#66ccff",
|
||||||
|
"#ffffcc"
|
||||||
|
];
|
||||||
|
const handleModal = () => {
|
||||||
|
setIsSignersModal(!isSignersModal);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const widgetRef = [
|
||||||
|
dragSignature,
|
||||||
|
dragStamp,
|
||||||
|
initials,
|
||||||
|
name,
|
||||||
|
jobTitle,
|
||||||
|
company,
|
||||||
|
date,
|
||||||
|
text,
|
||||||
|
textInput,
|
||||||
|
checkbox,
|
||||||
|
dropdown,
|
||||||
|
radioButton,
|
||||||
|
image,
|
||||||
|
email
|
||||||
|
];
|
||||||
|
const getWidgetArray = widgets;
|
||||||
|
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
||||||
|
return { ...obj, ref: widgetRef[ind] };
|
||||||
|
});
|
||||||
|
|
||||||
|
setWidget(newUpdateSigner);
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const filterWidgets = widget.filter(
|
||||||
|
(data) =>
|
||||||
|
data.type !== "dropdown" &&
|
||||||
|
data.type !== radioButtonWidget &&
|
||||||
|
data.type !== textInputWidget
|
||||||
|
);
|
||||||
|
const textWidgetData = widget.filter((data) => data.type !== textWidget);
|
||||||
|
const updateWidgets = isSignYourself
|
||||||
|
? filterWidgets
|
||||||
|
: isTemplateFlow
|
||||||
|
? textWidgetData
|
||||||
|
: widget;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isMobile ? (
|
||||||
|
!isMailSend && (
|
||||||
|
<div
|
||||||
|
data-tut={dataTut}
|
||||||
|
id="navbar"
|
||||||
|
className="stickyfooter"
|
||||||
|
style={{ width: window.innerWidth + "px" }}
|
||||||
|
>
|
||||||
|
<div className="mx-1">
|
||||||
|
{isSigners && (
|
||||||
|
<div
|
||||||
|
data-tut={dataTut}
|
||||||
|
className="py-[10px] flex justify-center items-center op-card"
|
||||||
|
style={{
|
||||||
|
background: blockColor
|
||||||
|
? blockColor
|
||||||
|
: isSelectListId
|
||||||
|
? color[isSelectListId % color.length]
|
||||||
|
: color[0]
|
||||||
|
}}
|
||||||
|
onClick={() => handleModal()}
|
||||||
|
>
|
||||||
|
<span className="text-[13px] font-bold flex items-center">
|
||||||
|
{title ? title : "Recipient"}
|
||||||
|
{signersdata[isSelectListId]?.Role && (
|
||||||
|
<div>
|
||||||
|
{signersdata[isSelectListId]?.Name ? (
|
||||||
|
<>
|
||||||
|
:{" "}
|
||||||
|
{signersdata[isSelectListId]?.Name?.length > 12
|
||||||
|
? `${signersdata[isSelectListId].Name.slice(
|
||||||
|
0,
|
||||||
|
12
|
||||||
|
)}...`
|
||||||
|
: signersdata[isSelectListId]?.Name}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
:{" "}
|
||||||
|
{signersdata[isSelectListId]?.Role?.length > 12
|
||||||
|
? `${signersdata[isSelectListId].Role.slice(
|
||||||
|
0,
|
||||||
|
12
|
||||||
|
)}...`
|
||||||
|
: signersdata[isSelectListId]?.Role}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="ml-[6px] text-[16px]">
|
||||||
|
<i className="fa-solid fa-angle-down"></i>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="mx-1">
|
||||||
|
{handleAddSigner ? (
|
||||||
|
<div
|
||||||
|
data-tut="reactourAddbtn"
|
||||||
|
className="op-btn op-btn-accent w-full my-[2px]"
|
||||||
|
onClick={() => handleAddSigner()}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i>
|
||||||
|
<span>Add role</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
setIsAddSigner && (
|
||||||
|
<div
|
||||||
|
data-tut="reactourAddbtn"
|
||||||
|
className="op-btn op-btn-accent w-full my-[2px]"
|
||||||
|
onClick={() => setIsAddSigner(true)}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i>
|
||||||
|
<span>Add recipients</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
ref={scrollContainerRef}
|
||||||
|
className="bg-base-100 border-[2px] border-t-primary"
|
||||||
|
>
|
||||||
|
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
|
||||||
|
<WidgetList
|
||||||
|
updateWidgets={updateWidgets}
|
||||||
|
handleDivClick={handleDivClick}
|
||||||
|
handleMouseLeave={handleMouseLeave}
|
||||||
|
signRef={signRef}
|
||||||
|
marginLeft={5}
|
||||||
|
addPositionOfSignature={addPositionOfSignature}
|
||||||
|
isMobile={isMobile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
data-tut={dataTut}
|
||||||
|
className={`${
|
||||||
|
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
|
||||||
|
} hidden md:block w-[180px] h-full bg-base-100`}
|
||||||
|
>
|
||||||
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
|
<span>Fields</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center my-3">
|
||||||
|
<WidgetList
|
||||||
|
updateWidgets={updateWidgets}
|
||||||
|
handleDivClick={handleDivClick}
|
||||||
|
handleMouseLeave={handleMouseLeave}
|
||||||
|
signRef={signRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isSignersModal && (
|
||||||
|
<ModalUi
|
||||||
|
title={title ? title : "Recipients"}
|
||||||
|
isOpen={isSignersModal}
|
||||||
|
handleClose={handleModal}
|
||||||
|
>
|
||||||
|
{signersdata.length > 0 ? (
|
||||||
|
<div className="max-h-[600px] overflow-auto pb-1">
|
||||||
|
<RecipientList
|
||||||
|
signerPos={signerPos}
|
||||||
|
signersdata={signersdata}
|
||||||
|
isSelectListId={isSelectListId}
|
||||||
|
setSignerObjId={setSignerObjId}
|
||||||
|
setIsSelectId={setIsSelectId}
|
||||||
|
setContractName={setContractName}
|
||||||
|
setUniqueId={setUniqueId}
|
||||||
|
setRoleName={setRoleName}
|
||||||
|
handleDeleteUser={handleDeleteUser}
|
||||||
|
handleRoleChange={handleRoleChange}
|
||||||
|
handleOnBlur={handleOnBlur}
|
||||||
|
handleModal={handleModal}
|
||||||
|
sendInOrder={sendInOrder}
|
||||||
|
setSignersData={setSignersData}
|
||||||
|
setBlockColor={setBlockColor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className=" p-[20px] text-[15px] font-medium text-center">
|
||||||
|
Please add a {title ? title : "recipients"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ModalUi>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WidgetComponent;
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { getWidgetType } from "../../constant/Utils";
|
||||||
|
|
||||||
|
function WidgetList(props) {
|
||||||
|
return props.updateWidgets.map((item, ind) => {
|
||||||
|
return (
|
||||||
|
<div key={ind} className="mb-[5px]">
|
||||||
|
<div
|
||||||
|
className="select-none"
|
||||||
|
onClick={() => {
|
||||||
|
props.addPositionOfSignature &&
|
||||||
|
props.addPositionOfSignature("onclick", item);
|
||||||
|
}}
|
||||||
|
ref={(element) => {
|
||||||
|
if (!props.isMobile) {
|
||||||
|
item.ref(element);
|
||||||
|
if (element) {
|
||||||
|
if (props?.signRef) {
|
||||||
|
props.signRef.current = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseMove={props?.handleDivClick}
|
||||||
|
onMouseDown={() => {
|
||||||
|
props?.handleMouseLeave();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.ref && getWidgetType(item, props?.marginLeft)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WidgetList;
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import "../../styles/AddUser.css";
|
||||||
|
import RegexParser from "regex-parser";
|
||||||
|
import { textInputWidget } from "../../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
|
import Tooltip from "../../primitives/Tooltip";
|
||||||
|
|
||||||
|
const WidgetNameModal = (props) => {
|
||||||
|
const [formdata, setFormdata] = useState({
|
||||||
|
name: "",
|
||||||
|
defaultValue: "",
|
||||||
|
status: "required",
|
||||||
|
hint: "",
|
||||||
|
textvalidate: ""
|
||||||
|
});
|
||||||
|
const [isValid, setIsValid] = useState(true);
|
||||||
|
const statusArr = ["Required", "Optional"];
|
||||||
|
const inputOpt = ["text", "email", "number"];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.defaultdata) {
|
||||||
|
setFormdata({
|
||||||
|
name: props.defaultdata?.options?.name || props?.widgetName || "",
|
||||||
|
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||||
|
status: props.defaultdata?.options?.status || "required",
|
||||||
|
hint: props.defaultdata?.options?.hint || "",
|
||||||
|
textvalidate:
|
||||||
|
props.defaultdata?.options?.validation?.type === "regex"
|
||||||
|
? props.defaultdata?.options?.validation?.pattern
|
||||||
|
: props.defaultdata?.options?.validation?.type || ""
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setFormdata({
|
||||||
|
...formdata,
|
||||||
|
name: props.defaultdata?.options?.name || props?.widgetName || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [props.defaultdata, props.widgetName]);
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (props.handleData) {
|
||||||
|
props.handleData(formdata);
|
||||||
|
setFormdata({
|
||||||
|
name: "",
|
||||||
|
defaultValue: "",
|
||||||
|
status: "required",
|
||||||
|
hint: "",
|
||||||
|
textvalidate: ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleChange = (e) => {
|
||||||
|
if (e) {
|
||||||
|
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||||
|
} else {
|
||||||
|
setFormdata({ ...formdata, textvalidate: "" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handledefaultChange = (e) => {
|
||||||
|
if (formdata.textvalidate) {
|
||||||
|
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
|
||||||
|
const isValidate = regexObject?.test(e.target.value);
|
||||||
|
setIsValid(isValidate);
|
||||||
|
} else {
|
||||||
|
setIsValid(true);
|
||||||
|
}
|
||||||
|
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleValidation(type) {
|
||||||
|
switch (type) {
|
||||||
|
case "email":
|
||||||
|
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||||
|
case "number":
|
||||||
|
return "/^\\d+$/";
|
||||||
|
case "text":
|
||||||
|
return "/^[a-zA-Zs]+$/";
|
||||||
|
default:
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBlurRegex() {
|
||||||
|
if (!formdata.textvalidate) {
|
||||||
|
setFormdata({ ...formdata, textvalidate: "" });
|
||||||
|
} else {
|
||||||
|
if (formdata.defaultValue) {
|
||||||
|
const regexObject = RegexParser(
|
||||||
|
handleValidation(formdata.textvalidate)
|
||||||
|
);
|
||||||
|
const isValidate = regexObject?.test(formdata.defaultValue);
|
||||||
|
if (isValidate === false) {
|
||||||
|
setFormdata({ ...formdata, defaultValue: "" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ModalUi
|
||||||
|
isOpen={props.isOpen}
|
||||||
|
handleClose={props.handleClose && props.handleClose}
|
||||||
|
title={"Widget info"}
|
||||||
|
>
|
||||||
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
|
props.widgetName === textInputWidget) &&
|
||||||
|
!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Field validations are free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className={`${
|
||||||
|
props.defaultdata?.type === textInputWidget ||
|
||||||
|
props.widgetName === textInputWidget
|
||||||
|
? "pt-0"
|
||||||
|
: ""
|
||||||
|
} p-[20px] text-base-content`}
|
||||||
|
>
|
||||||
|
<div className="mb-[0.75rem] text-[13px]">
|
||||||
|
<label htmlFor="name">
|
||||||
|
Name
|
||||||
|
<span className="text-[red]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
name="name"
|
||||||
|
value={formdata.name}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
|
props.widgetName === textInputWidget) && (
|
||||||
|
<>
|
||||||
|
<div className="mb-[0.75rem]">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<label
|
||||||
|
htmlFor="textvalidate"
|
||||||
|
className={`${
|
||||||
|
!props.isSubscribe && isEnableSubscription
|
||||||
|
? "bg-opacity-50 pointer-events-none"
|
||||||
|
: ""
|
||||||
|
} text-[13px]`}
|
||||||
|
>
|
||||||
|
Validation
|
||||||
|
</label>
|
||||||
|
<Tooltip
|
||||||
|
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
|
||||||
|
/>
|
||||||
|
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`${
|
||||||
|
!props.isSubscribe && isEnableSubscription
|
||||||
|
? "bg-opacity-50 pointer-events-none"
|
||||||
|
: ""
|
||||||
|
} flex flex-row gap-[10px] mb-[0.5rem]`}
|
||||||
|
>
|
||||||
|
<div className="w-full relative group">
|
||||||
|
<input
|
||||||
|
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
|
||||||
|
name="textvalidate"
|
||||||
|
placeholder="Enter custom regular expression"
|
||||||
|
value={formdata.textvalidate}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
// onBlur={() => handleBlurRegex()}
|
||||||
|
/>
|
||||||
|
<select
|
||||||
|
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
|
||||||
|
name="textvalidate"
|
||||||
|
value={formdata.textvalidate}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
onBlur={() => handleBlurRegex()}
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
disabled={formdata?.textvalidate}
|
||||||
|
className="text-[13px]"
|
||||||
|
>
|
||||||
|
Select...
|
||||||
|
</option>
|
||||||
|
{inputOpt.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<option className="text-[13px]" key={ind} value={data}>
|
||||||
|
{data}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-[0.75rem]">
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
|
Default value
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
name="defaultValue"
|
||||||
|
value={formdata.defaultValue}
|
||||||
|
onChange={(e) => handledefaultChange(e)}
|
||||||
|
autoComplete="off"
|
||||||
|
onBlur={() => {
|
||||||
|
if (isValid === false) {
|
||||||
|
setFormdata({ ...formdata, defaultValue: "" });
|
||||||
|
setIsValid(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{isValid === false && (
|
||||||
|
<div
|
||||||
|
className="warning defaultvalueWarning"
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-exclamation-circle text-[15px]"
|
||||||
|
style={{ color: "#fab005" }}
|
||||||
|
></i>
|
||||||
|
invalid default value
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<div className="mb-[0.75rem]">
|
||||||
|
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||||
|
{statusArr.map((data, ind) => {
|
||||||
|
return (
|
||||||
|
<div key={ind} className="flex flex-row gap-[5px] items-center">
|
||||||
|
<input
|
||||||
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
|
type="radio"
|
||||||
|
name="status"
|
||||||
|
onChange={() =>
|
||||||
|
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||||
|
}
|
||||||
|
checked={
|
||||||
|
formdata.status.toLowerCase() === data.toLowerCase()
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className="text-[13px] font-medium">{data}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
|
props?.widgetName === textInputWidget) && (
|
||||||
|
<div className="mb-[0.75rem]">
|
||||||
|
<label htmlFor="hint" className="text-[13px]">
|
||||||
|
Hint
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
name="hint"
|
||||||
|
value={formdata.hint}
|
||||||
|
onChange={(e) => handleChange(e)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</ModalUi>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WidgetNameModal;
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import Alert from "../../../primitives/Alert";
|
||||||
|
import Loader from "../../../primitives/Loader";
|
||||||
|
|
||||||
|
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: parentFolderId
|
||||||
|
};
|
||||||
|
const [name, setName] = useState("");
|
||||||
|
const [folderList, setFolderList] = useState([]);
|
||||||
|
const [isAlert, setIsAlert] = useState(false);
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [selectedParent, setSelectedParent] = useState();
|
||||||
|
const [alert, setAlert] = useState({ type: "info", message: "" });
|
||||||
|
useEffect(() => {
|
||||||
|
fetchFolder();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const fetchFolder = async () => {
|
||||||
|
try {
|
||||||
|
const FolderQuery = new Parse.Query(folderCls);
|
||||||
|
if (parentFolderId) {
|
||||||
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
} else {
|
||||||
|
FolderQuery.doesNotExist("Folder");
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await FolderQuery.find();
|
||||||
|
if (res) {
|
||||||
|
const result = JSON.parse(JSON.stringify(res));
|
||||||
|
if (result) {
|
||||||
|
setFolderList(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Err ", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleCreateFolder = async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
handleLoader(true);
|
||||||
|
if (name) {
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
const exsitQuery = new Parse.Query(folderCls);
|
||||||
|
exsitQuery.equalTo("Name", name);
|
||||||
|
exsitQuery.equalTo("Type", "Folder");
|
||||||
|
exsitQuery.notEqualTo("IsArchive", true);
|
||||||
|
if (parentFolderId) {
|
||||||
|
exsitQuery.equalTo("Folder", folderPtr);
|
||||||
|
}
|
||||||
|
const templExist = await exsitQuery.first();
|
||||||
|
if (templExist) {
|
||||||
|
setAlert({ type: "danger", message: "Folder already exist!" });
|
||||||
|
setIsAlert(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsAlert(false);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
const template = new Parse.Object(folderCls);
|
||||||
|
template.set("Name", name);
|
||||||
|
template.set("Type", "Folder");
|
||||||
|
|
||||||
|
if (selectedParent) {
|
||||||
|
template.set("Folder", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: selectedParent
|
||||||
|
});
|
||||||
|
} else if (parentFolderId) {
|
||||||
|
template.set("Folder", folderPtr);
|
||||||
|
}
|
||||||
|
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||||
|
const res = await template.save();
|
||||||
|
if (res) {
|
||||||
|
handleLoader(false);
|
||||||
|
setAlert({
|
||||||
|
type: "success",
|
||||||
|
message: "Folder created successfully!"
|
||||||
|
});
|
||||||
|
setIsAlert(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsAlert(false);
|
||||||
|
}, 1000);
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleLoader(false);
|
||||||
|
setAlert({ type: "info", message: "Please fill folder name" });
|
||||||
|
setIsAlert(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsAlert(false);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleOptions = (e) => {
|
||||||
|
setSelectedParent(e.target.value);
|
||||||
|
};
|
||||||
|
const handleLoader = (status) => {
|
||||||
|
setIsLoader(status);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
|
||||||
|
<div id="createFolder" className="relative">
|
||||||
|
{isLoader && (
|
||||||
|
<div className="absolute h-full w-full flex justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<h1 className="text-base font-semibold">Create Folder</h1>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
Name<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">Parent Folder</label>
|
||||||
|
<select
|
||||||
|
value={selectedParent}
|
||||||
|
onChange={handleOptions}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
>
|
||||||
|
<option>select</option>
|
||||||
|
{folderList.length > 0 &&
|
||||||
|
folderList.map((x) => (
|
||||||
|
<option key={x.objectId} value={x.objectId}>
|
||||||
|
{x.Name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={handleCreateFolder}
|
||||||
|
disabled={isLoader}
|
||||||
|
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus mr-1"></i>
|
||||||
|
<span>Create</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateFolder;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import React, { useCallback, useMemo } from "react";
|
||||||
|
import { useDropScript } from "../../../hook/useScript";
|
||||||
|
|
||||||
|
const DROPBOX_APP_KEY = process.env.REACT_APP_DROPBOX_API_KEY; // App key
|
||||||
|
const DROPBOX_SDK_URL = "https://www.dropbox.com/static/api/2/dropins.js";
|
||||||
|
const DROPBOX_SCRIPT_ID = "dropboxjs";
|
||||||
|
|
||||||
|
export default function DropboxChooser({ children, onSuccess, onCancel }) {
|
||||||
|
useDropScript(DROPBOX_SDK_URL, {
|
||||||
|
attrs: {
|
||||||
|
id: DROPBOX_SCRIPT_ID,
|
||||||
|
"data-app-key": DROPBOX_APP_KEY
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = useMemo(
|
||||||
|
() => ({
|
||||||
|
// Required. Called when a user selects an item in the Chooser.
|
||||||
|
success: (files) => {
|
||||||
|
// console.log("success", files);
|
||||||
|
onSuccess && onSuccess(files);
|
||||||
|
},
|
||||||
|
// Optional. Called when the user closes the dialog without selecting a file
|
||||||
|
// and does not include any parameters.
|
||||||
|
cancel: () => {
|
||||||
|
console.log("cancel");
|
||||||
|
onCancel && onCancel();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Optional. "preview" (default) is a preview link to the document for sharing,
|
||||||
|
// "direct" is an expiring link to download the contents of the file. For more
|
||||||
|
linkType: "direct", // or "preview"
|
||||||
|
multiselect: false, // 是否支持多选
|
||||||
|
extensions: [".pdf"],
|
||||||
|
|
||||||
|
// Optional. A value of false (default) limits selection to files,
|
||||||
|
// while true allows the user to select both folders and files.
|
||||||
|
// You cannot specify `linkType: "direct"` when using `folderselect: true`.
|
||||||
|
folderselect: false // or true
|
||||||
|
}),
|
||||||
|
[onSuccess, onCancel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChoose = useCallback(
|
||||||
|
(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (window.Dropbox) {
|
||||||
|
window.Dropbox.choose(options);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[options]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClick={handleChoose}>
|
||||||
|
{children || (
|
||||||
|
<button className="px-2 py-[2px] text-2xl rounded border-[1px] text-blue-400 border-gray-300 w-full">
|
||||||
|
<i className="fa-brands fa-dropbox"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import CreateFolder from "./CreateFolder";
|
||||||
|
import ModalUi from "../../../primitives/ModalUi";
|
||||||
|
|
||||||
|
const FolderModal = (props) => {
|
||||||
|
const [clickFolder, setClickFolder] = useState("");
|
||||||
|
const [folderList, setFolderList] = useState([]);
|
||||||
|
const [tabList, setTabList] = useState([]);
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [isAdd, setIsAdd] = useState(false);
|
||||||
|
// below useEffect is called when user open popup
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isOpenModal) {
|
||||||
|
fetchFolder();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [props.isOpenModal]);
|
||||||
|
|
||||||
|
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
|
||||||
|
const fetchFolder = async (folderPtr) => {
|
||||||
|
setIsLoader(true);
|
||||||
|
try {
|
||||||
|
const FolderQuery = new Parse.Query(props.folderCls);
|
||||||
|
if (folderPtr) {
|
||||||
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
} else {
|
||||||
|
FolderQuery.doesNotExist("Folder");
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await FolderQuery.find();
|
||||||
|
if (res) {
|
||||||
|
const result = JSON.parse(JSON.stringify(res));
|
||||||
|
if (result) {
|
||||||
|
setFolderList(result);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
|
||||||
|
const handleSelect = (item) => {
|
||||||
|
setFolderList([]);
|
||||||
|
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
|
||||||
|
if (tabList.length > 0) {
|
||||||
|
const tab = tabList.some((x) => x.objectId === item.objectId);
|
||||||
|
if (!tab) {
|
||||||
|
setTabList((tabs) => [...tabs, item]);
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: props.folderCls,
|
||||||
|
objectId: item.objectId
|
||||||
|
};
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTabList((tabs) => [...tabs, item]);
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: props.folderCls,
|
||||||
|
objectId: item.objectId
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleSubmit` is used to pass folderPtr to parent component
|
||||||
|
const handleSubmit = () => {
|
||||||
|
let url = "OpenSign™ Drive";
|
||||||
|
tabList.forEach((t) => {
|
||||||
|
url = url + " / " + t.Name;
|
||||||
|
});
|
||||||
|
if (props.onSuccess) {
|
||||||
|
props.onSuccess(clickFolder);
|
||||||
|
}
|
||||||
|
// SetIsOpen(false);
|
||||||
|
props.setIsOpenMoveModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleCancel` is used to clear list of folder, close popup and folderUrl
|
||||||
|
const handleCancel = () => {
|
||||||
|
// SetIsOpen(false);
|
||||||
|
props.setIsOpenMoveModal(false);
|
||||||
|
setClickFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||||
|
const removeTabListItem = async (e, i) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// setEditable(false);
|
||||||
|
if (!isAdd) {
|
||||||
|
setIsLoader(true);
|
||||||
|
let folderPtr;
|
||||||
|
if (i) {
|
||||||
|
setFolderList([]);
|
||||||
|
let list = tabList.filter((itm, j) => {
|
||||||
|
if (j <= i) {
|
||||||
|
return itm;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _len = list.length - 1;
|
||||||
|
folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: props.folderCls,
|
||||||
|
objectId: list[_len].objectId
|
||||||
|
};
|
||||||
|
setTabList(list);
|
||||||
|
} else {
|
||||||
|
setClickFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
}
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleCreate` is used to open folder creation form in popup
|
||||||
|
const handleCreate = () => {
|
||||||
|
setIsAdd(!isAdd);
|
||||||
|
};
|
||||||
|
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||||
|
const handleAddFolder = (newFolder) => {
|
||||||
|
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
|
||||||
|
if (clickFolder && clickFolder.ObjectId) {
|
||||||
|
fetchFolder({
|
||||||
|
__type: "Pointer",
|
||||||
|
className: props.folderCls,
|
||||||
|
objectId: clickFolder.ObjectId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fetchFolder();
|
||||||
|
}
|
||||||
|
handleCreate();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="text-xs mt-2 ">
|
||||||
|
<ModalUi
|
||||||
|
title={"Select Folder"}
|
||||||
|
isOpen={props.isOpenModal}
|
||||||
|
handleClose={handleCancel}
|
||||||
|
>
|
||||||
|
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||||
|
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
title="OpenSign™ Drive"
|
||||||
|
onClick={(e) => removeTabListItem(e)}
|
||||||
|
>
|
||||||
|
OpenSign™ Drive /{" "}
|
||||||
|
</span>
|
||||||
|
{tabList &&
|
||||||
|
tabList.map((tab, i) => (
|
||||||
|
<React.Fragment key={`${tab.objectId}-${i}`}>
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
title={tab.Name}
|
||||||
|
onClick={(e) => removeTabListItem(e, i)}
|
||||||
|
>
|
||||||
|
{tab.Name}
|
||||||
|
</span>
|
||||||
|
{" / "}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 mb-3">
|
||||||
|
<div className="max-h-[210px] overflow-auto">
|
||||||
|
{!isAdd &&
|
||||||
|
folderList.length > 0 &&
|
||||||
|
folderList.map((folder) => (
|
||||||
|
<div
|
||||||
|
key={folder.Name}
|
||||||
|
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||||
|
onClick={() => handleSelect(folder)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<i
|
||||||
|
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-semibold">{folder.Name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{isAdd && (
|
||||||
|
<CreateFolder
|
||||||
|
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||||
|
folderCls={props.folderCls}
|
||||||
|
onSuccess={handleAddFolder}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isLoader && (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||||
|
<div
|
||||||
|
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||||
|
title="Save Here"
|
||||||
|
onClick={handleCreate}
|
||||||
|
>
|
||||||
|
{isAdd ? (
|
||||||
|
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||||
|
) : (
|
||||||
|
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="text-[30px] cursor-pointer"
|
||||||
|
title="Save Here"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
<i className="fas fa-save" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FolderModal;
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import CreateFolder from "./CreateFolder";
|
||||||
|
import ModalUi from "../../../primitives/ModalUi";
|
||||||
|
import Tooltip from "../../../primitives/Tooltip";
|
||||||
|
|
||||||
|
const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||||
|
const [isOpen, SetIsOpen] = useState(false);
|
||||||
|
const [clickFolder, setClickFolder] = useState("");
|
||||||
|
const [selectFolder, setSelectedFolder] = useState({});
|
||||||
|
const [folderList, setFolderList] = useState([]);
|
||||||
|
const [tabList, setTabList] = useState([]);
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [folderPath, setFolderPath] = useState("");
|
||||||
|
const [isAdd, setIsAdd] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
setIsAdd(false);
|
||||||
|
setClickFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
fetchFolder();
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [isOpen]);
|
||||||
|
// below useEffect is used to reset folder selection if user pass isReset = true from parent component
|
||||||
|
useEffect(() => {
|
||||||
|
if (isReset == true) {
|
||||||
|
handleReset();
|
||||||
|
}
|
||||||
|
}, [isReset]);
|
||||||
|
const handleReset = () => {
|
||||||
|
setFolderPath({});
|
||||||
|
setSelectedFolder("");
|
||||||
|
setClickFolder("");
|
||||||
|
setIsAdd(false);
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
};
|
||||||
|
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
|
||||||
|
const fetchFolder = async (folderPtr) => {
|
||||||
|
setIsLoader(true);
|
||||||
|
try {
|
||||||
|
const FolderQuery = new Parse.Query(folderCls);
|
||||||
|
if (folderPtr) {
|
||||||
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
} else {
|
||||||
|
FolderQuery.doesNotExist("Folder");
|
||||||
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await FolderQuery.find();
|
||||||
|
if (res) {
|
||||||
|
const result = JSON.parse(JSON.stringify(res));
|
||||||
|
if (result) {
|
||||||
|
setFolderList(result);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
|
||||||
|
const handleSelect = (item) => {
|
||||||
|
setFolderList([]);
|
||||||
|
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
|
||||||
|
if (tabList.length > 0) {
|
||||||
|
const tab = tabList.some((x) => x.objectId === item.objectId);
|
||||||
|
if (!tab) {
|
||||||
|
setTabList((tabs) => [...tabs, item]);
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: item.objectId
|
||||||
|
};
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTabList((tabs) => [...tabs, item]);
|
||||||
|
const folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: item.objectId
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleSubmit` is used to pass folderPtr to parent component
|
||||||
|
const handleSubmit = () => {
|
||||||
|
let url = "OpenSign™ Drive";
|
||||||
|
tabList.forEach((t) => {
|
||||||
|
url = url + " / " + t.Name;
|
||||||
|
});
|
||||||
|
setFolderPath(url);
|
||||||
|
setSelectedFolder(clickFolder);
|
||||||
|
if (onSuccess) {
|
||||||
|
onSuccess(clickFolder);
|
||||||
|
}
|
||||||
|
SetIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleCancel` is used to clear list of folder, close popup and folderUrl
|
||||||
|
const handleCancel = () => {
|
||||||
|
SetIsOpen(false);
|
||||||
|
setClickFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||||
|
const removeTabListItem = async (e, i) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// setEditable(false);
|
||||||
|
if (!isAdd) {
|
||||||
|
setIsLoader(true);
|
||||||
|
let folderPtr;
|
||||||
|
if (i) {
|
||||||
|
setFolderList([]);
|
||||||
|
let list = tabList.filter((itm, j) => {
|
||||||
|
if (j <= i) {
|
||||||
|
return itm;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _len = list.length - 1;
|
||||||
|
folderPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: list[_len].objectId
|
||||||
|
};
|
||||||
|
setTabList(list);
|
||||||
|
} else {
|
||||||
|
setClickFolder({});
|
||||||
|
setSelectedFolder({});
|
||||||
|
setFolderList([]);
|
||||||
|
setTabList([]);
|
||||||
|
}
|
||||||
|
fetchFolder(folderPtr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleCreate` is used to open folder creation form in popup
|
||||||
|
const handleCreate = () => {
|
||||||
|
setIsAdd(!isAdd);
|
||||||
|
};
|
||||||
|
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||||
|
const handleAddFolder = () => {
|
||||||
|
setFolderList([]);
|
||||||
|
if (clickFolder && clickFolder.ObjectId) {
|
||||||
|
fetchFolder({
|
||||||
|
__type: "Pointer",
|
||||||
|
className: folderCls,
|
||||||
|
objectId: clickFolder.ObjectId
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fetchFolder();
|
||||||
|
}
|
||||||
|
handleCreate();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="text-xs mt-2 ">
|
||||||
|
<div>
|
||||||
|
<label className="block">
|
||||||
|
Select Folder
|
||||||
|
{required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="relative max-w-sm">
|
||||||
|
<div
|
||||||
|
onClick={() => SetIsOpen(true)}
|
||||||
|
className=" cursor-pointer rounded px-[20px] py-[20px] bg-base-100 border-[1px] border-base-200 shadow flex max-w-sm gap-8 items-center"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<i
|
||||||
|
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||||
|
style={{ fontSize: "40px" }}
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
<div className="font-semibold ">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p>
|
||||||
|
{selectFolder && selectFolder.Name
|
||||||
|
? selectFolder.Name
|
||||||
|
: "OpenSign™ Drive"}
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
className="text-black text-sm"
|
||||||
|
// onClick={() => SetIsOpen(true)}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-pencil cursor-pointer"
|
||||||
|
title="Select Folder"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-gray-400">
|
||||||
|
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-2 right-1 cursor-pointer">
|
||||||
|
<Tooltip
|
||||||
|
message={
|
||||||
|
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModalUi
|
||||||
|
title={"Select Folder"}
|
||||||
|
isOpen={isOpen}
|
||||||
|
handleClose={handleCancel}
|
||||||
|
>
|
||||||
|
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||||
|
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
title="OpenSign™ Drive"
|
||||||
|
onClick={(e) => removeTabListItem(e)}
|
||||||
|
>
|
||||||
|
OpenSign™ Drive /{" "}
|
||||||
|
</span>
|
||||||
|
{tabList &&
|
||||||
|
tabList.map((tab, i) => (
|
||||||
|
<React.Fragment key={`${tab.objectId}-${i}`}>
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
title={tab.Name}
|
||||||
|
onClick={(e) => removeTabListItem(e, i)}
|
||||||
|
>
|
||||||
|
{tab.Name}
|
||||||
|
</span>
|
||||||
|
{" / "}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 mb-3">
|
||||||
|
<div className="max-h-[210px] overflow-auto">
|
||||||
|
{!isAdd &&
|
||||||
|
folderList.length > 0 &&
|
||||||
|
folderList.map((folder) => (
|
||||||
|
<div
|
||||||
|
key={folder.Name}
|
||||||
|
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||||
|
onClick={() => handleSelect(folder)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<i
|
||||||
|
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-semibold">{folder.Name}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{isAdd && (
|
||||||
|
<CreateFolder
|
||||||
|
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||||
|
folderCls={folderCls}
|
||||||
|
onSuccess={handleAddFolder}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isLoader && (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||||
|
<div
|
||||||
|
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||||
|
title="Save Here"
|
||||||
|
onClick={handleCreate}
|
||||||
|
>
|
||||||
|
{isAdd ? (
|
||||||
|
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||||
|
) : (
|
||||||
|
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="text-[30px] cursor-pointer"
|
||||||
|
title="Save Here"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
>
|
||||||
|
<i className="fas fa-save" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectFolder;
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import Parse from "parse";
|
||||||
|
import "../../../styles/AddUser.css";
|
||||||
|
import AsyncSelect from "react-select/async";
|
||||||
|
|
||||||
|
const SelectSigners = (props) => {
|
||||||
|
const [userList, setUserList] = useState([]);
|
||||||
|
const [selected, setSelected] = useState();
|
||||||
|
const [userData, setUserData] = useState({});
|
||||||
|
const [isError, setIsError] = useState(false);
|
||||||
|
// `handleOptions` is used to set just save from quick form to selected option in dropdown
|
||||||
|
const handleOptions = (item) => {
|
||||||
|
setSelected(item);
|
||||||
|
const userData = userList.find((x) => x.objectId === item.value);
|
||||||
|
if (userData) {
|
||||||
|
setUserData(userData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleAdd = () => {
|
||||||
|
if (userData && userData.objectId) {
|
||||||
|
props.details(userData);
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsError(true);
|
||||||
|
setTimeout(() => setIsError(false), 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadOptions = async (inputValue) => {
|
||||||
|
try {
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||||
|
contactbook.equalTo(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
if (inputValue.length > 1) {
|
||||||
|
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||||
|
}
|
||||||
|
contactbook.notEqualTo("IsDeleted", true);
|
||||||
|
const contactRes = await contactbook.find();
|
||||||
|
if (contactRes) {
|
||||||
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
|
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||||
|
//and filter signers from total signer's list which already present in document's signers list
|
||||||
|
const compareArrays = (res, signerObj) => {
|
||||||
|
return res.filter(
|
||||||
|
(item1) =>
|
||||||
|
!signerObj.find((item2) => item2.objectId === item1.objectId)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
//get update signer's List if signersdata is present
|
||||||
|
const updateSignersList =
|
||||||
|
props?.signersData && compareArrays(res, props?.signersData);
|
||||||
|
|
||||||
|
const result = updateSignersList ? updateSignersList : res;
|
||||||
|
setUserList(result);
|
||||||
|
return await result.map((item) => ({
|
||||||
|
label: item.Email,
|
||||||
|
value: item.objectId
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("err", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full px-[20px] py-[10px] text-base-content">
|
||||||
|
<div className="w-full mx-auto p-[8px]">
|
||||||
|
<div className="mb-0">
|
||||||
|
<label className="text-[14px] font-bold">Choose from contacts</label>
|
||||||
|
<AsyncSelect
|
||||||
|
cacheOptions
|
||||||
|
defaultOptions
|
||||||
|
value={selected}
|
||||||
|
loadingMessage={() => "Loading..."}
|
||||||
|
noOptionsMessage={() => "Contact not found"}
|
||||||
|
loadOptions={loadOptions}
|
||||||
|
onChange={handleOptions}
|
||||||
|
unstyled
|
||||||
|
classNames={{
|
||||||
|
control: () =>
|
||||||
|
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
|
||||||
|
valueContainer: () =>
|
||||||
|
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
|
||||||
|
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
|
||||||
|
multiValueLabel: () => "mb-[2px]",
|
||||||
|
menu: () =>
|
||||||
|
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
|
||||||
|
menuList: () => "shadow-md rounded-lg overflow-hidden",
|
||||||
|
option: () =>
|
||||||
|
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
|
||||||
|
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
className={`${
|
||||||
|
isError ? "text-[red]" : "text-transparent"
|
||||||
|
} text-[11px] ml-[6px] my-[2px]`}
|
||||||
|
>
|
||||||
|
Please select signer
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectSigners;
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import Select from "react-select";
|
||||||
|
import AddSigner from "../../AddSigner";
|
||||||
|
import Parse from "parse";
|
||||||
|
import Tooltip from "../../../primitives/Tooltip";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
function arrayMove(array, from, to) {
|
||||||
|
array = array.slice();
|
||||||
|
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddSignerModal = ({ isOpen, children }) => {
|
||||||
|
if (!isOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createPortal(
|
||||||
|
<div className="op-modal op-modal-open">
|
||||||
|
<div className="op-modal-box p-0 min-w-[90%] md:min-w-[500px] max-h-90 overflow-y-auto hide-scrollbar text-sm">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* react-sortable-hoc is depcreated not usable from react 18.x.x
|
||||||
|
* need to replace it with @dnd-kit
|
||||||
|
* code changes required
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SignersInput = (props) => {
|
||||||
|
const [state, setState] = useState(undefined);
|
||||||
|
// const [editFormData, setEditFormData] = useState([]);
|
||||||
|
const [selected, setSelected] = useState([]);
|
||||||
|
const [isModal, setIsModel] = useState(false);
|
||||||
|
const onChange = (selectedOptions) => setSelected(selectedOptions);
|
||||||
|
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const onSortEnd = ({ oldIndex, newIndex }) => {
|
||||||
|
const newValue = arrayMove(selected, oldIndex, newIndex);
|
||||||
|
setSelected(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GetSelectListData = async () => {
|
||||||
|
try {
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||||
|
contactbook.equalTo(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
contactbook.notEqualTo("IsDeleted", true);
|
||||||
|
const contactRes = await contactbook.find();
|
||||||
|
if (contactRes) {
|
||||||
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
// let _selected = [];
|
||||||
|
res.forEach((x) => {
|
||||||
|
let obj = {
|
||||||
|
label: x.Name,
|
||||||
|
value: x.objectId,
|
||||||
|
isChecked: true
|
||||||
|
};
|
||||||
|
|
||||||
|
list.push(obj);
|
||||||
|
});
|
||||||
|
setState(list);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("err", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
GetSelectListData();
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
if (props.isReset && props.isReset === true) {
|
||||||
|
setSelected([]);
|
||||||
|
}
|
||||||
|
}, [props.isReset]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selected && selected.length) {
|
||||||
|
let newData = [];
|
||||||
|
selected.forEach((x) => {
|
||||||
|
newData.push(x.value);
|
||||||
|
});
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(newData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, [selected]);
|
||||||
|
|
||||||
|
const handleModalCloseClick = () => {
|
||||||
|
setIsModel(false);
|
||||||
|
setModalIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
setModalIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
|
||||||
|
const handleNewDetails = (data) => {
|
||||||
|
setState([...state, data]);
|
||||||
|
if (selected.length > 0) {
|
||||||
|
setSelected([...selected, data]);
|
||||||
|
} else {
|
||||||
|
setSelected([data]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-xs mt-2 ">
|
||||||
|
<label className="block relative">
|
||||||
|
Signers
|
||||||
|
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
|
<span className="absolute ml-1 text-xs z-30">
|
||||||
|
<Tooltip
|
||||||
|
id={"signer-tooltip"}
|
||||||
|
message={
|
||||||
|
"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div className="flex gap-x-[5px]">
|
||||||
|
<div className="w-full">
|
||||||
|
<Select
|
||||||
|
onSortEnd={onSortEnd}
|
||||||
|
distance={4}
|
||||||
|
isMulti
|
||||||
|
options={state || []}
|
||||||
|
value={selected}
|
||||||
|
onChange={onChange}
|
||||||
|
closeMenuOnSelect={false}
|
||||||
|
required={props.required}
|
||||||
|
noOptionsMessage={() => "Contact not found"}
|
||||||
|
unstyled
|
||||||
|
classNames={{
|
||||||
|
control: () =>
|
||||||
|
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
|
||||||
|
valueContainer: () =>
|
||||||
|
"flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]",
|
||||||
|
multiValue: () => "op-badge op-badge-primary h-full text-[11px]",
|
||||||
|
multiValueLabel: () => "mb-[2px]",
|
||||||
|
menu: () =>
|
||||||
|
"mt-1 shadow-md rounded-lg bg-base-200 text-base-content",
|
||||||
|
menuList: () => "shadow-md rounded-lg overflow-hidden",
|
||||||
|
option: () =>
|
||||||
|
"bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2",
|
||||||
|
noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setIsModel(true);
|
||||||
|
openModal();
|
||||||
|
}}
|
||||||
|
className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[38px] min-w-[48px] flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<i className="fas fa-plus"></i>
|
||||||
|
</div>
|
||||||
|
<AddSignerModal isOpen={modalIsOpen}>
|
||||||
|
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||||
|
Add Signer
|
||||||
|
</h3>
|
||||||
|
<button
|
||||||
|
onClick={handleModalCloseClick}
|
||||||
|
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
{isModal && (
|
||||||
|
<AddSigner
|
||||||
|
valueKey={"objectId"}
|
||||||
|
displayKey={"Name"}
|
||||||
|
details={handleNewDetails}
|
||||||
|
closePopup={handleModalCloseClick}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AddSignerModal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignersInput;
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { findContact } from "../../../constant/Utils";
|
||||||
|
const SuggestionInput = (props) => {
|
||||||
|
const [inputValue, setInputValue] = useState(props?.value || "");
|
||||||
|
const [suggestions, setSuggestions] = useState([]);
|
||||||
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener("mousedown", Clickout);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", Clickout);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const Clickout = (event) => {
|
||||||
|
if (ref.current && !ref.current.contains(event.target)) {
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// create debounce to avoid unnecessay api calls
|
||||||
|
useEffect(() => {
|
||||||
|
let timer;
|
||||||
|
if (inputValue) {
|
||||||
|
if (timer) clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
(async () => {
|
||||||
|
const res = await findContact(inputValue);
|
||||||
|
if (res?.length > 0) {
|
||||||
|
setSuggestions(res);
|
||||||
|
setShowSuggestions(true);
|
||||||
|
} else {
|
||||||
|
setSuggestions(res);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [inputValue]);
|
||||||
|
|
||||||
|
const handleInputChange = async (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setInputValue(value?.toLowerCase());
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.trim() === "") {
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleSuggestionClick = (suggestion) => {
|
||||||
|
setInputValue(suggestion.Email);
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(suggestion);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center relative text-base-content">
|
||||||
|
<input
|
||||||
|
type={props?.type || "text"}
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Email..."
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
|
||||||
|
required={props.required}
|
||||||
|
/>
|
||||||
|
{showSuggestions && (
|
||||||
|
<ul
|
||||||
|
ref={ref}
|
||||||
|
className="absolute z-50 left-0 top-[2.55rem] w-full max-h-[100px] overflow-auto bg-base-200 border border-gray-300 rounded shadow-md"
|
||||||
|
>
|
||||||
|
{suggestions.map((suggestion, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="py-2 px-2 w-full text-sm cursor-pointer hover:bg-base-300"
|
||||||
|
onClick={() => handleSuggestionClick(suggestion)}
|
||||||
|
>
|
||||||
|
{suggestion.Email}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SuggestionInput;
|
||||||
@@ -2,20 +2,28 @@ import React from "react";
|
|||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
|
|
||||||
const Menu = ({ item, isOpen, closeSidebar }) => {
|
const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||||
return (
|
return (
|
||||||
<li key={item.title} role="none">
|
<li key={item.title} role="none" className="my-0.5">
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`/${item.pageType}/${item.objectId}`}
|
to={
|
||||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
|
item.pageType
|
||||||
onClick={closeSidebar}
|
? `/${item.pageType}/${item.objectId}`
|
||||||
tabIndex={isOpen ? 0 : -1}
|
: `/${item.objectId}`
|
||||||
role="menuitem"
|
}
|
||||||
>
|
className={({ isActive }) =>
|
||||||
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
`${
|
||||||
<span className="ml-3 lg:ml-4">{item.title}</span>
|
isActive ? " bg-base-300 text-base-content" : ""
|
||||||
</NavLink>
|
} flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||||
</li>
|
}
|
||||||
)
|
onClick={closeSidebar}
|
||||||
}
|
tabIndex={isOpen ? 0 : -1}
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
||||||
|
<span className="ml-3 lg:ml-4">{item.title}</span>
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Menu;
|
export default Menu;
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
import Menu from './Menu';
|
import Menu from "./Menu";
|
||||||
import Submenu from "./SubMenu";
|
import Submenu from "./SubMenu";
|
||||||
import SocialMedia from './SocialMedia';
|
import SocialMedia from "./SocialMedia";
|
||||||
|
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import dp from "../../assets/images/dp.png";
|
import dp from "../../assets/images/dp.png";
|
||||||
|
|
||||||
const Sidebar = ({ isOpen, closeSidebar }) => {
|
const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||||
const [menuList, setmenuList] = useState([]);
|
const [menuList, setmenuList] = useState([]);
|
||||||
|
const [submenuOpen, setSubmenuOpen] = useState(false);
|
||||||
let username = localStorage.getItem("username");
|
let username = localStorage.getItem("username");
|
||||||
const image = localStorage.getItem("profileImg") || dp;
|
const image = localStorage.getItem("profileImg") || dp;
|
||||||
const tenantname = localStorage.getItem("TenantName");
|
const tenantname = localStorage.getItem("TenantName");
|
||||||
@@ -20,16 +21,10 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const menuItem = async () => {
|
const menuItem = async () => {
|
||||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Parse.serverURL = parseBaseUrl;
|
|
||||||
Parse.initialize(parseAppId);
|
|
||||||
var sideMenu = Parse.Object.extend("w_menu");
|
var sideMenu = Parse.Object.extend("w_menu");
|
||||||
var query = new Parse.Query(sideMenu);
|
var query = new Parse.Query(sideMenu);
|
||||||
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
|
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
|
||||||
|
|
||||||
const results = await query.first();
|
const results = await query.first();
|
||||||
const resultjson = results.toJSON();
|
const resultjson = results.toJSON();
|
||||||
let result = resultjson;
|
let result = resultjson;
|
||||||
@@ -37,38 +32,70 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Problem", e);
|
console.error("Problem", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleSubmenu = (title) => {
|
||||||
|
setSubmenuOpen({ [title]: !submenuOpen[title] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuItem = () => {
|
||||||
|
closeSidebar();
|
||||||
|
setSubmenuOpen({});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
className={`${isOpen ? "block" : "hidden"
|
className={`absolute md:relative bg-base-100 h-screen overflow-y-auto transition-all z-[100] hide-scrollbar
|
||||||
} bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 ${isOpen ? "translate-x-0" : "-translate-x-full"
|
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
||||||
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
|
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
|
||||||
<img className="w-full h-full object-cover" src={image} alt="Profile" />
|
<img
|
||||||
|
className="w-full h-full object-contain"
|
||||||
|
src={image}
|
||||||
|
alt="Profile"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-[14px] font-bold">{username}</p>
|
<p className="text-[14px] font-bold text-base-content">{username}</p>
|
||||||
<p className={`text-[12px] ${tenantname ? "mt-2" : ""}`}>
|
<p
|
||||||
|
className={`text-[12px] text-base-content ${
|
||||||
|
tenantname ? "mt-2" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
{tenantname}
|
{tenantname}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav aria-label="OpenSign Sidebar Navigation">
|
<nav
|
||||||
<ul className="text-sm" role="menubar" aria-label="OpenSign Sidebar Navigation">
|
className="op-menu op-menu-sm"
|
||||||
{menuList.map((item) => (
|
aria-label="OpenSign Sidebar Navigation"
|
||||||
|
>
|
||||||
|
<ul
|
||||||
|
className="text-sm"
|
||||||
|
role="menubar"
|
||||||
|
aria-label="OpenSign Sidebar Navigation"
|
||||||
|
>
|
||||||
|
{menuList.map((item) =>
|
||||||
!item.children ? (
|
!item.children ? (
|
||||||
<Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
|
<Menu
|
||||||
|
key={item.title}
|
||||||
|
item={item}
|
||||||
|
isOpen={isOpen}
|
||||||
|
closeSidebar={handleMenuItem}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
|
<Submenu
|
||||||
|
key={item.title}
|
||||||
|
item={item}
|
||||||
|
closeSidebar={closeSidebar}
|
||||||
|
toggleSubmenu={toggleSubmenu}
|
||||||
|
submenuOpen={submenuOpen}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
))}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
|
<footer className="mt-4 flex justify-center items-center text-[25px] text-base-content gap-3">
|
||||||
<SocialMedia />
|
<SocialMedia />
|
||||||
</footer>
|
</footer>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { NavLink } from "react-router-dom";
|
import { NavLink } from "react-router-dom";
|
||||||
|
|
||||||
const Submenu = ({ item, closeSidebar }) => {
|
const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||||
const [submenuOpen, setSubmenuOpen] = useState(false);
|
|
||||||
const { title, icon, children } = item;
|
const { title, icon, children } = item;
|
||||||
|
|
||||||
const toggleSubmenu = () => {
|
|
||||||
setSubmenuOpen(!submenuOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li role="none">
|
<li role="none" className="my-0.5">
|
||||||
<button
|
<button
|
||||||
onClick={toggleSubmenu}
|
onClick={() => toggleSubmenu(item.title)}
|
||||||
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
|
className="flex items-center text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none "
|
||||||
aria-expanded={submenuOpen}
|
aria-expanded={submenuOpen}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-controls={`submenu-${title}`}
|
aria-controls={`submenu-${title}`}
|
||||||
@@ -22,24 +16,38 @@ const Submenu = ({ item, closeSidebar }) => {
|
|||||||
<div className="flex justify-between items-center w-full">
|
<div className="flex justify-between items-center w-full">
|
||||||
<span className="ml-3 lg:ml-4">{title}</span>
|
<span className="ml-3 lg:ml-4">{title}</span>
|
||||||
<i
|
<i
|
||||||
className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
className={`${
|
||||||
}`}
|
submenuOpen[item.title]
|
||||||
|
? "fa-solid fa-angle-down"
|
||||||
|
: "fa-solid fa-angle-right"
|
||||||
|
}`}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{submenuOpen && (
|
{submenuOpen[item.title] && (
|
||||||
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
|
<ul id={`submenu-${title}`} role="menu" aria-label={`${title} submenu`}>
|
||||||
{children.map((childItem) => (
|
{children.map((childItem) => (
|
||||||
<li key={childItem.title} role="none">
|
<li key={childItem.title} role="none" className="my-0.5">
|
||||||
<NavLink
|
<NavLink
|
||||||
to={`/${childItem.pageType}/${childItem.objectId}`}
|
to={
|
||||||
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
|
childItem.pageType
|
||||||
|
? `/${childItem.pageType}/${childItem.objectId}`
|
||||||
|
: `/${childItem.objectId}`
|
||||||
|
}
|
||||||
|
className={({ isActive }) =>
|
||||||
|
`${
|
||||||
|
isActive ? "bg-base-300 text-base-content" : ""
|
||||||
|
} flex items-center text-left pl-6 md:pl-8 py-2 text-sm cursor-pointer text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none`
|
||||||
|
}
|
||||||
onClick={closeSidebar}
|
onClick={closeSidebar}
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
tabIndex={submenuOpen ? 0 : -1}
|
tabIndex={submenuOpen ? 0 : -1}
|
||||||
>
|
>
|
||||||
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
|
<i
|
||||||
|
className={`${childItem.icon} text-[18px]`}
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
<span className="ml-3 lg:ml-4">{childItem.title}</span>
|
<span className="ml-3 lg:ml-4">{childItem.title}</span>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ export const appInfo = {
|
|||||||
appTitle: "contracts",
|
appTitle: "contracts",
|
||||||
applogo: logo,
|
applogo: logo,
|
||||||
appname: "contracts",
|
appname: "contracts",
|
||||||
baseurl: process.env.REACT_APP_SERVERURL,
|
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
|
||||||
|
baseUrl: process.env.REACT_APP_SERVERURL
|
||||||
|
? process.env.REACT_APP_SERVERURL
|
||||||
|
: window.location.origin + "/api/app",
|
||||||
defaultRole: "contracts_User",
|
defaultRole: "contracts_User",
|
||||||
fbAppId: process.env.REACT_APP_FBAPPID
|
fbAppId: process.env.REACT_APP_FBAPPID
|
||||||
? `${process.env.REACT_APP_FBAPPID}`
|
? `${process.env.REACT_APP_FBAPPID}`
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export const contactCls = "contracts_Contactbook";
|
||||||
|
export const templateCls = "contracts_Template";
|
||||||
|
export const documentCls = "contracts_Document";
|
||||||
|
export const themeColor = "#47a3ad";
|
||||||
|
export const iconColor = "#686968";
|
||||||
|
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Parse from "parse";
|
const parseAppId = process.env.REACT_APP_APPID
|
||||||
const parseAppId = localStorage.getItem("AppID12");
|
? process.env.REACT_APP_APPID
|
||||||
const serverUrl = localStorage.getItem("BaseUrl12");
|
: "opensign";
|
||||||
Parse.serverURL = serverUrl;
|
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||||
Parse.initialize(parseAppId);
|
? process.env.REACT_APP_SERVERURL
|
||||||
|
: window.location.origin + "/api/app";
|
||||||
|
|
||||||
export const SaveFileSize = async (size, imageUrl) => {
|
export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||||
//checking server url and save file's size
|
//checking server url and save file's size
|
||||||
|
const tenantPtr = {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "partners_Tenant",
|
||||||
|
objectId: tenantId
|
||||||
|
};
|
||||||
|
const _tenantPtr = JSON.stringify(tenantPtr);
|
||||||
try {
|
try {
|
||||||
const response = await axios
|
const res = await axios.get(
|
||||||
.get(`${serverUrl}classes/partners_TenantCredits`, {
|
`${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`,
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": parseAppId
|
"X-Parse-Application-Id": parseAppId
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then((result) => {
|
);
|
||||||
const res = result.data;
|
const response = res.data.results;
|
||||||
// console.log("res", res);
|
|
||||||
|
|
||||||
return res.results;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
|
||||||
let data;
|
let data;
|
||||||
// console.log("response", response);
|
// console.log("response", response);
|
||||||
if (response && response.length > 0) {
|
if (response && response.length > 0) {
|
||||||
@@ -33,69 +33,48 @@ export const SaveFileSize = async (size, imageUrl) => {
|
|||||||
? response[0].usedStorage + size
|
? response[0].usedStorage + size
|
||||||
: size
|
: size
|
||||||
};
|
};
|
||||||
await axios
|
await axios.put(
|
||||||
.put(
|
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
|
||||||
`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`,
|
data,
|
||||||
data,
|
{
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
// const res = result.data;
|
|
||||||
// console.log("save res", res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
data = {
|
|
||||||
usedStorage: size
|
|
||||||
};
|
|
||||||
await axios
|
|
||||||
.post(`${serverUrl}classes/partners_TenantCredits`, data, {
|
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": parseAppId
|
"X-Parse-Application-Id": parseAppId
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then(() => {
|
);
|
||||||
// const res = result.data;
|
} else {
|
||||||
// console.log("res", res);
|
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
||||||
})
|
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
|
||||||
.catch((err) => {
|
headers: {
|
||||||
console.log("axois err ", err);
|
"Content-Type": "application/json",
|
||||||
});
|
"X-Parse-Application-Id": parseAppId
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.log("org app error", e);
|
console.log("err in save usage", err);
|
||||||
}
|
}
|
||||||
saveDataFile(size, imageUrl);
|
saveDataFile(size, imageUrl, tenantPtr);
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for save fileUrl and file size in particular client db class partners_DataFiles
|
//function for save fileUrl and file size in particular client db class partners_DataFiles
|
||||||
const saveDataFile = async (size, imageUrl) => {
|
const saveDataFile = async (size, imageUrl, tenantPtr) => {
|
||||||
const data = {
|
const data = {
|
||||||
FileUrl: imageUrl,
|
FileUrl: imageUrl,
|
||||||
FileSize: size
|
FileSize: size,
|
||||||
|
TenantPtr: tenantPtr
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log("data save",file, data)
|
// console.log("data save",file, data)
|
||||||
await axios
|
try {
|
||||||
.post(`${serverUrl}classes/partners_DataFiles`, data, {
|
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": parseAppId
|
"X-Parse-Application-Id": parseAppId
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// const res = result.data;
|
|
||||||
// console.log("res", res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in save usage ", err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
import moment from "moment";
|
|
||||||
|
|
||||||
export default function onSearchFilter(_search) {
|
|
||||||
switch (_search) {
|
|
||||||
case "Today": {
|
|
||||||
let d = new Date();
|
|
||||||
let formDate = new Date(
|
|
||||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
let Todate = new Date(
|
|
||||||
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let Query = `'$gte':'${formDate}','$lte':'${Todate}'`;
|
|
||||||
return Query;
|
|
||||||
}
|
|
||||||
case "Yesterday": {
|
|
||||||
let d1 = new Date();
|
|
||||||
d1.setDate(d1.getDate() - 1);
|
|
||||||
let yesFrDate = new Date(
|
|
||||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
|
|
||||||
let YesTodate = new Date(
|
|
||||||
Date.UTC(d1.getFullYear(), d1.getMonth(), d1.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let Query1 = `'$gte':'${yesFrDate}','$lte':'${YesTodate}'`;
|
|
||||||
return Query1;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "This week": {
|
|
||||||
let oneDay = 24 * 60 * 60 * 1000;
|
|
||||||
let curr = new Date(); // get current date
|
|
||||||
let first = curr.getDate() - curr.getDay(); // First day is the day of the month - the day of the week
|
|
||||||
|
|
||||||
let firstday = new Date(
|
|
||||||
new Date(
|
|
||||||
Date.UTC(curr.getFullYear(), curr.getMonth(), curr.getDate(), 0, 0, 0)
|
|
||||||
).setDate(first)
|
|
||||||
).toISOString();
|
|
||||||
let diffDays = Math.round(
|
|
||||||
Math.abs(
|
|
||||||
(new Date(
|
|
||||||
Date.UTC(
|
|
||||||
curr.getFullYear(),
|
|
||||||
curr.getMonth(),
|
|
||||||
curr.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
) -
|
|
||||||
new Date(firstday)) /
|
|
||||||
oneDay
|
|
||||||
)
|
|
||||||
); //Days since last week
|
|
||||||
let last = first + diffDays;
|
|
||||||
let lastday = new Date(
|
|
||||||
new Date(
|
|
||||||
Date.UTC(
|
|
||||||
curr.getFullYear(),
|
|
||||||
curr.getMonth(),
|
|
||||||
curr.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).setDate(last)
|
|
||||||
).toISOString();
|
|
||||||
let Query2 = `'$gte':'${firstday}','$lte':'${lastday}'`;
|
|
||||||
return Query2;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "Last 7 days": {
|
|
||||||
let d7 = new Date();
|
|
||||||
let d7l = new Date();
|
|
||||||
d7.setDate(d7.getDate() - 7);
|
|
||||||
let l7t = new Date(
|
|
||||||
Date.UTC(d7l.getFullYear(), d7l.getMonth(), d7l.getDate(), 23, 59, 59)
|
|
||||||
).toISOString();
|
|
||||||
let date7 = new Date(
|
|
||||||
Date.UTC(d7.getFullYear(), d7.getMonth(), d7.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
let Query7 = `'$gte':'${date7}','$lte':'${l7t}'`;
|
|
||||||
return Query7;
|
|
||||||
}
|
|
||||||
case "Last 14 days": {
|
|
||||||
let d14 = new Date();
|
|
||||||
let d14l = new Date();
|
|
||||||
let l14t = new Date(
|
|
||||||
Date.UTC(
|
|
||||||
d14l.getFullYear(),
|
|
||||||
d14l.getMonth(),
|
|
||||||
d14l.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).toISOString();
|
|
||||||
d14.setDate(d14.getDate() - 14);
|
|
||||||
let date14 = new Date(
|
|
||||||
Date.UTC(d14.getFullYear(), d14.getMonth(), d14.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
|
|
||||||
let Query14 = `'$gte':'${date14}','$lte':'${l14t}'`;
|
|
||||||
return Query14;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "Last 30 days": {
|
|
||||||
let d30 = new Date();
|
|
||||||
let d30l = new Date();
|
|
||||||
let l30t = new Date(
|
|
||||||
Date.UTC(
|
|
||||||
d30l.getFullYear(),
|
|
||||||
d30l.getMonth(),
|
|
||||||
d30l.getDate(),
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
)
|
|
||||||
).toISOString();
|
|
||||||
d30.setDate(d30.getDate() - 30);
|
|
||||||
let date30 = new Date(
|
|
||||||
Date.UTC(d30.getFullYear(), d30.getMonth(), d30.getDate(), 0, 0, 0)
|
|
||||||
).toISOString();
|
|
||||||
return `'$gte':'${date30}','$lte':'${l30t}'`;
|
|
||||||
}
|
|
||||||
case "Last week": {
|
|
||||||
const from_date = moment().startOf("week").subtract(7, "days");
|
|
||||||
const to_date = moment().endOf("week").subtract(7, "days");
|
|
||||||
return `'$gte':'${from_date.toISOString()}','$lte':'${to_date.toISOString()}'`;
|
|
||||||
}
|
|
||||||
case "This month": {
|
|
||||||
var tdate = new Date();
|
|
||||||
var tfirstDay = new Date(
|
|
||||||
tdate.getFullYear(),
|
|
||||||
tdate.getMonth(),
|
|
||||||
1
|
|
||||||
).toISOString();
|
|
||||||
var tlastDay = new Date(
|
|
||||||
tdate.getFullYear(),
|
|
||||||
tdate.getMonth() + 1,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let Querylt = `'$gte':'${tfirstDay}','$lte':'${tlastDay}'`;
|
|
||||||
return Querylt;
|
|
||||||
}
|
|
||||||
case "Last month": {
|
|
||||||
var ldate = new Date();
|
|
||||||
var lfirstDay = new Date(
|
|
||||||
ldate.getFullYear(),
|
|
||||||
ldate.getMonth() - 1,
|
|
||||||
2
|
|
||||||
).toISOString();
|
|
||||||
var llastDay = new Date(
|
|
||||||
ldate.getFullYear(),
|
|
||||||
ldate.getMonth(),
|
|
||||||
1
|
|
||||||
).toISOString();
|
|
||||||
let Queryltl = `'$gte':'${lfirstDay}','$lte':'${llastDay}'`;
|
|
||||||
return Queryltl;
|
|
||||||
}
|
|
||||||
case "All": {
|
|
||||||
let fall = new Date(
|
|
||||||
new Date().getFullYear() - 100,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let fall_year = `'$gte':'${fall}'`;
|
|
||||||
return fall_year;
|
|
||||||
}
|
|
||||||
case "This year": {
|
|
||||||
let fcy = new Date(new Date().getFullYear(), 0, 1, 0, 0, 0).toISOString();
|
|
||||||
let lcy = new Date(
|
|
||||||
new Date().getFullYear(),
|
|
||||||
11,
|
|
||||||
31,
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
).toISOString();
|
|
||||||
let cur_year = `'$gte':'${fcy}','$lte':'${lcy}'`;
|
|
||||||
return cur_year;
|
|
||||||
}
|
|
||||||
case "Last year": {
|
|
||||||
let fly = new Date(
|
|
||||||
new Date().getFullYear() - 1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
).toISOString();
|
|
||||||
let lly = new Date(
|
|
||||||
new Date().getFullYear() - 1,
|
|
||||||
11,
|
|
||||||
31,
|
|
||||||
23,
|
|
||||||
59,
|
|
||||||
59
|
|
||||||
).toISOString();
|
|
||||||
let last_year = `'$gte':'${fly}','$lte':'${lly}'`;
|
|
||||||
return last_year;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,3 +15,20 @@ export const useScript = (url, onload) => {
|
|||||||
};
|
};
|
||||||
}, [url, onload]);
|
}, [url, onload]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**`useScript` hook is generated scripte for google sign in button */
|
||||||
|
export const useDropScript = (url, onload) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const script = document.createElement("script");
|
||||||
|
//add url parameter to the script src, for load and it will remove after load in return
|
||||||
|
script.src = url;
|
||||||
|
script.async = true;
|
||||||
|
script.defer = true;
|
||||||
|
script.id = "dropboxjs";
|
||||||
|
script.setAttribute("data-app-key", "8k0thg9r1t7asqg");
|
||||||
|
document.head.appendChild(script);
|
||||||
|
return () => {
|
||||||
|
document.head.removeChild(script);
|
||||||
|
};
|
||||||
|
}, [url, onload]);
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body{
|
body {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom CSS to hide scrollbar */
|
||||||
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide-scrollbar {
|
||||||
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
@@ -1 +1,75 @@
|
|||||||
import("./Bootstrap");
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom/client";
|
||||||
|
import "./index.css";
|
||||||
|
import App from "./App";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
|
import { store } from "./redux/store";
|
||||||
|
import { CookiesProvider } from "react-cookie";
|
||||||
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
|
import { TouchBackend } from "react-dnd-touch-backend";
|
||||||
|
import {
|
||||||
|
DndProvider,
|
||||||
|
TouchTransition,
|
||||||
|
MouseTransition,
|
||||||
|
Preview
|
||||||
|
} from "react-dnd-multi-backend";
|
||||||
|
import DragElement from "./components/pdf/DragElement";
|
||||||
|
import TagManager from "react-gtm-module";
|
||||||
|
import Parse from "parse";
|
||||||
|
import './polyfills'
|
||||||
|
const appId = process.env.REACT_APP_APPID
|
||||||
|
? process.env.REACT_APP_APPID
|
||||||
|
: "opensign";
|
||||||
|
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||||
|
? process.env.REACT_APP_SERVERURL
|
||||||
|
: window.location.origin + "/api/app";
|
||||||
|
Parse.initialize(appId);
|
||||||
|
Parse.serverURL = serverUrl;
|
||||||
|
|
||||||
|
const HTML5toTouch = {
|
||||||
|
backends: [
|
||||||
|
{
|
||||||
|
id: "html5",
|
||||||
|
backend: HTML5Backend,
|
||||||
|
transition: MouseTransition
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "touch",
|
||||||
|
backend: TouchBackend,
|
||||||
|
options: { enableMouseEvents: true },
|
||||||
|
preview: true,
|
||||||
|
transition: TouchTransition
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const generatePreview = (props) => {
|
||||||
|
const { item, style } = props;
|
||||||
|
const newStyle = {
|
||||||
|
...style
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={newStyle}>
|
||||||
|
<DragElement {...item} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.REACT_APP_GTM) {
|
||||||
|
const tagManagerArgs = {
|
||||||
|
gtmId: process.env.REACT_APP_GTM
|
||||||
|
};
|
||||||
|
TagManager.initialize(tagManagerArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
|
root.render(
|
||||||
|
<CookiesProvider defaultSetOptions={{ path: "/" }}>
|
||||||
|
<Provider store={store}>
|
||||||
|
<DndProvider options={HTML5toTouch}>
|
||||||
|
<Preview>{generatePreview}</Preview>
|
||||||
|
<App />
|
||||||
|
</DndProvider>
|
||||||
|
</Provider>
|
||||||
|
</CookiesProvider>
|
||||||
|
);
|
||||||
|
|||||||