mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-05 01:07:39 +02:00
Compare commits
724
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 | ||
|
|
f08758fd71 | ||
|
|
d3b1a37a7c | ||
|
|
4718c653f7 | ||
|
|
f9b8437981 | ||
|
|
b0c73bc63e | ||
|
|
269246659b | ||
|
|
51fe5d133e | ||
|
|
36a1123535 | ||
|
|
3c1ceb4995 | ||
|
|
53d154fa59 |
@@ -205,6 +205,33 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -20,7 +20,7 @@ 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://localhost: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)
|
||||||
@@ -40,7 +40,7 @@ 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 or set SMTP_ENABLE true to use SMTP 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=
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: ci
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'staging'
|
- 'main'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
build:
|
build:
|
||||||
cp .env.local_dev .env
|
cp .env.local_dev .env
|
||||||
cd apps/OpenSign && npm install && npm run build
|
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
|
||||||
docker compose up --build --force-recreate
|
docker compose up --build --force-recreate
|
||||||
|
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -42,24 +42,24 @@ Please star ⭐ the repo to support us! 😀
|
|||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
Welcome to OpenSign, the premier open-source docusign alternative - 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 PDF E-Signing:** With the help of Robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
|
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
|
||||||
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that comes with hand drawn signatures support as well as uploaded images & saved signatures for the simplest signing experience.
|
- **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.
|
||||||
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "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.
|
- **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.
|
||||||
- **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.
|
- **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.
|
||||||
- **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.
|
- **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.
|
||||||
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign it. Not just this, OpenSign also allows signers to reject signing a document.
|
- **"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.
|
||||||
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
|
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
|
||||||
- **PDF Template Creation(coming soon):** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time.
|
- **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 signed documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
|
- **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.
|
- **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(coming soon):** OpenSign™ API allows seamless integration into existing systems and software. APIs will soon be available as a cloud hosted solution.
|
- **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 coming soon.
|
- **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'>
|
||||||
@@ -76,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).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -134,6 +134,9 @@ We would like to thank all our contributors and users for their support and feed
|
|||||||
</tr>
|
</tr>
|
||||||
<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/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>
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ EXPOSE 3000
|
|||||||
# ENV NODE_ENV production
|
# ENV NODE_ENV production
|
||||||
|
|
||||||
# Run the application
|
# Run the application
|
||||||
ENTRYPOINT npm run start-dev
|
ENTRYPOINT npm run start
|
||||||
|
|
||||||
|
|||||||
Generated
+4772
-4170
File diff suppressed because it is too large
Load Diff
+36
-31
@@ -3,49 +3,50 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/themes": "^2.0.3",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@react-pdf/renderer": "^3.3.8",
|
"@radix-ui/themes": "^3.0.5",
|
||||||
"@reduxjs/toolkit": "^2.2.1",
|
"@react-pdf/renderer": "^3.4.4",
|
||||||
"axios": "^1.6.0",
|
"@reduxjs/toolkit": "^2.2.5",
|
||||||
"daisyui": "^3.9.2",
|
"axios": "^1.7.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html-react-parser": "^4.2.2",
|
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.30.1",
|
||||||
"parse": "^4.3.1",
|
"nth-check": "^2.1.1",
|
||||||
|
"parse": "^5.1.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"radix-ui": "^1.0.1",
|
"radix-ui": "^1.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.10.1",
|
"react-bootstrap": "^2.10.2",
|
||||||
"react-datepicker": "^6.2.0",
|
"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-draggable": "^4.4.6",
|
|
||||||
"react-gtm-module": "^2.0.11",
|
"react-gtm-module": "^2.0.11",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-modal": "^3.16.1",
|
"react-pdf": "^9.0.0",
|
||||||
"react-pdf": "^7.7.1",
|
"react-quill": "^2.0.0",
|
||||||
"react-redux": "^8.1.3",
|
"react-redux": "^9.1.2",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.11",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-scrollbars-custom": "^4.1.1",
|
"react-scrollbars-custom": "^4.1.1",
|
||||||
"react-select": "^5.8.0",
|
"react-select": "^5.8.0",
|
||||||
"react-signature-canvas": "^1.0.6",
|
"react-signature-canvas": "^1.0.6",
|
||||||
"react-tooltip": "^5.26.3",
|
"react-tooltip": "^5.26.4",
|
||||||
"reactour": "^1.19.2",
|
"react-web-share": "^2.0.2",
|
||||||
"redux": "^4.2.1",
|
"reactour": "^1.19.4",
|
||||||
"redux-thunk": "^2.4.2",
|
"redux": "^5.0.1",
|
||||||
|
"redux-thunk": "^3.1.0",
|
||||||
"regex-parser": "^2.3.0",
|
"regex-parser": "^2.3.0",
|
||||||
"serve": "^14.2.1",
|
"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",
|
||||||
@@ -83,16 +84,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/runtime-corejs2": "^7.23.9",
|
"@babel/runtime-corejs2": "^7.24.7",
|
||||||
"autoprefixer": "^10.4.18",
|
"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.2.2",
|
"lint-staged": "^15.2.5",
|
||||||
"postcss": "^8.4.35",
|
"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.
|
||||||
@@ -50,4 +50,4 @@
|
|||||||
-->
|
-->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Binary file not shown.
+110
-91
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, Suspense, lazy } 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 { pdfjs } from "react-pdf";
|
import { pdfjs } from "react-pdf";
|
||||||
import Login from "./pages/Login";
|
import Login from "./pages/Login";
|
||||||
@@ -9,12 +9,15 @@ import HomeLayout from "./layout/HomeLayout";
|
|||||||
import PageNotFound from "./pages/PageNotFound";
|
import PageNotFound from "./pages/PageNotFound";
|
||||||
import ValidateRoute from "./primitives/ValidateRoute";
|
import ValidateRoute from "./primitives/ValidateRoute";
|
||||||
import Validate from "./primitives/Validate";
|
import Validate from "./primitives/Validate";
|
||||||
import ManageSign from "./pages/Managesign";
|
|
||||||
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
|
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
|
||||||
import SignYourSelf from "./pages/SignyourselfPdf";
|
import SignYourSelf from "./pages/SignyourselfPdf";
|
||||||
import DraftDocument from "./components/pdf/DraftDocument";
|
import DraftDocument from "./components/pdf/DraftDocument";
|
||||||
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
||||||
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
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 DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||||
@@ -23,28 +26,17 @@ const Subscriptions = lazy(() => import("./pages/PlanSubscriptions"));
|
|||||||
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
||||||
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
||||||
const Signup = lazy(() => import("./pages/Signup"));
|
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 GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||||
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
|
||||||
|
|
||||||
const Loader = () => {
|
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||||
|
|
||||||
|
const AppLoader = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex justify-center items-center h-[100vh]">
|
||||||
style={{
|
<Loader />
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -73,88 +65,115 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-[#eef1f5]">
|
<div className="bg-base-200">
|
||||||
{isloading ? (
|
{isloading ? (
|
||||||
<Loader />
|
<AppLoader />
|
||||||
) : (
|
) : (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Suspense fallback={<Loader />}>
|
<Routes>
|
||||||
<Routes>
|
<Route element={<ValidateRoute />}>
|
||||||
<Route element={<ValidateRoute />}>
|
<Route exact path="/" element={<Login />} />
|
||||||
<Route exact path="/" element={<Login />} />
|
<Route path="/signup" element={<LazyPage Page={Signup} />} />
|
||||||
<Route exact path="/signup" element={<Signup />} />
|
</Route>
|
||||||
</Route>
|
<Route element={<Validate />}>
|
||||||
<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
|
<Route
|
||||||
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
|
path="/load/template/:templateId"
|
||||||
element={<GuestLogin />}
|
element={<TemplatePlaceholder />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
exact
|
||||||
element={<GuestLogin />}
|
path="/load/placeholdersign/:docId"
|
||||||
|
element={<PlaceHolderSign />}
|
||||||
/>
|
/>
|
||||||
<Route path="/debugpdf" element={<DebugPdf />} />
|
<Route
|
||||||
<Route path="/forgetpassword" element={<ForgetPassword />} />
|
exact
|
||||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
path="/load/recipientSignPdf/:docId/:contactBookId"
|
||||||
<>
|
element={<PdfRequestFiles />}
|
||||||
<Route exact path="/pgsignup" element={<Pgsignup />} />
|
/>
|
||||||
<Route path="/subscription" element={<Subscriptions />} />
|
</Route>
|
||||||
</>
|
<Route
|
||||||
)}
|
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||||
<Route element={<HomeLayout />}>
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
<Route path="/changepassword" element={<ChangePassword />} />
|
/>
|
||||||
<Route path="/form/:id" element={<Form />} />
|
<Route
|
||||||
<Route path="/report/:id" element={<Report />} />
|
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||||
<Route path="/dashboard/:id" element={<Dashboard />} />
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
<Route path="/profile" element={<UserProfile />} />
|
/>
|
||||||
<Route path="/generatetoken" element={<GenerateToken />} />
|
<Route
|
||||||
<Route path="/webhook" element={<Webhook />} />
|
path="/login/:base64url"
|
||||||
<Route path="/managesign" element={<ManageSign />} />
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
<Route path="/opensigndrive" element={<Opensigndrive />} />
|
/>
|
||||||
|
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||||
|
<Route
|
||||||
|
path="/forgetpassword"
|
||||||
|
element={<LazyPage Page={ForgetPassword} />}
|
||||||
|
/>
|
||||||
|
{isEnableSubscription && (
|
||||||
|
<>
|
||||||
<Route
|
<Route
|
||||||
path="/template/:templateId"
|
path="/pgsignup"
|
||||||
element={<TemplatePlaceholder />}
|
element={<LazyPage Page={Pgsignup} />}
|
||||||
/>
|
/>
|
||||||
{/* signyouself route with no rowlevel data using docId from url */}
|
|
||||||
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
|
||||||
{/* draft document route to handle and navigate route page accordiing to document status */}
|
|
||||||
<Route path="/draftDocument" element={<DraftDocument />} />
|
|
||||||
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
|
||||||
<Route
|
<Route
|
||||||
path="/placeHolderSign/:docId"
|
path="/subscription"
|
||||||
element={<PlaceHolderSign />}
|
element={<LazyPage Page={Subscriptions} />}
|
||||||
/>
|
/>
|
||||||
{/* 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 element={<HomeLayout />}>
|
||||||
<Route
|
<Route
|
||||||
path="/pdfRequestFiles/:docId"
|
path="/changepassword"
|
||||||
element={<PdfRequestFiles />}
|
element={<LazyPage Page={ChangePassword} />}
|
||||||
/>
|
/>
|
||||||
{/* recipient signature route with no rowlevel data using docId from url */}
|
<Route path="/form/:id" element={<Form />} />
|
||||||
<Route
|
<Route path="/report/:id" element={<Report />} />
|
||||||
path="/recipientSignPdf/:docId/:contactBookId"
|
<Route path="/dashboard/:id" element={<Dashboard />} />
|
||||||
element={<PdfRequestFiles />}
|
<Route
|
||||||
/>
|
path="/profile"
|
||||||
</Route>
|
element={<LazyPage Page={UserProfile} />}
|
||||||
<Route path="*" element={<PageNotFound />} />
|
/>
|
||||||
</Routes>
|
<Route
|
||||||
</Suspense>
|
path="/opensigndrive"
|
||||||
|
element={<LazyPage Page={Opensigndrive} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/managesign"
|
||||||
|
element={<LazyPage Page={ManageSign} />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/generatetoken"
|
||||||
|
element={<LazyPage Page={GenerateToken} />}
|
||||||
|
/>
|
||||||
|
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
|
||||||
|
<Route
|
||||||
|
path="/template/:templateId"
|
||||||
|
element={<TemplatePlaceholder />}
|
||||||
|
/>
|
||||||
|
{/* signyouself route with no rowlevel data using docId from url */}
|
||||||
|
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
||||||
|
{/* draft document route to handle and navigate route page accordiing to document status */}
|
||||||
|
<Route path="/draftDocument" element={<DraftDocument />} />
|
||||||
|
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
||||||
|
<Route
|
||||||
|
path="/placeHolderSign/:docId"
|
||||||
|
element={<PlaceHolderSign />}
|
||||||
|
/>
|
||||||
|
{/* 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
|
||||||
|
path="/pdfRequestFiles/:docId"
|
||||||
|
element={<PdfRequestFiles />}
|
||||||
|
/>
|
||||||
|
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||||
|
<Route
|
||||||
|
path="/recipientSignPdf/:docId/:contactBookId"
|
||||||
|
element={<PdfRequestFiles />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
<Route path="/sso" element={<SSOVerify />} />
|
||||||
|
<Route path="*" element={<PageNotFound />} />
|
||||||
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 316 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB |
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
const AddSigner = (props) => {
|
const AddSigner = (props) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
@@ -23,7 +23,7 @@ const AddSigner = (props) => {
|
|||||||
);
|
);
|
||||||
if (savedUserDetails && addYourself) {
|
if (savedUserDetails && addYourself) {
|
||||||
setName(savedUserDetails.name);
|
setName(savedUserDetails.name);
|
||||||
setPhone(savedUserDetails.phone);
|
setPhone(savedUserDetails?.phone || "");
|
||||||
setEmail(savedUserDetails.email);
|
setEmail(savedUserDetails.email);
|
||||||
}
|
}
|
||||||
}, [addYourself]);
|
}, [addYourself]);
|
||||||
@@ -49,144 +49,162 @@ const AddSigner = (props) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsLoader(true);
|
setIsLoader(true);
|
||||||
try {
|
if (localStorage.getItem("TenantId")) {
|
||||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
|
||||||
contactQuery.set("Name", name);
|
|
||||||
contactQuery.set("Phone", phone);
|
|
||||||
contactQuery.set("Email", email);
|
|
||||||
contactQuery.set("UserRole", "contracts_Guest");
|
|
||||||
|
|
||||||
if (localStorage.getItem("TenetId")) {
|
|
||||||
contactQuery.set("TenantId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "partners_Tenant",
|
|
||||||
objectId: localStorage.getItem("TenetId")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const _users = Parse.Object.extend("User");
|
const user = Parse.User.current();
|
||||||
const _user = new _users();
|
const query = new Parse.Query("contracts_Contactbook");
|
||||||
_user.set("name", name);
|
query.equalTo("CreatedBy", user);
|
||||||
_user.set("username", email);
|
query.notEqualTo("IsDeleted", true);
|
||||||
_user.set("email", email);
|
query.equalTo("Email", email);
|
||||||
_user.set("phone", phone);
|
const res = await query.first();
|
||||||
_user.set("password", phone);
|
// console.log(res);
|
||||||
|
if (!res) {
|
||||||
const user = await _user.save();
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
if (user) {
|
contactQuery.set("Name", name);
|
||||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
if (phone) {
|
||||||
const headers = {
|
contactQuery.set("Phone", phone);
|
||||||
"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);
|
|
||||||
}
|
}
|
||||||
|
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);
|
setIsLoader(false);
|
||||||
// Reset the form fields
|
|
||||||
setAddYourself(false);
|
|
||||||
setName("");
|
|
||||||
setPhone("");
|
|
||||||
setEmail("");
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
console.log("err in fetch contact", err);
|
||||||
if (err.code === 202) {
|
setIsLoader(false);
|
||||||
const params = { email: email };
|
alert("something went wrong, please try again later");
|
||||||
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("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} else {
|
||||||
// console.log("err", err);
|
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
alert("something went wrong!");
|
alert("something went wrong, please try again later");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,38 +227,29 @@ const AddSigner = (props) => {
|
|||||||
<div className="h-full p-[20px]">
|
<div className="h-full p-[20px]">
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||||
<div
|
<Loader />
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="w-full mx-auto p-2">
|
<div className="w-full mx-auto p-2 text-base-content">
|
||||||
{isUserExist && (
|
{isUserExist && (
|
||||||
<div className="mb-3">
|
<div className="mb-3 flex items-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="addYourself"
|
id="addYourself"
|
||||||
checked={addYourself}
|
checked={addYourself}
|
||||||
onChange={handleAddYourselfChange}
|
onChange={handleAddYourselfChange}
|
||||||
className="form-checkbox"
|
className="op-checkbox op-checkbox-sm"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="addYourself" className="ml-2 text-gray-700">
|
<label htmlFor="addYourself" className="ml-2 mb-0">
|
||||||
Add Yourself
|
Add Yourself
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="name" className="block text-xs font-semibold">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Name
|
Name
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -249,59 +258,45 @@ const AddSigner = (props) => {
|
|||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="email" className="block text-xs font-semibold">
|
||||||
htmlFor="email"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Email
|
Email
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="phone" className="block text-xs font-semibold">
|
||||||
htmlFor="phone"
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Phone
|
Phone
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="phone"
|
id="phone"
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-4 flex gap-x-2 justify-start">
|
||||||
<div className="mt-4 flex justify-start">
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
style={{ backgroundColor: modalSubmitBtnColor }}
|
|
||||||
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
|
||||||
>
|
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleReset()}
|
onClick={() => handleReset()}
|
||||||
style={{ backgroundColor: modalCancelBtnColor }}
|
className="op-btn op-btn-secondary"
|
||||||
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
|
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Parse from "parse";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Title from "./Title";
|
import Title from "./Title";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
const AddUser = (props) => {
|
const AddUser = (props) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
@@ -48,11 +49,11 @@ const AddUser = (props) => {
|
|||||||
contactQuery.set("Email", email);
|
contactQuery.set("Email", email);
|
||||||
contactQuery.set("UserRole", "contracts_User");
|
contactQuery.set("UserRole", "contracts_User");
|
||||||
|
|
||||||
if (localStorage.getItem("TenetId")) {
|
if (localStorage.getItem("TenantId")) {
|
||||||
contactQuery.set("TenantId", {
|
contactQuery.set("TenantId", {
|
||||||
__type: "Pointer",
|
__type: "Pointer",
|
||||||
className: "partners_Tenant",
|
className: "partners_Tenant",
|
||||||
objectId: localStorage.getItem("TenetId")
|
objectId: localStorage.getItem("TenantId")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,8 +63,10 @@ const AddUser = (props) => {
|
|||||||
_user.set("name", name);
|
_user.set("name", name);
|
||||||
_user.set("username", email);
|
_user.set("username", email);
|
||||||
_user.set("email", email);
|
_user.set("email", email);
|
||||||
_user.set("phone", phone);
|
_user.set("password", email);
|
||||||
_user.set("password", phone);
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await _user.save();
|
const user = await _user.save();
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -193,13 +196,7 @@ const AddUser = (props) => {
|
|||||||
{isUserExist && <Alert type="danger">User already exists!</Alert>}
|
{isUserExist && <Alert type="danger">User already exists!</Alert>}
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
|
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
|
||||||
<div
|
<Loader />
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="w-full mx-auto">
|
<div className="w-full mx-auto">
|
||||||
@@ -211,7 +208,7 @@ const AddUser = (props) => {
|
|||||||
className="block text-xs text-gray-700 font-semibold"
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
>
|
>
|
||||||
Name
|
Name
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -219,7 +216,7 @@ const AddUser = (props) => {
|
|||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
@@ -228,7 +225,7 @@ const AddUser = (props) => {
|
|||||||
className="block text-xs text-gray-700 font-semibold"
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
>
|
>
|
||||||
Email
|
Email
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
@@ -236,7 +233,7 @@ const AddUser = (props) => {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
@@ -245,7 +242,7 @@ const AddUser = (props) => {
|
|||||||
className="block text-xs text-gray-700 font-semibold"
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
>
|
>
|
||||||
Phone
|
Phone
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -253,7 +250,7 @@ const AddUser = (props) => {
|
|||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
required
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -45,12 +45,16 @@ const Footer = () => {
|
|||||||
};
|
};
|
||||||
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>
|
||||||
<span onClick={openUrl} className="hover:underline cursor-pointer">
|
<p>
|
||||||
{appName} ( version: {version ? version : `${Package.version} `})
|
All Rights Reserved © {new Date().getFullYear()}
|
||||||
</span>
|
<span onClick={openUrl} className="hover:underline cursor-pointer">
|
||||||
</div>
|
{appName} ( version: {version ? version : `${Package.version} `})
|
||||||
|
</span>
|
||||||
|
</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,23 +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 { openInNewTab } from "../constant/Utils";
|
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 [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.User.logOut();
|
if (Parse?.User?.current()) {
|
||||||
|
try {
|
||||||
|
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");
|
||||||
@@ -43,7 +73,7 @@ const Header = ({ showSidebar }) => {
|
|||||||
localStorage.setItem("baseUrl", baseUrl);
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
localStorage.setItem("parseAppId", 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
|
||||||
@@ -63,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"
|
||||||
@@ -79,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>
|
||||||
@@ -99,52 +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"
|
|
||||||
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
|
|
||||||
>
|
|
||||||
<i className="fa-solid fa-book"></i> Docs
|
<i className="fa-solid fa-book"></i> Docs
|
||||||
</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("/profile");
|
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 = ({
|
||||||
@@ -49,7 +50,7 @@ const LoginFacebook = ({
|
|||||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
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) {
|
||||||
@@ -59,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));
|
||||||
@@ -75,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);
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ 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_User",
|
role: "contracts_User",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
}
|
}
|
||||||
@@ -96,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));
|
||||||
@@ -112,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
|
||||||
@@ -146,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">
|
||||||
@@ -163,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"
|
||||||
@@ -186,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"
|
||||||
@@ -212,16 +183,14 @@ const LoginFacebook = ({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleSubmitbtn()}
|
onClick={() => handleSubmitbtn()}
|
||||||
className="btn btn-info"
|
className="btn btn-info mr-[10px]"
|
||||||
style={{ marginRight: 10 }}
|
|
||||||
>
|
>
|
||||||
Sign up
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary w-[90px]"
|
||||||
onClick={() => setIsModal(false)}
|
onClick={() => setIsModal(false)}
|
||||||
style={{ width: 90 }}
|
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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 ModalUi from "../primitives/ModalUi";
|
||||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
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
|
||||||
@@ -110,22 +110,22 @@ const GoogleSignInBtn = ({
|
|||||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
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 {
|
||||||
@@ -139,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);
|
||||||
@@ -149,7 +149,7 @@ 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_User",
|
role: "contracts_User",
|
||||||
company: userDetails.Company,
|
company: userDetails.Company,
|
||||||
jobTitle: userDetails.Destination
|
jobTitle: userDetails.Destination
|
||||||
@@ -161,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
|
||||||
};
|
};
|
||||||
@@ -207,45 +207,22 @@ const GoogleSignInBtn = ({
|
|||||||
localStorage.setItem("parseAppId", appid);
|
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">
|
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
|
||||||
<form className="px-4 py-3">
|
<form className="px-4 py-3 text-base-content">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="Phone" className="block text-xs font-semibold">
|
||||||
htmlFor="Phone"
|
Phone <span className="text-[13px] text-[red]">*</span>
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
id="Phone"
|
id="Phone"
|
||||||
value={userDetails.Phone}
|
value={userDetails.Phone}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -258,16 +235,12 @@ const GoogleSignInBtn = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="Company" className="block text-xs font-semibold">
|
||||||
htmlFor="Company"
|
Company <span className="text-[13px] text-[red]">*</span>
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
id="Company"
|
id="Company"
|
||||||
value={userDetails.Company}
|
value={userDetails.Company}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -280,16 +253,12 @@ const GoogleSignInBtn = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label
|
<label htmlFor="JobTitle" className="block text-xs font-semibold">
|
||||||
htmlFor="JobTitle"
|
Job Title <span className="text-[13px] text-[red]">*</span>
|
||||||
style={{ display: "flex" }}
|
|
||||||
className="block text-xs text-gray-700 font-semibold"
|
|
||||||
>
|
|
||||||
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
id="JobTitle"
|
id="JobTitle"
|
||||||
value={userDetails.Destination}
|
value={userDetails.Destination}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -301,20 +270,18 @@ const GoogleSignInBtn = ({
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex flex-row gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
className="op-btn op-btn-primary"
|
||||||
onClick={() => handleSubmitbtn()}
|
onClick={() => handleSubmitbtn()}
|
||||||
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
|
|
||||||
>
|
>
|
||||||
Sign up
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
|
className="op-btn op-btn-ghost"
|
||||||
onClick={handleCloseModal}
|
onClick={handleCloseModal}
|
||||||
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
|
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -4,26 +4,11 @@ import { Stage, Layer, Rect } from "react-konva";
|
|||||||
const RenderDebugPdf = (props) => {
|
const RenderDebugPdf = (props) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div className="sticky top-0 p-[10px] z-10 bg-white border-[1px] border-[gray] my-[5px]">
|
||||||
style={{
|
|
||||||
position: "sticky",
|
|
||||||
top: 0,
|
|
||||||
padding: 10,
|
|
||||||
zIndex: 1,
|
|
||||||
backgroundColor: "white",
|
|
||||||
border: "1px solid grey",
|
|
||||||
margin: "5px 0"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
|
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
className="relative flex-1 cursor-crosshair border-[1px] border-[gray] overflow-auto"
|
||||||
flex: 1,
|
|
||||||
position: "relative",
|
|
||||||
border: "1px solid grey",
|
|
||||||
overflow: "auto"
|
|
||||||
}}
|
|
||||||
onMouseMove={props.handleMouseMoveDiv}
|
onMouseMove={props.handleMouseMoveDiv}
|
||||||
>
|
>
|
||||||
<Document
|
<Document
|
||||||
@@ -56,7 +41,7 @@ const RenderDebugPdf = (props) => {
|
|||||||
onMouseUp={props.handleMouseUp}
|
onMouseUp={props.handleMouseUp}
|
||||||
width={props.pdfDimension.width}
|
width={props.pdfDimension.width}
|
||||||
height={props.pdfDimension.height}
|
height={props.pdfDimension.height}
|
||||||
style={{ position: "absolute", top: 0, left: 0 }}
|
className="absolute top-0 right-0"
|
||||||
>
|
>
|
||||||
<Layer>
|
<Layer>
|
||||||
{props.annotations
|
{props.annotations
|
||||||
|
|||||||
@@ -26,18 +26,18 @@ const DashboardButton = (props) => {
|
|||||||
props.Data && props.Data.Redirect_type
|
props.Data && props.Data.Redirect_type
|
||||||
? "cursor-pointer"
|
? "cursor-pointer"
|
||||||
: "cursor-default"
|
: "cursor-default"
|
||||||
} w-full px-3 py-2 flex text-white rounded-md shadow overflow-hidden`}
|
} w-full shadow-md px-3 py-2 op-card bg-base-100`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-start gap-5">
|
<div className="flex flex-row items-center">
|
||||||
<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-base-content 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="op-card-title text-lg ml-3 text-base-content">
|
||||||
<div className="text-base lg:text-lg text-black"> {props.Label}</div>
|
{props.Label}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Parse from "parse";
|
|||||||
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();
|
||||||
@@ -148,7 +149,7 @@ const DashboardCard = (props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const listData = res.data?.result.filter(
|
const listData = res.data?.result?.filter(
|
||||||
(x) => x.Signers.length > 0
|
(x) => x.Signers.length > 0
|
||||||
);
|
);
|
||||||
let arr = [];
|
let arr = [];
|
||||||
@@ -335,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,6 +3,7 @@ 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([]);
|
||||||
@@ -97,11 +98,13 @@ function DashboardReport(props) {
|
|||||||
setIsMoreDocs(false);
|
setIsMoreDocs(false);
|
||||||
}
|
}
|
||||||
setIsNextRecord(false);
|
setIsNextRecord(false);
|
||||||
setList((prevRecord) =>
|
if (!res.data.result.error) {
|
||||||
prevRecord.length > 0
|
setList((prevRecord) =>
|
||||||
? [...prevRecord, ...res.data.result]
|
prevRecord.length > 0
|
||||||
: res.data.result
|
? [...prevRecord, ...res.data.result]
|
||||||
);
|
: res.data.result
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -110,6 +113,7 @@ function DashboardReport(props) {
|
|||||||
console.log("err ", err);
|
console.log("err ", err);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
@@ -118,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>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -6,61 +6,38 @@ const buttonList = [
|
|||||||
{
|
{
|
||||||
label: "Sign yourself",
|
label: "Sign yourself",
|
||||||
redirectId: "sHAnZphf69",
|
redirectId: "sHAnZphf69",
|
||||||
redirectType: "Form"
|
redirectType: "Form",
|
||||||
|
icon: "fas fa-pen-nib"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Request signature",
|
label: "Request signature",
|
||||||
redirectId: "8mZzFxbG1z",
|
redirectId: "8mZzFxbG1z",
|
||||||
redirectType: "Form"
|
redirectType: "Form",
|
||||||
|
icon: "fa-solid fa-paper-plane"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const GetDashboard = (props) => {
|
const GetDashboard = (props) => {
|
||||||
const Button = ({ label, redirectId, redirectType }) => (
|
const Button = ({ label, redirectId, redirectType, icon }) => (
|
||||||
<div className={"bg-white rounded-md shadow w-full"}>
|
<DashboardButton
|
||||||
<Suspense
|
Icon={icon}
|
||||||
fallback={
|
Label={label}
|
||||||
<div style={{ height: "300px" }}>
|
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
|
||||||
<div
|
/>
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<DashboardButton
|
|
||||||
Icon={"fa-solid fa-plus"}
|
|
||||||
Label={label}
|
|
||||||
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
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-3 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
|
<Suspense
|
||||||
fallback={
|
fallback={
|
||||||
<div style={{ height: "300px" }}>
|
<div className="h-[150px] w-full flex justify-center items-center">
|
||||||
<div
|
loading...
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -70,7 +47,6 @@ const GetDashboard = (props) => {
|
|||||||
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>
|
||||||
@@ -87,7 +63,7 @@ const GetDashboard = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const renderSwitch = (col) => {
|
const renderSwitch = (col) => {
|
||||||
@@ -95,8 +71,9 @@ const GetDashboard = (props) => {
|
|||||||
case "Card":
|
case "Card":
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={"bg-[#2ed8b6] rounded-md shadow mb-3 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
|
||||||
@@ -105,7 +82,6 @@ const GetDashboard = (props) => {
|
|||||||
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>
|
||||||
@@ -120,7 +96,7 @@ const GetDashboard = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@@ -128,7 +104,7 @@ const GetDashboard = (props) => {
|
|||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<div
|
<div
|
||||||
data-tut={"tourbutton"}
|
data-tut={"tourbutton"}
|
||||||
className="flex flex-col md:flex-row gap-6 md:gap-8"
|
className="flex flex-col md:flex-row gap-4"
|
||||||
>
|
>
|
||||||
{buttonList.map((btn) => (
|
{buttonList.map((btn) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -136,25 +112,24 @@ const GetDashboard = (props) => {
|
|||||||
label={btn.label}
|
label={btn.label}
|
||||||
redirectType={btn.redirectType}
|
redirectType={btn.redirectType}
|
||||||
redirectId={btn.redirectId}
|
redirectId={btn.redirectId}
|
||||||
|
icon={btn.icon}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{props.dashboard.map((val, key) => (
|
<div className="grid grid-cols-12 w-full gap-x-4">
|
||||||
<div key={"a" + key} className="row">
|
{props?.dashboard?.columns?.map((col, i) =>
|
||||||
{val.columns.map((col, i) =>
|
col.widget.data && col.widget.data.tourSection ? (
|
||||||
col.widget.data && col.widget.data.tourSection ? (
|
<div key={i} className={col?.colsize}>
|
||||||
<div key={i} className={props.classnameArray[key][i]}>
|
{renderSwitchWithTour(col)}
|
||||||
{renderSwitchWithTour(col)}
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
<div key={i} className={col?.colsize}>
|
||||||
<div key={i} className={props.classnameArray[key][i]}>
|
{renderSwitch(col)}
|
||||||
{renderSwitch(col)}
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,19 +8,10 @@ import { saveAs } from "file-saver";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Table from "react-bootstrap/Table";
|
import Table from "react-bootstrap/Table";
|
||||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||||
import SelectFolder from "../shared/fields/SelectFolder"; //check this one
|
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { themeColor } from "../../constant/const";
|
import FolderModal from "../shared/fields/FolderModal";
|
||||||
|
|
||||||
function DriveBody({
|
function DriveBody(props) {
|
||||||
pdfData,
|
|
||||||
setFolderName,
|
|
||||||
setDocId,
|
|
||||||
setIsLoading,
|
|
||||||
setPdfData,
|
|
||||||
isList,
|
|
||||||
setIsAlert
|
|
||||||
}) {
|
|
||||||
const [rename, setRename] = useState("");
|
const [rename, setRename] = useState("");
|
||||||
const [renameValue, setRenameValue] = useState("");
|
const [renameValue, setRenameValue] = useState("");
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
@@ -50,28 +41,25 @@ function DriveBody({
|
|||||||
name: data.Name,
|
name: data.Name,
|
||||||
objectId: data.objectId
|
objectId: data.objectId
|
||||||
};
|
};
|
||||||
setFolderName((prev) => [...prev, folderData]);
|
props.setFolderName((prev) => [...prev, folderData]);
|
||||||
const loadObj = {
|
props.setIsLoading({
|
||||||
isLoad: true,
|
isLoad: true,
|
||||||
message: "This might take some time"
|
message: "This might take some time"
|
||||||
};
|
});
|
||||||
|
props.setDocId(data.objectId);
|
||||||
setIsLoading(loadObj);
|
props.setPdfData([]);
|
||||||
setDocId(data.objectId);
|
props.setSkip(0);
|
||||||
};
|
};
|
||||||
//function for change doc name and update doc name in _document class
|
//function for change doc name and update doc name in _document class
|
||||||
const handledRenameDoc = async (data) => {
|
const handledRenameDoc = async (data) => {
|
||||||
setRename("");
|
setRename("");
|
||||||
const trimmedValue = renameValue.trim();
|
const trimmedValue = renameValue.trim();
|
||||||
|
|
||||||
if (trimmedValue.length > 0) {
|
if (trimmedValue.length > 0) {
|
||||||
const updateName = {
|
const updateName = {
|
||||||
Name: renameValue
|
Name: renameValue
|
||||||
};
|
};
|
||||||
const docId = data.objectId;
|
const docId = data.objectId;
|
||||||
|
const docData = props.pdfData;
|
||||||
const docData = pdfData;
|
|
||||||
|
|
||||||
const updatedData = docData.map((item) => {
|
const updatedData = docData.map((item) => {
|
||||||
if (item.objectId === docId) {
|
if (item.objectId === docId) {
|
||||||
// If the item's ID matches the target ID, update the name
|
// If the item's ID matches the target ID, update the name
|
||||||
@@ -80,8 +68,8 @@ function DriveBody({
|
|||||||
// If the item's ID doesn't match, keep it unchanged
|
// If the item's ID doesn't match, keep it unchanged
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
|
props.setPdfData(updatedData);
|
||||||
setPdfData(updatedData);
|
props.sortingData(null, null, updatedData);
|
||||||
await axios
|
await axios
|
||||||
.put(
|
.put(
|
||||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||||
@@ -102,7 +90,7 @@ function DriveBody({
|
|||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Err ", err);
|
console.log("Err ", err);
|
||||||
setIsAlert({
|
props.setIsAlert({
|
||||||
isShow: true,
|
isShow: true,
|
||||||
alertMessage: "something went wrong"
|
alertMessage: "something went wrong"
|
||||||
});
|
});
|
||||||
@@ -199,13 +187,13 @@ function DriveBody({
|
|||||||
.then((result) => {
|
.then((result) => {
|
||||||
const res = result.data;
|
const res = result.data;
|
||||||
if (res) {
|
if (res) {
|
||||||
const updatedData = pdfData.filter((x) => x.objectId !== docId);
|
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
|
||||||
setPdfData(updatedData);
|
props.setPdfData(updatedData);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Err ", err);
|
console.log("Err ", err);
|
||||||
setIsAlert({
|
props.setIsAlert({
|
||||||
isShow: true,
|
isShow: true,
|
||||||
alertMessage: "something went wrong"
|
alertMessage: "something went wrong"
|
||||||
});
|
});
|
||||||
@@ -262,10 +250,10 @@ function DriveBody({
|
|||||||
// console.log("Listdata ", Listdata);
|
// console.log("Listdata ", Listdata);
|
||||||
const res = Listdata.data;
|
const res = Listdata.data;
|
||||||
if (res) {
|
if (res) {
|
||||||
const updatedData = pdfData.filter(
|
const updatedData = props.pdfData.filter(
|
||||||
(x) => x.objectId !== updateDocId
|
(x) => x.objectId !== updateDocId
|
||||||
);
|
);
|
||||||
setPdfData(updatedData);
|
props.setPdfData(updatedData);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@@ -292,14 +280,13 @@ function DriveBody({
|
|||||||
|
|
||||||
//component to handle type of document and render according to type
|
//component to handle type of document and render according to type
|
||||||
const handleFolderData = (data, ind, listType) => {
|
const handleFolderData = (data, ind, listType) => {
|
||||||
let createddate, status, isDecline, signerExist, isComplete, isPlaceholder;
|
let createddate, status, isDecline, signerExist, isComplete;
|
||||||
if (data.Type !== "Folder") {
|
if (data.Type !== "Folder") {
|
||||||
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
|
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
|
||||||
const createdDate = data.createdAt && data.createdAt;
|
const createdDate = data.createdAt && data.createdAt;
|
||||||
createddate = new Date(createdDate).toLocaleDateString();
|
createddate = new Date(createdDate).toLocaleDateString();
|
||||||
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
|
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
|
||||||
isDecline = data.IsDeclined && data.IsDeclined;
|
isDecline = data.IsDeclined && data.IsDeclined;
|
||||||
isPlaceholder = data.Placeholders && data.Placeholders;
|
|
||||||
signerExist = data.Signers && data.Signers;
|
signerExist = data.Signers && data.Signers;
|
||||||
const signedUrl = data.SignedUrl;
|
const signedUrl = data.SignedUrl;
|
||||||
|
|
||||||
@@ -314,18 +301,7 @@ function DriveBody({
|
|||||||
status = "Completed";
|
status = "Completed";
|
||||||
} else if (isDecline) {
|
} else if (isDecline) {
|
||||||
status = "Declined";
|
status = "Declined";
|
||||||
} else if (!signerExist || signerExist?.length === 0) {
|
} else if (!signedUrl) {
|
||||||
status = "Draft";
|
|
||||||
} else if (
|
|
||||||
signerExist?.length > 0 &&
|
|
||||||
(!isPlaceholder || isPlaceholder?.length === 0)
|
|
||||||
) {
|
|
||||||
status = "Draft";
|
|
||||||
} else if (
|
|
||||||
signerExist?.length > 0 &&
|
|
||||||
isPlaceholder?.length > 0 &&
|
|
||||||
!signedUrl
|
|
||||||
) {
|
|
||||||
status = "Draft";
|
status = "Draft";
|
||||||
} else if (isExpire) {
|
} else if (isExpire) {
|
||||||
status = "Expired";
|
status = "Expired";
|
||||||
@@ -336,32 +312,21 @@ function DriveBody({
|
|||||||
|
|
||||||
const signersName = () => {
|
const signersName = () => {
|
||||||
const getSignersName = signerExist.map((data) => data.Name);
|
const getSignersName = signerExist.map((data) => data.Name);
|
||||||
const signerName = getSignersName.join(",");
|
const signerName = getSignersName.join(", ");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span className="statusSpan w-[90%] break-words">{signerName}</span>
|
||||||
className="statusSpan"
|
|
||||||
style={{ width: "90%", wordWrap: "break-word" }}
|
|
||||||
>
|
|
||||||
{signerName}{" "}
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return listType === "table" ? (
|
return listType === "table" ? (
|
||||||
data.Type === "Folder" ? (
|
data.Type === "Folder" ? (
|
||||||
<tr onClick={() => handleOnclikFolder(data)}>
|
<tr onClick={() => handleOnclikFolder(data)}>
|
||||||
<td
|
<td className="cursor-pointer flex items-center">
|
||||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
|
||||||
>
|
|
||||||
<i
|
<i
|
||||||
className="fa fa-folder"
|
className="fa fa-folder mr-[8px] text-[26px] text-[#f0ad26]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ color: "#f0ad26", marginRight: "8px", fontSize: "26px" }}
|
|
||||||
></i>
|
></i>
|
||||||
|
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||||
<span style={{ fontSize: "12px", fontWeight: "500" }}>
|
|
||||||
{data.Name}
|
|
||||||
</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td>_</td>
|
<td>_</td>
|
||||||
<td>Folder</td>
|
<td>Folder</td>
|
||||||
@@ -370,17 +335,9 @@ function DriveBody({
|
|||||||
</tr>
|
</tr>
|
||||||
) : (
|
) : (
|
||||||
<tr onClick={() => checkPdfStatus(data)}>
|
<tr onClick={() => checkPdfStatus(data)}>
|
||||||
<td
|
<td className="cursor-pointer flex items-center">
|
||||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
<i className="fa fa-file-pdf mr-[8px] text-[26px] text-[#ed4d0e]"></i>
|
||||||
>
|
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||||
<i
|
|
||||||
className="fa fa-file-pdf"
|
|
||||||
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
|
|
||||||
></i>
|
|
||||||
|
|
||||||
<span style={{ fontSize: "12px", fontWeight: "500" }}>
|
|
||||||
{data.Name}
|
|
||||||
</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td>{createddate}</td>
|
<td>{createddate}</td>
|
||||||
<td>Pdf</td>
|
<td>Pdf</td>
|
||||||
@@ -391,30 +348,22 @@ function DriveBody({
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleMenuItemClick("Download", data);
|
handleMenuItemClick("Download", data);
|
||||||
}}
|
}}
|
||||||
className="fa fa-download"
|
className="fa fa-download mr-[8px] text-[#ed280e]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ color: "#ed280e", marginRight: "8px" }}
|
|
||||||
></i>
|
></i>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
) : listType === "list" && data.Type === "Folder" ? (
|
) : listType === "list" && data.Type === "Folder" ? (
|
||||||
<div key={ind} className="folderBox">
|
<div key={ind} className="icon-container">
|
||||||
<ContextMenu.Root>
|
<ContextMenu.Root>
|
||||||
<ContextMenu.Trigger className="ContextMenuTrigger">
|
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||||
<div
|
<div data-tut={props.dataTutSeventh}>
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
cursor: "pointer"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
alt="no img"
|
alt="folder"
|
||||||
onClick={() => handleOnclikFolder(data)}
|
onClick={() => handleOnclikFolder(data)}
|
||||||
src={folder}
|
src={folder}
|
||||||
width={100}
|
className="w-full h-full"
|
||||||
height={103}
|
|
||||||
/>
|
/>
|
||||||
{rename === data.objectId ? (
|
{rename === data.objectId ? (
|
||||||
<input
|
<input
|
||||||
@@ -433,12 +382,7 @@ function DriveBody({
|
|||||||
defaultValue={renameValue}
|
defaultValue={renameValue}
|
||||||
// value={renameValue}
|
// value={renameValue}
|
||||||
onChange={(e) => setRenameValue(e.target.value)}
|
onChange={(e) => setRenameValue(e.target.value)}
|
||||||
style={{
|
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||||
width: "100px",
|
|
||||||
border: "1.5px solid black",
|
|
||||||
borderRadius: "2px",
|
|
||||||
fontSize: "10px"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="foldName">{data.Name}</span>
|
<span className="foldName">{data.Name}</span>
|
||||||
@@ -456,8 +400,8 @@ function DriveBody({
|
|||||||
onClick={() => handleMenuItemClick("Rename", data)}
|
onClick={() => handleMenuItemClick("Rename", data)}
|
||||||
className="ContextMenuItem"
|
className="ContextMenuItem"
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-font"></i>
|
<i className="fa-solid fa-font mr-[8px]"></i>
|
||||||
<span style={{ marginLeft: "8px" }}>Rename</span>
|
<span>Rename</span>
|
||||||
</ContextMenu.Item>
|
</ContextMenu.Item>
|
||||||
</ContextMenu.Content>
|
</ContextMenu.Content>
|
||||||
</ContextMenu.Portal>
|
</ContextMenu.Portal>
|
||||||
@@ -472,9 +416,10 @@ function DriveBody({
|
|||||||
<ContextMenu.Trigger className="ContextMenuTrigger">
|
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||||
<img
|
<img
|
||||||
alt="PDF"
|
alt="PDF"
|
||||||
className="pdf-icon"
|
className="w-full h-full"
|
||||||
src={pdfLogo}
|
src={pdfLogo}
|
||||||
onClick={() => checkPdfStatus(data)}
|
onClick={() => checkPdfStatus(data)}
|
||||||
|
data-tut={props.dataTutSixth}
|
||||||
/>
|
/>
|
||||||
{rename === data.objectId ? (
|
{rename === data.objectId ? (
|
||||||
<input
|
<input
|
||||||
@@ -493,12 +438,7 @@ function DriveBody({
|
|||||||
defaultValue={renameValue}
|
defaultValue={renameValue}
|
||||||
// value={renameValue}
|
// value={renameValue}
|
||||||
onChange={(e) => setRenameValue(e.target.value)}
|
onChange={(e) => setRenameValue(e.target.value)}
|
||||||
style={{
|
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||||
width: "100px",
|
|
||||||
border: "1.5px solid black",
|
|
||||||
borderRadius: "2px",
|
|
||||||
fontSize: "10px"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="fileName">{data.Name}</span>
|
<span className="fileName">{data.Name}</span>
|
||||||
@@ -543,7 +483,7 @@ function DriveBody({
|
|||||||
className="ContextMenuItem"
|
className="ContextMenuItem"
|
||||||
>
|
>
|
||||||
<i className={menu.icon}></i>
|
<i className={menu.icon}></i>
|
||||||
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
|
<span className="ml-[8px]">{menu.type}</span>
|
||||||
</ContextMenu.Item>
|
</ContextMenu.Item>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -554,21 +494,21 @@ function DriveBody({
|
|||||||
</HoverCard.Trigger>
|
</HoverCard.Trigger>
|
||||||
<HoverCard.Portal>
|
<HoverCard.Portal>
|
||||||
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
|
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
|
||||||
<strong style={{ fontSize: "13px" }}>Title: </strong>
|
<strong className="text-[13px]">Title: </strong>
|
||||||
<span className="statusSpan" style={{ marginBottom: "0px" }}>
|
<span className="statusSpan" style={{ marginBottom: "0px" }}>
|
||||||
{" "}
|
{" "}
|
||||||
{data.Name}
|
{data.Name}
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
<strong style={{ fontSize: "13px" }}>Status: </strong>
|
<strong className="text-[13px]">Status: </strong>
|
||||||
<span className="statusSpan"> {status}</span>
|
<span className="statusSpan"> {status}</span>
|
||||||
<br />
|
<br />
|
||||||
<strong style={{ fontSize: "13px" }}>Created Date: </strong>
|
<strong className="text-[13px]">Created Date: </strong>
|
||||||
<span className="statusSpan">{createddate}</span>
|
<span className="statusSpan">{createddate}</span>
|
||||||
<br />
|
<br />
|
||||||
{signerExist && (
|
{signerExist && (
|
||||||
<>
|
<>
|
||||||
<strong style={{ fontSize: "13px" }}>Signers: </strong>
|
<strong className="text-[13px]">Signers: </strong>
|
||||||
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
||||||
{signersName()}
|
{signersName()}
|
||||||
</>
|
</>
|
||||||
@@ -584,7 +524,7 @@ function DriveBody({
|
|||||||
//component to handle type of document and render according to type
|
//component to handle type of document and render according to type
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isList ? (
|
{props.isList ? (
|
||||||
<div className="container" style={{ overflowX: "auto" }}>
|
<div className="container" style={{ overflowX: "auto" }}>
|
||||||
<Table striped bordered hover>
|
<Table striped bordered hover>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -597,7 +537,7 @@ function DriveBody({
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{pdfData.map((data, ind) => {
|
{props.pdfData.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={ind}>
|
<React.Fragment key={ind}>
|
||||||
{handleFolderData(data, ind, "table")}
|
{handleFolderData(data, ind, "table")}
|
||||||
@@ -608,65 +548,41 @@ function DriveBody({
|
|||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="pdfContainer">
|
<div className="flex flex-row flex-wrap items-center mt-1 pb-[20px] mx-[5px]">
|
||||||
{pdfData.map((data, ind) => {
|
{props.pdfData.map((data, ind) => {
|
||||||
return (
|
return <div key={ind}>{handleFolderData(data, ind, "list")}</div>;
|
||||||
<div className="box" key={ind}>
|
|
||||||
{handleFolderData(data, ind, "list")}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isOpenMoveModal && (
|
{isOpenMoveModal && (
|
||||||
<SelectFolder
|
<FolderModal
|
||||||
onSuccess={handleMoveFolder}
|
onSuccess={handleMoveFolder}
|
||||||
isOpenModal={isOpenMoveModal}
|
isOpenModal={isOpenMoveModal}
|
||||||
folderCls={"contracts_Document"}
|
folderCls={"contracts_Document"}
|
||||||
setIsOpenMoveModal={setIsOpenMoveModal}
|
setIsOpenMoveModal={setIsOpenMoveModal}
|
||||||
setPdfData={setPdfData}
|
setPdfData={props.setPdfData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={isDeleteDoc}
|
isOpen={isDeleteDoc}
|
||||||
headerColor={themeColor}
|
|
||||||
title={"Delete Document"}
|
title={"Delete Document"}
|
||||||
handleClose={() => {
|
handleClose={() => setIsDeleteDoc(false)}
|
||||||
setIsDeleteDoc(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px] text-base-content">
|
||||||
<p>Are you sure you want to delete this document?</p>
|
<p>Are you sure you want to delete this document?</p>
|
||||||
|
<div className="h-[1px] w-full bg-[#9f9f9f] my-[15px]"></div>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => handleDeleteDocument(selectDoc)}
|
||||||
handleDeleteDocument(selectDoc);
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn"
|
className="op-btn op-btn-primary mr-2"
|
||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => setIsDeleteDoc(false)}
|
||||||
setIsDeleteDoc(false);
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-neutral"
|
||||||
>
|
>
|
||||||
No
|
No
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -8,11 +8,8 @@ const AddRoleModal = (props) => {
|
|||||||
isOpen={props.isModalRole}
|
isOpen={props.isModalRole}
|
||||||
handleClose={props.handleCloseRoleModal}
|
handleClose={props.handleCloseRoleModal}
|
||||||
>
|
>
|
||||||
<div className="addusercontainer">
|
<div className="h-full py-[10px] px-[20px]">
|
||||||
<form
|
<form className="flex flex-col" onSubmit={props.handleAddRole}>
|
||||||
style={{ display: "flex", flexDirection: "column" }}
|
|
||||||
onSubmit={props.handleAddRole}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
value={props.roleName}
|
value={props.roleName}
|
||||||
onChange={(e) => props.setRoleName(e.target.value)}
|
onChange={(e) => props.setRoleName(e.target.value)}
|
||||||
@@ -21,39 +18,20 @@ const AddRoleModal = (props) => {
|
|||||||
? "User " + (props.signersdata.length + 1)
|
? "User " + (props.signersdata.length + 1)
|
||||||
: "User 1"
|
: "User 1"
|
||||||
}
|
}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1"
|
||||||
/>
|
/>
|
||||||
<p
|
<p className="text-[gray] text-[11px] mt-[5px] mb-[10px] ml-[10px]">
|
||||||
style={{
|
e.g: Customer, Hr, Director, Manager, Student, etc...
|
||||||
color: "grey",
|
|
||||||
fontSize: 11,
|
|
||||||
margin: "10px 0 10px 5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
e.g: Hr, Director, Manager, New joinee, Accountant, etc...
|
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div className="h-[1px] w-full bg-[#9f9f9f] mb-[10px]"></div>
|
||||||
style={{
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
style={{
|
|
||||||
background: "#00a2b7"
|
|
||||||
}}
|
|
||||||
className="finishBtn"
|
|
||||||
>
|
|
||||||
Add
|
Add
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={props.handleCloseRoleModal}
|
onClick={props.handleCloseRoleModal}
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost ml-2"
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||||
const confirmToaddDefaultSign = () => {
|
const confirmToaddDefaultSign = () => {
|
||||||
if (xyPostion.length > 0) {
|
if (xyPostion.length > 0) {
|
||||||
@@ -17,55 +16,26 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div data-tut="reactourThird">
|
||||||
<div
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white",
|
|
||||||
padding: "5px",
|
|
||||||
fontFamily: "sans-serif",
|
|
||||||
marginTop: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Signature
|
Signature
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex flex-col items-center mt-[10px] font-semibold">
|
||||||
style={{
|
<p className="text-base-content">Your Signature</p>
|
||||||
display: "flex",
|
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
|
||||||
flexDirection: "column",
|
<img
|
||||||
alignItems: "center",
|
alt="signature"
|
||||||
marginTop: "10px",
|
className="w-full h-full object-contain"
|
||||||
fontWeight: "600"
|
src={defaultSignImg}
|
||||||
}}
|
/>
|
||||||
>
|
</div>
|
||||||
<>
|
<button
|
||||||
<p>Your Signature</p>
|
type="button"
|
||||||
<div className="defaultSignBox">
|
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
|
||||||
<img
|
onClick={() => confirmToaddDefaultSign()}
|
||||||
alt="default img"
|
>
|
||||||
style={{
|
Auto Sign All
|
||||||
width: "100%",
|
</button>
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain"
|
|
||||||
}}
|
|
||||||
src={defaultSignImg}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white",
|
|
||||||
marginTop: "20px",
|
|
||||||
cursor: "pointer",
|
|
||||||
marginBottom: "10px"
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="finishBtn finishnHover"
|
|
||||||
onClick={() => confirmToaddDefaultSign()}
|
|
||||||
>
|
|
||||||
Auto Sign All
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Loader from "../../primitives/LoaderWithMsg";
|
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
|
||||||
import { contractDocument } from "../../constant/Utils";
|
import { contractDocument } from "../../constant/Utils";
|
||||||
import HandleError from "../../primitives/HandleError";
|
import HandleError from "../../primitives/HandleError";
|
||||||
function DraftDocument() {
|
function DraftDocument() {
|
||||||
@@ -86,7 +86,7 @@ function DraftDocument() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{isLoading.isLoader ? (
|
{isLoading.isLoader ? (
|
||||||
<Loader isLoading={isLoading} />
|
<LoaderWithMsg isLoading={isLoading} />
|
||||||
) : (
|
) : (
|
||||||
<HandleError handleError={isLoading.message} />
|
<HandleError handleError={isLoading.message} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { themeColor } from "../../constant/const";
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { radioButtonWidget } from "../../constant/Utils";
|
import { radioButtonWidget } from "../../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
function DropdownWidgetOption(props) {
|
function DropdownWidgetOption(props) {
|
||||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||||
"option-1",
|
"option-1",
|
||||||
@@ -144,53 +146,34 @@ function DropdownWidgetOption(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//props.showDropdown
|
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
|
||||||
<ModalUi
|
<div className="h-full p-[15px] text-base-content">
|
||||||
// styleClass={"dropdownModal"}
|
|
||||||
isOpen={props.showDropdown}
|
|
||||||
title={props.title}
|
|
||||||
showClose={false}
|
|
||||||
>
|
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
|
||||||
<form
|
<form
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleSaveOption();
|
handleSaveOption();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="dropdownContainer">
|
<div>
|
||||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
<label className="text-[13px] font-semibold">
|
||||||
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
|
Name<span className="text-[red] text-[13px]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
defaultValue={dropdownName}
|
defaultValue={dropdownName}
|
||||||
value={dropdownName}
|
value={dropdownName}
|
||||||
onChange={(e) => setDropdownName(e.target.value)}
|
onChange={(e) => setDropdownName(e.target.value)}
|
||||||
className="drodown-input"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<label
|
<label className="text-[13px] font-semibold mt-[5px]">
|
||||||
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
|
|
||||||
>
|
|
||||||
Options
|
Options
|
||||||
</label>
|
</label>
|
||||||
|
<div className="flex flex-col">
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{dropdownOptionList.map((option, index) => (
|
{dropdownOptionList.map((option, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
className="flex flex-row mb-[5px] items-center"
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
marginBottom: "5px",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{props.type === "checkbox" && !props.isSignYourself && (
|
{props.type === "checkbox" && !props.isSignYourself && (
|
||||||
<input
|
<input
|
||||||
@@ -210,101 +193,95 @@ function DropdownWidgetOption(props) {
|
|||||||
setDefaultCheckbox(removeOption);
|
setDefaultCheckbox(removeOption);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
className="op-checkbox focus:outline-none hover:border-base-content mr-[5px]"
|
||||||
width: "23px",
|
|
||||||
height: "19px",
|
|
||||||
marginRight: "5px"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
required
|
required
|
||||||
className="drodown-input"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
type="text"
|
type="text"
|
||||||
value={option}
|
value={option}
|
||||||
onChange={(e) => handleInputChange(index, e.target.value)}
|
onChange={(e) => handleInputChange(index, e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-rectangle-xmark"
|
className="fa-solid fa-rectangle-xmark text-[25px] ml-[10px] text-error"
|
||||||
onClick={() => handleDeleteInput(index)}
|
onClick={() => handleDeleteInput(index)}
|
||||||
style={{
|
|
||||||
color: "red",
|
|
||||||
fontSize: "25px",
|
|
||||||
marginLeft: "10px"
|
|
||||||
}}
|
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<i
|
<i
|
||||||
onClick={handleAddInput}
|
onClick={handleAddInput}
|
||||||
style={{
|
className="fa-solid fa-square-plus text-[25px] ml-[10px] text-primary"
|
||||||
cursor: "pointer",
|
|
||||||
color: themeColor,
|
|
||||||
fontSize: "25px",
|
|
||||||
marginLeft: "10px"
|
|
||||||
}}
|
|
||||||
className="fa-solid fa-square-plus"
|
|
||||||
></i>
|
></i>
|
||||||
{props.type === "checkbox" && !props.isSignYourself && (
|
<div>
|
||||||
<>
|
{props.type === "checkbox" && !props.isSignYourself && (
|
||||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
<>
|
||||||
Minimun check
|
<label
|
||||||
</label>
|
className={`${
|
||||||
<input
|
!props.isSubscribe ? "text-[gray]" : ""
|
||||||
required
|
} text-[13px] font-semibold`}
|
||||||
defaultValue={0}
|
>
|
||||||
value={minCount}
|
Minimun check
|
||||||
onChange={(e) => {
|
</label>
|
||||||
const count = handleSetMinMax(e);
|
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
setMinCount(count);
|
<input
|
||||||
}}
|
required
|
||||||
className="drodown-input"
|
defaultValue={0}
|
||||||
/>
|
value={minCount}
|
||||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
onChange={(e) => {
|
||||||
Maximum check
|
const count = handleSetMinMax(e);
|
||||||
</label>
|
setMinCount(count);
|
||||||
<input
|
}}
|
||||||
required
|
className={`${
|
||||||
defaultValue={0}
|
props.isSubscribe || !isEnableSubscription
|
||||||
value={maxCount}
|
? ""
|
||||||
onChange={(e) => {
|
: "pointer-events-none bg-opacity-50"
|
||||||
const count = handleSetMinMax(e);
|
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||||
setMaxCount(count);
|
/>
|
||||||
}}
|
<label
|
||||||
className="drodown-input"
|
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>
|
</div>
|
||||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||||
<>
|
<>
|
||||||
<label
|
<label className="text-[13px] font-semibold mt-[5px]">
|
||||||
style={{
|
|
||||||
fontSize: "13px",
|
|
||||||
fontWeight: "600",
|
|
||||||
marginTop: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Default value
|
Default value
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
onChange={(e) => setDefaultValue(e.target.value)}
|
onChange={(e) => setDefaultValue(e.target.value)}
|
||||||
className="drodown-input"
|
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="defaultvalue"
|
name="defaultvalue"
|
||||||
value={defaultValue}
|
value={defaultValue}
|
||||||
placeholder="select default value"
|
placeholder="select default value"
|
||||||
>
|
>
|
||||||
<option value="" disabled hidden style={{ fontSize: "13px" }}>
|
<option value="" disabled hidden className="text-[13px]">
|
||||||
Select...
|
Select...
|
||||||
</option>
|
</option>
|
||||||
{dropdownOptionList.map((data, ind) => {
|
{dropdownOptionList.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<option
|
<option className="text-[13px]" key={ind} value={data}>
|
||||||
style={{ fontSize: "13px" }}
|
|
||||||
key={ind}
|
|
||||||
value={data}
|
|
||||||
>
|
|
||||||
{data}
|
{data}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
@@ -314,35 +291,21 @@ function DropdownWidgetOption(props) {
|
|||||||
)}
|
)}
|
||||||
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
|
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div className="flex flex-row gap-[10px] mt-[0.5rem]">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 10,
|
|
||||||
marginTop: "0.5rem"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{statusArr.map((data, ind) => {
|
{statusArr.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={ind}
|
key={ind}
|
||||||
style={{
|
className="flex flex-row gap-[5px] items-center"
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 5,
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
style={{ accentColor: "red" }}
|
className="op-radio op-radio-xs my-1"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="status"
|
name="status"
|
||||||
onChange={() => setStatus(data.toLowerCase())}
|
onChange={() => setStatus(data.toLowerCase())}
|
||||||
checked={status.toLowerCase() === data.toLowerCase()}
|
checked={status.toLowerCase() === data.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
<div className="text-[13px] font-500">{data}</div>
|
||||||
{data}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -350,64 +313,65 @@ function DropdownWidgetOption(props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{["checkbox", radioButtonWidget].includes(props.type) && (
|
{["checkbox", radioButtonWidget].includes(props.type) && (
|
||||||
<div className="flex flex-row gap-5 mt-2 items-center text-center">
|
<div className="flex flex-row gap-5 my-2 items-center text-center">
|
||||||
{!props.isSignYourself && (
|
{!props.isSignYourself && (
|
||||||
<div>
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
id="isreadonly"
|
id="isreadonly"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={isReadOnly}
|
checked={isReadOnly}
|
||||||
onChange={(e) => {
|
className="op-checkbox op-checkbox-sm"
|
||||||
setIsReadOnly(e.target.checked);
|
onChange={(e) => setIsReadOnly(e.target.checked)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<label className="ml-1" htmlFor="isreadonly">
|
<label className="ml-1 mb-0" htmlFor="isreadonly">
|
||||||
Is read only
|
Is read only
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div className="flex items-center">
|
||||||
<input
|
<input
|
||||||
id="ishidelabel"
|
id="ishidelabel"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={isHideLabel}
|
checked={isHideLabel}
|
||||||
onChange={(e) => {
|
className="op-checkbox op-checkbox-sm"
|
||||||
setIsHideLabel(e.target.checked);
|
onChange={(e) => setIsHideLabel(e.target.checked)}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<label className="ml-1" htmlFor="ishidelabel">
|
<label className="ml-1 mb-0" htmlFor="ishidelabel">
|
||||||
Hide labels
|
Hide labels
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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
|
<div
|
||||||
style={{
|
className={`${
|
||||||
height: "1px",
|
props.type === "checkbox" && !props.isSignYourself
|
||||||
backgroundColor: "#9f9f9f",
|
? "mb-[15px]"
|
||||||
width: "100%",
|
: "my-[15px]"
|
||||||
marginTop: "15px",
|
} w-full h-[1px] bg-[#9f9f9f]`}
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
// onClick={() => handleSaveOption()}
|
|
||||||
disabled={dropdownOptionList.length === 0 && true}
|
disabled={dropdownOptionList.length === 0 && true}
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
className={"finishBtn"}
|
className="op-btn op-btn-primary"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost ml-1"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.handleClose && props.handleClose();
|
props.handleClose && props.handleClose();
|
||||||
resetState();
|
resetState();
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import "../../styles/AddUser.css";
|
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";
|
// import SelectFolder from "../../premitives/SelectFolder";
|
||||||
|
|
||||||
const EditTemplate = ({ template, onSuccess }) => {
|
const EditTemplate = ({ template, onSuccess }) => {
|
||||||
@@ -8,9 +11,21 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
Name: template?.Name || "",
|
Name: template?.Name || "",
|
||||||
Note: template?.Note || "",
|
Note: template?.Note || "",
|
||||||
Description: template?.Description || "",
|
Description: template?.Description || "",
|
||||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
|
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) => {
|
const handleStrInput = (e) => {
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
};
|
};
|
||||||
@@ -24,34 +39,40 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||||
const data = { ...formData, SendinOrder: isChecked };
|
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);
|
onSuccess(data);
|
||||||
};
|
};
|
||||||
|
const handleAutoReminder = () => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
AutomaticReminders: !formData.AutomaticReminders
|
||||||
|
}));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="addusercontainer">
|
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||||
<div className="form-wrapper">
|
<div className="text-base-content">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
<label htmlFor="name" className="text-[13px]">
|
||||||
File
|
File
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div className="op-input op-input-bordered op-input-sm focus:outline-none py-2 font-semibold w-full text-xs">
|
||||||
style={{
|
{getFileName(template.URL)}
|
||||||
padding: "0.5rem 0.75rem",
|
|
||||||
border: "1px solid #d1d5db",
|
|
||||||
borderRadius: "0.375rem",
|
|
||||||
fontSize: "0.75rem",
|
|
||||||
fontWeight: "700"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{template.URL?.split("/")?.pop()?.split("_")[1]}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
<label htmlFor="name" className="text-[13px]">
|
||||||
Name
|
Name
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[13px] text-[red]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -59,11 +80,11 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
value={formData.Name}
|
value={formData.Name}
|
||||||
onChange={(e) => handleStrInput(e)}
|
onChange={(e) => handleStrInput(e)}
|
||||||
required
|
required
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label htmlFor="Note" style={{ fontSize: 13 }}>
|
<label htmlFor="Note" className="text-[13px]">
|
||||||
Note
|
Note
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -72,11 +93,11 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
id="Note"
|
id="Note"
|
||||||
value={formData.Note}
|
value={formData.Note}
|
||||||
onChange={(e) => handleStrInput(e)}
|
onChange={(e) => handleStrInput(e)}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label htmlFor="Description" style={{ fontSize: 13 }}>
|
<label htmlFor="Description" className="text-[13px]">
|
||||||
Description
|
Description
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -85,51 +106,85 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
id="Description"
|
id="Description"
|
||||||
value={formData.Description}
|
value={formData.Description}
|
||||||
onChange={(e) => handleStrInput(e)}
|
onChange={(e) => handleStrInput(e)}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label style={{ fontSize: 13 }}>Send In Order</label>
|
<label className="text-[13px]">Send In Order</label>
|
||||||
<div
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 8,
|
|
||||||
marginLeft: 8,
|
|
||||||
marginBottom: 5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={"true"}
|
value={"true"}
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
name="SendinOrder"
|
name="SendinOrder"
|
||||||
checked={formData.SendinOrder === "true"}
|
checked={formData.SendinOrder === "true"}
|
||||||
onChange={handleStrInput}
|
onChange={handleStrInput}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: 12 }}>Yes</div>
|
<div className="text-[12px]">Yes</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 8,
|
|
||||||
marginLeft: 8,
|
|
||||||
marginBottom: 5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
value={"false"}
|
value={"false"}
|
||||||
name="SendinOrder"
|
name="SendinOrder"
|
||||||
|
className="op-radio op-radio-xs"
|
||||||
checked={formData.SendinOrder === "false"}
|
checked={formData.SendinOrder === "false"}
|
||||||
onChange={handleStrInput}
|
onChange={handleStrInput}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: 12 }}>No</div>
|
<div className="text-[12px]">No</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
|
<div className="text-xs mt-2">
|
||||||
<div className="buttoncontainer">
|
{!isEnableSubscription && (
|
||||||
<button type="submit" className="submitbutton">
|
<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
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,24 +5,27 @@ import axios from "axios";
|
|||||||
import { getBase64FromUrl } from "../../constant/Utils";
|
import { getBase64FromUrl } from "../../constant/Utils";
|
||||||
import { themeColor } from "../../constant/const";
|
import { themeColor } from "../../constant/const";
|
||||||
import printModule from "print-js";
|
import printModule from "print-js";
|
||||||
import loader from "../../assets/images/loader2.gif";
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
function EmailComponent({
|
function EmailComponent({
|
||||||
isEmail,
|
isEmail,
|
||||||
pdfUrl,
|
pdfUrl,
|
||||||
isCeleb,
|
|
||||||
setIsEmail,
|
setIsEmail,
|
||||||
setSuccessEmail,
|
setSuccessEmail,
|
||||||
pdfName,
|
pdfName,
|
||||||
sender,
|
sender,
|
||||||
setIsAlert
|
setIsAlert,
|
||||||
|
extUserId,
|
||||||
|
activeMailAdapter
|
||||||
}) {
|
}) {
|
||||||
const [emailList, setEmailList] = useState([]);
|
const [emailList, setEmailList] = useState([]);
|
||||||
const [emailValue, setEmailValue] = useState();
|
const [emailValue, setEmailValue] = useState();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isEmailCelebration, setIsEmailCelebration] = useState(false);
|
||||||
//function for send email
|
//function for send email
|
||||||
const sendEmail = async () => {
|
const sendEmail = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
let sendMail;
|
let sendMail;
|
||||||
for (let i = 0; i < emailList.length; i++) {
|
for (let i = 0; i < emailList.length; i++) {
|
||||||
try {
|
try {
|
||||||
@@ -38,6 +41,8 @@ function EmailComponent({
|
|||||||
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||||
const themeBGcolor = themeColor;
|
const themeBGcolor = themeColor;
|
||||||
let params = {
|
let params = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
extUserId: extUserId,
|
||||||
pdfName: pdfName,
|
pdfName: pdfName,
|
||||||
url: pdfUrl,
|
url: pdfUrl,
|
||||||
recipient: emailList[i],
|
recipient: emailList[i],
|
||||||
@@ -57,6 +62,10 @@ function EmailComponent({
|
|||||||
" target=_blank>here</a> </p></div></div></body></html>"
|
" target=_blank>here</a> </p></div></div></body></html>"
|
||||||
};
|
};
|
||||||
sendMail = await axios.post(url, params, { headers: headers });
|
sendMail = await axios.post(url, params, { headers: headers });
|
||||||
|
setIsEmailCelebration(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsEmailCelebration(false);
|
||||||
|
}, 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
console.log("error", error);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -164,148 +173,59 @@ function EmailComponent({
|
|||||||
<div className="modaloverlay">
|
<div className="modaloverlay">
|
||||||
<div className="modalcontainer">
|
<div className="modalcontainer">
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div
|
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2] bg-opacity-80">
|
||||||
style={{
|
<Loader />
|
||||||
position: "absolute",
|
<span className="text-[12px] font-bold">
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
zIndex: "20",
|
|
||||||
backgroundColor: "#e6f2f2",
|
|
||||||
opacity: 0.8
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "70px", height: "70px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
|
|
||||||
This might take some time
|
This might take some time
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ background: "#32a3ac" }} className="modalheader">
|
<div style={{ background: "#32a3ac" }} className="modalheader">
|
||||||
<span style={{ color: "white" }}>Successfully signed!</span>
|
<span className="text-white">Successfully signed!</span>
|
||||||
|
<div className="flex flex-row">
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
||||||
<div></div>
|
<div></div>
|
||||||
{!isAndroid && (
|
{!isAndroid && (
|
||||||
<button
|
<button
|
||||||
onClick={handleToPrint}
|
onClick={handleToPrint}
|
||||||
style={{
|
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
className="btn btn-primary btn-sm"
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-print" aria-hidden="true"></i>
|
||||||
className="fa fa-print"
|
|
||||||
style={{
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px",
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Print
|
Print
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-danger btn-sm"
|
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||||
onClick={() => handleDownloadPdf()}
|
onClick={() => handleDownloadPdf()}
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
marginLeft: "10px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-download" aria-hidden="true"></i>
|
||||||
className="fa fa-download"
|
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Download
|
Download
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px]">
|
||||||
{isCeleb && (
|
{isEmailCelebration && (
|
||||||
<div
|
<div className="absolute w-[100%] flex justify-center items-center">
|
||||||
style={{
|
<img alt="celeb" width={300} height={250} src={celebration} />
|
||||||
position: "absolute",
|
|
||||||
marginLeft: "50px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="print img"
|
|
||||||
width={300}
|
|
||||||
height={250}
|
|
||||||
// style={styles.gifCeleb}
|
|
||||||
src={celebration}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p
|
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
|
||||||
style={{
|
|
||||||
fontFamily: "system-ui",
|
|
||||||
verticalAlign: "baseline",
|
|
||||||
fontWeight: "500",
|
|
||||||
color: "#403f3e",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginBottom: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Recipients added here will get a copy of the signed document.
|
Recipients added here will get a copy of the signed document.
|
||||||
</p>
|
</p>
|
||||||
{emailList.length > 0 ? (
|
{emailList.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<div className="addEmail">
|
<div className="addEmail">
|
||||||
<div
|
<div className="flex flex-row flex-wrap">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
|
|
||||||
flexWrap: "wrap"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{emailList.map((data, ind) => {
|
{emailList.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="emailChip"
|
className="flex flex-row items-center bg-[#47a3ad] m-[4px] rounded-md py-[5px] px-[10px]"
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
key={ind}
|
key={ind}
|
||||||
>
|
>
|
||||||
<span
|
<span className="text-white text-[13px]">
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "13px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{data}
|
{data}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
style={{
|
className="text-white text-[13px] font-semibold ml-[7px] cursor-pointer"
|
||||||
color: "white",
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: 600,
|
|
||||||
marginLeft: 7,
|
|
||||||
cursor: "pointer"
|
|
||||||
}}
|
|
||||||
onClick={() => removeChip(ind)}
|
onClick={() => removeChip(ind)}
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-xmark"></i>
|
<i className="fa-solid fa-xmark"></i>
|
||||||
@@ -318,7 +238,7 @@ function EmailComponent({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={emailValue}
|
value={emailValue}
|
||||||
className="addEmailInput"
|
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-white outline-none"
|
||||||
onChange={handleEmailValue}
|
onChange={handleEmailValue}
|
||||||
onKeyDown={handleEnterPress}
|
onKeyDown={handleEnterPress}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
@@ -336,7 +256,7 @@ function EmailComponent({
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={emailValue}
|
value={emailValue}
|
||||||
className="emailInput"
|
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-white border-2 border-[#47a3ad]"
|
||||||
onChange={handleEmailValue}
|
onChange={handleEmailValue}
|
||||||
onKeyDown={handleEnterPress}
|
onKeyDown={handleEnterPress}
|
||||||
placeholder="Add the email addresses"
|
placeholder="Add the email addresses"
|
||||||
@@ -349,67 +269,36 @@ function EmailComponent({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span
|
<button
|
||||||
style={{
|
className={`${
|
||||||
cursor: emailValue && "pointer"
|
emailValue ? "cursor-pointer" : "cursor-default"
|
||||||
}}
|
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (emailValue) {
|
if (emailValue) {
|
||||||
handleEnterPress("add");
|
handleEnterPress("add");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-plus" aria-hidden="true"></i>
|
||||||
style={{
|
</button>
|
||||||
backgroundColor: themeColor,
|
|
||||||
padding: "5px 7px",
|
|
||||||
marginTop: "10px",
|
|
||||||
color: "white",
|
|
||||||
borderRadius: "2px"
|
|
||||||
}}
|
|
||||||
className="fa fa-plus"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<div
|
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
|
||||||
style={{
|
<span className="font-bold">Note: </span>
|
||||||
background: "#e3e2e1",
|
<span className="text-[15px]">
|
||||||
marginTop: "10px",
|
|
||||||
padding: "5px",
|
|
||||||
borderRadius: "3px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ fontWeight: "700" }}>Note:</span>
|
|
||||||
<span style={{ fontSize: "15px" }}>
|
|
||||||
{" "}
|
|
||||||
You can only send to ten recipients at a time.
|
You can only send to ten recipients at a time.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
<button
|
||||||
disabled={emailList.length === 0 && true}
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
|
className="op-btn op-btn-primary"
|
||||||
onClick={() => sendEmail()}
|
onClick={() => emailList.length > 0 && sendEmail()}
|
||||||
>
|
>
|
||||||
Send
|
Send
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost ml-2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsEmail(false);
|
setIsEmail(false);
|
||||||
setEmailValue("");
|
setEmailValue("");
|
||||||
|
|||||||
@@ -1,26 +1,24 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import PrevNext from "./PrevNext";
|
import PrevNext from "./PrevNext";
|
||||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
import {
|
||||||
import Certificate from "./Certificate";
|
handleDownloadCertificate,
|
||||||
import printModule from "print-js";
|
handleDownloadPdf,
|
||||||
import { getBase64FromUrl } from "../../constant/Utils";
|
handleToPrint
|
||||||
import { saveAs } from "file-saver";
|
} from "../../constant/Utils";
|
||||||
import "../../styles/signature.css";
|
import "../../styles/signature.css";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { themeColor } from "../../constant/const";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
|
import Loader from "../../primitives/Loader";
|
||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
isPdfRequestFiles,
|
isPdfRequestFiles,
|
||||||
isPlaceholder,
|
isPlaceholder,
|
||||||
recipient,
|
|
||||||
setIsDecline,
|
setIsDecline,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
isAlreadySign,
|
|
||||||
allPages,
|
allPages,
|
||||||
changePage,
|
changePage,
|
||||||
pdfUrl,
|
pdfUrl,
|
||||||
documentStatus,
|
|
||||||
embedWidgetsData,
|
embedWidgetsData,
|
||||||
pdfDetails,
|
pdfDetails,
|
||||||
signerPos,
|
signerPos,
|
||||||
@@ -42,15 +40,9 @@ function Header({
|
|||||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||||
const isMobile = window.innerWidth < 767;
|
const isMobile = window.innerWidth < 767;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [isDownloading, setIsDownloading] = useState("");
|
||||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||||
//for go to previous page
|
|
||||||
function previousPage() {
|
|
||||||
changePage(-1);
|
|
||||||
}
|
|
||||||
//for go to next page
|
|
||||||
function nextPage() {
|
|
||||||
changePage(1);
|
|
||||||
}
|
|
||||||
//function for show decline alert
|
//function for show decline alert
|
||||||
const handleDeclinePdfAlert = async () => {
|
const handleDeclinePdfAlert = async () => {
|
||||||
const currentDecline = {
|
const currentDecline = {
|
||||||
@@ -60,143 +52,6 @@ function Header({
|
|||||||
setIsDecline(currentDecline);
|
setIsDecline(currentDecline);
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for print digital sign 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 = () => {
|
|
||||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
|
||||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sanitizeFileName = (pdfName) => {
|
|
||||||
// Replace spaces with underscore
|
|
||||||
return pdfName.replace(/ /g, "_");
|
|
||||||
};
|
|
||||||
//certificate generate and download component in mobile view
|
|
||||||
const CertificateDropDown = () => {
|
|
||||||
//after generate download certifcate pdf
|
|
||||||
const handleDownload = (pdfBlob, fileName) => {
|
|
||||||
if (pdfBlob) {
|
|
||||||
const url = window.URL.createObjectURL(pdfBlob);
|
|
||||||
// Create a temporary anchor element
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = fileName;
|
|
||||||
// Append the anchor to the body
|
|
||||||
document.body.appendChild(link);
|
|
||||||
// Programmatically click the anchor to trigger the download
|
|
||||||
link.click();
|
|
||||||
// Remove the anchor from the body
|
|
||||||
document.body.removeChild(link);
|
|
||||||
// Release the object URL to free up resources
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PDFDownloadLink
|
|
||||||
onClick={(e) => e.preventDefault()}
|
|
||||||
style={{ textDecoration: "none", zIndex: "35" }}
|
|
||||||
document={<Certificate pdfData={pdfDetails} />}
|
|
||||||
>
|
|
||||||
{({ blob, loading }) => (
|
|
||||||
<>
|
|
||||||
{loading ? (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border: "none",
|
|
||||||
backgroundColor: "#fff"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa-solid fa-award"
|
|
||||||
style={{ marginRight: "2px" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Certificate
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={{ border: "none", backgroundColor: "#fff" }}
|
|
||||||
onClick={() =>
|
|
||||||
handleDownload(
|
|
||||||
blob,
|
|
||||||
`completion certificate-${
|
|
||||||
pdfDetails[0] && pdfDetails[0].Name
|
|
||||||
}.pdf`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa-solid fa-award"
|
|
||||||
style={{ marginRight: "2px" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Certificate
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</PDFDownloadLink>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const CertificateComponent = () => {
|
|
||||||
return (
|
|
||||||
<PDFDownloadLink
|
|
||||||
style={{ textDecoration: "none" }}
|
|
||||||
document={<Certificate pdfData={pdfDetails} />}
|
|
||||||
fileName={`completion certificate-${
|
|
||||||
pdfDetails[0] && pdfDetails[0].Name
|
|
||||||
}.pdf`}
|
|
||||||
>
|
|
||||||
{({ loading }) => (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="defaultBtn certificateBtn"
|
|
||||||
disabled={loading}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa-solid fa-award"
|
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Certificate
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</PDFDownloadLink>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||||
{isMobile && isShowHeader ? (
|
{isMobile && isShowHeader ? (
|
||||||
@@ -209,64 +64,25 @@ function Header({
|
|||||||
: window.innerWidth - 30 + "px"
|
: window.innerWidth - 30 + "px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="preBtn2">
|
<div className="flex justify-between items-center py-[5px] px-[10px] ">
|
||||||
<div
|
<div onClick={() => navigate(-1)}>
|
||||||
onClick={() => {
|
|
||||||
navigate(-1);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
<i
|
||||||
className="fa fa-arrow-left"
|
className="fa fa-arrow-left text-base-content"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ color: themeColor, cursor: "pointer" }}
|
|
||||||
></i>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<i
|
|
||||||
onClick={() => {
|
|
||||||
if (pageNumber > 1) {
|
|
||||||
previousPage();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="fa fa-backward"
|
|
||||||
style={{ color: "gray", cursor: "pointer" }}
|
|
||||||
></i>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
fontSize: "13px",
|
|
||||||
fontWeight: "600",
|
|
||||||
marginLeft: "10px",
|
|
||||||
marginRight: "10px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
|
||||||
</span>
|
|
||||||
<i
|
|
||||||
onClick={() => {
|
|
||||||
if (pageNumber < allPages) {
|
|
||||||
nextPage();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className="fa fa-forward"
|
|
||||||
style={{ color: "gray", cursor: "pointer" }}
|
|
||||||
></i>
|
></i>
|
||||||
</div>
|
</div>
|
||||||
|
<PrevNext
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
allPages={allPages}
|
||||||
|
changePage={changePage}
|
||||||
|
/>
|
||||||
{pdfUrl && alreadySign ? (
|
{pdfUrl && alreadySign ? (
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<div
|
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
|
||||||
style={{
|
|
||||||
color: themeColor,
|
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px",
|
|
||||||
padding: "0px 3px 0px 5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content
|
<DropdownMenu.Content
|
||||||
className="DropdownMenuContent"
|
className="DropdownMenuContent"
|
||||||
@@ -274,74 +90,61 @@ function Header({
|
|||||||
>
|
>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
className="DropdownMenuItem"
|
className="DropdownMenuItem"
|
||||||
onClick={() => handleDownloadPdf()}
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex flex-row">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
<i
|
||||||
className="fa fa-arrow-down"
|
className="fa fa-arrow-down mr-[2px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ marginRight: "2px" }}
|
|
||||||
></i>
|
></i>
|
||||||
Download
|
Download
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
{recipient && pdfDetails[0] && pdfDetails.length > 0 ? (
|
{isCompleted && (
|
||||||
<DropdownMenu.Item className="DropdownMenuItem">
|
<DropdownMenu.Item
|
||||||
<CertificateDropDown />
|
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>
|
</DropdownMenu.Item>
|
||||||
) : isPdfRequestFiles &&
|
)}
|
||||||
alreadySign &&
|
{isSignYourself && (
|
||||||
isCompleted.isCertificate ? (
|
<DropdownMenu.Item
|
||||||
<DropdownMenu.Item className="DropdownMenuItem">
|
className="DropdownMenuItem"
|
||||||
<CertificateDropDown />
|
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>
|
||||||
) : (
|
|
||||||
isSignYourself && (
|
|
||||||
<>
|
|
||||||
<DropdownMenu.Item className="DropdownMenuItem">
|
|
||||||
<CertificateDropDown />
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
<DropdownMenu.Item
|
|
||||||
className="DropdownMenuItem"
|
|
||||||
onClick={() => setIsEmail(true)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa fa-envelope"
|
|
||||||
style={{ marginRight: "2px" }}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Mail
|
|
||||||
</div>
|
|
||||||
</DropdownMenu.Item>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
className="DropdownMenuItem"
|
className="DropdownMenuItem"
|
||||||
onClick={handleToPrint}
|
onClick={(e) =>
|
||||||
|
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex flex-row">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
<i
|
<i
|
||||||
className="fa fa-print"
|
className="fa fa-print mr-[2px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
style={{ marginRight: "2px" }}
|
|
||||||
></i>
|
></i>
|
||||||
Print
|
Print
|
||||||
</div>
|
</div>
|
||||||
@@ -350,24 +153,16 @@ function Header({
|
|||||||
</DropdownMenu.Portal>
|
</DropdownMenu.Portal>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: "flex", justifyContent: "space-around" }}>
|
<div className="flex justify-around items-center">
|
||||||
{/* current signer is checking user send request and check status of pdf sign than if current
|
{/* current signer is checking user send request and check status of pdf sign than if current
|
||||||
user exist than show finish button else no
|
user exist than show finish button else no
|
||||||
*/}
|
*/}
|
||||||
{currentSigner && (
|
{currentSigner && (
|
||||||
<>
|
<div className="flex items-center" data-tut="reactourFifth">
|
||||||
{decline && (
|
{decline && (
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
handleDeclinePdfAlert();
|
className="text-[red] border-none font-[650] text-[14px] mr-2"
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
color: "red",
|
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px",
|
|
||||||
marginRight: "10px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Decline
|
Decline
|
||||||
</div>
|
</div>
|
||||||
@@ -379,12 +174,9 @@ function Header({
|
|||||||
alertSendEmail();
|
alertSendEmail();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color: isMailSend ? "gray" : themeColor,
|
isMailSend ? "" : "op-link-primary"
|
||||||
border: "none",
|
} op-link no-underline font-[650] text-[14px]`}
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px"
|
|
||||||
}}
|
|
||||||
data-tut={dataTut4}
|
data-tut={dataTut4}
|
||||||
>
|
>
|
||||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||||
@@ -399,343 +191,259 @@ function Header({
|
|||||||
embedWidgetsData();
|
embedWidgetsData();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
|
||||||
color: themeColor,
|
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="signatureHeader headerBtn">
|
<div className="flex flex-wrap justify-between items-center mt-[5px]">
|
||||||
<PrevNext
|
<PrevNext
|
||||||
pageNumber={pageNumber}
|
pageNumber={pageNumber}
|
||||||
allPages={allPages}
|
allPages={allPages}
|
||||||
changePage={changePage}
|
changePage={changePage}
|
||||||
/>
|
/>
|
||||||
|
{isPlaceholder ? (
|
||||||
{recipient ? (
|
|
||||||
pdfUrl || isAlreadySign.mssg ? (
|
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
||||||
{pdfDetails && pdfDetails.length > 0 && (
|
|
||||||
<CertificateComponent />
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={handleToPrint}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
className="defaultBtn printBtn"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa fa-print"
|
|
||||||
style={{
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px",
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Print
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
className="defaultBtn downloadBtn"
|
|
||||||
onClick={() => handleDownloadPdf()}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fa fa-download"
|
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Download
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
||||||
<button
|
|
||||||
style={{
|
|
||||||
background: "#de4337"
|
|
||||||
}}
|
|
||||||
className="finishBtn hoverCss"
|
|
||||||
onClick={() => {
|
|
||||||
handleDeclinePdfAlert();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Decline
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
data-tut="reactourThird"
|
|
||||||
style={{
|
|
||||||
background: !pdfUrl ? "#188ae2" : "#d3edeb"
|
|
||||||
}}
|
|
||||||
className="finishBtn sendHover"
|
|
||||||
onClick={() => {
|
|
||||||
if (!pdfUrl) {
|
|
||||||
embedWidgetsData();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Finish
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
) : isPlaceholder ? (
|
|
||||||
<>
|
<>
|
||||||
{!isMailSend &&
|
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
|
||||||
signersdata.length > 0 &&
|
{!isMailSend &&
|
||||||
signersdata.length !== filterPrefill.length && (
|
signersdata.length > 0 &&
|
||||||
<div>
|
signersdata.length !== filterPrefill.length && (
|
||||||
{filterPrefill.length === 0 ? (
|
<div>
|
||||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
{filterPrefill.length === 0 ? (
|
||||||
Add {signersdata.length - filterPrefill.length}{" "}
|
<span className="text-[13px] text-[#f5405e]">
|
||||||
recipients signature
|
Add {signersdata.length - filterPrefill.length}{" "}
|
||||||
</span>
|
recipients signature
|
||||||
) : (
|
</span>
|
||||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
) : (
|
||||||
Add {signersdata.length - filterPrefill.length} more
|
<span className="text-[13px] text-[#f5405e]">
|
||||||
recipients signature
|
Add {signersdata.length - filterPrefill.length} more
|
||||||
</span>
|
recipients signature
|
||||||
)}
|
</span>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
<div>
|
</div>
|
||||||
|
<div className="flex">
|
||||||
{setIsEditTemplate && (
|
{setIsEditTemplate && (
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsEditTemplate(true)}
|
onClick={() => setIsEditTemplate(true)}
|
||||||
style={{
|
className="outline-none border-none text-center mr-[5px]"
|
||||||
border: "none",
|
|
||||||
outline: "none",
|
|
||||||
textAlign: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<i className="fa-solid fa-gear fa-lg"></i>
|
<i className="fa-solid fa-gear fa-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => navigate(-1)}
|
||||||
navigate(-1);
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn backBtn"
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
disabled={isMailSend && true}
|
disabled={isMailSend && true}
|
||||||
data-tut="reactourFour"
|
data-tut="reactourFour"
|
||||||
style={{
|
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||||
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
|
onClick={() => alertSendEmail()}
|
||||||
color: isMailSend && "rgb(77, 75, 75)"
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
alertSendEmail();
|
|
||||||
}}
|
|
||||||
className={isMailSend ? "sendMail" : "sendMail sendHover"}
|
|
||||||
>
|
>
|
||||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
{completeBtnTitle
|
||||||
|
? completeBtnTitle
|
||||||
|
: isMailSend
|
||||||
|
? "Sent"
|
||||||
|
: "Send"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : isPdfRequestFiles ? (
|
) : isPdfRequestFiles ? (
|
||||||
alreadySign ? (
|
alreadySign ? (
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
<div className="flex flex-row">
|
||||||
{isCompleted.isCertificate && <CertificateComponent />}
|
{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
|
<button
|
||||||
onClick={handleToPrint}
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn printBtn"
|
className="op-btn op-btn-neutral op-btn-sm mr-[5px] shadow"
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
|
||||||
className="fa fa-print"
|
<span className="hidden lg:block">Print</span>
|
||||||
style={{
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px",
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Print
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn downloadBtn"
|
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||||
style={{
|
onClick={() =>
|
||||||
display: "flex",
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
flexDirection: "row",
|
}
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
onClick={() => handleDownloadPdf()}
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||||
className="fa fa-download"
|
<span className="hidden lg:block">Download</span>
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Download
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className="flex" data-tut="reactourFifth">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => navigate(-1)}
|
||||||
navigate(-1);
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn backBtn"
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
{currentSigner && (
|
{currentSigner && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
style={{
|
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
|
||||||
background: "#de4337"
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
}}
|
|
||||||
className="finishBtn hoverCss"
|
|
||||||
onClick={() => {
|
|
||||||
handleDeclinePdfAlert();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Decline
|
Decline
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
style={{
|
|
||||||
background: "#188ae2"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn sendHover"
|
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||||
onClick={() => {
|
onClick={() => embedWidgetsData()}
|
||||||
embedWidgetsData();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
</button>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
|
) : isCompleted ? (
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
<div className="flex flex-row">
|
||||||
<CertificateComponent />
|
{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
|
<button
|
||||||
onClick={handleToPrint}
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
type="button"
|
type="button"
|
||||||
style={{
|
className="op-btn op-btn-neutral op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
className="defaultBtn printBtn"
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-print" aria-hidden="true"></i>
|
||||||
className="fa fa-print"
|
<span className="hidden lg:block">Print</span>
|
||||||
style={{
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px",
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Print
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn downloadBtn"
|
className="op-btn op-btn-primary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
style={{
|
onClick={() =>
|
||||||
display: "flex",
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
flexDirection: "row",
|
}
|
||||||
alignItems: "center",
|
|
||||||
marginLeft: "10px"
|
|
||||||
}}
|
|
||||||
onClick={() => handleDownloadPdf()}
|
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-download" aria-hidden="true"></i>
|
||||||
className="fa fa-download"
|
<span className="hidden lg:block">Download</span>
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Download
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn mailBtn"
|
className="op-btn op-btn-info op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
marginLeft: "10px"
|
|
||||||
}}
|
|
||||||
onClick={() => setIsEmail(true)}
|
onClick={() => setIsEmail(true)}
|
||||||
>
|
>
|
||||||
<i
|
<i className="fa fa-envelope" aria-hidden="true"></i>
|
||||||
className="fa fa-envelope"
|
<span className="hidden lg:block">Mail</span>
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
fontSize: "15px",
|
|
||||||
marginRight: "3px"
|
|
||||||
}}
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
Mail
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className="flex">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => navigate(-1)}
|
||||||
navigate(-1);
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="defaultBtn backBtn"
|
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
style={{
|
|
||||||
background: "#188ae2"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn sendHover"
|
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!pdfUrl) {
|
if (!pdfUrl) {
|
||||||
embedWidgetsData();
|
embedWidgetsData();
|
||||||
@@ -748,6 +456,31 @@ function Header({
|
|||||||
)}
|
)}
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import PlaceholderBorder from "./PlaceholderBorder";
|
|||||||
import { Rnd } from "react-rnd";
|
import { Rnd } from "react-rnd";
|
||||||
import {
|
import {
|
||||||
defaultWidthHeight,
|
defaultWidthHeight,
|
||||||
|
handleCopyNextToWidget,
|
||||||
isMobile,
|
isMobile,
|
||||||
onChangeInput,
|
onChangeInput,
|
||||||
radioButtonWidget,
|
radioButtonWidget,
|
||||||
@@ -274,6 +275,7 @@ function Placeholder(props) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleOnClickPlaceholder = () => {
|
const handleOnClickPlaceholder = () => {
|
||||||
|
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
||||||
if (!props.isNeedSign) {
|
if (!props.isNeedSign) {
|
||||||
props.setWidgetType(props.pos.type);
|
props.setWidgetType(props.pos.type);
|
||||||
}
|
}
|
||||||
@@ -302,8 +304,14 @@ function Placeholder(props) {
|
|||||||
props.setWidgetType(props.pos.type);
|
props.setWidgetType(props.pos.type);
|
||||||
props.setCurrWidgetsDetails(props.pos);
|
props.setCurrWidgetsDetails(props.pos);
|
||||||
};
|
};
|
||||||
//function ro set state value of onclick on widget's copy icon
|
//function to set required state value onclick on widget's copy icon
|
||||||
const handleCopyPlaceholder = (e) => {
|
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) {
|
if (props.data && props?.pos?.type !== textWidget) {
|
||||||
props.setSignerObjId(props?.data?.signerObjId);
|
props.setSignerObjId(props?.data?.signerObjId);
|
||||||
props.setUniqueId(props?.data?.Id);
|
props.setUniqueId(props?.data?.Id);
|
||||||
@@ -312,9 +320,24 @@ function Placeholder(props) {
|
|||||||
props.setSignerObjId(props?.data?.signerObjId);
|
props.setSignerObjId(props?.data?.signerObjId);
|
||||||
props.setUniqueId(props?.data?.Id);
|
props.setUniqueId(props?.data?.Id);
|
||||||
}
|
}
|
||||||
e.stopPropagation();
|
|
||||||
props.setIsPageCopy(true);
|
//checking widget's type and open widget copy modal for required widgets
|
||||||
props.setSignKey(props.pos.key);
|
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
|
//function to save date and format on local array onchange date and onclick format
|
||||||
@@ -393,7 +416,7 @@ function Placeholder(props) {
|
|||||||
|
|
||||||
{props.pos.type !== textWidget && !props.isSignYourself && (
|
{props.pos.type !== textWidget && !props.isSignYourself && (
|
||||||
<i
|
<i
|
||||||
data-tut="reactourLinkUser"
|
data-tut="assignSigner"
|
||||||
className="fa-regular fa-user signUserIcon"
|
className="fa-regular fa-user signUserIcon"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -519,7 +542,15 @@ function Placeholder(props) {
|
|||||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||||
key={props.pos.key}
|
key={props.pos.key}
|
||||||
lockAspectRatio={
|
lockAspectRatio={
|
||||||
props.pos.type !== textWidget &&
|
// props.pos.type !== textWidget
|
||||||
|
![
|
||||||
|
textWidget,
|
||||||
|
"email",
|
||||||
|
"name",
|
||||||
|
"company",
|
||||||
|
"job title",
|
||||||
|
textInputWidget
|
||||||
|
].includes(props.pos.type) &&
|
||||||
(props.pos.Width
|
(props.pos.Width
|
||||||
? props.pos.Width / props.pos.Height
|
? props.pos.Width / props.pos.Height
|
||||||
: defaultWidthHeight(props.pos.type).width /
|
: defaultWidthHeight(props.pos.type).width /
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
import ModalUi from "../../primitives/ModalUi";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { randomId, textWidget } from "../../constant/Utils";
|
import { randomId, textWidget } from "../../constant/Utils";
|
||||||
|
|
||||||
@@ -195,13 +194,13 @@ function PlaceholderCopy(props) {
|
|||||||
props.setIsPageCopy(false);
|
props.setIsPageCopy(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px] text-base-content">
|
||||||
{copyType.map((data, key) => {
|
{copyType.map((data, key) => {
|
||||||
return (
|
return (
|
||||||
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
|
<div key={key} className="flex flex-col">
|
||||||
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
|
<label className="text-[16px] font-medium">
|
||||||
<input
|
<input
|
||||||
style={{ accentColor: "red", marginRight: "10px" }}
|
className="mr-[8px] op-radio op-radio-xs"
|
||||||
type="radio"
|
type="radio"
|
||||||
value={data}
|
value={data}
|
||||||
onChange={() => setSelectCopyType(data)}
|
onChange={() => setSelectCopyType(data)}
|
||||||
@@ -214,33 +213,22 @@ function PlaceholderCopy(props) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<div
|
<div className="flex bg-[#9f9f9f] w-full my-[15px]"></div>
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleApplyCopy();
|
handleApplyCopy();
|
||||||
handleUniqueId();
|
handleUniqueId();
|
||||||
}}
|
}}
|
||||||
style={{ background: themeColor }}
|
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!selectCopyType}
|
disabled={!selectCopyType}
|
||||||
className="finishBtn"
|
className="op-btn op-btn-primary"
|
||||||
>
|
>
|
||||||
Apply
|
Apply
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost"
|
||||||
onClick={() => {
|
onClick={() => handleUniqueId()}
|
||||||
handleUniqueId();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
getMonth,
|
getMonth,
|
||||||
getYear,
|
getYear,
|
||||||
isMobile,
|
isMobile,
|
||||||
|
onChangeHeightOfTextArea,
|
||||||
onChangeInput,
|
onChangeInput,
|
||||||
radioButtonWidget,
|
radioButtonWidget,
|
||||||
range,
|
range,
|
||||||
@@ -37,15 +38,17 @@ function PlaceholderType(props) {
|
|||||||
"December"
|
"December"
|
||||||
];
|
];
|
||||||
const validateExpression = (regexValidation) => {
|
const validateExpression = (regexValidation) => {
|
||||||
let regexObject = regexValidation;
|
if (textValue && regexValidation) {
|
||||||
if (props.pos?.options.validation.type === "regex") {
|
let regexObject = regexValidation;
|
||||||
regexObject = RegexParser(regexValidation);
|
if (props.pos?.options?.validation?.type === "regex") {
|
||||||
}
|
regexObject = RegexParser(regexValidation);
|
||||||
// new RegExp(regexValidation);
|
}
|
||||||
let isValidate = regexObject.test(textValue);
|
// new RegExp(regexValidation);
|
||||||
if (!isValidate) {
|
let isValidate = regexObject.test(textValue);
|
||||||
props?.setValidateAlert(true);
|
if (!isValidate) {
|
||||||
inputRef.current.focus();
|
props?.setValidateAlert(true);
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,22 +56,18 @@ function PlaceholderType(props) {
|
|||||||
props.setDraggingEnabled(true);
|
props.setDraggingEnabled(true);
|
||||||
const validateType = props.pos?.options?.validation?.type;
|
const validateType = props.pos?.options?.validation?.type;
|
||||||
let regexValidation;
|
let regexValidation;
|
||||||
if (validateType) {
|
if (validateType && validateType !== "text") {
|
||||||
switch (validateType) {
|
switch (validateType) {
|
||||||
case "email":
|
case "email":
|
||||||
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
validateExpression(regexValidation);
|
validateExpression(regexValidation);
|
||||||
break;
|
break;
|
||||||
case "number":
|
case "number":
|
||||||
regexValidation = /^\d+$/;
|
regexValidation = /^[0-9\s]*$/;
|
||||||
validateExpression(regexValidation);
|
|
||||||
break;
|
|
||||||
case "text":
|
|
||||||
regexValidation = /^[a-zA-Z\s]+$/;
|
|
||||||
validateExpression(regexValidation);
|
validateExpression(regexValidation);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
regexValidation = props.pos?.options.validation.pattern;
|
regexValidation = props.pos?.options?.validation?.pattern || "";
|
||||||
validateExpression(regexValidation);
|
validateExpression(regexValidation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,11 +85,11 @@ function PlaceholderType(props) {
|
|||||||
case "number":
|
case "number":
|
||||||
setValidatePlaceholder("12345");
|
setValidatePlaceholder("12345");
|
||||||
break;
|
break;
|
||||||
case textInputWidget:
|
case "text":
|
||||||
setValidatePlaceholder("enter text");
|
setValidatePlaceholder("please enter text");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
setValidatePlaceholder("enter text");
|
setValidatePlaceholder("please enter value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ function PlaceholderType(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.pos]);
|
}, [props.pos?.options?.defaultValue]);
|
||||||
|
|
||||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||||
<div
|
<div
|
||||||
@@ -285,11 +284,28 @@ function PlaceholderType(props) {
|
|||||||
};
|
};
|
||||||
props.handleSaveDate(dateObj, isDateChange);
|
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) {
|
switch (type) {
|
||||||
case "signature":
|
case "signature":
|
||||||
return props.pos.SignUrl ? (
|
return props.pos.SignUrl ? (
|
||||||
<img
|
<img
|
||||||
alt="signimg"
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
src={props.pos.SignUrl}
|
src={props.pos.SignUrl}
|
||||||
style={{
|
style={{
|
||||||
width: "99%",
|
width: "99%",
|
||||||
@@ -311,6 +327,7 @@ function PlaceholderType(props) {
|
|||||||
return props.pos.SignUrl ? (
|
return props.pos.SignUrl ? (
|
||||||
<img
|
<img
|
||||||
alt="signimg"
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
src={props.pos.SignUrl}
|
src={props.pos.SignUrl}
|
||||||
style={{
|
style={{
|
||||||
width: "99%",
|
width: "99%",
|
||||||
@@ -335,12 +352,9 @@ function PlaceholderType(props) {
|
|||||||
return (
|
return (
|
||||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||||
<input
|
<input
|
||||||
style={{
|
className={`${
|
||||||
width: props.pos.Width,
|
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||||
display: "flex",
|
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
|
||||||
justifyContent: "center",
|
|
||||||
marginTop: ind === 0 ? 0 : "5px"
|
|
||||||
}}
|
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
disabled={
|
disabled={
|
||||||
props.isNeedSign &&
|
props.isNeedSign &&
|
||||||
@@ -384,19 +398,12 @@ function PlaceholderType(props) {
|
|||||||
case textInputWidget:
|
case textInputWidget:
|
||||||
return props.isSignYourself ||
|
return props.isSignYourself ||
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
<input
|
<textarea
|
||||||
className="inputPlaceholder"
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={validatePlaceholder}
|
placeholder={validatePlaceholder || "please enter value"}
|
||||||
style={{ fontSize: calculateFontSize() }}
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
value={textValue}
|
value={textValue}
|
||||||
type="text"
|
|
||||||
tabIndex="0"
|
|
||||||
disabled={
|
|
||||||
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
|
|
||||||
? true
|
|
||||||
: props.isPlaceholder
|
|
||||||
}
|
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setTextValue(e.target.value);
|
setTextValue(e.target.value);
|
||||||
@@ -410,6 +417,13 @@ function PlaceholderType(props) {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -426,7 +440,7 @@ function PlaceholderType(props) {
|
|||||||
<select
|
<select
|
||||||
className="inputPlaceholder"
|
className="inputPlaceholder"
|
||||||
id="myDropdown"
|
id="myDropdown"
|
||||||
style={{ fontSize: "12px" }}
|
style={{ fontSize: "12px", color: "initial" }}
|
||||||
value={selectOption}
|
value={selectOption}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSelectOption(e.target.value);
|
setSelectOption(e.target.value);
|
||||||
@@ -443,7 +457,7 @@ function PlaceholderType(props) {
|
|||||||
>
|
>
|
||||||
{/* Default/Title option */}
|
{/* Default/Title option */}
|
||||||
<option value="" disabled hidden>
|
<option value="" disabled hidden>
|
||||||
{props.pos.options.name}
|
{props?.pos?.options?.name}
|
||||||
</option>
|
</option>
|
||||||
|
|
||||||
{props.pos?.options?.values.map((data, ind) => {
|
{props.pos?.options?.values.map((data, ind) => {
|
||||||
@@ -466,6 +480,7 @@ function PlaceholderType(props) {
|
|||||||
return props.pos.SignUrl ? (
|
return props.pos.SignUrl ? (
|
||||||
<img
|
<img
|
||||||
alt="signimg"
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
src={props.pos.SignUrl}
|
src={props.pos.SignUrl}
|
||||||
style={{
|
style={{
|
||||||
width: "99%",
|
width: "99%",
|
||||||
@@ -486,15 +501,12 @@ function PlaceholderType(props) {
|
|||||||
case "name":
|
case "name":
|
||||||
return props.isSignYourself ||
|
return props.isSignYourself ||
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
<input
|
<textarea
|
||||||
tabIndex="0"
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={"name"}
|
placeholder={"name"}
|
||||||
style={{ fontSize: calculateFontSize() }}
|
rows={1}
|
||||||
className="inputPlaceholder"
|
onKeyDown={handleEnterPress}
|
||||||
type="text"
|
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const isDefault = false;
|
const isDefault = false;
|
||||||
handleTextValid(e);
|
handleTextValid(e);
|
||||||
@@ -508,6 +520,13 @@ function PlaceholderType(props) {
|
|||||||
isDefault
|
isDefault
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -522,14 +541,12 @@ function PlaceholderType(props) {
|
|||||||
case "company":
|
case "company":
|
||||||
return props.isSignYourself ||
|
return props.isSignYourself ||
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
<input
|
<textarea
|
||||||
className="inputPlaceholder"
|
|
||||||
type="text"
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={"company"}
|
placeholder="Enter text"
|
||||||
style={{ fontSize: calculateFontSize() }}
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleTextValid(e);
|
handleTextValid(e);
|
||||||
onChangeInput(
|
onChangeInput(
|
||||||
@@ -542,6 +559,13 @@ function PlaceholderType(props) {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -556,14 +580,12 @@ function PlaceholderType(props) {
|
|||||||
case "job title":
|
case "job title":
|
||||||
return props.isSignYourself ||
|
return props.isSignYourself ||
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
<input
|
<textarea
|
||||||
className="inputPlaceholder"
|
|
||||||
type="text"
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={"job title"}
|
placeholder={"job title"}
|
||||||
style={{ fontSize: calculateFontSize() }}
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onBlur={handleInputBlur}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
handleTextValid(e);
|
handleTextValid(e);
|
||||||
onChangeInput(
|
onChangeInput(
|
||||||
@@ -576,6 +598,13 @@ function PlaceholderType(props) {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -651,6 +680,7 @@ function PlaceholderType(props) {
|
|||||||
return props.pos.SignUrl ? (
|
return props.pos.SignUrl ? (
|
||||||
<img
|
<img
|
||||||
alt="signimg"
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
src={props.pos.SignUrl}
|
src={props.pos.SignUrl}
|
||||||
style={{
|
style={{
|
||||||
width: "99%",
|
width: "99%",
|
||||||
@@ -671,12 +701,11 @@ function PlaceholderType(props) {
|
|||||||
case "email":
|
case "email":
|
||||||
return props.isSignYourself ||
|
return props.isSignYourself ||
|
||||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||||
<input
|
<textarea
|
||||||
className="inputPlaceholder"
|
|
||||||
type="text"
|
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={"email"}
|
placeholder={"enter email"}
|
||||||
style={{ fontSize: calculateFontSize() }}
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -691,6 +720,13 @@ function PlaceholderType(props) {
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
className={
|
||||||
|
isMobile
|
||||||
|
? "labelTextArea labelWidthMobile"
|
||||||
|
: "labelTextArea labelWidthDesktop"
|
||||||
|
}
|
||||||
|
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||||
|
cols="50"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
@@ -709,12 +745,11 @@ function PlaceholderType(props) {
|
|||||||
return (
|
return (
|
||||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||||
<input
|
<input
|
||||||
style={{
|
className={`${ind === 0 ? "mt-0" : "mt-[5px]"} ${
|
||||||
width: props.pos.Width,
|
props?.pos?.Width
|
||||||
display: "flex",
|
? `w-[${props?.pos?.Width}px] h-[${props?.pos?.Width}px]`
|
||||||
justifyContent: "center",
|
: "w-[15px] h-[15px]"
|
||||||
marginTop: ind === 0 ? 0 : "5px"
|
} flex justify-center op-radio`}
|
||||||
}}
|
|
||||||
type="radio"
|
type="radio"
|
||||||
disabled={
|
disabled={
|
||||||
props.isNeedSign &&
|
props.isNeedSign &&
|
||||||
@@ -739,8 +774,9 @@ function PlaceholderType(props) {
|
|||||||
case textWidget:
|
case textWidget:
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
placeholder="Enter label"
|
placeholder="Enter text"
|
||||||
rows={1}
|
rows={1}
|
||||||
|
onKeyDown={handleEnterPress}
|
||||||
value={textValue}
|
value={textValue}
|
||||||
onBlur={handleInputBlur}
|
onBlur={handleInputBlur}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -760,7 +796,11 @@ function PlaceholderType(props) {
|
|||||||
? "labelTextArea labelWidthMobile"
|
? "labelTextArea labelWidthMobile"
|
||||||
: "labelTextArea labelWidthDesktop"
|
: "labelTextArea labelWidthDesktop"
|
||||||
}
|
}
|
||||||
style={{ whiteSpace: "pre-wrap" }}
|
style={{
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
color: "initial"
|
||||||
|
}}
|
||||||
cols="50"
|
cols="50"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -769,6 +809,7 @@ function PlaceholderType(props) {
|
|||||||
<div style={{ pointerEvents: "none" }}>
|
<div style={{ pointerEvents: "none" }}>
|
||||||
<img
|
<img
|
||||||
alt="signimg"
|
alt="signimg"
|
||||||
|
draggable="false"
|
||||||
src={props.pos.SignUrl}
|
src={props.pos.SignUrl}
|
||||||
style={{
|
style={{
|
||||||
width: "99%",
|
width: "99%",
|
||||||
|
|||||||
@@ -11,46 +11,30 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex items-center">
|
||||||
<div className="preBtn1">
|
<button
|
||||||
<button
|
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||||
style={{
|
disabled={pageNumber <= 1}
|
||||||
padding: "3px 20px ",
|
onClick={previousPage}
|
||||||
fontSize: "10px",
|
>
|
||||||
background: "#d3edeb",
|
<span className="block lg:hidden">
|
||||||
fontWeight: "600",
|
<i className="fa fa-backward" aria-hidden="true"></i>
|
||||||
|
|
||||||
border: "0px",
|
|
||||||
marginRight: "5px"
|
|
||||||
}}
|
|
||||||
disabled={pageNumber <= 1}
|
|
||||||
onClick={previousPage}
|
|
||||||
>
|
|
||||||
Prev
|
|
||||||
</button>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
fontSize: "13px",
|
|
||||||
fontWeight: "600"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
|
||||||
</span>
|
</span>
|
||||||
<button
|
<span className="lg:block hidden">Prev</span>
|
||||||
style={{
|
</button>
|
||||||
padding: "3px 20px ",
|
<span className="text-xs text-base-content font-medium mx-2">
|
||||||
fontSize: "10px",
|
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
||||||
background: "#d3edeb",
|
</span>
|
||||||
fontWeight: "600",
|
<button
|
||||||
marginLeft: "5px",
|
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||||
border: "0px"
|
disabled={pageNumber >= allPages}
|
||||||
}}
|
onClick={nextPage}
|
||||||
disabled={pageNumber >= allPages}
|
>
|
||||||
onClick={nextPage}
|
<span className="block lg:hidden">
|
||||||
>
|
<i className="fa fa-forward" aria-hidden="true"></i>
|
||||||
Next
|
</span>
|
||||||
</button>
|
<span className="lg:block hidden">Next</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,126 +1,137 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { darkenColor, getFirstLetter } from "../../constant/Utils";
|
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 RecipientList = (props) => {
|
||||||
const color = [
|
const [animationParent] = useAutoAnimate();
|
||||||
"#93a3db",
|
|
||||||
"#e6c3db",
|
|
||||||
"#c0e3bc",
|
|
||||||
"#bce3db",
|
|
||||||
"#b8ccdb",
|
|
||||||
"#ceb8db",
|
|
||||||
"#ffccff",
|
|
||||||
"#99ffcc",
|
|
||||||
"#cc99ff",
|
|
||||||
"#ffcc99",
|
|
||||||
"#66ccff",
|
|
||||||
"#ffffcc"
|
|
||||||
];
|
|
||||||
|
|
||||||
const nameColor = [
|
|
||||||
"#304fbf",
|
|
||||||
"#7d5270",
|
|
||||||
"#5f825b",
|
|
||||||
"#578077",
|
|
||||||
"#576e80",
|
|
||||||
"#6d527d",
|
|
||||||
"#cc00cc",
|
|
||||||
"#006666",
|
|
||||||
"#cc00ff",
|
|
||||||
"#ff9900",
|
|
||||||
"#336699",
|
|
||||||
"#cc9900"
|
|
||||||
];
|
|
||||||
const [isHover, setIsHover] = useState();
|
const [isHover, setIsHover] = useState();
|
||||||
const [isEdit, setIsEdit] = useState(false);
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
//function for onhover signer name change background color
|
//function for onhover signer name change background color
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const onHoverStyle = (ind, blockColor) => {
|
|
||||||
const style = {
|
|
||||||
background: blockColor ? blockColor : color[ind % color.length],
|
|
||||||
padding: "10px",
|
|
||||||
marginTop: "2px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
borderBottom: "1px solid #e3e1e1",
|
|
||||||
alignItems: "center"
|
|
||||||
};
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
//function for onhover signer name remove background color
|
|
||||||
const nonHoverStyle = () => {
|
|
||||||
const style = {
|
|
||||||
padding: "10px",
|
|
||||||
marginTop: "2px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
borderBottom: "1px solid #e3e1e1",
|
|
||||||
alignItems: "center"
|
|
||||||
};
|
|
||||||
return style;
|
|
||||||
};
|
|
||||||
const isWidgetExist = (Id) => {
|
const isWidgetExist = (Id) => {
|
||||||
return props.signerPos.some((x) => x.Id === 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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.signersdata.length > 0 &&
|
{props.signersdata.length > 0 &&
|
||||||
props.signersdata.map((obj, ind) => {
|
props.signersdata.map((obj, ind) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<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"
|
data-tut="reactourFirst"
|
||||||
onMouseEnter={() => setIsHover(ind)}
|
onMouseEnter={() => setIsHover(ind)}
|
||||||
onMouseLeave={() => setIsHover(null)}
|
onMouseLeave={() => setIsHover(null)}
|
||||||
key={ind}
|
className={`${
|
||||||
style={
|
props.sendInOrder
|
||||||
isHover === ind || props.isSelectListId === ind
|
? props.isMailSend
|
||||||
? onHoverStyle(ind, obj.blockColor)
|
? "pointer-events-none bg-opacity-80"
|
||||||
: nonHoverStyle(ind)
|
: `text-[12px] font-bold ${cursor}`
|
||||||
}
|
: props.isMailSend && "pointer-events-none bg-opacity-80"
|
||||||
onClick={() => {
|
} 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.setSignerObjId(obj?.objectId || "");
|
||||||
props.setIsSelectId(ind);
|
props.setIsSelectId(ind);
|
||||||
props.setContractName(obj?.className || "");
|
props.setContractName(obj?.className || "");
|
||||||
props.setUniqueId(obj.Id);
|
props.setUniqueId(obj.Id);
|
||||||
props.setRoleName(obj.Role);
|
props.setRoleName(obj.Role);
|
||||||
|
props.setBlockColor(obj?.blockColor);
|
||||||
if (props.handleModal) {
|
if (props.handleModal) {
|
||||||
props.handleModal();
|
props.handleModal();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex flex-row items-center w-full">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
width: "100%"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
className="signerStyle"
|
className="flex w-[30px] h-[30px] rounded-full items-center justify-center mr-2"
|
||||||
style={{
|
style={{
|
||||||
background: obj.blockColor
|
background: obj?.blockColor
|
||||||
? darkenColor(obj.blockColor, 0.4)
|
? darkenColor(obj?.blockColor, 0.4)
|
||||||
: nameColor[ind % nameColor.length],
|
: nameColor[ind % nameColor.length]
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
display: "flex",
|
|
||||||
borderRadius: 30 / 2,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
marginRight: "12px"
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span className="text-white uppercase font-bold text-center text-[12px]">
|
||||||
style={{
|
|
||||||
fontSize: "12px",
|
|
||||||
textAlign: "center",
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "white",
|
|
||||||
textTransform: "uppercase"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isWidgetExist(obj.Id) ? (
|
{isWidgetExist(obj.Id) ? (
|
||||||
<i className="fa-solid fa-check"></i>
|
<i className="fa-solid fa-check"></i>
|
||||||
) : (
|
) : (
|
||||||
@@ -133,65 +144,112 @@ const RecipientList = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
className={`${
|
||||||
display: "flex",
|
obj?.Name ? "flex-col" : "flex-row"
|
||||||
flexDirection: obj.Name ? "column" : "row",
|
} flex items-center`}
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{obj.Name ? (
|
{obj.Name ? (
|
||||||
<span className="userName" style={{ cursor: "default" }}>
|
<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}
|
{obj.Name}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<span
|
||||||
<span
|
className={`${
|
||||||
className="userName"
|
(!isMobile && isHover === ind) ||
|
||||||
onClick={() => {
|
props.isSelectListId === ind
|
||||||
setIsEdit({ [obj.Id]: true });
|
? "text-[#424242]"
|
||||||
props.setRoleName(obj.Role);
|
: "text-base-content"
|
||||||
}}
|
} text-[12px] font-bold w-[100px] whitespace-nowrap overflow-hidden text-ellipsis cursor-pointer`}
|
||||||
>
|
onClick={() => {
|
||||||
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
setIsEdit({ [obj.Id]: true });
|
||||||
<input
|
props.setRoleName(obj.Role);
|
||||||
ref={inputRef}
|
}}
|
||||||
style={{
|
>
|
||||||
backgroundColor: "transparent",
|
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||||
width: "inherit"
|
<input
|
||||||
}}
|
ref={inputRef}
|
||||||
value={obj.Role}
|
className="bg-transparent p-[3px"
|
||||||
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
value={obj.Role}
|
||||||
onBlur={() => {
|
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||||
setIsEdit({});
|
onBlur={() => {
|
||||||
props.handleOnBlur(obj.Role, obj.Id);
|
setIsEdit({});
|
||||||
}}
|
props.handleOnBlur(obj.Role, obj.Id);
|
||||||
onKeyDown={(e) => {
|
}}
|
||||||
if (e.key === "Enter") {
|
onKeyDown={(e) => {
|
||||||
e.preventDefault();
|
if (e.key === "Enter") {
|
||||||
inputRef.current.blur();
|
e.preventDefault();
|
||||||
}
|
inputRef.current.blur();
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
) : (
|
/>
|
||||||
obj.Role
|
) : (
|
||||||
)}
|
<span className="p-[3px]">{obj.Role}</span>
|
||||||
</span>
|
)}
|
||||||
</>
|
</span>
|
||||||
)}
|
)}
|
||||||
{obj.Name && (
|
{obj.Name && (
|
||||||
<span className="useEmail" style={{ cursor: "default" }}>
|
<span
|
||||||
{obj.Role}
|
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>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</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 && (
|
{props.handleDeleteUser && (
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
props.handleDeleteUser(obj.Id);
|
props.handleDeleteUser(obj.Id);
|
||||||
}}
|
}}
|
||||||
style={{ cursor: "pointer" }}
|
className={`${
|
||||||
|
(!isMobile && isHover === ind) ||
|
||||||
|
props.isSelectListId === ind
|
||||||
|
? "text-[#424242]"
|
||||||
|
: "text-base-content"
|
||||||
|
} cursor-pointer ml-[5px]`}
|
||||||
>
|
>
|
||||||
<i className="fa-regular fa-trash-can"></i>
|
<i className="fa-regular fa-trash-can"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,41 +1,58 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import RSC from "react-scrollbars-custom";
|
import RSC from "react-scrollbars-custom";
|
||||||
import { Document, Page } from "react-pdf";
|
import { Document, Page } from "react-pdf";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
|
|
||||||
function RenderAllPdfPage({
|
function RenderAllPdfPage({
|
||||||
pdfUrl,
|
|
||||||
signPdfUrl,
|
signPdfUrl,
|
||||||
allPages,
|
allPages,
|
||||||
setAllPages,
|
setAllPages,
|
||||||
setPageNumber,
|
setPageNumber,
|
||||||
setSignBtnPosition,
|
setSignBtnPosition,
|
||||||
pageNumber
|
pageNumber,
|
||||||
|
signerPos,
|
||||||
|
signerObjectId
|
||||||
}) {
|
}) {
|
||||||
|
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||||
|
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||||
//set all number of pages after load pdf
|
//set all number of pages after load pdf
|
||||||
function onDocumentLoad({ numPages }) {
|
function onDocumentLoad({ numPages }) {
|
||||||
setAllPages(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 (
|
return (
|
||||||
<div className="showPages">
|
<div>
|
||||||
<div
|
<div className=" hidden md:flex flex-row bg-base-100 h-full">
|
||||||
style={{
|
<div className="w-[140px]">
|
||||||
display: "flex",
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
flexDirection: "row",
|
|
||||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
|
||||||
backgroundColor: "white",
|
|
||||||
height: "100%"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ width: "140px" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
background: themeColor
|
|
||||||
// color:"white"
|
|
||||||
}}
|
|
||||||
className="signedStyle"
|
|
||||||
>
|
|
||||||
Pages
|
Pages
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -50,26 +67,16 @@ function RenderAllPdfPage({
|
|||||||
<Document
|
<Document
|
||||||
loading={"Loading Document.."}
|
loading={"Loading Document.."}
|
||||||
onLoadSuccess={onDocumentLoad}
|
onLoadSuccess={onDocumentLoad}
|
||||||
file={pdfUrl ? pdfUrl : signPdfUrl}
|
file={signPdfUrl}
|
||||||
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
|
|
||||||
>
|
>
|
||||||
{Array.from(new Array(allPages), (el, index) => (
|
{Array.from(new Array(allPages), (el, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
className={`${
|
||||||
width: "100px",
|
pageNumber - 1 === index
|
||||||
border:
|
? "border-[red]"
|
||||||
pageNumber - 1 === index
|
: "border-[#878787]"
|
||||||
? "2px solid red"
|
} border-2 w-[100px] m-[10px] flex justify-center items-center relative`}
|
||||||
: "2px solid #878787",
|
|
||||||
// padding: "5px",
|
|
||||||
margin: "10px",
|
|
||||||
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
contain: "content"
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPageNumber(index + 1);
|
setPageNumber(index + 1);
|
||||||
if (setSignBtnPosition) {
|
if (setSignBtnPosition) {
|
||||||
@@ -77,15 +84,19 @@ function RenderAllPdfPage({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Page
|
{signerPos && addSignatureBookmark(index)}
|
||||||
key={`page_${index + 1}`}
|
|
||||||
pageNumber={index + 1}
|
<div className="relative z-[1] overflow-hidden">
|
||||||
width={100}
|
<Page
|
||||||
height={100}
|
key={`page_${index + 1}`}
|
||||||
scale={1}
|
pageNumber={index + 1}
|
||||||
renderAnnotationLayer={false}
|
width={100}
|
||||||
renderTextLayer={false}
|
height={100}
|
||||||
/>
|
scale={1}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Document>
|
</Document>
|
||||||
|
|||||||
@@ -204,34 +204,32 @@ function RenderPdf({
|
|||||||
};
|
};
|
||||||
//function for render placeholder block over pdf document
|
//function for render placeholder block over pdf document
|
||||||
const checkSignedSignes = (data) => {
|
const checkSignedSignes = (data) => {
|
||||||
const checkSign = signedSigners.filter(
|
let checkSign = [];
|
||||||
(sign) => sign.objectId === data.signerObjId
|
//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) {
|
if (data.signerObjId === signerObjectId) {
|
||||||
setCurrentSigner(true);
|
setCurrentSigner(true);
|
||||||
}
|
}
|
||||||
const handleAllUserName = (Id, Role, type) => {
|
const handleAllUserName = (Id, Role, type) => {
|
||||||
return pdfDetails[0].Signers.map((signerData, key) => {
|
return (
|
||||||
return (
|
<React.Fragment>
|
||||||
signerData.objectId === data.signerObjId && (
|
<div className="text-black text-[8px] font-medium">
|
||||||
<React.Fragment key={key}>
|
{
|
||||||
{signerData?.Name && (
|
pdfDetails[0].Signers?.find(
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
(signer) => signer.objectId === data.signerObjId
|
||||||
{signerData.Name}
|
)?.Name
|
||||||
</div>
|
}
|
||||||
)}
|
</div>
|
||||||
{type && (
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
{Role && (
|
||||||
)}
|
<div className="text-black text-[8px] font-medium">
|
||||||
{Role && (
|
{`(${Role})`}
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
</div>
|
||||||
{`(${Role})`}
|
)}
|
||||||
</div>
|
</React.Fragment>
|
||||||
)}
|
);
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -272,6 +270,7 @@ function RenderPdf({
|
|||||||
unSignedWidgetId={unSignedWidgetId}
|
unSignedWidgetId={unSignedWidgetId}
|
||||||
setSelectWidgetId={setSelectWidgetId}
|
setSelectWidgetId={setSelectWidgetId}
|
||||||
selectWidgetId={selectWidgetId}
|
selectWidgetId={selectWidgetId}
|
||||||
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
@@ -289,38 +288,30 @@ function RenderPdf({
|
|||||||
if (checkSign?.Name) {
|
if (checkSign?.Name) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
<div className="text-black text-[8px] font-medium">
|
||||||
{checkSign?.Name}
|
{checkSign?.Name}
|
||||||
</div>
|
</div>
|
||||||
{type && (
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
{Role && (
|
||||||
|
<div className="text-black text-[8px] font-medium">
|
||||||
|
{`(${Role})`}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
|
||||||
{`(${Role})`}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{type && (
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
<div className="text-black text-[8px] font-medium">{Role}</div>
|
||||||
)}
|
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
|
||||||
{Role}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{type && (
|
{type && <div className="text-[11px] font-bold">{type}</div>}
|
||||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
<div className="text-black text-[8px] font-medium">{Role}</div>
|
||||||
)}
|
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
|
||||||
{Role}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -331,6 +322,7 @@ function RenderPdf({
|
|||||||
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
||||||
{isMobile && scale ? (
|
{isMobile && scale ? (
|
||||||
<div
|
<div
|
||||||
|
data-tut="reactourForth"
|
||||||
style={{
|
style={{
|
||||||
border: "0.1px solid #ebe8e8",
|
border: "0.1px solid #ebe8e8",
|
||||||
marginTop: isGuestSigner && "30px"
|
marginTop: isGuestSigner && "30px"
|
||||||
@@ -458,17 +450,9 @@ function RenderPdf({
|
|||||||
}))}
|
}))}
|
||||||
|
|
||||||
{/* this component for render pdf document is in middle of the component */}
|
{/* this component for render pdf document is in middle of the component */}
|
||||||
<div
|
<div className="flex items-center justify-center">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Document
|
<Document
|
||||||
onLoadError={() => {
|
onLoadError={() => setPdfLoadFail(true)}
|
||||||
setPdfLoadFail(true);
|
|
||||||
}}
|
|
||||||
loading={"Loading Document.."}
|
loading={"Loading Document.."}
|
||||||
onLoadSuccess={pageDetails}
|
onLoadSuccess={pageDetails}
|
||||||
// ref={pdfRef}'
|
// ref={pdfRef}'
|
||||||
@@ -514,10 +498,8 @@ function RenderPdf({
|
|||||||
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
data-tut="reactourForth"
|
||||||
border: "0.1px solid #ebe8e8",
|
style={{ border: "0.1px solid #ebe8e8", width: pdfOriginalWidth }}
|
||||||
width: pdfOriginalWidth
|
|
||||||
}}
|
|
||||||
ref={drop}
|
ref={drop}
|
||||||
id="container"
|
id="container"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useRef, useState, useEffect } from "react";
|
import React, { useRef, useState, useEffect } from "react";
|
||||||
import SignatureCanvas from "react-signature-canvas";
|
import SignatureCanvas from "react-signature-canvas";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
|
|
||||||
function SignPad({
|
function SignPad({
|
||||||
isSignPad,
|
isSignPad,
|
||||||
@@ -20,7 +19,9 @@ function SignPad({
|
|||||||
isInitial,
|
isInitial,
|
||||||
setIsInitial,
|
setIsInitial,
|
||||||
setIsStamp,
|
setIsStamp,
|
||||||
widgetType
|
widgetType,
|
||||||
|
currWidgetsDetails,
|
||||||
|
setCurrWidgetsDetails
|
||||||
}) {
|
}) {
|
||||||
const [penColor, setPenColor] = useState("blue");
|
const [penColor, setPenColor] = useState("blue");
|
||||||
const allColor = ["blue", "red", "black"];
|
const allColor = ["blue", "red", "black"];
|
||||||
@@ -31,6 +32,7 @@ function SignPad({
|
|||||||
const [signValue, setSignValue] = useState("");
|
const [signValue, setSignValue] = useState("");
|
||||||
const [textWidth, setTextWidth] = useState(null);
|
const [textWidth, setTextWidth] = useState(null);
|
||||||
const [textHeight, setTextHeight] = useState(null);
|
const [textHeight, setTextHeight] = useState(null);
|
||||||
|
const [signatureType, setSignatureType] = useState("draw");
|
||||||
const fontOptions = [
|
const fontOptions = [
|
||||||
{ value: "Fasthand" },
|
{ value: "Fasthand" },
|
||||||
{ value: "Dancing Script" },
|
{ value: "Dancing Script" },
|
||||||
@@ -48,19 +50,11 @@ function SignPad({
|
|||||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||||
);
|
);
|
||||||
const jsonSender = JSON.parse(senderUser);
|
const jsonSender = JSON.parse(senderUser);
|
||||||
|
|
||||||
const currentUserName = jsonSender && jsonSender.name;
|
const currentUserName = jsonSender && jsonSender.name;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
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
|
|
||||||
}, []);
|
|
||||||
//function for clear signature image
|
//function for clear signature image
|
||||||
const handleClear = () => {
|
const handleClear = () => {
|
||||||
|
setCurrWidgetsDetails({});
|
||||||
if (isTab === "draw") {
|
if (isTab === "draw") {
|
||||||
if (canvasRef.current) {
|
if (canvasRef.current) {
|
||||||
canvasRef.current.clear();
|
canvasRef.current.clear();
|
||||||
@@ -72,11 +66,10 @@ function SignPad({
|
|||||||
} else if (isTab === "uploadImage") {
|
} else if (isTab === "uploadImage") {
|
||||||
setImage("");
|
setImage("");
|
||||||
}
|
}
|
||||||
setIsInitial(false);
|
// setIsInitial(false);
|
||||||
};
|
};
|
||||||
//function for set signature url
|
//function for set signature url
|
||||||
const handleSignatureChange = () => {
|
const handleSignatureChange = () => {
|
||||||
// canvasRef.current.backgroundColor = 'rgb(165, 26, 26)'
|
|
||||||
setSignature(canvasRef.current.toDataURL());
|
setSignature(canvasRef.current.toDataURL());
|
||||||
setIsSignImg(canvasRef.current.toDataURL());
|
setIsSignImg(canvasRef.current.toDataURL());
|
||||||
};
|
};
|
||||||
@@ -84,45 +77,40 @@ function SignPad({
|
|||||||
//save button component
|
//save button component
|
||||||
const SaveBtn = () => {
|
const SaveBtn = () => {
|
||||||
return (
|
return (
|
||||||
<div style={{ marginTop: "2px" }}>
|
<div>
|
||||||
{(isTab === "draw" || isTab === "uploadImage") && (
|
{(isTab === "draw" || isTab === "uploadImage") && (
|
||||||
<button
|
<button
|
||||||
style={{
|
|
||||||
color: "black",
|
|
||||||
border: "1px solid #ccc"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn saveBtn"
|
className="op-btn op-btn-ghost mr-1 mt-[2px]"
|
||||||
onClick={() => handleClear()}
|
onClick={() => handleClear()}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
setCurrWidgetsDetails({});
|
||||||
if (!image) {
|
if (!image) {
|
||||||
if (isTab === "mysignature") {
|
if (isTab === "mysignature") {
|
||||||
setIsSignImg("");
|
setIsSignImg("");
|
||||||
if (isInitial) {
|
if (isInitial) {
|
||||||
onSaveSign("initials");
|
onSaveSign(signatureType, "initials");
|
||||||
} else {
|
} else {
|
||||||
onSaveSign("default");
|
onSaveSign(null, "default");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isTab === "type") {
|
if (isTab === "type") {
|
||||||
setIsSignImg("");
|
setIsSignImg("");
|
||||||
onSaveSign(false, textWidth, textHeight);
|
onSaveSign(null, false, textWidth, textHeight);
|
||||||
} else {
|
} else {
|
||||||
setIsSignImg("");
|
onSaveSign(signatureType);
|
||||||
onSaveSign();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPenColor("blue");
|
setPenColor("blue");
|
||||||
} else {
|
} else {
|
||||||
setIsSignImg("");
|
setIsSignImg("");
|
||||||
onSaveImage();
|
onSaveImage(signatureType);
|
||||||
}
|
}
|
||||||
setIsSignPad(false);
|
setIsSignPad(false);
|
||||||
setIsInitial(false);
|
setIsInitial(false);
|
||||||
@@ -133,26 +121,47 @@ function SignPad({
|
|||||||
setSignValue("");
|
setSignValue("");
|
||||||
setIsStamp(false);
|
setIsStamp(false);
|
||||||
}}
|
}}
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
disabled={
|
|
||||||
isSignImg || image || isDefaultSign || textWidth ? false : true
|
|
||||||
}
|
|
||||||
type="button"
|
type="button"
|
||||||
className={
|
className={`${
|
||||||
isSignImg || image || isDefaultSign || textWidth
|
isSignImg || image || isDefaultSign || textWidth
|
||||||
? "finishBtn saveBtn"
|
? ""
|
||||||
: "disabledFinish saveBtn"
|
: "pointer-events-none"
|
||||||
}
|
} op-btn op-btn-primary shadow-lg`}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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(() => {
|
useEffect(() => {
|
||||||
const loadFont = async () => {
|
const loadFont = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -186,7 +195,7 @@ function SignPad({
|
|||||||
}, [isTab]);
|
}, [isTab]);
|
||||||
|
|
||||||
//function for convert input text value in image
|
//function for convert input text value in image
|
||||||
const convertToImg = async (fontStyle, text) => {
|
const convertToImg = async (fontStyle, text, color) => {
|
||||||
//get text content to convert in image
|
//get text content to convert in image
|
||||||
const textContent = text;
|
const textContent = text;
|
||||||
const fontfamily = fontStyle
|
const fontfamily = fontStyle
|
||||||
@@ -199,6 +208,7 @@ function SignPad({
|
|||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
span.textContent = textContent;
|
span.textContent = textContent;
|
||||||
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
||||||
|
span.style.color = color ? color : penColor;
|
||||||
span.style.display = "hidden";
|
span.style.display = "hidden";
|
||||||
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
||||||
|
|
||||||
@@ -216,13 +226,9 @@ function SignPad({
|
|||||||
canvasElement.width = width * pixelRatio;
|
canvasElement.width = width * pixelRatio;
|
||||||
canvasElement.height = height * pixelRatio;
|
canvasElement.height = height * pixelRatio;
|
||||||
|
|
||||||
// Render the content of the span onto the canvas
|
|
||||||
// ctx.fillStyle = "white"; // Set the background color of the canvas
|
|
||||||
// ctx.fillRect(0, 0, width*pixelRatio, height*pixelRatio);
|
|
||||||
|
|
||||||
// You can customize text styles if needed
|
// You can customize text styles if needed
|
||||||
ctx.font = font;
|
ctx.font = font;
|
||||||
ctx.fillStyle = "black"; // Set the text color
|
ctx.fillStyle = color ? color : penColor; // Set the text color
|
||||||
ctx.textAlign = "center";
|
ctx.textAlign = "center";
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
ctx.scale(pixelRatio, pixelRatio);
|
ctx.scale(pixelRatio, pixelRatio);
|
||||||
@@ -235,36 +241,56 @@ function SignPad({
|
|||||||
setSignature(dataUrl);
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{isSignPad && (
|
{isSignPad && (
|
||||||
<div className="modaloverlay">
|
<div className="op-modal op-modal-open">
|
||||||
<div className="modalcontainer">
|
<div className="op-modal-box px-[13px] pt-2 pb-0">
|
||||||
<div
|
<div className="flex justify-between text-base-content items-center">
|
||||||
className="modalheader"
|
<div className="text-[1.2rem]">
|
||||||
style={{ padding: "0 13px", marginTop: "5px" }}
|
<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">
|
||||||
<div className="modaltitle">
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
background: "white",
|
|
||||||
marginTop: "3px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-around",
|
|
||||||
alignItems: "end",
|
|
||||||
gap: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isStamp ? (
|
{isStamp ? (
|
||||||
<span style={{ color: themeColor }} className="signTab">
|
<span className="op-link-primary op-link">
|
||||||
{widgetType === "image"
|
{widgetType === "image"
|
||||||
? "Upload image"
|
? "Upload image"
|
||||||
: "Upload stamp image"}
|
: "Upload stamp image"}
|
||||||
@@ -279,23 +305,14 @@ function SignPad({
|
|||||||
setIsTab("draw");
|
setIsTab("draw");
|
||||||
setImage();
|
setImage();
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color: isTab === "draw" ? themeColor : "#515252",
|
isTab === "draw"
|
||||||
marginLeft: "2px"
|
? "op-link-primary"
|
||||||
}}
|
: "no-underline"
|
||||||
className="signTab"
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
>
|
>
|
||||||
Draw
|
Draw
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border:
|
|
||||||
isTab === "draw"
|
|
||||||
? "1.5px solid #108783"
|
|
||||||
: "1.5px solid #ffffff"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
@@ -303,23 +320,16 @@ function SignPad({
|
|||||||
setIsDefaultSign(false);
|
setIsDefaultSign(false);
|
||||||
setIsImageSelect(true);
|
setIsImageSelect(true);
|
||||||
setIsTab("uploadImage");
|
setIsTab("uploadImage");
|
||||||
|
setSignatureType("");
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color:
|
isTab === "uploadImage"
|
||||||
isTab === "uploadImage" ? themeColor : "#515252"
|
? "op-link-primary"
|
||||||
}}
|
: "no-underline"
|
||||||
className="signTab"
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
>
|
>
|
||||||
Upload Image
|
Upload Image
|
||||||
</span>
|
</span>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border:
|
|
||||||
isTab === "uploadImage"
|
|
||||||
? "1.5px solid #108783"
|
|
||||||
: "1.5px solid #ffffff"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
@@ -327,25 +337,17 @@ function SignPad({
|
|||||||
setIsDefaultSign(false);
|
setIsDefaultSign(false);
|
||||||
setIsImageSelect(false);
|
setIsImageSelect(false);
|
||||||
setIsTab("type");
|
setIsTab("type");
|
||||||
|
setSignatureType("");
|
||||||
setImage();
|
setImage();
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color: isTab === "type" ? themeColor : "#515252",
|
isTab === "type"
|
||||||
marginLeft: "2px"
|
? "op-link-primary"
|
||||||
}}
|
: "no-underline"
|
||||||
className="signTab"
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
>
|
>
|
||||||
Type
|
Type
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border:
|
|
||||||
isTab === "type"
|
|
||||||
? "1.5px solid #108783"
|
|
||||||
: "1.5px solid #ffffff"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
{!isInitial && defaultSign ? (
|
{!isInitial && defaultSign ? (
|
||||||
<div>
|
<div>
|
||||||
@@ -354,26 +356,17 @@ function SignPad({
|
|||||||
setIsDefaultSign(true);
|
setIsDefaultSign(true);
|
||||||
setIsImageSelect(true);
|
setIsImageSelect(true);
|
||||||
setIsTab("mysignature");
|
setIsTab("mysignature");
|
||||||
|
setSignatureType("");
|
||||||
setImage();
|
setImage();
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color:
|
isTab === "mysignature"
|
||||||
isTab === "mysignature"
|
? "op-link-primary"
|
||||||
? themeColor
|
: "no-underline"
|
||||||
: "#515252"
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
}}
|
|
||||||
className="signTab"
|
|
||||||
>
|
>
|
||||||
My Signature
|
My Signature
|
||||||
</span>
|
</span>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border:
|
|
||||||
isTab === "mysignature"
|
|
||||||
? "1.5px solid #108783"
|
|
||||||
: "1.5px solid #ffffff"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
isInitial &&
|
isInitial &&
|
||||||
@@ -384,26 +377,17 @@ function SignPad({
|
|||||||
setIsDefaultSign(true);
|
setIsDefaultSign(true);
|
||||||
setIsImageSelect(true);
|
setIsImageSelect(true);
|
||||||
setIsTab("mysignature");
|
setIsTab("mysignature");
|
||||||
|
setSignatureType("");
|
||||||
setImage();
|
setImage();
|
||||||
}}
|
}}
|
||||||
style={{
|
className={`${
|
||||||
color:
|
isTab === "mysignature"
|
||||||
isTab === "mysignature"
|
? "op-link-primary"
|
||||||
? themeColor
|
: "no-underline"
|
||||||
: "#515252"
|
} op-link underline-offset-8 ml-[2px]`}
|
||||||
}}
|
|
||||||
className="signTab"
|
|
||||||
>
|
>
|
||||||
My Initials
|
My Initials
|
||||||
</span>
|
</span>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
border:
|
|
||||||
isTab === "mysignature"
|
|
||||||
? "1.5px solid #108783"
|
|
||||||
: "1.5px solid #ffffff"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@@ -413,8 +397,7 @@ function SignPad({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="closebtn"
|
className="text-[1.5rem] cursor-pointer"
|
||||||
style={{ color: "black" }}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPenColor("blue");
|
setPenColor("blue");
|
||||||
setIsSignPad(false);
|
setIsSignPad(false);
|
||||||
@@ -423,6 +406,7 @@ function SignPad({
|
|||||||
setIsDefaultSign(false);
|
setIsDefaultSign(false);
|
||||||
setImage();
|
setImage();
|
||||||
setIsTab("draw");
|
setIsTab("draw");
|
||||||
|
setSignatureType("draw");
|
||||||
setSignValue("");
|
setSignValue("");
|
||||||
setIsStamp(false);
|
setIsStamp(false);
|
||||||
}}
|
}}
|
||||||
@@ -430,10 +414,10 @@ function SignPad({
|
|||||||
×
|
×
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="p-[20px] h-full">
|
||||||
{isDefaultSign ? (
|
{isDefaultSign ? (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
<div className="flex justify-center">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
border: "1.3px solid #007bff",
|
border: "1.3px solid #007bff",
|
||||||
@@ -451,28 +435,23 @@ function SignPad({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
alt="stamp img"
|
alt="stamp img"
|
||||||
style={{
|
className="w-full h-full object-contain bg-white"
|
||||||
width: "100%",
|
draggable="false"
|
||||||
height: "100%",
|
|
||||||
background: "rgb(255, 255, 255)",
|
|
||||||
objectFit: "contain"
|
|
||||||
}}
|
|
||||||
src={isInitial ? myInitial : defaultSign}
|
src={isInitial ? myInitial : defaultSign}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
<div className="flex justify-end">
|
||||||
<SaveBtn />
|
<SaveBtn />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : isImageSelect || isStamp ? (
|
) : isImageSelect || isStamp ? (
|
||||||
!image ? (
|
!image ? (
|
||||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
<div className="flex justify-center">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
border: "1.3px solid #007bff",
|
border: "1.3px solid #007bff",
|
||||||
borderRadius: "2px",
|
borderRadius: "2px",
|
||||||
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
@@ -489,7 +468,7 @@ function SignPad({
|
|||||||
type="file"
|
type="file"
|
||||||
onChange={onImageChange}
|
onChange={onImageChange}
|
||||||
className="filetype"
|
className="filetype"
|
||||||
accept="image/*"
|
accept="image/png,image/jpeg"
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
hidden
|
hidden
|
||||||
/>
|
/>
|
||||||
@@ -499,7 +478,7 @@ function SignPad({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
<div className="flex justify-center">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
border: "1.3px solid #007bff",
|
border: "1.3px solid #007bff",
|
||||||
@@ -517,39 +496,27 @@ function SignPad({
|
|||||||
alt="print img"
|
alt="print img"
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
src={image.src}
|
src={image.src}
|
||||||
style={{
|
draggable="false"
|
||||||
objectFit: "contain",
|
className=" object-contain h-full w-full"
|
||||||
height: "100%",
|
|
||||||
width: "100%"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex justify-end">
|
||||||
style={{ display: "flex", justifyContent: "flex-end" }}
|
|
||||||
>
|
|
||||||
<SaveBtn />
|
<SaveBtn />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
) : isTab === "type" ? (
|
) : isTab === "type" ? (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div className="flex justify-between items-center">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="signatureText">
|
<span className="signatureText">
|
||||||
{isInitial ? "Initials" : "Signature"}:
|
{isInitial ? "Initials" : "Signature"}:
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
maxLength={isInitial ? 3 : 30}
|
maxLength={isInitial ? 3 : 30}
|
||||||
style={{ fontFamily: fontSelect }}
|
style={{ fontFamily: fontSelect, color: penColor }}
|
||||||
type="text"
|
type="text"
|
||||||
className="signatureInput"
|
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"
|
placeholder="Your signature"
|
||||||
value={signValue}
|
value={signValue}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -558,7 +525,6 @@ function SignPad({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="fontOptionContainer">
|
<div className="fontOptionContainer">
|
||||||
{fontOptions.map((font, ind) => {
|
{fontOptions.map((font, ind) => {
|
||||||
return (
|
return (
|
||||||
@@ -578,7 +544,8 @@ function SignPad({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
padding: "5px 10px 5px 10px",
|
padding: "5px 10px 5px 10px",
|
||||||
fontSize: "20px"
|
fontSize: "20px",
|
||||||
|
color: penColor
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{signValue ? signValue : "Your signature"}
|
{signValue ? signValue : "Your signature"}
|
||||||
@@ -588,13 +555,14 @@ function SignPad({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
<div className="flex flex-row justify-between mt-[10px]">
|
||||||
|
<PenColorComponent convertToImg={convertToImg} />
|
||||||
<SaveBtn />
|
<SaveBtn />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
<div className="flex justify-center">
|
||||||
<SignatureCanvas
|
<SignatureCanvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
penColor={penColor}
|
penColor={penColor}
|
||||||
@@ -614,47 +582,8 @@ function SignPad({
|
|||||||
dotSize={1}
|
dotSize={1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="flex flex-row justify-between mt-[10px]">
|
||||||
style={{
|
<PenColorComponent />
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
marginTop: "10px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
|
||||||
{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={() => {
|
|
||||||
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>
|
|
||||||
<SaveBtn />
|
<SaveBtn />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
import "../../styles/signature.css";
|
import "../../styles/signature.css";
|
||||||
function Signedby({ pdfDetails }) {
|
function Signedby({ pdfDetails }) {
|
||||||
const getFirstLetter = (name) => {
|
const getFirstLetter = (name) => {
|
||||||
@@ -8,50 +7,18 @@ function Signedby({ pdfDetails }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="signerComponent">
|
<div className="hidden md:block w-[180px] h-full bg-base-100">
|
||||||
<div
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
className="signedStyle"
|
|
||||||
>
|
|
||||||
Signed By
|
Signed By
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginTop: "2px", background: "white" }}>
|
<div className="mt-[2px] bg-base-100">
|
||||||
<div
|
<div className="bg-[#93a3db] rounded-xl mx-1 flex flex-row items-center py-[10px]">
|
||||||
style={{
|
<div className="bg-[#576081] flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1">
|
||||||
display: "flex",
|
<span className="text-[12px] text-center font-bold text-white uppercase">
|
||||||
flexDirection: "row",
|
|
||||||
padding: "10px",
|
|
||||||
background: "#93a3db"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className="signerStyle"
|
|
||||||
style={{
|
|
||||||
background: "#abd1d0",
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
display: "flex",
|
|
||||||
borderRadius: 30 / 2,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
marginRight: "20px",
|
|
||||||
marginTop: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
fontSize: "8px",
|
|
||||||
textAlign: "center",
|
|
||||||
fontWeight: "bold"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
|
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
<div className="flex flex-col">
|
||||||
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
|
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
|
||||||
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
|
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,33 +1,84 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
import RecipientList from "./RecipientList";
|
import RecipientList from "./RecipientList";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
|
||||||
function SignerListPlace(props) {
|
function SignerListPlace(props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||||
style={{
|
<span>
|
||||||
background: themeColor,
|
|
||||||
padding: "5px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="signedStyle">
|
|
||||||
{props.title ? props.title : "Recipients"}
|
{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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="signerList">
|
<div className="overflow-auto hide-scrollbar max-h-[180px]">
|
||||||
<RecipientList {...props} />
|
<RecipientList {...props} />
|
||||||
</div>
|
</div>
|
||||||
{props.handleAddSigner && (
|
<div className="mx-1">
|
||||||
<div
|
{props.handleAddSigner ? (
|
||||||
data-tut="reactourAddbtn"
|
<div
|
||||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
role="button"
|
||||||
onClick={() => props.handleAddSigner()}
|
data-tut="reactourAddbtn"
|
||||||
>
|
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||||
<i className="fa-solid fa-plus"></i>
|
disabled={props?.isMailSend ? true : false}
|
||||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
onClick={() => props.handleAddSigner()}
|
||||||
</div>
|
>
|
||||||
)}
|
<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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
textWidget,
|
textWidget,
|
||||||
widgets
|
widgets
|
||||||
} from "../../constant/Utils";
|
} from "../../constant/Utils";
|
||||||
import { themeColor } from "../../constant/const";
|
|
||||||
function WidgetComponent({
|
function WidgetComponent({
|
||||||
dragSignature,
|
dragSignature,
|
||||||
signRef,
|
signRef,
|
||||||
@@ -34,8 +33,12 @@ function WidgetComponent({
|
|||||||
handleRoleChange,
|
handleRoleChange,
|
||||||
handleOnBlur,
|
handleOnBlur,
|
||||||
title,
|
title,
|
||||||
|
setSignersData,
|
||||||
isTemplateFlow
|
sendInOrder,
|
||||||
|
isTemplateFlow,
|
||||||
|
setBlockColor,
|
||||||
|
blockColor,
|
||||||
|
setIsAddSigner
|
||||||
}) {
|
}) {
|
||||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||||
|
|
||||||
@@ -233,14 +236,14 @@ function WidgetComponent({
|
|||||||
(data) =>
|
(data) =>
|
||||||
data.type !== "dropdown" &&
|
data.type !== "dropdown" &&
|
||||||
data.type !== radioButtonWidget &&
|
data.type !== radioButtonWidget &&
|
||||||
data.type !== textWidget
|
data.type !== textInputWidget
|
||||||
);
|
);
|
||||||
const textWidgetData = widget.filter((data) => data.type !== textWidget);
|
const textWidgetData = widget.filter((data) => data.type !== textWidget);
|
||||||
const updateWidgets = isSignYourself
|
const updateWidgets = isSignYourself
|
||||||
? filterWidgets
|
? filterWidgets
|
||||||
: isTemplateFlow
|
: isTemplateFlow
|
||||||
? textWidgetData
|
? textWidgetData
|
||||||
: widget;
|
: widget;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -252,99 +255,82 @@ function WidgetComponent({
|
|||||||
className="stickyfooter"
|
className="stickyfooter"
|
||||||
style={{ width: window.innerWidth + "px" }}
|
style={{ width: window.innerWidth + "px" }}
|
||||||
>
|
>
|
||||||
{isSigners && (
|
<div className="mx-1">
|
||||||
<div
|
{isSigners && (
|
||||||
data-tut={dataTut}
|
<div
|
||||||
style={{
|
data-tut={dataTut}
|
||||||
background: isSelectListId
|
className="py-[10px] flex justify-center items-center op-card"
|
||||||
? color[isSelectListId % color.length]
|
|
||||||
: color[0],
|
|
||||||
padding: "10px 20px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
onClick={() => handleModal()}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
style={{
|
||||||
fontSize: "13px",
|
background: blockColor
|
||||||
fontWeight: "700",
|
? blockColor
|
||||||
textAlign: "center"
|
: isSelectListId
|
||||||
|
? color[isSelectListId % color.length]
|
||||||
|
: color[0]
|
||||||
}}
|
}}
|
||||||
|
onClick={() => handleModal()}
|
||||||
>
|
>
|
||||||
{title ? title : "Recipient"}
|
<span className="text-[13px] font-bold flex items-center">
|
||||||
</span>
|
{title ? title : "Recipient"}
|
||||||
<span
|
{signersdata[isSelectListId]?.Role && (
|
||||||
style={{
|
<div>
|
||||||
fontSize: "13px",
|
{signersdata[isSelectListId]?.Name ? (
|
||||||
fontWeight: "700",
|
<>
|
||||||
display: "flex",
|
:{" "}
|
||||||
alignItems: "center"
|
{signersdata[isSelectListId]?.Name?.length > 12
|
||||||
}}
|
? `${signersdata[isSelectListId].Name.slice(
|
||||||
>
|
0,
|
||||||
{signersdata[isSelectListId]?.Role && (
|
12
|
||||||
<div>
|
)}...`
|
||||||
{signersdata[isSelectListId]?.Name ? (
|
: signersdata[isSelectListId]?.Name}
|
||||||
<>
|
</>
|
||||||
:{" "}
|
) : (
|
||||||
{signersdata[isSelectListId]?.Name?.length > 12
|
<>
|
||||||
? `${signersdata[isSelectListId].Name.slice(
|
:{" "}
|
||||||
0,
|
{signersdata[isSelectListId]?.Role?.length > 12
|
||||||
12
|
? `${signersdata[isSelectListId].Role.slice(
|
||||||
)}...`
|
0,
|
||||||
: signersdata[isSelectListId]?.Name}
|
12
|
||||||
</>
|
)}...`
|
||||||
) : (
|
: signersdata[isSelectListId]?.Role}
|
||||||
<>
|
</>
|
||||||
:{" "}
|
)}
|
||||||
{signersdata[isSelectListId]?.Role?.length > 12
|
</div>
|
||||||
? `${signersdata[isSelectListId].Role.slice(
|
)}
|
||||||
0,
|
<div className="ml-[6px] text-[16px]">
|
||||||
12
|
<i className="fa-solid fa-angle-down"></i>
|
||||||
)}...`
|
|
||||||
: signersdata[isSelectListId]?.Role}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</span>
|
||||||
<div style={{ marginLeft: 6, fontSize: 16 }}>
|
</div>
|
||||||
<i className="fa-solid fa-angle-down"></i>
|
)}
|
||||||
|
</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>
|
||||||
</span>
|
)
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
{handleAddSigner && (
|
|
||||||
<div
|
|
||||||
data-tut="reactourAddbtn"
|
|
||||||
style={{
|
|
||||||
margin: "5px 0 5px 0",
|
|
||||||
backgroundColor: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
|
||||||
onClick={() => handleAddSigner()}
|
|
||||||
>
|
|
||||||
<i className="fa-solid fa-plus"></i>
|
|
||||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref={scrollContainerRef}
|
ref={scrollContainerRef}
|
||||||
style={{
|
className="bg-base-100 border-[2px] border-t-primary"
|
||||||
background: "white",
|
|
||||||
borderTop: `2px solid ${themeColor}`
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex whitespace-nowrap overflow-x-scroll pt-[10px] pb-[5px] pr-[5px]">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
overflowX: "scroll",
|
|
||||||
whiteSpace: "nowrap",
|
|
||||||
padding: "10px 5px 10px 1px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WidgetList
|
<WidgetList
|
||||||
updateWidgets={updateWidgets}
|
updateWidgets={updateWidgets}
|
||||||
handleDivClick={handleDivClick}
|
handleDivClick={handleDivClick}
|
||||||
@@ -359,17 +345,16 @@ function WidgetComponent({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<div data-tut={dataTut} className="signerComponent">
|
<div
|
||||||
<div
|
data-tut={dataTut}
|
||||||
style={{
|
className={`${
|
||||||
background: themeColor,
|
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
|
||||||
padding: "5px"
|
} 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 className="signedStyle">Fields</span>
|
<span>Fields</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col items-center my-3">
|
||||||
<div className="signLayoutContainer1">
|
|
||||||
<WidgetList
|
<WidgetList
|
||||||
updateWidgets={updateWidgets}
|
updateWidgets={updateWidgets}
|
||||||
handleDivClick={handleDivClick}
|
handleDivClick={handleDivClick}
|
||||||
@@ -381,35 +366,33 @@ function WidgetComponent({
|
|||||||
)}
|
)}
|
||||||
{isSignersModal && (
|
{isSignersModal && (
|
||||||
<ModalUi
|
<ModalUi
|
||||||
title={"Roles"}
|
title={title ? title : "Recipients"}
|
||||||
isOpen={isSignersModal}
|
isOpen={isSignersModal}
|
||||||
handleClose={handleModal}
|
handleClose={handleModal}
|
||||||
>
|
>
|
||||||
{signersdata.length > 0 ? (
|
{signersdata.length > 0 ? (
|
||||||
<RecipientList
|
<div className="max-h-[600px] overflow-auto pb-1">
|
||||||
signerPos={signerPos}
|
<RecipientList
|
||||||
signersdata={signersdata}
|
signerPos={signerPos}
|
||||||
isSelectListId={isSelectListId}
|
signersdata={signersdata}
|
||||||
setSignerObjId={setSignerObjId}
|
isSelectListId={isSelectListId}
|
||||||
setIsSelectId={setIsSelectId}
|
setSignerObjId={setSignerObjId}
|
||||||
setContractName={setContractName}
|
setIsSelectId={setIsSelectId}
|
||||||
setUniqueId={setUniqueId}
|
setContractName={setContractName}
|
||||||
setRoleName={setRoleName}
|
setUniqueId={setUniqueId}
|
||||||
handleDeleteUser={handleDeleteUser}
|
setRoleName={setRoleName}
|
||||||
handleRoleChange={handleRoleChange}
|
handleDeleteUser={handleDeleteUser}
|
||||||
handleOnBlur={handleOnBlur}
|
handleRoleChange={handleRoleChange}
|
||||||
handleModal={handleModal}
|
handleOnBlur={handleOnBlur}
|
||||||
/>
|
handleModal={handleModal}
|
||||||
|
sendInOrder={sendInOrder}
|
||||||
|
setSignersData={setSignersData}
|
||||||
|
setBlockColor={setBlockColor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className=" p-[20px] text-[15px] font-medium text-center">
|
||||||
style={{
|
Please add a {title ? title : "recipients"}
|
||||||
padding: 20,
|
|
||||||
fontSize: 15,
|
|
||||||
fontWeight: "500",
|
|
||||||
textAlign: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Please add a role
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { getWidgetType } from "../../constant/Utils";
|
|||||||
function WidgetList(props) {
|
function WidgetList(props) {
|
||||||
return props.updateWidgets.map((item, ind) => {
|
return props.updateWidgets.map((item, ind) => {
|
||||||
return (
|
return (
|
||||||
<div key={ind} style={{ marginBottom: "5px" }}>
|
<div key={ind} className="mb-[5px]">
|
||||||
<div
|
<div
|
||||||
className="widgets"
|
className="select-none"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.addPositionOfSignature &&
|
props.addPositionOfSignature &&
|
||||||
props.addPositionOfSignature("onclick", item);
|
props.addPositionOfSignature("onclick", item);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import ModalUi from "../../primitives/ModalUi";
|
|||||||
import "../../styles/AddUser.css";
|
import "../../styles/AddUser.css";
|
||||||
import RegexParser from "regex-parser";
|
import RegexParser from "regex-parser";
|
||||||
import { textInputWidget } from "../../constant/Utils";
|
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 WidgetNameModal = (props) => {
|
||||||
const [formdata, setFormdata] = useState({
|
const [formdata, setFormdata] = useState({
|
||||||
@@ -10,11 +14,11 @@ const WidgetNameModal = (props) => {
|
|||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
status: "required",
|
status: "required",
|
||||||
hint: "",
|
hint: "",
|
||||||
textvalidate: "text"
|
textvalidate: ""
|
||||||
});
|
});
|
||||||
const [isValid, setIsValid] = useState(true);
|
const [isValid, setIsValid] = useState(true);
|
||||||
const statusArr = ["Required", "Optional"];
|
const statusArr = ["Required", "Optional"];
|
||||||
const inputOpt = ["email", "number"];
|
const inputOpt = ["text", "email", "number"];
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.defaultdata) {
|
if (props.defaultdata) {
|
||||||
@@ -45,12 +49,16 @@ const WidgetNameModal = (props) => {
|
|||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
status: "required",
|
status: "required",
|
||||||
hint: "",
|
hint: "",
|
||||||
textvalidate: "text"
|
textvalidate: ""
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
if (e) {
|
||||||
|
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||||
|
} else {
|
||||||
|
setFormdata({ ...formdata, textvalidate: "" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handledefaultChange = (e) => {
|
const handledefaultChange = (e) => {
|
||||||
@@ -70,7 +78,7 @@ const WidgetNameModal = (props) => {
|
|||||||
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||||
case "number":
|
case "number":
|
||||||
return "/^\\d+$/";
|
return "/^\\d+$/";
|
||||||
case textInputWidget:
|
case "text":
|
||||||
return "/^[a-zA-Zs]+$/";
|
return "/^[a-zA-Zs]+$/";
|
||||||
default:
|
default:
|
||||||
return type;
|
return type;
|
||||||
@@ -79,7 +87,7 @@ const WidgetNameModal = (props) => {
|
|||||||
|
|
||||||
function handleBlurRegex() {
|
function handleBlurRegex() {
|
||||||
if (!formdata.textvalidate) {
|
if (!formdata.textvalidate) {
|
||||||
setFormdata({ ...formdata, textvalidate: "text" });
|
setFormdata({ ...formdata, textvalidate: "" });
|
||||||
} else {
|
} else {
|
||||||
if (formdata.defaultValue) {
|
if (formdata.defaultValue) {
|
||||||
const regexObject = RegexParser(
|
const regexObject = RegexParser(
|
||||||
@@ -98,14 +106,31 @@ const WidgetNameModal = (props) => {
|
|||||||
handleClose={props.handleClose && props.handleClose}
|
handleClose={props.handleClose && props.handleClose}
|
||||||
title={"Widget info"}
|
title={"Widget info"}
|
||||||
>
|
>
|
||||||
<form onSubmit={handleSubmit} style={{ padding: 20 }}>
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
<div className="form-section">
|
props.widgetName === textInputWidget) &&
|
||||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
!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
|
Name
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[red]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="name"
|
name="name"
|
||||||
value={formdata.name}
|
value={formdata.name}
|
||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
@@ -115,44 +140,55 @@ const WidgetNameModal = (props) => {
|
|||||||
{(props.defaultdata?.type === textInputWidget ||
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
props.widgetName === textInputWidget) && (
|
props.widgetName === textInputWidget) && (
|
||||||
<>
|
<>
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="textvalidate" style={{ fontSize: 13 }}>
|
<div className="flex items-center gap-1">
|
||||||
Validation
|
<label
|
||||||
</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
|
<div
|
||||||
style={{
|
className={`${
|
||||||
display: "flex",
|
!props.isSubscribe && isEnableSubscription
|
||||||
flexDirection: "row",
|
? "bg-opacity-50 pointer-events-none"
|
||||||
gap: 10,
|
: ""
|
||||||
marginBottom: "0.5rem"
|
} flex flex-row gap-[10px] mb-[0.5rem]`}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div style={{ width: "100%", position: "relative" }}>
|
<div className="w-full relative group">
|
||||||
<input
|
<input
|
||||||
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
|
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"
|
name="textvalidate"
|
||||||
placeholder="Enter custom expression"
|
placeholder="Enter custom regular expression"
|
||||||
value={formdata.textvalidate}
|
value={formdata.textvalidate}
|
||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
// onBlur={() => handleBlurRegex()}
|
// onBlur={() => handleBlurRegex()}
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
className="validationlist addUserInput"
|
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
|
||||||
name="textvalidate"
|
name="textvalidate"
|
||||||
value={formdata.textvalidate}
|
value={formdata.textvalidate}
|
||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
onBlur={() => handleBlurRegex()}
|
onBlur={() => handleBlurRegex()}
|
||||||
>
|
>
|
||||||
<option disabled style={{ fontSize: "13px" }}>
|
<option
|
||||||
|
disabled={formdata?.textvalidate}
|
||||||
|
className="text-[13px]"
|
||||||
|
>
|
||||||
Select...
|
Select...
|
||||||
</option>
|
</option>
|
||||||
{inputOpt.map((data, ind) => {
|
{inputOpt.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<option
|
<option className="text-[13px]" key={ind} value={data}>
|
||||||
style={{ fontSize: "13px" }}
|
|
||||||
key={ind}
|
|
||||||
value={data}
|
|
||||||
>
|
|
||||||
{data}
|
{data}
|
||||||
</option>
|
</option>
|
||||||
);
|
);
|
||||||
@@ -161,60 +197,46 @@ const WidgetNameModal = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
|
||||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
<div className="mb-[0.75rem]">
|
||||||
|
<label htmlFor="name" className="text-[13px]">
|
||||||
Default value
|
Default value
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="defaultValue"
|
name="defaultValue"
|
||||||
value={formdata.defaultValue}
|
value={formdata.defaultValue}
|
||||||
onChange={(e) => handledefaultChange(e)}
|
onChange={(e) => handledefaultChange(e)}
|
||||||
onBlur={() =>
|
autoComplete="off"
|
||||||
isValid === false &&
|
onBlur={() => {
|
||||||
setFormdata({ ...formdata, defaultValue: "" })
|
if (isValid === false) {
|
||||||
}
|
setFormdata({ ...formdata, defaultValue: "" });
|
||||||
|
setIsValid(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{isValid === false ? (
|
{isValid === false && (
|
||||||
<p style={{ color: "Red", fontSize: 8 }}>
|
<div
|
||||||
invalid default value
|
className="warning defaultvalueWarning"
|
||||||
</p>
|
style={{ fontSize: 12 }}
|
||||||
) : (
|
|
||||||
<p
|
|
||||||
style={{
|
|
||||||
color: "transparent",
|
|
||||||
fontSize: 10,
|
|
||||||
margin: "3px 8px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
.
|
<i
|
||||||
</p>
|
className="fas fa-exclamation-circle text-[15px]"
|
||||||
|
style={{ color: "#fab005" }}
|
||||||
|
></i>
|
||||||
|
invalid default value
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<div
|
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 10,
|
|
||||||
marginBottom: "0.5rem"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{statusArr.map((data, ind) => {
|
{statusArr.map((data, ind) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={ind} className="flex flex-row gap-[5px] items-center">
|
||||||
key={ind}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 5,
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
style={{ accentColor: "red", marginRight: "10px" }}
|
className="mr-[2px] op-radio op-radio-xs"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="status"
|
name="status"
|
||||||
onChange={() =>
|
onChange={() =>
|
||||||
@@ -224,9 +246,7 @@ const WidgetNameModal = (props) => {
|
|||||||
formdata.status.toLowerCase() === data.toLowerCase()
|
formdata.status.toLowerCase() === data.toLowerCase()
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
<div className="text-[13px] font-medium">{data}</div>
|
||||||
{data}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -234,35 +254,20 @@ const WidgetNameModal = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
{(props.defaultdata?.type === textInputWidget ||
|
{(props.defaultdata?.type === textInputWidget ||
|
||||||
props?.widgetName === textInputWidget) && (
|
props?.widgetName === textInputWidget) && (
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="hint" style={{ fontSize: 13 }}>
|
<label htmlFor="hint" className="text-[13px]">
|
||||||
Hint
|
Hint
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="hint"
|
name="hint"
|
||||||
value={formdata.hint}
|
value={formdata.hint}
|
||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
|
||||||
style={{
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
height: 1,
|
|
||||||
backgroundColor: "#b7b3b3",
|
|
||||||
width: "100%",
|
|
||||||
marginBottom: "16px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
|
||||||
style={{
|
|
||||||
color: "white",
|
|
||||||
padding: "5px 20px",
|
|
||||||
backgroundColor: "#32a3ac"
|
|
||||||
}}
|
|
||||||
type="submit"
|
|
||||||
className="finishBtn"
|
|
||||||
>
|
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import Alert from "../../../primitives/Alert";
|
import Alert from "../../../primitives/Alert";
|
||||||
|
import Loader from "../../../primitives/Loader";
|
||||||
|
|
||||||
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||||
const folderPtr = {
|
const folderPtr = {
|
||||||
@@ -25,9 +26,11 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|||||||
if (parentFolderId) {
|
if (parentFolderId) {
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
} else {
|
} else {
|
||||||
FolderQuery.doesNotExist("Folder");
|
FolderQuery.doesNotExist("Folder");
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
const res = await FolderQuery.find();
|
||||||
@@ -49,6 +52,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|||||||
const exsitQuery = new Parse.Query(folderCls);
|
const exsitQuery = new Parse.Query(folderCls);
|
||||||
exsitQuery.equalTo("Name", name);
|
exsitQuery.equalTo("Name", name);
|
||||||
exsitQuery.equalTo("Type", "Folder");
|
exsitQuery.equalTo("Type", "Folder");
|
||||||
|
exsitQuery.notEqualTo("IsArchive", true);
|
||||||
if (parentFolderId) {
|
if (parentFolderId) {
|
||||||
exsitQuery.equalTo("Folder", folderPtr);
|
exsitQuery.equalTo("Folder", folderPtr);
|
||||||
}
|
}
|
||||||
@@ -110,11 +114,8 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|||||||
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
|
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
|
||||||
<div id="createFolder" className="relative">
|
<div id="createFolder" className="relative">
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
<div className="absolute h-full w-full flex justify-center items-center ">
|
<div className="absolute h-full w-full flex justify-center items-center">
|
||||||
<div
|
<Loader />
|
||||||
style={{ fontSize: "45px", color: "#3dd3e0" }}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<h1 className="text-base font-semibold">Create Folder</h1>
|
<h1 className="text-base font-semibold">Create Folder</h1>
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -45,9 +45,11 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
if (folderPtr) {
|
if (folderPtr) {
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
} else {
|
} else {
|
||||||
FolderQuery.doesNotExist("Folder");
|
FolderQuery.doesNotExist("Folder");
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
const res = await FolderQuery.find();
|
||||||
@@ -169,34 +171,42 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
{required && <span className="text-red-500 text-[13px]">*</span>}
|
{required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
|
<div className="relative max-w-sm">
|
||||||
<div>
|
<div
|
||||||
<i
|
onClick={() => SetIsOpen(true)}
|
||||||
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
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"
|
||||||
style={{ fontSize: "40px" }}
|
>
|
||||||
aria-hidden="true"
|
<div>
|
||||||
></i>
|
<i
|
||||||
</div>
|
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||||
<div className="font-semibold ">
|
style={{ fontSize: "40px" }}
|
||||||
<div className="flex items-center gap-2">
|
aria-hidden="true"
|
||||||
<p>
|
></i>
|
||||||
{selectFolder && selectFolder.Name
|
</div>
|
||||||
? selectFolder.Name
|
<div className="font-semibold ">
|
||||||
: "OpenSign™ Drive"}
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<p>
|
||||||
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
|
{selectFolder && selectFolder.Name
|
||||||
<i
|
? selectFolder.Name
|
||||||
className="fa fa-pencil"
|
: "OpenSign™ Drive"}
|
||||||
title="Select Folder"
|
</p>
|
||||||
aria-hidden="true"
|
<div
|
||||||
></i>
|
className="text-black text-sm"
|
||||||
</div>
|
// 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>
|
||||||
<p className="text-[10px] text-gray-400">
|
|
||||||
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-1 right-1 cursor-pointer">
|
<div className="absolute top-2 right-1 cursor-pointer">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
message={
|
message={
|
||||||
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
|
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
|
||||||
|
|||||||
@@ -3,16 +3,6 @@ import Parse from "parse";
|
|||||||
import "../../../styles/AddUser.css";
|
import "../../../styles/AddUser.css";
|
||||||
import AsyncSelect from "react-select/async";
|
import AsyncSelect from "react-select/async";
|
||||||
|
|
||||||
const customStyles = {
|
|
||||||
control: (provided) => ({
|
|
||||||
...provided,
|
|
||||||
fontSize: "13px" // Font size for the control
|
|
||||||
}),
|
|
||||||
option: (provided) => ({
|
|
||||||
...provided,
|
|
||||||
fontSize: "13px" // Font size for the options
|
|
||||||
})
|
|
||||||
};
|
|
||||||
const SelectSigners = (props) => {
|
const SelectSigners = (props) => {
|
||||||
const [userList, setUserList] = useState([]);
|
const [userList, setUserList] = useState([]);
|
||||||
const [selected, setSelected] = useState();
|
const [selected, setSelected] = useState();
|
||||||
@@ -53,9 +43,22 @@ const SelectSigners = (props) => {
|
|||||||
const contactRes = await contactbook.find();
|
const contactRes = await contactbook.find();
|
||||||
if (contactRes) {
|
if (contactRes) {
|
||||||
const res = JSON.parse(JSON.stringify(contactRes));
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
// console.log("userList ", res);
|
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||||
setUserList(res);
|
//and filter signers from total signer's list which already present in document's signers list
|
||||||
return await res.map((item) => ({
|
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,
|
label: item.Email,
|
||||||
value: item.objectId
|
value: item.objectId
|
||||||
}));
|
}));
|
||||||
@@ -66,32 +69,44 @@ const SelectSigners = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="addusercontainer">
|
<div className="h-full px-[20px] py-[10px] text-base-content">
|
||||||
<div className="form-wrapper">
|
<div className="w-full mx-auto p-[8px]">
|
||||||
<div className="form-section" style={{ marginBottom: 0 }}>
|
<div className="mb-0">
|
||||||
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
|
<label className="text-[14px] font-bold">Choose from contacts</label>
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
cacheOptions
|
cacheOptions
|
||||||
defaultOptions
|
defaultOptions
|
||||||
value={selected}
|
value={selected}
|
||||||
loadingMessage={() => "Loading..."}
|
loadingMessage={() => "Loading..."}
|
||||||
noOptionsMessage={() => "User not Found"}
|
noOptionsMessage={() => "Contact not found"}
|
||||||
loadOptions={loadOptions}
|
loadOptions={loadOptions}
|
||||||
onChange={handleOptions}
|
onChange={handleOptions}
|
||||||
styles={customStyles}
|
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>
|
||||||
{isError ? (
|
<p
|
||||||
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
|
className={`${
|
||||||
Please select signer
|
isError ? "text-[red]" : "text-transparent"
|
||||||
</p>
|
} text-[11px] ml-[6px] my-[2px]`}
|
||||||
) : (
|
>
|
||||||
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
|
Please select signer
|
||||||
.
|
</p>
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<div>
|
<div>
|
||||||
<button className="submitbutton" onClick={() => handleAdd()}>
|
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import Select from "react-select";
|
import Select from "react-select";
|
||||||
import AddSigner from "../../AddSigner";
|
import AddSigner from "../../AddSigner";
|
||||||
import Modal from "react-modal";
|
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import Tooltip from "../../../primitives/Tooltip";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
function arrayMove(array, from, to) {
|
function arrayMove(array, from, to) {
|
||||||
array = array.slice();
|
array = array.slice();
|
||||||
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
||||||
return array;
|
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
|
* react-sortable-hoc is depcreated not usable from react 18.x.x
|
||||||
* need to replace it with @dnd-kit
|
* need to replace it with @dnd-kit
|
||||||
@@ -16,7 +32,6 @@ function arrayMove(array, from, to) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const SignersInput = (props) => {
|
const SignersInput = (props) => {
|
||||||
Modal.setAppElement("body");
|
|
||||||
const [state, setState] = useState(undefined);
|
const [state, setState] = useState(undefined);
|
||||||
// const [editFormData, setEditFormData] = useState([]);
|
// const [editFormData, setEditFormData] = useState([]);
|
||||||
const [selected, setSelected] = useState([]);
|
const [selected, setSelected] = useState([]);
|
||||||
@@ -104,12 +119,20 @@ const SignersInput = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-xs mt-2 ">
|
<div className="text-xs mt-2 ">
|
||||||
<label className="block">
|
<label className="block relative">
|
||||||
Signers
|
Signers
|
||||||
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
{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>
|
</label>
|
||||||
<div style={{ display: "flex", gap: 5 }}>
|
<div className="flex gap-x-[5px]">
|
||||||
<div style={{ flexWrap: "wrap", width: "100%" }}>
|
<div className="w-full">
|
||||||
<Select
|
<Select
|
||||||
onSortEnd={onSortEnd}
|
onSortEnd={onSortEnd}
|
||||||
distance={4}
|
distance={4}
|
||||||
@@ -119,6 +142,22 @@ const SignersInput = (props) => {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
closeMenuOnSelect={false}
|
closeMenuOnSelect={false}
|
||||||
required={props.required}
|
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>
|
||||||
<div
|
<div
|
||||||
@@ -126,66 +165,29 @@ const SignersInput = (props) => {
|
|||||||
setIsModel(true);
|
setIsModel(true);
|
||||||
openModal();
|
openModal();
|
||||||
}}
|
}}
|
||||||
style={{
|
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"
|
||||||
cursor: "pointer",
|
|
||||||
borderRadius: 4,
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
minHeight: 38,
|
|
||||||
minWidth: 48,
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<i className="fas fa-plus"></i>
|
<i className="fas fa-plus"></i>
|
||||||
</div>
|
</div>
|
||||||
<Modal
|
<AddSignerModal isOpen={modalIsOpen}>
|
||||||
isOpen={modalIsOpen}
|
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||||
onRequestClose={handleModalCloseClick}
|
Add Signer
|
||||||
shouldCloseOnOverlayClick={false}
|
</h3>
|
||||||
id="modal1"
|
<button
|
||||||
contentLabel="Modal"
|
onClick={handleModalCloseClick}
|
||||||
style={{
|
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
|
||||||
content: {
|
>
|
||||||
top: "50%",
|
✕
|
||||||
left: "50%",
|
</button>
|
||||||
right: "auto",
|
{isModal && (
|
||||||
bottom: "auto",
|
<AddSigner
|
||||||
transform: "translate(-50%, -50%)",
|
valueKey={"objectId"}
|
||||||
border: "1px transparent",
|
displayKey={"Name"}
|
||||||
padding: 0
|
details={handleNewDetails}
|
||||||
},
|
closePopup={handleModalCloseClick}
|
||||||
overlay: {
|
/>
|
||||||
width: "100%",
|
)}
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
</AddSignerModal>
|
||||||
zIndex: 50
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="min-w-full md:min-w-[500px]">
|
|
||||||
<div
|
|
||||||
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
|
|
||||||
style={{ background: "#32a3ac" }}
|
|
||||||
>
|
|
||||||
<div className="text-[1.2rem] font-normal">Add Signer</div>
|
|
||||||
<button
|
|
||||||
onClick={handleModalCloseClick}
|
|
||||||
className="text-[1.5rem] cursor-pointer focus:outline-none"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isModal && (
|
|
||||||
<AddSigner
|
|
||||||
valueKey={"objectId"}
|
|
||||||
displayKey={"Name"}
|
|
||||||
details={handleNewDetails}
|
|
||||||
closePopup={handleModalCloseClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -3,14 +3,18 @@ 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={
|
to={
|
||||||
item.pageType
|
item.pageType
|
||||||
? `/${item.pageType}/${item.objectId}`
|
? `/${item.pageType}/${item.objectId}`
|
||||||
: `/${item.objectId}`
|
: `/${item.objectId}`
|
||||||
}
|
}
|
||||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer"
|
className={({ isActive }) =>
|
||||||
|
`${
|
||||||
|
isActive ? " bg-base-300 text-base-content" : ""
|
||||||
|
} 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`
|
||||||
|
}
|
||||||
onClick={closeSidebar}
|
onClick={closeSidebar}
|
||||||
tabIndex={isOpen ? 0 : -1}
|
tabIndex={isOpen ? 0 : -1}
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ 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");
|
||||||
@@ -33,27 +34,42 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleSubmenu = (title) => {
|
||||||
|
setSubmenuOpen({ [title]: !submenuOpen[title] });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuItem = () => {
|
||||||
|
closeSidebar();
|
||||||
|
setSubmenuOpen({});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
className={`bg-white text-[#444] h-screen w-full md:w-[300px] overflow-y-auto transform transition-transform z-40 md:translate-x-0
|
className={`absolute md:relative bg-base-100 h-screen overflow-y-auto transition-all z-[100] hide-scrollbar
|
||||||
${isOpen ? "translate-x-0 block" : "-translate-x-full hidden"}`}
|
${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
|
<img
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-contain"
|
||||||
src={image}
|
src={image}
|
||||||
alt="Profile"
|
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
|
||||||
|
className="op-menu op-menu-sm"
|
||||||
|
aria-label="OpenSign Sidebar Navigation"
|
||||||
|
>
|
||||||
<ul
|
<ul
|
||||||
className="text-sm"
|
className="text-sm"
|
||||||
role="menubar"
|
role="menubar"
|
||||||
@@ -65,19 +81,21 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
|||||||
key={item.title}
|
key={item.title}
|
||||||
item={item}
|
item={item}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
closeSidebar={closeSidebar}
|
closeSidebar={handleMenuItem}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Submenu
|
<Submenu
|
||||||
key={item.title}
|
key={item.title}
|
||||||
item={item}
|
item={item}
|
||||||
closeSidebar={closeSidebar}
|
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}`}
|
||||||
@@ -23,23 +17,29 @@ const Submenu = ({ item, closeSidebar }) => {
|
|||||||
<span className="ml-3 lg:ml-4">{title}</span>
|
<span className="ml-3 lg:ml-4">{title}</span>
|
||||||
<i
|
<i
|
||||||
className={`${
|
className={`${
|
||||||
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
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={
|
to={
|
||||||
childItem.pageType
|
childItem.pageType
|
||||||
? `/${childItem.pageType}/${childItem.objectId}`
|
? `/${childItem.pageType}/${childItem.objectId}`
|
||||||
: `/${childItem.objectId}`
|
: `/${childItem.objectId}`
|
||||||
}
|
}
|
||||||
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white cursor-pointer"
|
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}
|
||||||
|
|||||||
+701
-107
@@ -1,17 +1,115 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { themeColor } from "./const";
|
import { isEnableSubscription } from "./const";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { rgb } from "pdf-lib";
|
import { rgb } from "pdf-lib";
|
||||||
|
import Parse from "parse";
|
||||||
|
import { appInfo } from "./appinfo";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import printModule from "print-js";
|
||||||
|
|
||||||
export const isMobile = window.innerWidth < 767;
|
export const isMobile = window.innerWidth < 767;
|
||||||
export const textInputWidget = "text input";
|
export const textInputWidget = "text input";
|
||||||
export const textWidget = "text";
|
export const textWidget = "text";
|
||||||
export const radioButtonWidget = "radio button";
|
export const radioButtonWidget = "radio button";
|
||||||
export const openInNewTab = (url) => {
|
export const openInNewTab = (url, target) => {
|
||||||
window.open(url, "_blank", "noopener,noreferrer");
|
if (target) {
|
||||||
|
window.open(url, target, "noopener,noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url, "_blank", "noopener,noreferrer");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function fetchSubscription(
|
||||||
|
extUserId,
|
||||||
|
contactObjId,
|
||||||
|
isGuestSign = false
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||||
|
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||||
|
// console.log("extClass ", extClass);
|
||||||
|
let extUser;
|
||||||
|
if (extClass && extClass.length > 0) {
|
||||||
|
extUser = extClass[0].objectId;
|
||||||
|
} else {
|
||||||
|
extUser = extUserId;
|
||||||
|
}
|
||||||
|
const baseURL = localStorage.getItem("baseUrl");
|
||||||
|
const url = `${baseURL}functions/getsubscriptions`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const params = isGuestSign
|
||||||
|
? { contactId: contactObjId }
|
||||||
|
: { extUserId: extUser };
|
||||||
|
const tenatRes = await axios.post(url, params, { headers: headers });
|
||||||
|
let plan, status, billingDate;
|
||||||
|
if (isGuestSign) {
|
||||||
|
plan = tenatRes.data?.result?.result?.plan;
|
||||||
|
status = tenatRes.data?.result?.result?.isSubscribed;
|
||||||
|
} else {
|
||||||
|
plan = tenatRes.data?.result?.result?.PlanCode;
|
||||||
|
billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
|
||||||
|
}
|
||||||
|
return { plan, billingDate, status };
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in fetch subscription", err);
|
||||||
|
return { plan: "", billingDate: "" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//function to get subcripition details from Extand user class
|
||||||
|
export async function checkIsSubscribed() {
|
||||||
|
try {
|
||||||
|
const res = await fetchSubscription();
|
||||||
|
if (res.plan === "freeplan") {
|
||||||
|
return false;
|
||||||
|
} else if (res.billingDate) {
|
||||||
|
if (new Date(res.billingDate) > new Date()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in fetch subscription", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const color = [
|
||||||
|
"#93a3db",
|
||||||
|
"#e6c3db",
|
||||||
|
"#c0e3bc",
|
||||||
|
"#bce3db",
|
||||||
|
"#b8ccdb",
|
||||||
|
"#ceb8db",
|
||||||
|
"#ffccff",
|
||||||
|
"#99ffcc",
|
||||||
|
"#cc99ff",
|
||||||
|
"#ffcc99",
|
||||||
|
"#66ccff",
|
||||||
|
"#ffffcc"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const nameColor = [
|
||||||
|
"#304fbf",
|
||||||
|
"#7d5270",
|
||||||
|
"#5f825b",
|
||||||
|
"#578077",
|
||||||
|
"#576e80",
|
||||||
|
"#6d527d",
|
||||||
|
"#cc00cc",
|
||||||
|
"#006666",
|
||||||
|
"#cc00ff",
|
||||||
|
"#ff9900",
|
||||||
|
"#336699",
|
||||||
|
"#cc9900"
|
||||||
|
];
|
||||||
export const toDataUrl = (file) => {
|
export const toDataUrl = (file) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@@ -22,10 +120,12 @@ export const toDataUrl = (file) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for getting document details for getDrive
|
//function for getting document details for getDrive cloud function
|
||||||
export const getDrive = async (documentId) => {
|
export const getDrive = async (documentId, skip = 0, limit = 100) => {
|
||||||
const data = {
|
const data = {
|
||||||
docId: documentId && documentId
|
docId: documentId && documentId,
|
||||||
|
limit: limit,
|
||||||
|
skip: skip
|
||||||
};
|
};
|
||||||
const driveDeatils = await axios
|
const driveDeatils = await axios
|
||||||
.post(`${localStorage.getItem("baseUrl")}functions/getDrive`, data, {
|
.post(`${localStorage.getItem("baseUrl")}functions/getDrive`, data, {
|
||||||
@@ -79,11 +179,10 @@ export const contractUsers = async (email) => {
|
|||||||
.then((Listdata) => {
|
.then((Listdata) => {
|
||||||
const json = Listdata.data;
|
const json = Listdata.data;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
|
||||||
if (json && json.result) {
|
if (json && json.result) {
|
||||||
data.push(json.result);
|
data.push(json.result);
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Err ", err);
|
console.log("Err ", err);
|
||||||
@@ -243,15 +342,15 @@ export const addWidgetOptions = (type) => {
|
|||||||
options: { isReadOnly: false, isHideLabel: false }
|
options: { isReadOnly: false, isHideLabel: false }
|
||||||
};
|
};
|
||||||
case textInputWidget:
|
case textInputWidget:
|
||||||
return { ...defaultOpt, validation: { type: "text", pattern: "" } };
|
return { ...defaultOpt };
|
||||||
case "initials":
|
case "initials":
|
||||||
return defaultOpt;
|
return defaultOpt;
|
||||||
case "name":
|
case "name":
|
||||||
return { ...defaultOpt, validation: { type: "text", pattern: "" } };
|
return { ...defaultOpt };
|
||||||
case "company":
|
case "company":
|
||||||
return { ...defaultOpt, validation: { type: "text", pattern: "" } };
|
return { ...defaultOpt };
|
||||||
case "job title":
|
case "job title":
|
||||||
return { ...defaultOpt, validation: { type: "text", pattern: "" } };
|
return { ...defaultOpt };
|
||||||
case "date":
|
case "date":
|
||||||
return {
|
return {
|
||||||
...defaultOpt,
|
...defaultOpt,
|
||||||
@@ -277,43 +376,21 @@ export const addWidgetOptions = (type) => {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const getWidgetType = (item, marginLeft) => {
|
export const getWidgetType = (item) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="op-btn op-btn-primary op-btn-outline op-btn-sm focus:outline-none outline outline-[1.5px] w-fit md:w-[150px] ml-[6px] md:ml-0 p-0 overflow-hidden">
|
||||||
<div
|
<div className="w-full h-full flex md:justify-between items-center">
|
||||||
className="signatureBtn widgets"
|
<div className="flex justify-start items-center text-[13px] ml-1">
|
||||||
style={{
|
{!isMobile && <i className="fa-sharp fa-solid fa-grip-vertical"></i>}
|
||||||
boxShadow:
|
<span className="text-center text-[15px] ml-[5px] font-semibold">
|
||||||
"0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18)",
|
|
||||||
marginLeft: marginLeft && `${marginLeft}px`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center mr-1">
|
|
||||||
{!isMobile && (
|
|
||||||
<i
|
|
||||||
className="fa-sharp fa-solid fa-grip-vertical"
|
|
||||||
style={{ color: "#908d8d", fontSize: "13px", marginLeft: 4 }}
|
|
||||||
></i>
|
|
||||||
)}
|
|
||||||
<span className=" font-[400] text-[15px] text-black ml-[5px]">
|
|
||||||
{item.type}
|
{item.type}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className="text-[20px] op-btn op-btn-primary w-[40px] h-full flex justify-center items-center">
|
||||||
style={{
|
<i className={item.icon}></i>
|
||||||
backgroundColor: themeColor,
|
|
||||||
padding: "0 5px",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
style={{ color: "white", fontSize: item.iconSize }}
|
|
||||||
className={item.icon}
|
|
||||||
></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -446,38 +523,96 @@ export const signPdfFun = async (
|
|||||||
base64Url,
|
base64Url,
|
||||||
documentId,
|
documentId,
|
||||||
signerObjectId,
|
signerObjectId,
|
||||||
setIsAlert
|
setIsAlert,
|
||||||
|
objectId,
|
||||||
|
isSubscribed,
|
||||||
|
activeMailAdapter,
|
||||||
|
xyPosition
|
||||||
) => {
|
) => {
|
||||||
let singleSign;
|
let singleSign,
|
||||||
|
isCustomCompletionMail = false;
|
||||||
|
|
||||||
singleSign = {
|
try {
|
||||||
pdfFile: base64Url,
|
//get tenant details
|
||||||
docId: documentId,
|
const tenantDetails = await getTenantDetails(objectId);
|
||||||
userId: signerObjectId
|
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||||
};
|
alert("User does not exist");
|
||||||
|
} else {
|
||||||
const response = await axios
|
if (
|
||||||
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
tenantDetails?.CompletionBody &&
|
||||||
headers: {
|
tenantDetails?.CompletionSubject &&
|
||||||
"Content-Type": "application/json",
|
(!isEnableSubscription || isSubscribed)
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
) {
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
isCustomCompletionMail = true;
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then((Listdata) => {
|
|
||||||
const json = Listdata.data;
|
|
||||||
const res = json.result;
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("Err ", err);
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "something went wrong"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
// below for loop is used to get first signature of user to send if to signpdf
|
||||||
|
// for adding it in completion certificate
|
||||||
|
let getSignature;
|
||||||
|
for (let item of xyPosition) {
|
||||||
|
if (!getSignature) {
|
||||||
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let base64Sign = getSignature.SignUrl;
|
||||||
|
//check https type signature (default signature exist) then convert in base64
|
||||||
|
const isUrl = base64Sign.includes("https");
|
||||||
|
if (isUrl) {
|
||||||
|
try {
|
||||||
|
base64Sign = await fetchImageBase64(base64Sign);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//change image width and height to 104/44 in png base64
|
||||||
|
const getNewse64 = await changeImageWH(base64Sign);
|
||||||
|
//remove suffiix of base64
|
||||||
|
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||||
|
|
||||||
|
singleSign = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
pdfFile: base64Url,
|
||||||
|
docId: documentId,
|
||||||
|
userId: signerObjectId,
|
||||||
|
isCustomCompletionMail: isCustomCompletionMail,
|
||||||
|
signature: suffixbase64
|
||||||
|
};
|
||||||
|
const response = await axios
|
||||||
|
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
// sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((Listdata) => {
|
||||||
|
const json = Listdata.data;
|
||||||
|
const res = json.result;
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Err ", err);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const randomId = () => {
|
export const randomId = () => {
|
||||||
@@ -512,6 +647,7 @@ export const createDocument = async (template, placeholders, signerData) => {
|
|||||||
Name: Doc.Name,
|
Name: Doc.Name,
|
||||||
URL: Doc.URL,
|
URL: Doc.URL,
|
||||||
SignedUrl: Doc.SignedUrl,
|
SignedUrl: Doc.SignedUrl,
|
||||||
|
SentToOthers: Doc.SentToOthers,
|
||||||
Description: Doc.Description,
|
Description: Doc.Description,
|
||||||
Note: Doc.Note,
|
Note: Doc.Note,
|
||||||
Placeholders: placeholdersArr,
|
Placeholders: placeholdersArr,
|
||||||
@@ -526,7 +662,9 @@ export const createDocument = async (template, placeholders, signerData) => {
|
|||||||
objectId: Doc.CreatedBy.objectId
|
objectId: Doc.CreatedBy.objectId
|
||||||
},
|
},
|
||||||
Signers: signers,
|
Signers: signers,
|
||||||
SendinOrder: Doc?.SendinOrder || false
|
SendinOrder: Doc?.SendinOrder || false,
|
||||||
|
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||||
|
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5)
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -729,6 +867,82 @@ export const onChangeInput = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//function to increase height of text area on press enter
|
||||||
|
export const onChangeHeightOfTextArea = (
|
||||||
|
height,
|
||||||
|
widgetType,
|
||||||
|
signKey,
|
||||||
|
xyPostion,
|
||||||
|
index,
|
||||||
|
setXyPostion,
|
||||||
|
userId
|
||||||
|
) => {
|
||||||
|
const isSigners = xyPostion.some((data) => data.signerPtr);
|
||||||
|
let filterSignerPos;
|
||||||
|
if (isSigners) {
|
||||||
|
if (userId) {
|
||||||
|
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
|
||||||
|
}
|
||||||
|
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
|
||||||
|
|
||||||
|
const getPageNumer = getPlaceHolder.filter(
|
||||||
|
(data) => data.pageNumber === index
|
||||||
|
);
|
||||||
|
if (getPageNumer.length > 0) {
|
||||||
|
const getXYdata = getPageNumer[0].pos;
|
||||||
|
const getPosData = getXYdata;
|
||||||
|
const addSignPos = getPosData.map((position) => {
|
||||||
|
if (position.key === signKey) {
|
||||||
|
return {
|
||||||
|
...position,
|
||||||
|
Height: position.Height
|
||||||
|
? position.Height + height
|
||||||
|
: defaultWidthHeight(widgetType).height + height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
});
|
||||||
|
const newUpdateSignPos = getPlaceHolder.map((obj) => {
|
||||||
|
if (obj.pageNumber === index) {
|
||||||
|
return { ...obj, pos: addSignPos };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newUpdateSigner = xyPostion.map((obj) => {
|
||||||
|
if (obj.Id === userId) {
|
||||||
|
return { ...obj, placeHolder: newUpdateSignPos };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
setXyPostion(newUpdateSigner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let getXYdata = xyPostion[index].pos;
|
||||||
|
|
||||||
|
const updatePosition = getXYdata.map((position) => {
|
||||||
|
if (position.key === signKey) {
|
||||||
|
return {
|
||||||
|
...position,
|
||||||
|
Height: position.Height
|
||||||
|
? position.Height + height
|
||||||
|
: defaultWidthHeight(widgetType).height + height
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatePlaceholder = xyPostion.map((obj, ind) => {
|
||||||
|
if (ind === index) {
|
||||||
|
return { ...obj, pos: updatePosition };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
setXyPostion(updatePlaceholder);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const addInitialData = (signerPos, setXyPostion, value, userId) => {
|
export const addInitialData = (signerPos, setXyPostion, value, userId) => {
|
||||||
function widgetDataValue(type) {
|
function widgetDataValue(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -828,6 +1042,7 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
|||||||
|
|
||||||
//function for save button to save signature or image url
|
//function for save button to save signature or image url
|
||||||
export function onSaveSign(
|
export function onSaveSign(
|
||||||
|
type,
|
||||||
xyPostion,
|
xyPostion,
|
||||||
index,
|
index,
|
||||||
signKey,
|
signKey,
|
||||||
@@ -868,6 +1083,7 @@ export function onSaveSign(
|
|||||||
Width: posWidth,
|
Width: posWidth,
|
||||||
Height: posHeight,
|
Height: posHeight,
|
||||||
SignUrl: signatureImg,
|
SignUrl: signatureImg,
|
||||||
|
signatureType: type && type,
|
||||||
options: {
|
options: {
|
||||||
...position.options,
|
...position.options,
|
||||||
response: signatureImg
|
response: signatureImg
|
||||||
@@ -936,7 +1152,6 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
|||||||
const imageType = event.target.files[0].type;
|
const imageType = event.target.files[0].type;
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(event.target.files[0]);
|
reader.readAsDataURL(event.target.files[0]);
|
||||||
|
|
||||||
reader.onloadend = function (e) {
|
reader.onloadend = function (e) {
|
||||||
let width, height;
|
let width, height;
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
@@ -964,6 +1179,50 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//convert https url to base64
|
||||||
|
export const fetchImageBase64 = async (imageUrl) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(imageUrl);
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
const base64data = reader.result;
|
||||||
|
resolve(base64data);
|
||||||
|
};
|
||||||
|
reader.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Error converting URL to base64:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function for select image and upload image
|
||||||
|
export const changeImageWH = async (base64Image) => {
|
||||||
|
const newWidth = 100;
|
||||||
|
const newHeight = 40;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = base64Image;
|
||||||
|
img.onload = async () => {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = newWidth;
|
||||||
|
canvas.height = newHeight;
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||||
|
const resizedBase64 = canvas.toDataURL("image/png", 1);
|
||||||
|
resolve(resizedBase64);
|
||||||
|
};
|
||||||
|
img.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
//function for embed multiple signature using pdf-lib
|
//function for embed multiple signature using pdf-lib
|
||||||
export const multiSignEmbed = async (
|
export const multiSignEmbed = async (
|
||||||
pngUrl,
|
pngUrl,
|
||||||
@@ -1002,10 +1261,10 @@ export const multiSignEmbed = async (
|
|||||||
const newUrl = await convertPNGtoJPEG(signUrl);
|
const newUrl = await convertPNGtoJPEG(signUrl);
|
||||||
signUrl = newUrl;
|
signUrl = newUrl;
|
||||||
}
|
}
|
||||||
const checkUrl = urlValidator(signUrl);
|
// const checkUrl = urlValidator(signUrl);
|
||||||
if (checkUrl) {
|
// if (checkUrl) {
|
||||||
signUrl = signUrl + "?get";
|
// signUrl = signUrl + "?get";
|
||||||
}
|
// }
|
||||||
const res = await fetch(signUrl);
|
const res = await fetch(signUrl);
|
||||||
return res.arrayBuffer();
|
return res.arrayBuffer();
|
||||||
}
|
}
|
||||||
@@ -1078,8 +1337,8 @@ export const multiSignEmbed = async (
|
|||||||
position.type === radioButtonWidget
|
position.type === radioButtonWidget
|
||||||
? 10
|
? 10
|
||||||
: position.type === "checkbox"
|
: position.type === "checkbox"
|
||||||
? 10
|
? 10
|
||||||
: newUpdateHeight;
|
: newUpdateHeight;
|
||||||
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
|
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
|
||||||
|
|
||||||
if (signyourself) {
|
if (signyourself) {
|
||||||
@@ -1140,6 +1399,7 @@ export const multiSignEmbed = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const widgetTypeExist = [
|
const widgetTypeExist = [
|
||||||
|
textWidget,
|
||||||
textInputWidget,
|
textInputWidget,
|
||||||
"name",
|
"name",
|
||||||
"company",
|
"company",
|
||||||
@@ -1148,13 +1408,14 @@ export const multiSignEmbed = async (
|
|||||||
"email"
|
"email"
|
||||||
].includes(position.type);
|
].includes(position.type);
|
||||||
if (position.type === "checkbox") {
|
if (position.type === "checkbox") {
|
||||||
let addYPosition, isCheck;
|
let addYPosition = 0,
|
||||||
|
isCheck;
|
||||||
|
|
||||||
if (position?.options?.values.length > 0) {
|
if (position?.options?.values.length > 0) {
|
||||||
position?.options?.values.forEach((item, ind) => {
|
position?.options?.values.forEach((item, ind) => {
|
||||||
const checkboxRandomId = "checkbox" + randomId();
|
const checkboxRandomId = "checkbox" + randomId();
|
||||||
let yPosition;
|
let yPosition;
|
||||||
const height = 10;
|
const height = 13;
|
||||||
if (
|
if (
|
||||||
position?.options?.response &&
|
position?.options?.response &&
|
||||||
position?.options?.response?.length > 0
|
position?.options?.response?.length > 0
|
||||||
@@ -1175,14 +1436,14 @@ export const multiSignEmbed = async (
|
|||||||
if (!position?.options?.isHideLabel) {
|
if (!position?.options?.isHideLabel) {
|
||||||
// below line of code is used to embed label with radio button in pdf
|
// below line of code is used to embed label with radio button in pdf
|
||||||
page.drawText(item, {
|
page.drawText(item, {
|
||||||
x: xPos(position) + 15,
|
x: xPos(position) + 17,
|
||||||
y: yPosition + 2,
|
y: yPosition + 2,
|
||||||
size: height
|
size: height
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
checkbox.addToPage(page, {
|
checkbox.addToPage(page, {
|
||||||
x: xPos(position),
|
x: xPos(position),
|
||||||
y: yPosition,
|
y: yPosition - 3,
|
||||||
width: height,
|
width: height,
|
||||||
height: height
|
height: height
|
||||||
});
|
});
|
||||||
@@ -1194,7 +1455,7 @@ export const multiSignEmbed = async (
|
|||||||
checkbox.enableReadOnly();
|
checkbox.enableReadOnly();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (position.type === textWidget) {
|
} else if (widgetTypeExist) {
|
||||||
const font = await pdfDoc.embedFont("Helvetica");
|
const font = await pdfDoc.embedFont("Helvetica");
|
||||||
const fontSize = 12;
|
const fontSize = 12;
|
||||||
let textContent;
|
let textContent;
|
||||||
@@ -1203,19 +1464,21 @@ export const multiSignEmbed = async (
|
|||||||
} else if (position?.options?.defaultValue) {
|
} else if (position?.options?.defaultValue) {
|
||||||
textContent = position?.options?.defaultValue;
|
textContent = position?.options?.defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fixedWidth = scaleWidth; // Set your fixed width
|
const fixedWidth = scaleWidth; // Set your fixed width
|
||||||
|
const isNewOnEnterLineExist = textContent.includes("\n");
|
||||||
|
|
||||||
// Function to break text into lines based on the fixed width
|
// Function to break text into lines based on the fixed width
|
||||||
const breakTextIntoLines = (textContent, width) => {
|
const NewbreakTextIntoLines = (textContent, width) => {
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let currentLine = "";
|
let currentLine = "";
|
||||||
|
|
||||||
for (const word of textContent.split(" ")) {
|
for (const word of textContent.split(" ")) {
|
||||||
|
//get text line width
|
||||||
const lineWidth = font.widthOfTextAtSize(
|
const lineWidth = font.widthOfTextAtSize(
|
||||||
`${currentLine} ${word}`,
|
`${currentLine} ${word}`,
|
||||||
fontSize
|
fontSize
|
||||||
);
|
);
|
||||||
|
//check text content line width is less or equal to container width
|
||||||
if (lineWidth <= width) {
|
if (lineWidth <= width) {
|
||||||
currentLine += ` ${word}`;
|
currentLine += ` ${word}`;
|
||||||
} else {
|
} else {
|
||||||
@@ -1226,7 +1489,33 @@ export const multiSignEmbed = async (
|
|||||||
lines.push(currentLine.trim());
|
lines.push(currentLine.trim());
|
||||||
return lines;
|
return lines;
|
||||||
};
|
};
|
||||||
const lines = breakTextIntoLines(textContent, fixedWidth);
|
|
||||||
|
// Function to break text into lines based on when user go next line on press enter button
|
||||||
|
const breakTextIntoLines = (textContent, width) => {
|
||||||
|
const lines = [];
|
||||||
|
|
||||||
|
for (const word of textContent.split("\n")) {
|
||||||
|
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
|
||||||
|
//checking string length to container width
|
||||||
|
//if string length is less then container width it means user press enter button
|
||||||
|
if (lineWidth <= width) {
|
||||||
|
lines.push(word);
|
||||||
|
}
|
||||||
|
//else adjust text content according to width and send it in new line
|
||||||
|
else {
|
||||||
|
const newLine = NewbreakTextIntoLines(word, width);
|
||||||
|
lines.push(...newLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
};
|
||||||
|
|
||||||
|
//check if text content have `\n` string it means user press enter to go next line and handle condition
|
||||||
|
//else auto adjust text content according to container width
|
||||||
|
const lines = isNewOnEnterLineExist
|
||||||
|
? breakTextIntoLines(textContent, fixedWidth)
|
||||||
|
: NewbreakTextIntoLines(textContent, fixedWidth);
|
||||||
// Set initial y-coordinate for the first line
|
// Set initial y-coordinate for the first line
|
||||||
const labelDefaultHeight = defaultWidthHeight(position.type).height;
|
const labelDefaultHeight = defaultWidthHeight(position.type).height;
|
||||||
|
|
||||||
@@ -1242,24 +1531,8 @@ export const multiSignEmbed = async (
|
|||||||
color: rgb(0, 0, 0),
|
color: rgb(0, 0, 0),
|
||||||
size: fontSize
|
size: fontSize
|
||||||
});
|
});
|
||||||
y -= 15; // Adjust the line height as needed
|
y -= 18; // Adjust the line height as needed
|
||||||
}
|
}
|
||||||
} else if (widgetTypeExist) {
|
|
||||||
const font = await pdfDoc.embedFont("Helvetica");
|
|
||||||
const fontSize = 12;
|
|
||||||
let textContent;
|
|
||||||
if (position?.options?.response) {
|
|
||||||
textContent = position.options?.response;
|
|
||||||
} else if (position?.options?.defaultValue) {
|
|
||||||
textContent = position?.options?.defaultValue;
|
|
||||||
}
|
|
||||||
page.drawText(textContent, {
|
|
||||||
x: xPos(position),
|
|
||||||
y: yPos(position) + 10,
|
|
||||||
size: fontSize,
|
|
||||||
font,
|
|
||||||
color: rgb(0, 0, 0)
|
|
||||||
});
|
|
||||||
} else if (position.type === "dropdown") {
|
} else if (position.type === "dropdown") {
|
||||||
const dropdownRandomId = "dropdown" + randomId();
|
const dropdownRandomId = "dropdown" + randomId();
|
||||||
const dropdown = form.createDropdown(dropdownRandomId);
|
const dropdown = form.createDropdown(dropdownRandomId);
|
||||||
@@ -1327,9 +1600,7 @@ export const multiSignEmbed = async (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
|
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
|
||||||
|
|
||||||
return pdfBytes;
|
return pdfBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1569,3 +1840,326 @@ export const getYear = (date) => {
|
|||||||
const newYear = new Date(date).getFullYear();
|
const newYear = new Date(date).getFullYear();
|
||||||
return newYear;
|
return newYear;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//function to create/copy widget next to already dropped widget
|
||||||
|
export const handleCopyNextToWidget = (
|
||||||
|
position,
|
||||||
|
widgetType,
|
||||||
|
xyPostion,
|
||||||
|
index,
|
||||||
|
setXyPostion,
|
||||||
|
userId
|
||||||
|
) => {
|
||||||
|
const isSigners = xyPostion.some((data) => data.signerPtr);
|
||||||
|
let filterSignerPos;
|
||||||
|
//get position of previous widget and create new widget next to that widget on same data except
|
||||||
|
// xPosition and key
|
||||||
|
let newpos = position;
|
||||||
|
const calculateXPosition =
|
||||||
|
parseInt(position.xPosition) +
|
||||||
|
defaultWidthHeight(widgetType).width +
|
||||||
|
resizeBorderExtraWidth();
|
||||||
|
const newId = randomId();
|
||||||
|
newpos = { ...newpos, xPosition: calculateXPosition, key: newId };
|
||||||
|
//if condition to create widget in request-sign flow
|
||||||
|
if (isSigners) {
|
||||||
|
if (userId) {
|
||||||
|
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
|
||||||
|
}
|
||||||
|
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
|
||||||
|
const getPageNumer = getPlaceHolder.filter(
|
||||||
|
(data) => data.pageNumber === index
|
||||||
|
);
|
||||||
|
const getXYdata = getPageNumer[0].pos;
|
||||||
|
getXYdata.push(newpos);
|
||||||
|
if (getPageNumer.length > 0) {
|
||||||
|
const newUpdateSignPos = getPlaceHolder.map((obj) => {
|
||||||
|
if (obj.pageNumber === index) {
|
||||||
|
return { ...obj, pos: getXYdata };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newUpdateSigner = xyPostion.map((obj) => {
|
||||||
|
if (obj.Id === userId) {
|
||||||
|
return { ...obj, placeHolder: newUpdateSignPos };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
setXyPostion(newUpdateSigner);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// else condition to create widget in sign-yourself flow
|
||||||
|
let getXYdata = xyPostion[index].pos;
|
||||||
|
getXYdata.push(newpos);
|
||||||
|
const updatePlaceholder = xyPostion.map((obj, ind) => {
|
||||||
|
if (ind === index) {
|
||||||
|
return { ...obj, pos: getXYdata };
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
setXyPostion(updatePlaceholder);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFileName = (fileUrl) => {
|
||||||
|
if (fileUrl) {
|
||||||
|
const url = new URL(fileUrl);
|
||||||
|
const filename = url.pathname.substring(url.pathname.indexOf("_") + 1);
|
||||||
|
return filename || "";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//fetch tenant app logo from `partners_Tenant` class by domain name
|
||||||
|
export const getAppLogo = async () => {
|
||||||
|
const domainName = window.location.host;
|
||||||
|
try {
|
||||||
|
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
|
||||||
|
tenantCreditsQuery.equalTo("Domain", domainName);
|
||||||
|
const res = await tenantCreditsQuery.first();
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
const updateRes = JSON.parse(JSON.stringify(res));
|
||||||
|
return updateRes?.Logo;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTenantDetails = async (objectId) => {
|
||||||
|
try {
|
||||||
|
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
|
||||||
|
tenantCreditsQuery.equalTo("UserId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "_User",
|
||||||
|
objectId: objectId
|
||||||
|
});
|
||||||
|
const res = await tenantCreditsQuery.first();
|
||||||
|
if (res) {
|
||||||
|
const updateRes = JSON.parse(JSON.stringify(res));
|
||||||
|
return updateRes;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return "user does not exist!";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//function to convert variable string name to variable value of email body and subject
|
||||||
|
export function replaceMailVaribles(subject, body, variables) {
|
||||||
|
let replacedSubject = subject;
|
||||||
|
let replacedBody = body;
|
||||||
|
|
||||||
|
for (const variable in variables) {
|
||||||
|
const regex = new RegExp(`{{${variable}}}`, "g");
|
||||||
|
if (subject) {
|
||||||
|
replacedSubject = replacedSubject.replace(regex, variables[variable]);
|
||||||
|
}
|
||||||
|
if (body) {
|
||||||
|
replacedBody = replacedBody.replace(regex, variables[variable]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
subject: replacedSubject,
|
||||||
|
body: replacedBody
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const copytoData = (text) => {
|
||||||
|
// navigator.clipboard.writeText(text);
|
||||||
|
if (navigator.clipboard) {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
} else {
|
||||||
|
// Fallback for browsers that don't support navigator.clipboard
|
||||||
|
const textArea = document.createElement("textarea");
|
||||||
|
textArea.value = text;
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
textArea.select();
|
||||||
|
document.execCommand("copy");
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertPdfArrayBuffer = async (url) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
// Check if the response was successful (status 200)
|
||||||
|
if (!response.ok) {
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
// Convert the response to ArrayBuffer
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
return arrayBuffer;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function
|
||||||
|
export const handleSendOTP = async (email) => {
|
||||||
|
try {
|
||||||
|
let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||||
|
};
|
||||||
|
const body = { email: email };
|
||||||
|
await axios.post(url, body, { headers: headers });
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//handle download signed pdf
|
||||||
|
export const handleDownloadPdf = async (
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
) => {
|
||||||
|
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||||
|
setIsDownloading("pdf");
|
||||||
|
try {
|
||||||
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
|
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||||
|
setIsDownloading("");
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in getsignedurl", err);
|
||||||
|
setIsDownloading("");
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sanitizeFileName = (pdfName) => {
|
||||||
|
// Replace spaces with underscore
|
||||||
|
return pdfName.replace(/ /g, "_");
|
||||||
|
};
|
||||||
|
//function for print digital sign pdf
|
||||||
|
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsDownloading("pdf");
|
||||||
|
try {
|
||||||
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
|
const pdf = await getBase64FromUrl(url);
|
||||||
|
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");
|
||||||
|
setIsDownloading("");
|
||||||
|
} else {
|
||||||
|
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||||
|
setIsDownloading("");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setIsDownloading("");
|
||||||
|
console.log("err in getsignedurl", err);
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle download signed pdf
|
||||||
|
export const handleDownloadCertificate = async (
|
||||||
|
pdfDetails,
|
||||||
|
setIsDownloading
|
||||||
|
) => {
|
||||||
|
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||||
|
try {
|
||||||
|
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||||
|
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||||
|
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in download in certificate", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsDownloading("certificate");
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
docId: pdfDetails[0]?.objectId
|
||||||
|
};
|
||||||
|
const docDetails = await axios.post(
|
||||||
|
`${localStorage.getItem("baseUrl")}functions/getDocument`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (docDetails.data && docDetails.data.result) {
|
||||||
|
const doc = docDetails.data.result;
|
||||||
|
if (doc?.CertificateUrl) {
|
||||||
|
await fetch(doc?.CertificateUrl);
|
||||||
|
const certificateUrl = doc?.CertificateUrl;
|
||||||
|
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||||
|
setIsDownloading("");
|
||||||
|
} else {
|
||||||
|
setIsDownloading("certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in download in certificate", err);
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export async function findContact(value) {
|
||||||
|
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);
|
||||||
|
contactbook.matches("Email", new RegExp(value, "i"));
|
||||||
|
|
||||||
|
const contactRes = await contactbook.find();
|
||||||
|
if (contactRes) {
|
||||||
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching suggestions:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
export const contactCls = "contracts_Contactbook";
|
export const contactCls = "contracts_Contactbook";
|
||||||
export const templateCls = "contracts_Template";
|
export const templateCls = "contracts_Template";
|
||||||
export const documentCls = "contracts_Document";
|
export const documentCls = "contracts_Document";
|
||||||
export const rejectBtn =
|
|
||||||
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
|
|
||||||
export const submitBtn =
|
|
||||||
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
|
|
||||||
export const modalSubmitBtnColor = "#17a2b8";
|
|
||||||
export const modalCancelBtnColor = "white";
|
|
||||||
export const themeColor = "#47a3ad";
|
export const themeColor = "#47a3ad";
|
||||||
export const iconColor = "#686968";
|
export const iconColor = "#686968";
|
||||||
|
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = process.env.REACT_APP_APPID
|
||||||
const serverUrl = localStorage.getItem("baseUrl");
|
? process.env.REACT_APP_APPID
|
||||||
|
: "opensign";
|
||||||
|
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||||
|
? 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) {
|
||||||
@@ -30,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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,17 +2,16 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
body{
|
body {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom CSS to hide scrollbar */
|
/* Custom CSS to hide scrollbar */
|
||||||
.hide-scrollbar::-webkit-scrollbar {
|
.hide-scrollbar::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-scrollbar {
|
.hide-scrollbar {
|
||||||
-ms-overflow-style: none; /* IE and Edge */
|
-ms-overflow-style: none; /* IE and Edge */
|
||||||
scrollbar-width: none; /* Firefox */
|
scrollbar-width: none; /* Firefox */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ import "./index.css";
|
|||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { store } from "./redux/store";
|
import { store } from "./redux/store";
|
||||||
|
import { CookiesProvider } from "react-cookie";
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
import { TouchBackend } from "react-dnd-touch-backend";
|
import { TouchBackend } from "react-dnd-touch-backend";
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
import DragElement from "./components/pdf/DragElement";
|
import DragElement from "./components/pdf/DragElement";
|
||||||
import TagManager from "react-gtm-module";
|
import TagManager from "react-gtm-module";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import './polyfills'
|
||||||
const appId = process.env.REACT_APP_APPID
|
const appId = process.env.REACT_APP_APPID
|
||||||
? process.env.REACT_APP_APPID
|
? process.env.REACT_APP_APPID
|
||||||
: "opensign";
|
: "opensign";
|
||||||
@@ -62,10 +64,12 @@ if (process.env.REACT_APP_GTM) {
|
|||||||
|
|
||||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||||
root.render(
|
root.render(
|
||||||
<Provider store={store}>
|
<CookiesProvider defaultSetOptions={{ path: "/" }}>
|
||||||
<DndProvider options={HTML5toTouch}>
|
<Provider store={store}>
|
||||||
<Preview>{generatePreview}</Preview>
|
<DndProvider options={HTML5toTouch}>
|
||||||
<App />
|
<Preview>{generatePreview}</Preview>
|
||||||
</DndProvider>
|
<App />
|
||||||
</Provider>
|
</DndProvider>
|
||||||
|
</Provider>
|
||||||
|
</CookiesProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default function reportJson(id) {
|
|||||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||||
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
||||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||||
|
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||||
switch (id) {
|
switch (id) {
|
||||||
// draft documents report
|
// draft documents report
|
||||||
case "ByHuevtCFY":
|
case "ByHuevtCFY":
|
||||||
@@ -12,13 +12,24 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "sign",
|
btnId: "1231",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "Edit",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-plus",
|
btnIcon: "fa-solid fa-pen",
|
||||||
redirectUrl: "draftDocument"
|
redirectUrl: "draftDocument",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2142",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"These are documents you have started but have not finalized for sending."
|
||||||
};
|
};
|
||||||
// Need your sign report
|
// Need your sign report
|
||||||
case "4Hhwbp482K":
|
case "4Hhwbp482K":
|
||||||
@@ -27,13 +38,17 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
|
btnId: "4536",
|
||||||
btnLabel: "sign",
|
btnLabel: "sign",
|
||||||
btnColor: "#3ac9d6",
|
hoverLabel: "Sign",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa-solid fa-signature",
|
||||||
redirectUrl: "pdfRequestFiles"
|
redirectUrl: "pdfRequestFiles",
|
||||||
|
action: "redirect"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of documents that are waiting for your signature"
|
||||||
};
|
};
|
||||||
// In progess report
|
// In progess report
|
||||||
case "1MwEuxLEkF":
|
case "1MwEuxLEkF":
|
||||||
@@ -42,13 +57,58 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "View",
|
btnId: "8901",
|
||||||
btnColor: "#3ac9d6",
|
hoverLabel: "Share",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
|
btnIcon: "fa-solid fa-share",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1588",
|
||||||
|
hoverLabel: "View",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa fa-eye",
|
||||||
redirectUrl: "pdfRequestFiles"
|
redirectUrl: "pdfRequestFiles",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2234",
|
||||||
|
hoverLabel: "option",
|
||||||
|
btnColor: "",
|
||||||
|
textColor: "black",
|
||||||
|
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||||
|
action: "option",
|
||||||
|
subaction: [
|
||||||
|
{
|
||||||
|
btnId: "1630",
|
||||||
|
btnLabel: "Resend",
|
||||||
|
hoverLabel: "Resend",
|
||||||
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "resend"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1688",
|
||||||
|
btnLabel: "Revoke",
|
||||||
|
hoverLabel: "Revoke",
|
||||||
|
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "revoke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1488",
|
||||||
|
btnLabel: "Delete",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of documents you've sent to other parties for signature."
|
||||||
};
|
};
|
||||||
// completed documents report
|
// completed documents report
|
||||||
case "kQUoW4hUXz":
|
case "kQUoW4hUXz":
|
||||||
@@ -57,13 +117,24 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "View",
|
btnId: "1378",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "Edit",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa fa-eye",
|
||||||
redirectUrl: "draftDocument"
|
redirectUrl: "draftDocument",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1278",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of documents that have been signed by all parties."
|
||||||
};
|
};
|
||||||
// declined documents report
|
// declined documents report
|
||||||
case "UPr2Fm5WY3":
|
case "UPr2Fm5WY3":
|
||||||
@@ -72,13 +143,24 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "View",
|
btnId: "1458",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "View",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa fa-eye",
|
||||||
redirectUrl: "draftDocument"
|
redirectUrl: "draftDocument",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1358",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of documents that have been declined by one of the signers."
|
||||||
};
|
};
|
||||||
// Expired Documents report
|
// Expired Documents report
|
||||||
case "zNqBHXHsYH":
|
case "zNqBHXHsYH":
|
||||||
@@ -87,13 +169,24 @@ export default function reportJson(id) {
|
|||||||
heading: head,
|
heading: head,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "View",
|
btnId: "1898",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "View",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa fa-eye",
|
||||||
redirectUrl: "draftDocument"
|
redirectUrl: "draftDocument",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1998",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of documents that have reached their expiration date."
|
||||||
};
|
};
|
||||||
// Recently sent for signatures report show on dashboard
|
// Recently sent for signatures report show on dashboard
|
||||||
case "d9k3UfYHBc":
|
case "d9k3UfYHBc":
|
||||||
@@ -102,11 +195,54 @@ export default function reportJson(id) {
|
|||||||
heading: dashboardReportHead,
|
heading: dashboardReportHead,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "View",
|
btnId: "1999",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "Share",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
|
btnIcon: "fa-solid fa-share",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "share"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1999",
|
||||||
|
hoverLabel: "View",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
btnIcon: "fa fa-eye",
|
btnIcon: "fa fa-eye",
|
||||||
redirectUrl: "pdfRequestFiles"
|
redirectUrl: "pdfRequestFiles",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2234",
|
||||||
|
hoverLabel: "option",
|
||||||
|
btnColor: "",
|
||||||
|
textColor: "black",
|
||||||
|
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||||
|
action: "option",
|
||||||
|
subaction: [
|
||||||
|
{
|
||||||
|
btnId: "1631",
|
||||||
|
btnLabel: "Resend",
|
||||||
|
hoverLabel: "Resend",
|
||||||
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "resend"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "6788",
|
||||||
|
btnLabel: "Revoke",
|
||||||
|
hoverLabel: "Revoke",
|
||||||
|
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "revoke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2000",
|
||||||
|
btnLabel: "Delete",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -117,11 +253,13 @@ export default function reportJson(id) {
|
|||||||
heading: dashboardReportHead,
|
heading: dashboardReportHead,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "Sign",
|
btnId: "2001",
|
||||||
btnColor: "#4bd396",
|
btnLabel: "sign",
|
||||||
textColor: "white",
|
hoverLabel: "Sign",
|
||||||
btnIcon: "fa fa-eye",
|
btnColor: "op-btn-primary",
|
||||||
redirectUrl: "pdfRequestFiles"
|
btnIcon: "fa-solid fa-signature",
|
||||||
|
redirectUrl: "pdfRequestFiles",
|
||||||
|
action: "redirect"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -132,11 +270,20 @@ export default function reportJson(id) {
|
|||||||
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
|
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "sign",
|
btnId: "2003",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "Edit",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-plus",
|
btnIcon: "fa-solid fa-pen",
|
||||||
redirectUrl: "draftDocument"
|
redirectUrl: "draftDocument",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2004",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnColor: "op-btn-secondary",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@@ -147,35 +294,79 @@ export default function reportJson(id) {
|
|||||||
heading: contactbook,
|
heading: contactbook,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnLabel: "",
|
btnId: "2204",
|
||||||
btnColor: "#f55a42",
|
hoverLabel: "Delete",
|
||||||
textColor: "white",
|
btnColor: "op-btn-outline op-btn-error",
|
||||||
btnIcon: "fa-solid fa-trash"
|
btnIcon: "fa-solid fa-trash",
|
||||||
|
action: "delete"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
form: "ContactBook"
|
form: "ContactBook",
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
|
||||||
};
|
};
|
||||||
// template report
|
// template report
|
||||||
case "6TeaPr321t":
|
case "6TeaPr321t":
|
||||||
return {
|
return {
|
||||||
reportName: "Templates",
|
reportName: "Templates",
|
||||||
heading: head,
|
heading: templateReport,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
|
btnId: "2234",
|
||||||
btnLabel: "Use",
|
btnLabel: "Use",
|
||||||
btnColor: "#4bd396",
|
hoverLabel: "Use",
|
||||||
textColor: "white",
|
btnColor: "op-btn-primary",
|
||||||
btnIcon: "fa fa-plus",
|
btnIcon: "fa fa-plus",
|
||||||
redirectUrl: "placeHolderSign"
|
redirectUrl: "placeHolderSign",
|
||||||
|
action: "redirect",
|
||||||
|
selector: "reactourSecond",
|
||||||
|
message:
|
||||||
|
"Click the 'Use' button to create a new document from an existing template. "
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
btnLabel: "Edit",
|
btnId: "1631",
|
||||||
btnColor: "#00c9d5",
|
btnLabel: "Quick send",
|
||||||
textColor: "white",
|
hoverLabel: "Quick send",
|
||||||
btnIcon: "fa fa-plus",
|
btnColor: "op-btn-secondary",
|
||||||
redirectUrl: "template"
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "bulksend",
|
||||||
|
selector: "tourbulksend",
|
||||||
|
message:
|
||||||
|
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "2234",
|
||||||
|
hoverLabel: "option",
|
||||||
|
btnColor: "",
|
||||||
|
textColor: "black",
|
||||||
|
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||||
|
action: "option",
|
||||||
|
selector: "reactourThird",
|
||||||
|
message:
|
||||||
|
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won’t affect existing documents.Use the Delete button you can delete template. ",
|
||||||
|
subaction: [
|
||||||
|
{
|
||||||
|
btnId: "2434",
|
||||||
|
btnLabel: "Edit",
|
||||||
|
hoverLabel: "Edit",
|
||||||
|
btnIcon: "fa-solid fa-pen",
|
||||||
|
redirectUrl: "template",
|
||||||
|
action: "redirect"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
btnId: "1834",
|
||||||
|
btnLabel: "Delete",
|
||||||
|
hoverLabel: "Delete",
|
||||||
|
btnIcon: "fa fa-trash",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "delete"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
helpMsg:
|
||||||
|
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
const dashboardJson = [
|
||||||
|
{
|
||||||
|
id: "35KBoSgoAK",
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||||
|
widget: {
|
||||||
|
type: "Card",
|
||||||
|
icon: "fas fa-signature",
|
||||||
|
bgColor: "bg-[#f0058e]",
|
||||||
|
label: "Need your Signature",
|
||||||
|
description: null,
|
||||||
|
data: {
|
||||||
|
id: "e04ee9dc-932d-6b80-0202-703fd154eb74",
|
||||||
|
queryType: "",
|
||||||
|
class: "contracts_Document",
|
||||||
|
query:
|
||||||
|
'where={"$and":[{"AuditTrail.UserPtr":{"$ne":{"__type":"Pointer","className": "contracts_Users","objectId":"#objectId#"}}},{"AuditTrail.Activity":{"$ne":"Signed"}}],"Signers":{"$in":[{"__type":"Pointer","className":"contracts_Users","objectId":"#objectId#"}]},"IsDeclined":{"$ne":true},"IsCompleted":{"$ne":true},"Type":{"$ne":"Folder"},"Placeholders":{"$ne":null},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
|
||||||
|
key: "count",
|
||||||
|
Redirect_type: "Report",
|
||||||
|
Redirect_id: "4Hhwbp482K",
|
||||||
|
tourSection: "tourcard1",
|
||||||
|
tourMessage:
|
||||||
|
"Clicking on this card will take you to the list of documents awaiting your review."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||||
|
widget: {
|
||||||
|
type: "Card",
|
||||||
|
icon: "fas fa-sign-out-alt",
|
||||||
|
bgColor: "bg-[#2ed8b6]",
|
||||||
|
label: "Out for signatures",
|
||||||
|
description: null,
|
||||||
|
data: {
|
||||||
|
id: "c1935cc0-28d4-3d73-b72a-8c1e3d18a17f",
|
||||||
|
queryType: "",
|
||||||
|
class: "contracts_Document",
|
||||||
|
query:
|
||||||
|
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"IsCompleted":{"#*ne":true},"IsDeclined":{"#*ne":true},"IsArchive":{"#*ne":true},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"#UserId.objectId#"},"ExpiryDate":{"#*gt":{"__type":"#Date#","iso":"#today#"}}}&count=1',
|
||||||
|
key: "count",
|
||||||
|
Redirect_type: "Report",
|
||||||
|
Redirect_id: "1MwEuxLEkF",
|
||||||
|
tourSection: "tourcard2",
|
||||||
|
tourMessage:
|
||||||
|
"Clicking on this card will take you to a list of documents awaiting signature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||||
|
widget: {
|
||||||
|
type: "report",
|
||||||
|
reportId: "5Go51Q7T8r",
|
||||||
|
data: {
|
||||||
|
tourSection: "tourreport1",
|
||||||
|
tourMessage:
|
||||||
|
"This is a list of documents that are waiting for your signature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||||
|
widget: {
|
||||||
|
type: "report",
|
||||||
|
reportId: "d9k3UfYHBc",
|
||||||
|
data: {
|
||||||
|
tourSection: "tourreport2",
|
||||||
|
tourMessage:
|
||||||
|
"This is a list of documents you've sent to other parties for signature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
colsize: "col-span-12 md:col-span-12 lg:col-span-12",
|
||||||
|
widget: {
|
||||||
|
type: "report",
|
||||||
|
reportId: "kC5mfynCi4",
|
||||||
|
data: {
|
||||||
|
tourSection: "tourreport3",
|
||||||
|
tourMessage:
|
||||||
|
"This are documents you have started but have not finalized for sending."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
export default dashboardJson;
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
const plans = [
|
||||||
|
{
|
||||||
|
planName: "OPENSIGN™ FREE",
|
||||||
|
currency: "",
|
||||||
|
monthlyPrice: "Free",
|
||||||
|
yearlyPrice: "Free",
|
||||||
|
subtitle: "Free Unlimited E-signatures, Forever.",
|
||||||
|
btnText: "Subscribe",
|
||||||
|
url: "",
|
||||||
|
target: "_blank",
|
||||||
|
benefits: [
|
||||||
|
"Unlimited envelopes",
|
||||||
|
"Sign documents yourself",
|
||||||
|
"Request signatures from others",
|
||||||
|
"14 field types",
|
||||||
|
"Automatic e-signatures",
|
||||||
|
"Completion certificates",
|
||||||
|
"Send in order",
|
||||||
|
"Document templates",
|
||||||
|
"Import from Dropbox",
|
||||||
|
"Contact book",
|
||||||
|
"Document expiry support",
|
||||||
|
"Decline document support",
|
||||||
|
"Email notifications",
|
||||||
|
"Recipient authentication using OTP",
|
||||||
|
"And much more"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
planName: "OPENSIGN™ PROFESSIONAL",
|
||||||
|
currency: "$",
|
||||||
|
monthlyPrice: "29.99",
|
||||||
|
yearlyPrice: "329.99",
|
||||||
|
subtitle: "Exclusive Access to advanced features.",
|
||||||
|
btnText: "Subscribe",
|
||||||
|
url:
|
||||||
|
window.location.origin === "https://staging-app.opensignlabs.com"
|
||||||
|
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||||
|
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
|
||||||
|
yearlyUrl:
|
||||||
|
window.location.origin === "https://staging-app.opensignlabs.com"
|
||||||
|
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||||
|
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||||
|
target: "_self",
|
||||||
|
benefits: [
|
||||||
|
"Everything in OpenSign™ free",
|
||||||
|
"Field validations",
|
||||||
|
"Regular expression validations",
|
||||||
|
"Organize docs in OpenSign™ Drive",
|
||||||
|
"Webhooks",
|
||||||
|
"Zapier integration",
|
||||||
|
"API Access",
|
||||||
|
"100 API signatures included",
|
||||||
|
"DocumentId removal from signed docs",
|
||||||
|
"Custom email templates",
|
||||||
|
"Auto reminders"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
planName: "OPENSIGN™ ENTERPRISE",
|
||||||
|
currency: "",
|
||||||
|
monthlyPrice: "Request Price",
|
||||||
|
yearlyPrice: "Request Price",
|
||||||
|
subtitle: "Scalable Features with priority support.",
|
||||||
|
btnText: "Contact us",
|
||||||
|
url: "https://www.opensignlabs.com/contact-us",
|
||||||
|
target: "_blank",
|
||||||
|
benefits: [
|
||||||
|
"All features",
|
||||||
|
"Custom domain",
|
||||||
|
"Custom branding",
|
||||||
|
"Uptime SLA",
|
||||||
|
"SSO support",
|
||||||
|
"Priority support"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default plans;
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"planName": "SELF HOSTED",
|
|
||||||
"currency": "",
|
|
||||||
"price": "Free",
|
|
||||||
"subtitle": "Free for life One click install.",
|
|
||||||
"btnText": "Visit github",
|
|
||||||
"url": "https://github.com/opensignlabs/opensign",
|
|
||||||
"target": "_blank",
|
|
||||||
"benefits": [
|
|
||||||
"Sign Unlimited Documents",
|
|
||||||
"S3 Doc Storage",
|
|
||||||
"Unlimited Guest Signers",
|
|
||||||
"Unlimited completion certificates",
|
|
||||||
"Unique Code(OTP) verification for guest signers",
|
|
||||||
"Expiring docs & reject signature support",
|
|
||||||
"PDF Template Creation(coming soon)",
|
|
||||||
"Audit Trails",
|
|
||||||
"API support",
|
|
||||||
"Add-ons support(coming soon)",
|
|
||||||
"Self hosted cloud infrastructure",
|
|
||||||
"Free for lifetime",
|
|
||||||
"Community support"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"planName": "THANKS PLAN",
|
|
||||||
"currency": "$",
|
|
||||||
"price": "29.99",
|
|
||||||
"subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)",
|
|
||||||
"btnText": "Subscribe",
|
|
||||||
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
|
|
||||||
"target": "_self",
|
|
||||||
"benefits": [
|
|
||||||
"Sign Unlimited Documents",
|
|
||||||
"Unlimited Secure Doc Storage with OpenSign™ Drive",
|
|
||||||
"Unlimited Guest Signers",
|
|
||||||
"Unlimited completion certificates",
|
|
||||||
"Unique Code(OTP) verification for guest signers",
|
|
||||||
"Expiring docs & reject signature support",
|
|
||||||
"PDF Template Creation(coming soon)",
|
|
||||||
"Audit Trails",
|
|
||||||
"API support",
|
|
||||||
"Add-ons support(coming soon)",
|
|
||||||
"Self hosted cloud infrastructure"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"planName": "ENTERPRISE",
|
|
||||||
"currency": "",
|
|
||||||
"price": "Request Price",
|
|
||||||
"subtitle": "Customization available Priority support.",
|
|
||||||
"btnText": "Contact us",
|
|
||||||
"url": "https://www.opensignlabs.com/contact-us",
|
|
||||||
"target": "_blank",
|
|
||||||
"benefits": [
|
|
||||||
"All features",
|
|
||||||
"Custom domain",
|
|
||||||
"Whitelabeling",
|
|
||||||
"Uptime SLA",
|
|
||||||
"SSO support",
|
|
||||||
"24/7 support"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -9,6 +9,10 @@ import { useSelector } from "react-redux";
|
|||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import { useNavigate, useLocation, Outlet } from "react-router-dom";
|
import { useNavigate, useLocation, Outlet } from "react-router-dom";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import { useCookies } from "react-cookie";
|
||||||
|
import { fetchSubscription } from "../constant/Utils";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
const HomeLayout = () => {
|
const HomeLayout = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -18,33 +22,80 @@ const HomeLayout = () => {
|
|||||||
const arr = useSelector((state) => state.TourSteps);
|
const arr = useSelector((state) => state.TourSteps);
|
||||||
const [isUserValid, setIsUserValid] = useState(true);
|
const [isUserValid, setIsUserValid] = useState(true);
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
useEffect(() => {
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
// Use the session token to validate the user
|
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
|
||||||
const user = await userQuery.get(Parse.User.current().id, {
|
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
|
||||||
});
|
|
||||||
if (user) {
|
|
||||||
setIsUserValid(true);
|
|
||||||
setIsLoader(false);
|
|
||||||
} else {
|
|
||||||
setIsUserValid(false);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Session token is invalid or there was an error
|
|
||||||
setIsUserValid(false);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// reactour state
|
// reactour state
|
||||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||||
const [isTour, setIsTour] = useState(false);
|
const [isTour, setIsTour] = useState(false);
|
||||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||||
const [tourConfigs, setTourConfigs] = useState([]);
|
const [tourConfigs, setTourConfigs] = useState([]);
|
||||||
|
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
||||||
|
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
useEffect(() => {
|
||||||
|
if (!tenantId) {
|
||||||
|
setIsUserValid(false);
|
||||||
|
} else {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// Use the session token to validate the user
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
const user = await userQuery.get(Parse.User.current().id, {
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
});
|
||||||
|
if (user) {
|
||||||
|
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||||
|
checkIsSubscribed();
|
||||||
|
} else {
|
||||||
|
setIsUserValid(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Session token is invalid or there was an error
|
||||||
|
setIsUserValid(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
saveCookies();
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [tenantId]);
|
||||||
|
//function to use save data in cookies storage
|
||||||
|
const saveCookies = () => {
|
||||||
|
const main_Domain = window.location.origin;
|
||||||
|
const domainName = window.location.hostname; //app.opensignlabs.com
|
||||||
|
// Find the index of the first dot in the string
|
||||||
|
const indexOfFirstDot = domainName.indexOf(".");
|
||||||
|
// Remove the first dot and get the substring starting from the next character
|
||||||
|
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
|
||||||
|
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
|
||||||
|
secure: true,
|
||||||
|
domain: updateDomain
|
||||||
|
});
|
||||||
|
setCookie("main_Domain", main_Domain, {
|
||||||
|
secure: true,
|
||||||
|
domain: updateDomain
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkIsSubscribed() {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const res = await fetchSubscription();
|
||||||
|
if (res.plan === "freeplan") {
|
||||||
|
setIsUserValid(true);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else if (res.billingDate) {
|
||||||
|
if (new Date(res.billingDate) > new Date()) {
|
||||||
|
setIsUserValid(true);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsUserValid(true);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
const showSidebar = () => {
|
const showSidebar = () => {
|
||||||
setIsOpen((value) => !value);
|
setIsOpen((value) => !value);
|
||||||
};
|
};
|
||||||
@@ -197,35 +248,24 @@ const HomeLayout = () => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="sticky top-0 z-50">
|
<div className="sticky top-0 z-[101]">
|
||||||
<Header showSidebar={showSidebar} />
|
{!isLoader && <Header showSidebar={showSidebar} />}
|
||||||
</div>
|
</div>
|
||||||
{isUserValid ? (
|
{isUserValid ? (
|
||||||
<>
|
<>
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="flex h-[100vh] 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>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="flex md:flex-row flex-col z-50">
|
<div className="flex md:flex-row flex-col z-50">
|
||||||
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||||
|
<div
|
||||||
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
|
id="renderList"
|
||||||
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
|
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
|
||||||
|
>
|
||||||
|
<div className="bg-base-200 p-3">{<Outlet />}</div>
|
||||||
<div className="z-30">
|
<div className="z-30">
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
@@ -246,13 +286,10 @@ const HomeLayout = () => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
|
<ModalUi showHeader={false} isOpen={true} showClose={false}>
|
||||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
||||||
<p className="text-xl font-normal">Your session has expired.</p>
|
<p className="text-xl font-medium">Your session has expired.</p>
|
||||||
<button
|
<button onClick={handleLoginBtn} className="op-btn op-btn-neutral">
|
||||||
onClick={handleLoginBtn}
|
|
||||||
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
|
|
||||||
>
|
|
||||||
Login
|
Login
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ function ChangePassword() {
|
|||||||
return <Navigate to="/" />;
|
return <Navigate to="/" />;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="w-full bg-white shadow rounded p-2">
|
<div className="w-full bg-base-100 text-base-content shadow rounded p-2">
|
||||||
<Title title={"Change Password"} />
|
<Title title={"Change Password"} />
|
||||||
<div className="text-xl font-bold border-b-[1px] border-gray-300">
|
<div className="text-xl font-bold border-b-[1px] border-gray-300">
|
||||||
Change Password
|
Change Password
|
||||||
</div>
|
</div>
|
||||||
<div className="m-2">
|
<div className="m-2">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit} className=" flex flex-col gap-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="currentpassword" className="block text-xs ml-1">
|
<label htmlFor="currentpassword" className="block text-xs ml-1">
|
||||||
Current Password
|
Current Password
|
||||||
@@ -65,7 +65,7 @@ function ChangePassword() {
|
|||||||
name="currentpassword"
|
name="currentpassword"
|
||||||
value={currentpassword}
|
value={currentpassword}
|
||||||
onChange={(e) => setCurrentPassword(e.target.value)}
|
onChange={(e) => setCurrentPassword(e.target.value)}
|
||||||
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||||
placeholder="Current Password"
|
placeholder="Current Password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -79,7 +79,7 @@ function ChangePassword() {
|
|||||||
name="newpassword"
|
name="newpassword"
|
||||||
value={newpassword}
|
value={newpassword}
|
||||||
onChange={(e) => setnewpassword(e.target.value)}
|
onChange={(e) => setnewpassword(e.target.value)}
|
||||||
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||||
placeholder="New Password"
|
placeholder="New Password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
@@ -91,17 +91,14 @@ function ChangePassword() {
|
|||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
name="confirmpassword"
|
name="confirmpassword"
|
||||||
className="px-3 py-2 mb-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm text-xs w-full"
|
||||||
value={confirmpassword}
|
value={confirmpassword}
|
||||||
onChange={(e) => setconfirmpassword(e.target.value)}
|
onChange={(e) => setconfirmpassword(e.target.value)}
|
||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button type="submit" className="op-btn op-btn-primary shadow-md m-2">
|
||||||
type="submit"
|
|
||||||
className="bg-[#188ae2] m-2 px-4 py-3 rounded-sm shadow-md text-white text-xs font-semibold uppercase"
|
|
||||||
>
|
|
||||||
Change Password
|
Change Password
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import Parse from "parse";
|
|
||||||
import "../styles/loader.css";
|
import "../styles/loader.css";
|
||||||
import GetDashboard from "../components/dashboard/GetDashboard";
|
import GetDashboard from "../components/dashboard/GetDashboard";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import { save_tourSteps } from "../redux/actions";
|
import { useDispatch } from "react-redux";
|
||||||
import { connect } from "react-redux";
|
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
|
||||||
const Dashboard = (props) => {
|
import dashboardJson from "../json/dashboardJson";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
|
const Dashboard = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [dashboard, setdashboard] = useState([]);
|
const [dashboard, setdashboard] = useState({});
|
||||||
const [classnameArray, setclassnameArray] = useState([]);
|
const [loading, setloading] = useState(true);
|
||||||
const [loading, setloading] = useState(false);
|
|
||||||
const [defaultQuery, setDefaultQuery] = useState("");
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem("accesstoken")) {
|
if (localStorage.getItem("accesstoken")) {
|
||||||
@@ -22,59 +23,16 @@ const Dashboard = (props) => {
|
|||||||
getDashboard(localStorage.getItem("PageLanding"));
|
getDashboard(localStorage.getItem("PageLanding"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
navigate("/", { replace: true, state: { from: location } });
|
navigate("/", { replace: true, state: { from: "" } });
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const getDashboard = async (id) => {
|
const getDashboard = async (id) => {
|
||||||
setloading(true);
|
|
||||||
try {
|
try {
|
||||||
var forms = Parse.Object.extend("w_dashboard");
|
const dashboard = dashboardJson.find((x) => x.id === id);
|
||||||
var query = new Parse.Query(forms);
|
setdashboard(dashboard);
|
||||||
query.equalTo("objectId", id);
|
const dashboardTour = dashboard.columns
|
||||||
const results = await query.first();
|
|
||||||
const resultjson = results.toJSON();
|
|
||||||
let classArray = [];
|
|
||||||
let DefaultQuery = "";
|
|
||||||
resultjson.rows.forEach((x) => {
|
|
||||||
let col_lg = "",
|
|
||||||
col_md = "",
|
|
||||||
col_xs = "",
|
|
||||||
col_sm = "",
|
|
||||||
col_ = "",
|
|
||||||
obj = {};
|
|
||||||
let subItem = [];
|
|
||||||
let item = "";
|
|
||||||
x.columns.forEach((y) => {
|
|
||||||
if (y.widget.type === "customeHtml") {
|
|
||||||
DefaultQuery = y.widget.data[0].default;
|
|
||||||
}
|
|
||||||
if (Number(y["collg"]) > 0) {
|
|
||||||
col_lg = "col-lg-" + y["collg"];
|
|
||||||
}
|
|
||||||
if (Number(y["colmd"]) > 0) {
|
|
||||||
col_md = "col-md-" + y["colmd"];
|
|
||||||
}
|
|
||||||
if (Number(y["colxs"]) > 0) {
|
|
||||||
col_xs = "col-xs-" + y["colxs"];
|
|
||||||
}
|
|
||||||
if (Number(y["colsm"]) > 0) {
|
|
||||||
col_sm = "col-sm-" + y["colsm"];
|
|
||||||
}
|
|
||||||
if (Number(y["col"]) > 0) {
|
|
||||||
col_ = "col-" + y["col"];
|
|
||||||
}
|
|
||||||
item =
|
|
||||||
col_lg + " " + col_md + " " + col_xs + " " + col_sm + " " + col_;
|
|
||||||
subItem.push(item);
|
|
||||||
});
|
|
||||||
obj = subItem;
|
|
||||||
classArray.push(obj);
|
|
||||||
});
|
|
||||||
setdashboard(resultjson.rows);
|
|
||||||
// console.log("resultjson.rows ", resultjson.rows);
|
|
||||||
const arr = resultjson.rows[0].columns
|
|
||||||
.filter((col) => {
|
.filter((col) => {
|
||||||
if (col.widget.data && col.widget.data.tourSection) {
|
if (col.widget.data && col.widget.data.tourSection) {
|
||||||
return col;
|
return col;
|
||||||
@@ -88,14 +46,7 @@ const Dashboard = (props) => {
|
|||||||
// style: { backgroundColor: "#abd4d2" },
|
// style: { backgroundColor: "#abd4d2" },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
props.save_tourSteps(arr);
|
dispatch(saveTourSteps(dashboardTour));
|
||||||
// console.log("arr ", arr);
|
|
||||||
setclassnameArray(classArray);
|
|
||||||
if (localStorage.getItem("DashboardDefaultFilter")) {
|
|
||||||
setDefaultQuery(localStorage.getItem("DashboardDefaultFilter"));
|
|
||||||
} else {
|
|
||||||
setDefaultQuery(DefaultQuery);
|
|
||||||
}
|
|
||||||
setloading(false);
|
setloading(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Problem", e);
|
console.error("Problem", e);
|
||||||
@@ -103,36 +54,18 @@ const Dashboard = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let _dash = (
|
|
||||||
<GetDashboard
|
|
||||||
dashboard={dashboard}
|
|
||||||
classnameArray={classnameArray}
|
|
||||||
DefaultQuery={defaultQuery}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (loading) {
|
|
||||||
_dash = (
|
|
||||||
<div style={{ height: "300px", backgroundColor: "white" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title="Dashboard" />
|
<Title title="Dashboard" />
|
||||||
{_dash}
|
{loading ? (
|
||||||
|
<div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<GetDashboard dashboard={dashboard} />
|
||||||
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(null, {
|
export default Dashboard;
|
||||||
save_tourSteps
|
|
||||||
})(Dashboard);
|
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { fetchAppInfo, forgetPassword } from "../redux/actions";
|
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import { NavLink } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import login_img from "../assets/images/login_img.svg";
|
import login_img from "../assets/images/login_img.svg";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
function ForgotPassword(props) {
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import { getAppLogo } from "../constant/Utils";
|
||||||
|
|
||||||
|
function ForgotPassword() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
email: "",
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
hideNav: ""
|
hideNav: ""
|
||||||
});
|
});
|
||||||
const [sentStatus, setSentStatus] = useState("");
|
const [sentStatus, setSentStatus] = useState("");
|
||||||
|
const [image, setImage] = useState();
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = (event) => {
|
||||||
const { name, value } = event.target;
|
const { name, value } = event.target;
|
||||||
setState({ ...state, [name]: value });
|
setState({ ...state, [name]: value });
|
||||||
@@ -27,14 +35,11 @@ function ForgotPassword(props) {
|
|||||||
|
|
||||||
const handleSubmit = async (event) => {
|
const handleSubmit = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
localStorage.setItem("appLogo", props.appInfo.applogo);
|
localStorage.setItem("appLogo", appInfo.applogo);
|
||||||
localStorage.setItem("appName", props.appInfo.appname);
|
localStorage.setItem("appName", appInfo.appname);
|
||||||
localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
|
// localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
|
||||||
localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
|
// localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
|
||||||
localStorage.setItem(
|
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||||
"userSettings",
|
|
||||||
JSON.stringify(props.appInfo.settings)
|
|
||||||
);
|
|
||||||
if (state.email) {
|
if (state.email) {
|
||||||
const username = state.email;
|
const username = state.email;
|
||||||
try {
|
try {
|
||||||
@@ -50,16 +55,27 @@ function ForgotPassword(props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.fetchAppInfo();
|
dispatch(fetchAppInfo());
|
||||||
|
saveLogo();
|
||||||
resize();
|
resize();
|
||||||
window.addEventListener("resize", resize);
|
window.addEventListener("resize", resize);
|
||||||
return () => window.removeEventListener("resize", resize);
|
return () => window.removeEventListener("resize", resize);
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
const saveLogo = async () => {
|
||||||
let image = props.appInfo.applogo;
|
if (isEnableSubscription) {
|
||||||
|
const logo = await getAppLogo();
|
||||||
|
if (logo) {
|
||||||
|
setImage(logo);
|
||||||
|
} else {
|
||||||
|
setImage(appInfo?.applogo || undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setImage(appInfo?.applogo || undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="bg-white">
|
<div>
|
||||||
<Title title="Forgot password page" />
|
<Title title="Forgot password page" />
|
||||||
{sentStatus === "success" && (
|
{sentStatus === "success" && (
|
||||||
<Alert type="success">
|
<Alert type="success">
|
||||||
@@ -69,29 +85,31 @@ function ForgotPassword(props) {
|
|||||||
{sentStatus === "failed" && (
|
{sentStatus === "failed" && (
|
||||||
<Alert type={"danger"}>Please setup email adapter </Alert>
|
<Alert type={"danger"}>Please setup email adapter </Alert>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div className="md:p-10 lg:p-16">
|
||||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
||||||
<div className="w-[250px] h-[66px] inline-block">
|
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||||
{state.hideNav ? (
|
{image && (
|
||||||
<img src={image} width="100%" alt="" />
|
<img
|
||||||
) : (
|
src={image}
|
||||||
<img src={image} width="100%" alt="" />
|
className="object-contain h-full"
|
||||||
|
alt="applogo"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
||||||
<div className="">
|
<div>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
|
<h2 className="text-[30px] mt-6">Welcome Back !</h2>
|
||||||
<span className="text-[12px] text-[#878787]">
|
<span className="text-[12px] text-[#878787]">
|
||||||
Reset Your Password
|
Reset Your Password
|
||||||
</span>
|
</span>
|
||||||
<div className="shadow-md rounded my-4">
|
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
|
||||||
<div className="px-6 py-4">
|
<div className="px-6 py-4">
|
||||||
<label className="block text-xs">Email</label>
|
<label className="block text-xs">Email</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="email"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
|
||||||
name="email"
|
name="email"
|
||||||
|
className="op-input op-input-bordered op-input-sm w-full"
|
||||||
value={state.email}
|
value={state.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
@@ -99,19 +117,16 @@ function ForgotPassword(props) {
|
|||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between items-center px-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold">
|
||||||
<button
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
type="submit"
|
|
||||||
className="rounded-sm bg-[#3ac9d6] text-white px-4 py-3 text-xs font-semibold shadow uppercase"
|
|
||||||
>
|
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<NavLink
|
<button
|
||||||
to="/"
|
onClick={() => navigate("/", { replace: true })}
|
||||||
className="rounded-sm bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-3 text-xs font-semibold shadow uppercase"
|
className="op-btn op-btn-secondary"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</NavLink>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,10 +144,4 @@ function ForgotPassword(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
export default ForgotPassword;
|
||||||
return { appInfo: state.appInfo, forgotPassword: state.forgotPassword };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, { fetchAppInfo, forgetPassword })(
|
|
||||||
ForgotPassword
|
|
||||||
);
|
|
||||||
|
|||||||
+697
-214
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,17 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import { rejectBtn, submitBtn } from "../constant/const";
|
import { isEnableSubscription } from "../constant/const";
|
||||||
import { openInNewTab } from "../constant/Utils";
|
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||||
|
import Tooltip from "../primitives/Tooltip";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
function GenerateToken() {
|
function GenerateToken() {
|
||||||
|
const navigation = useNavigate();
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||||
const [apiToken, SetApiToken] = useState("");
|
const [apiToken, SetApiToken] = useState("");
|
||||||
@@ -15,6 +20,7 @@ function GenerateToken() {
|
|||||||
const [isGenerate, setIsGenerate] = useState(false);
|
const [isGenerate, setIsGenerate] = useState(false);
|
||||||
const [isErr, setIsErr] = useState(false);
|
const [isErr, setIsErr] = useState(false);
|
||||||
const [isModal, setIsModal] = useState(false);
|
const [isModal, setIsModal] = useState(false);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchToken();
|
fetchToken();
|
||||||
@@ -23,6 +29,11 @@ function GenerateToken() {
|
|||||||
|
|
||||||
const fetchToken = async () => {
|
const fetchToken = async () => {
|
||||||
try {
|
try {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
|
|
||||||
const url = parseBaseUrl + "functions/getapitoken";
|
const url = parseBaseUrl + "functions/getapitoken";
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -33,6 +44,7 @@ function GenerateToken() {
|
|||||||
if (res) {
|
if (res) {
|
||||||
SetApiToken(res.data.result.result);
|
SetApiToken(res.data.result.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
SetApiToken();
|
SetApiToken();
|
||||||
@@ -81,13 +93,14 @@ function GenerateToken() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const copytoclipboard = (text) => {
|
const copytoclipboard = (text) => {
|
||||||
navigator.clipboard.writeText(text);
|
copytoData(text);
|
||||||
setCopied(true);
|
setCopied(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setCopied(false);
|
setCopied(false);
|
||||||
}, 1500); // Reset copied state after 1.5 seconds
|
}, 1500); // Reset copied state after 1.5 seconds
|
||||||
};
|
};
|
||||||
const handleModal = () => setIsModal(!isModal);
|
const handleModal = () => setIsModal(!isModal);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title={"API Token"} />
|
<Title title={"API Token"} />
|
||||||
@@ -97,47 +110,55 @@ function GenerateToken() {
|
|||||||
{copied && <Alert type="success">Copied</Alert>}
|
{copied && <Alert type="success">Copied</Alert>}
|
||||||
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="flex justify-center items-center h-screen">
|
||||||
style={{
|
<Loader />
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white flex flex-col justify-center shadow rounded">
|
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
|
||||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">API Token</h1>
|
{!isEnableSubscription && <PremiumAlertHeader />}
|
||||||
<ul className="w-full flex flex-col p-2 text-sm">
|
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||||
<li
|
OpenSign™ API{" "}
|
||||||
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
<Tooltip
|
||||||
>
|
url={
|
||||||
<span className="w-[40%]">Api Token:</span>{" "}
|
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||||
<span
|
}
|
||||||
id="token"
|
isSubscribe={true}
|
||||||
className="w-[60%] md:text-end cursor-pointer"
|
/>
|
||||||
onClick={() => copytoclipboard(apiToken)}
|
</h1>
|
||||||
|
<ul
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "w-full flex flex-col p-2 text-sm "
|
||||||
|
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
|
||||||
|
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5 ">
|
||||||
|
<span className="">Api Token:</span>{" "}
|
||||||
|
<span id="token" className="md:text-end py-2 md:py-0">
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() => copytoclipboard(apiToken)}
|
||||||
|
>
|
||||||
|
{apiToken ? apiToken : "_____"}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2"
|
||||||
|
onClick={() => copytoclipboard(apiToken)}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-copy"></i>
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={apiToken ? handleModal : handleSubmit}
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
>
|
>
|
||||||
{apiToken && apiToken}
|
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||||
</span>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
|
<div className="flex items-center justify-center ">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={apiToken ? handleModal : handleSubmit}
|
|
||||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
|
||||||
>
|
|
||||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -145,19 +166,48 @@ function GenerateToken() {
|
|||||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
className="op-btn op-btn-secondary my-2"
|
||||||
>
|
>
|
||||||
View Docs
|
View Docs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!isSubscribe && isEnableSubscription && (
|
||||||
|
<>
|
||||||
|
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||||
|
Upgrade to PRO Plan
|
||||||
|
</h1>
|
||||||
|
<ul className={"w-full flex flex-col p-2 text-sm "}>
|
||||||
|
<li
|
||||||
|
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||||
|
>
|
||||||
|
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
|
||||||
|
<span className="">$29.99/month:</span>{" "}
|
||||||
|
<span id="token" className=" md:text-end cursor-pointer">
|
||||||
|
Sign 100 docs using API. Only 0.15/docs after that.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{!isSubscribe && isEnableSubscription && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigation("/subscription")}
|
||||||
|
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||||
|
>
|
||||||
|
Upgrade Now
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={isModal}
|
isOpen={isModal}
|
||||||
title={"Regenerate Token"}
|
title={"Regenerate Token"}
|
||||||
handleClose={handleModal}
|
handleClose={handleModal}
|
||||||
>
|
>
|
||||||
<div className="m-[20px]">
|
<div className="m-[20px]">
|
||||||
<div className="text-lg font-normal text-black">
|
<div className="text-lg font-normal text-base-content">
|
||||||
Are you sure you want to regenerate token it will expire old
|
Are you sure you want to regenerate token it will expire old
|
||||||
token?
|
token?
|
||||||
</div>
|
</div>
|
||||||
@@ -165,11 +215,14 @@ function GenerateToken() {
|
|||||||
<div className="flex items-center mt-3 gap-2 text-white">
|
<div className="flex items-center mt-3 gap-2 text-white">
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
className={submitBtn + "ml-[2px]"}
|
className="op-btn op-btn-primary ml-[2px]"
|
||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</button>
|
</button>
|
||||||
<button onClick={handleModal} className={rejectBtn}>
|
<button
|
||||||
|
onClick={handleModal}
|
||||||
|
className="op-btn op-btn-secondary"
|
||||||
|
>
|
||||||
No
|
No
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,80 +1,103 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import "../styles/loginPage.css";
|
|
||||||
import loader from "../assets/images/loader2.gif";
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { themeColor } from "../constant/const";
|
import { isEnableSubscription } from "../constant/const";
|
||||||
import { contractUsers } from "../constant/Utils";
|
import { contractUsers, getAppLogo } from "../constant/Utils";
|
||||||
|
import logo from "../assets/images/logo.png";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import Parse from "parse";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
function GuestLogin() {
|
function GuestLogin() {
|
||||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
const { id, userMail, contactBookId, base64url } = useParams();
|
||||||
let navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [email, setEmail] = useState(userMail);
|
const [email, setEmail] = useState(userMail);
|
||||||
const [OTP, setOTP] = useState("");
|
const [OTP, setOTP] = useState("");
|
||||||
const [EnterOTP, setEnterOtp] = useState(false);
|
const [EnterOTP, setEnterOtp] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [appLogo, setAppLogo] = useState("");
|
||||||
|
const [documentId, setDocumentId] = useState(id);
|
||||||
|
const [contactId, setContactId] = useState(contactBookId);
|
||||||
|
const [sendmail, setSendmail] = useState();
|
||||||
|
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleServerUrl();
|
handleServerUrl();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||||
const handleServerUrl = () => {
|
const handleServerUrl = async () => {
|
||||||
//split url in array from '&'
|
if (isEnableSubscription) {
|
||||||
localStorage.clear();
|
const applogo = await getAppLogo();
|
||||||
const checkSplit = serverUrl.split("&");
|
if (applogo) {
|
||||||
const server = checkSplit[0];
|
setAppLogo(applogo);
|
||||||
const parseId = checkSplit[1];
|
} else {
|
||||||
const appName = checkSplit[2];
|
setAppLogo(logo);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAppLogo(logo);
|
||||||
|
}
|
||||||
|
|
||||||
const newServer = server.replaceAll("%2F", "/");
|
localStorage.clear();
|
||||||
|
const parseId = appInfo.appId;
|
||||||
|
const newServer = `${appInfo.baseUrl}/`;
|
||||||
|
const appName = appInfo.appname;
|
||||||
localStorage.setItem("baseUrl", newServer);
|
localStorage.setItem("baseUrl", newServer);
|
||||||
localStorage.setItem("parseAppId", parseId);
|
localStorage.setItem("parseAppId", parseId);
|
||||||
localStorage.setItem("_appName", appName);
|
localStorage.setItem("_appName", appName);
|
||||||
|
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
|
||||||
|
if (!id) {
|
||||||
|
//`atob` function is used to decode base64
|
||||||
|
const decodebase64 = atob(base64url);
|
||||||
|
//split url in array from '/'
|
||||||
|
const checkSplit = decodebase64.split("/");
|
||||||
|
setDocumentId(checkSplit[0]);
|
||||||
|
setContact((prev) => ({ ...prev, email: checkSplit[1] }));
|
||||||
|
setEmail(checkSplit[1]);
|
||||||
|
const contactId = checkSplit?.[2];
|
||||||
|
setSendmail(checkSplit[3]);
|
||||||
|
if (!contactId) {
|
||||||
|
const params = { email: checkSplit[1], docId: checkSplit[0] };
|
||||||
|
try {
|
||||||
|
const linkContactRes = await Parse.Cloud.run(
|
||||||
|
"linkcontacttodoc",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes?.contactId);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setContactId(checkSplit[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (event) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
setOTP(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//send email OTP function
|
//send email OTP function
|
||||||
const SendOtp = async (e) => {
|
const SendOtp = async () => {
|
||||||
const serverUrl =
|
setLoading(true);
|
||||||
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
|
setEmail(email);
|
||||||
const parseId =
|
try {
|
||||||
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
|
const params = { email: email.toString(), docId: documentId };
|
||||||
if (serverUrl && localStorage) {
|
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
|
||||||
setLoading(true);
|
if (Otp) {
|
||||||
e.preventDefault();
|
setLoading(false);
|
||||||
setEmail(email);
|
setEnterOtp(true);
|
||||||
|
|
||||||
try {
|
|
||||||
let url = `${serverUrl}functions/SendOTPMailV1/`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseId
|
|
||||||
};
|
|
||||||
let body = {
|
|
||||||
email: email.toString()
|
|
||||||
};
|
|
||||||
let Otp = await axios.post(url, body, { headers: headers });
|
|
||||||
|
|
||||||
if (Otp) {
|
|
||||||
setLoading(false);
|
|
||||||
setEnterOtp(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert("something went wrong!");
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
alert("something went wrong!");
|
alert("something went wrong!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSendOTPBtn = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await SendOtp();
|
||||||
|
};
|
||||||
|
|
||||||
//verify OTP send on via email
|
//verify OTP send on via email
|
||||||
const VerifyOTP = async (e) => {
|
const VerifyOTP = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -110,7 +133,8 @@ function GuestLogin() {
|
|||||||
`Parse/${parseId}/currentUser`,
|
`Parse/${parseId}/currentUser`,
|
||||||
JSON.stringify(_user)
|
JSON.stringify(_user)
|
||||||
);
|
);
|
||||||
if (contractUserDetails) {
|
// console.log("contractUserDetails ", contractUserDetails);
|
||||||
|
if (contractUserDetails && contractUserDetails.length > 0) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"Extand_Class",
|
"Extand_Class",
|
||||||
JSON.stringify(contractUserDetails)
|
JSON.stringify(contractUserDetails)
|
||||||
@@ -122,7 +146,13 @@ function GuestLogin() {
|
|||||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||||
localStorage.setItem("isGuestSigner", true);
|
localStorage.setItem("isGuestSigner", true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
|
if (sendmail === "false") {
|
||||||
|
navigate(
|
||||||
|
`/load/recipientSignPdf/${documentId}/${contactId}?sendmail=${sendmail}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("err ", error);
|
console.log("err ", error);
|
||||||
@@ -131,154 +161,169 @@ function GuestLogin() {
|
|||||||
alert("Please Enter OTP!");
|
alert("Please Enter OTP!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleUserData = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const params = { ...contact, docId: documentId };
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes.contactId);
|
||||||
|
setEnterOtp(true);
|
||||||
|
await SendOtp();
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
alert("something went wron, please try agian later.");
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "2rem", background: "white" }}>
|
<div className="p-[2rem]">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div
|
<div className="flex flex-col justify-center items-center h-[100vh]">
|
||||||
style={{
|
<Loader />
|
||||||
display: "flex",
|
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "80px", height: "80px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "13px", color: "gray" }}>
|
|
||||||
{isLoading.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
|
||||||
style={{
|
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
|
||||||
margin: "10px",
|
{appLogo && (
|
||||||
border: "0.5px solid #c5c7c9",
|
<img src={appLogo} className="object-contain h-full" alt="logo" />
|
||||||
padding: "30px",
|
)}
|
||||||
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="main_head">
|
|
||||||
<div className="main-logo">
|
|
||||||
<img
|
|
||||||
alt="sign img"
|
|
||||||
src="https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"
|
|
||||||
width="100%"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{contactId ? (
|
||||||
{!EnterOTP ? (
|
<>
|
||||||
<div className="row">
|
{!EnterOTP ? (
|
||||||
<div className="col-sm-6 KLO">
|
<div className="w-full md:w-[50%] text-base-content">
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
<br />
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
<span className="KNLO">
|
Verification code is sent to your email
|
||||||
Verification code is sent to your email
|
</legend>
|
||||||
</span>
|
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||||
<div className="card card-box" style={{ borderRadius: "0px" }}>
|
|
||||||
<div className="card-body">
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="mobile"
|
name="mobile"
|
||||||
value={email}
|
value={email}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
disabled
|
disabled
|
||||||
className="loginInput"
|
|
||||||
/>
|
/>
|
||||||
<br />
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
onClick={(e) => handleSendOTPBtn(e)}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Send OTP"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="btnContainer">
|
) : (
|
||||||
{loading ? (
|
<form
|
||||||
<button
|
className="w-full md:w-[50%] text-base-content"
|
||||||
type="button"
|
onSubmit={VerifyOTP}
|
||||||
style={{
|
>
|
||||||
background: themeColor,
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
color: "white"
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
}}
|
You will get a OTP via Email
|
||||||
className="verifyBtn"
|
</legend>
|
||||||
disabled
|
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
|
||||||
>
|
<p className="text-sm">Enter Verification Code</p>
|
||||||
<span
|
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
className="verifyBtn"
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white",
|
|
||||||
marginLeft: "0px !important"
|
|
||||||
}}
|
|
||||||
onClick={(e) => SendOtp(e)}
|
|
||||||
>
|
|
||||||
Send OTP
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-sm-6 KLO">
|
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
|
||||||
<br />
|
|
||||||
<span className="KNLO">You will get a OTP via Email</span>
|
|
||||||
<div className="card card-box">
|
|
||||||
<div className="card-body">
|
|
||||||
<label>Enter Verification Code</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
className="loginInput"
|
className="mt-2 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="OTP"
|
name="OTP"
|
||||||
value={OTP}
|
value={OTP}
|
||||||
onChange={handleChange}
|
onChange={(e) => setOTP(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-2">
|
||||||
<div>
|
|
||||||
{loading ? (
|
|
||||||
<button
|
<button
|
||||||
style={{
|
className="op-btn op-btn-primary"
|
||||||
background: themeColor,
|
type="submit"
|
||||||
color: "white"
|
disabled={loading}
|
||||||
}}
|
|
||||||
className="verifyBtn"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
>
|
>
|
||||||
<span
|
{loading ? "Loading..." : "Verify"}
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
</button>
|
||||||
) : (
|
</div>
|
||||||
<button
|
</form>
|
||||||
type="button"
|
)}
|
||||||
onClick={(e) => VerifyOTP(e)}
|
</>
|
||||||
style={{
|
) : (
|
||||||
background: themeColor,
|
<div className="w-full md:w-[50%] text-base-content">
|
||||||
color: "white"
|
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
|
||||||
}}
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
className="verifyBtn"
|
Provide your details
|
||||||
>
|
</legend>
|
||||||
Verify
|
<form
|
||||||
</button>
|
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md"
|
||||||
)}
|
onSubmit={handleUserData}
|
||||||
|
>
|
||||||
|
<div className="mb-2">
|
||||||
|
<label
|
||||||
|
htmlFor="name"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={contact.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mb-2">
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={contact.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-2">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="phone"
|
||||||
|
value={contact.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex justify-start">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="op-btn op-btn-primary"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Next"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+414
-344
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,9 @@ import "../styles/signature.css";
|
|||||||
import { toDataUrl } from "../constant/Utils";
|
import { toDataUrl } from "../constant/Utils";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import { SaveFileSize } from "../constant/saveFileSize";
|
||||||
|
import Alert from "../primitives/Alert";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
const ManageSign = () => {
|
const ManageSign = () => {
|
||||||
const appName = appInfo.appname;
|
const appName = appInfo.appname;
|
||||||
const [penColor, setPenColor] = useState("blue");
|
const [penColor, setPenColor] = useState("blue");
|
||||||
@@ -13,11 +16,10 @@ const ManageSign = () => {
|
|||||||
const [signName, setSignName] = useState("");
|
const [signName, setSignName] = useState("");
|
||||||
const [signature, setSignature] = useState("");
|
const [signature, setSignature] = useState("");
|
||||||
const [warning, setWarning] = useState(false);
|
const [warning, setWarning] = useState(false);
|
||||||
const [nameWarning, setNameWarning] = useState(false);
|
|
||||||
const [isvalue, setIsValue] = useState(false);
|
const [isvalue, setIsValue] = useState(false);
|
||||||
const allColor = ["blue", "red", "black"];
|
const allColor = ["blue", "red", "black"];
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
const [isSuccess, setIsSuccess] = useState(false);
|
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
|
||||||
const [Initials, setInitials] = useState("");
|
const [Initials, setInitials] = useState("");
|
||||||
const [isInitials, setIsInitials] = useState(false);
|
const [isInitials, setIsInitials] = useState(false);
|
||||||
const [id, setId] = useState("");
|
const [id, setId] = useState("");
|
||||||
@@ -105,9 +107,7 @@ const ManageSign = () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const isUrl = image.includes("https");
|
const isUrl = image.includes("https");
|
||||||
|
|
||||||
if (!signName) {
|
if (!isvalue) {
|
||||||
setNameWarning(true);
|
|
||||||
} else if (!isvalue) {
|
|
||||||
setWarning(true);
|
setWarning(true);
|
||||||
setTimeout(() => setWarning(false), 1000);
|
setTimeout(() => setWarning(false), 1000);
|
||||||
} else {
|
} else {
|
||||||
@@ -161,7 +161,11 @@ const ManageSign = () => {
|
|||||||
try {
|
try {
|
||||||
const parseFile = new Parse.File(file.name, file);
|
const parseFile = new Parse.File(file.name, file);
|
||||||
const response = await parseFile.save();
|
const response = await parseFile.save();
|
||||||
return response?.url();
|
if (response?.url()) {
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(file.size, response.url(), tenantId);
|
||||||
|
return response?.url();
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("sign upload err", err);
|
console.log("sign upload err", err);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
@@ -183,12 +187,20 @@ const ManageSign = () => {
|
|||||||
updateSign.set("UserId", userId);
|
updateSign.set("UserId", userId);
|
||||||
const res = await updateSign.save();
|
const res = await updateSign.save();
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
setIsSuccess(true);
|
setIsAlert({
|
||||||
|
type: "success",
|
||||||
|
message: "Signature saved successfully."
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert({}), 2000);
|
||||||
return res;
|
return res;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
alert(`${err.message}`);
|
setIsAlert({
|
||||||
|
type: "danger",
|
||||||
|
message: `${err.message}`
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert({}), 2000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@@ -199,12 +211,20 @@ const ManageSign = () => {
|
|||||||
updateSign.set("UserId", userId);
|
updateSign.set("UserId", userId);
|
||||||
const res = await updateSign.save();
|
const res = await updateSign.save();
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
setIsSuccess(true);
|
setIsAlert({
|
||||||
|
type: "success",
|
||||||
|
message: "Signature saved successfully."
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert({}), 2000);
|
||||||
return res;
|
return res;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
alert(`${err.message}`);
|
setIsAlert({
|
||||||
|
type: "success",
|
||||||
|
message: `${err.message}`
|
||||||
|
});
|
||||||
|
setTimeout(() => setIsAlert({}), 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -222,37 +242,13 @@ const ManageSign = () => {
|
|||||||
setIsValue(true);
|
setIsValue(true);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="relative h-full bg-base-100 text-base-content flex shadow rounded overflow-auto">
|
||||||
style={{
|
|
||||||
backgroundColor: "white",
|
|
||||||
maxHeight: "500px",
|
|
||||||
overflow: "auto"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
<div className="absolute bg-black bg-opacity-75 inset-0 z-50 flex justify-center items-center">
|
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
|
||||||
<div style={{ height: "300px" }}>
|
<Loader />
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginLeft: "45%",
|
|
||||||
marginTop: "150px",
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isSuccess && (
|
|
||||||
<div
|
|
||||||
className="alert alert-success successBox"
|
|
||||||
role="alert"
|
|
||||||
onAnimationEnd={() => setIsSuccess(false)}
|
|
||||||
>
|
|
||||||
Signature saved successfully!
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
|
||||||
<div
|
<div
|
||||||
className="mainDiv"
|
className="mainDiv"
|
||||||
style={{
|
style={{
|
||||||
@@ -260,91 +256,13 @@ const ManageSign = () => {
|
|||||||
paddingRight: "10px"
|
paddingRight: "10px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ margin: 20 }}>
|
<div className="m-[20px]">
|
||||||
<div
|
<div className="font-[700] text-[15px] pb-[8px]">My Signature</div>
|
||||||
style={{
|
<div className="signBlock mt-4">
|
||||||
fontWeight: "700",
|
|
||||||
fontSize: 15,
|
|
||||||
color: "#000",
|
|
||||||
paddingBottom: 8
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Manage Signature
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
|
||||||
<label
|
|
||||||
htmlFor="signName"
|
|
||||||
style={{
|
|
||||||
fontSize: 12,
|
|
||||||
position: "relative",
|
|
||||||
paddingBottom: 5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Signer's Name
|
|
||||||
{nameWarning && (
|
|
||||||
<div
|
|
||||||
className="warning nameWarning"
|
|
||||||
style={{ fontSize: 12 }}
|
|
||||||
onAnimationEnd={() => setNameWarning(false)}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fas fa-exclamation-circle"
|
|
||||||
style={{ color: "#fab005", fontSize: 15 }}
|
|
||||||
></i>{" "}
|
|
||||||
Please fill input field
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="signName"
|
|
||||||
className="signInputManage"
|
|
||||||
value={signName}
|
|
||||||
style={{
|
|
||||||
border: "1px solid #ccc",
|
|
||||||
borderRadius: 4,
|
|
||||||
|
|
||||||
height: 35,
|
|
||||||
padding: "6px 12px",
|
|
||||||
marginBottom: 10,
|
|
||||||
fontSize: 12
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
onFocus={() => setNameWarning(false)}
|
|
||||||
onChange={(e) => setSignName(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<label
|
|
||||||
htmlFor="imgoverview"
|
|
||||||
style={{
|
|
||||||
fontSize: 12,
|
|
||||||
paddingBottom: 5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Signature/Image
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div className="signBlock">
|
|
||||||
<div>
|
<div>
|
||||||
<div style={{ position: "relative" }}>
|
<div className="relative">
|
||||||
<div
|
<div className="flex flex-row justify-between w-1/2 pl-[10px]">
|
||||||
style={{
|
<div className="flex flex-row justify-around items-center gap-[10px] mb-[10px]">
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
width: "50%",
|
|
||||||
paddingLeft: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-around",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 10,
|
|
||||||
marginBottom: 10
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<>
|
<>
|
||||||
<span
|
<span
|
||||||
onClick={() => handleSignatureBtn()}
|
onClick={() => handleSignatureBtn()}
|
||||||
@@ -370,24 +288,14 @@ const ManageSign = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ position: "relative" }}>
|
<div className="relative">
|
||||||
<div>
|
<div>
|
||||||
{image ? (
|
{image ? (
|
||||||
<div
|
<div className="signatureCanvas relative border-[2px] border-[#888]">
|
||||||
style={{
|
|
||||||
position: "relative",
|
|
||||||
border: "2px solid #888"
|
|
||||||
}}
|
|
||||||
className="signatureCanvas"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
alt="preview image"
|
alt="preview image"
|
||||||
src={image}
|
src={image}
|
||||||
style={{
|
className="w-full h-full object-contain"
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
objectFit: "contain"
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -572,20 +480,11 @@ const ManageSign = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* {!warning && (
|
|
||||||
<div className="warning signWarning" style={{ fontSize: 12 }}>
|
|
||||||
<i
|
|
||||||
className="fas fa-exclamation-circle"
|
|
||||||
style={{ color: "#fab005", fontSize: 15 }}
|
|
||||||
></i>{" "}
|
|
||||||
Please upload signature/Image
|
|
||||||
</div>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ paddingTop: 10 }}>
|
<div style={{ paddingTop: 10 }}>
|
||||||
<button
|
<button
|
||||||
className="customBtn successBtn"
|
className="op-btn op-btn-primary"
|
||||||
onClick={(e) => handleSubmit(e)}
|
onClick={(e) => handleSubmit(e)}
|
||||||
>
|
>
|
||||||
save
|
save
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,13 +4,11 @@ import Title from "../components/Title";
|
|||||||
|
|
||||||
const PageNotFound = ({ prefix }) => {
|
const PageNotFound = ({ prefix }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
|
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded">
|
||||||
<Title title={"Page Not Found"} />
|
<Title title={"Page Not Found"} />
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="text-[60px] lg:text-[120px] font-semibold text-black">
|
<h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1>
|
||||||
404
|
<p className="text-[30px] lg:text-[50px]">
|
||||||
</h1>
|
|
||||||
<p className="text-[30px] lg:text-[50px] text-black">
|
|
||||||
{prefix ? prefix : "Page"} Not Found
|
{prefix ? prefix : "Page"} Not Found
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,18 @@ import React, { useState, useEffect } from "react";
|
|||||||
import "../styles/pgsignup.css";
|
import "../styles/pgsignup.css";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { fetchAppInfo, showTenantName } from "../redux/actions/index";
|
import { useDispatch } from "react-redux";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
const PgSignUp = (props) => {
|
const PgSignUp = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -24,8 +28,7 @@ const PgSignUp = (props) => {
|
|||||||
|
|
||||||
// below useEffect is used to fetch App data and save to redux state
|
// below useEffect is used to fetch App data and save to redux state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Parse.User.logOut()
|
dispatch(fetchAppInfo());
|
||||||
props.fetchAppInfo();
|
|
||||||
handleSignedUpUser();
|
handleSignedUpUser();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
@@ -39,12 +42,8 @@ const PgSignUp = (props) => {
|
|||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
let paramString = url.split("?")[1];
|
let paramString = url.split("?")[1];
|
||||||
let queryString = new URLSearchParams(paramString);
|
let queryString = new URLSearchParams(paramString);
|
||||||
// console.log("url ", queryString);
|
|
||||||
|
|
||||||
let obj = { ...formData };
|
let obj = { ...formData };
|
||||||
for (let pair of queryString.entries()) {
|
for (let pair of queryString.entries()) {
|
||||||
// console.log("Key is: " + pair[0]);
|
|
||||||
// console.log("Value is: " + pair[1]);
|
|
||||||
obj = { ...obj, [pair[0]]: pair[1] };
|
obj = { ...obj, [pair[0]]: pair[1] };
|
||||||
}
|
}
|
||||||
const zohoRes = await axios.post(
|
const zohoRes = await axios.post(
|
||||||
@@ -59,34 +58,27 @@ const PgSignUp = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// console.log("zohoRes ", zohoRes);
|
|
||||||
const userSettings = JSON.parse(localStorage.getItem("userSettings"));
|
const userSettings = JSON.parse(localStorage.getItem("userSettings"));
|
||||||
const extClass = userSettings[0].extended_class;
|
const params = { subscription: zohoRes.data.result.subscription };
|
||||||
// console.log("extClass ", extClass);
|
const baseurl = `${parseBaseUrl}functions/savesubscription`;
|
||||||
const params = { email: userDetails.email };
|
try {
|
||||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
const res = await axios.post(baseurl, params, {
|
||||||
// console.log("res", res);
|
headers: {
|
||||||
if (res) {
|
"Content-Type": "application/json",
|
||||||
const updateQuery = new Parse.Object(extClass);
|
"X-Parse-Application-Id": parseAppId,
|
||||||
updateQuery.id = res.id;
|
sessiontoken: localStorage.getItem("accesstoken")
|
||||||
updateQuery.set(
|
}
|
||||||
"Next_billing_date",
|
});
|
||||||
new Date(zohoRes.data.result.nextBillingDate)
|
if (res) {
|
||||||
);
|
|
||||||
updateQuery.set("Plan", zohoRes.data.result.plan);
|
|
||||||
updateQuery.set("Customer_id", zohoRes.data.result.customer_id);
|
|
||||||
updateQuery.set(
|
|
||||||
"Subscription_id",
|
|
||||||
zohoRes.data.result.subscription_id
|
|
||||||
);
|
|
||||||
const updateRes = await updateQuery.save();
|
|
||||||
if (updateRes) {
|
|
||||||
localStorage.removeItem("userDetails");
|
localStorage.removeItem("userDetails");
|
||||||
navigate(
|
navigate(
|
||||||
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId
|
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId
|
||||||
);
|
);
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err ", err.message);
|
||||||
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
console.log("err ", err);
|
||||||
@@ -106,12 +98,8 @@ const PgSignUp = (props) => {
|
|||||||
const url = window.location.href;
|
const url = window.location.href;
|
||||||
let paramString = url.split("?")[1];
|
let paramString = url.split("?")[1];
|
||||||
let queryString = new URLSearchParams(paramString);
|
let queryString = new URLSearchParams(paramString);
|
||||||
// console.log("url ", queryString);
|
|
||||||
|
|
||||||
let obj = { ...formData };
|
let obj = { ...formData };
|
||||||
for (let pair of queryString.entries()) {
|
for (let pair of queryString.entries()) {
|
||||||
// console.log("Key is: " + pair[0]);
|
|
||||||
// console.log("Value is: " + pair[1]);
|
|
||||||
obj = { ...obj, [pair[0]]: pair[1] };
|
obj = { ...obj, [pair[0]]: pair[1] };
|
||||||
}
|
}
|
||||||
saveUser(obj);
|
saveUser(obj);
|
||||||
@@ -140,7 +128,9 @@ const PgSignUp = (props) => {
|
|||||||
user.set("username", zohoRes.data.result.email);
|
user.set("username", zohoRes.data.result.email);
|
||||||
user.set("password", obj.password);
|
user.set("password", obj.password);
|
||||||
user.set("email", zohoRes.data.result.email);
|
user.set("email", zohoRes.data.result.email);
|
||||||
user.set("phone", zohoRes.data.result.phone);
|
if (zohoRes?.data?.result?.phone) {
|
||||||
|
user.set("phone", zohoRes.data.result.phone);
|
||||||
|
}
|
||||||
user.set("name", zohoRes.data.result.name);
|
user.set("name", zohoRes.data.result.name);
|
||||||
|
|
||||||
const res = await user.signUp();
|
const res = await user.signUp();
|
||||||
@@ -153,21 +143,10 @@ const PgSignUp = (props) => {
|
|||||||
company: zohoRes.data.result.company,
|
company: zohoRes.data.result.company,
|
||||||
name: zohoRes.data.result.name,
|
name: zohoRes.data.result.name,
|
||||||
email: zohoRes.data.result.email,
|
email: zohoRes.data.result.email,
|
||||||
phone: zohoRes.data.result.phone,
|
phone: zohoRes?.data?.result?.phone || "",
|
||||||
role: obj.role
|
role: obj.role
|
||||||
// "pincode": "",
|
|
||||||
// "address": "",
|
|
||||||
// "country": "",
|
|
||||||
// "state": "",
|
|
||||||
// "city": ""
|
|
||||||
},
|
},
|
||||||
|
subscription: zohoRes.data.result.subscription
|
||||||
planDetails: {
|
|
||||||
plan: zohoRes.data.result.plan || {},
|
|
||||||
nextBillingDate: zohoRes.data.result.nextBillingDate || "",
|
|
||||||
customer_id: zohoRes.data.result.customer_id || "",
|
|
||||||
subscription_id: zohoRes.data.result.subscription_id || ""
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
const usersignup = await Parse.Cloud.run("usersignup", params);
|
const usersignup = await Parse.Cloud.run("usersignup", params);
|
||||||
// console.log("usersignup ", usersignup);
|
// console.log("usersignup ", usersignup);
|
||||||
@@ -243,8 +222,8 @@ const PgSignUp = (props) => {
|
|||||||
// Check extended class user role and tenentId
|
// Check extended class user role and tenentId
|
||||||
try {
|
try {
|
||||||
let userRoles = [];
|
let userRoles = [];
|
||||||
if (props.appInfo.settings) {
|
if (appInfo.settings) {
|
||||||
let userSettings = props.appInfo.settings;
|
let userSettings = appInfo.settings;
|
||||||
|
|
||||||
//Get Current user roles
|
//Get Current user roles
|
||||||
let url = `${baseUrl}functions/UserGroups`;
|
let url = `${baseUrl}functions/UserGroups`;
|
||||||
@@ -319,8 +298,8 @@ const PgSignUp = (props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (tenentInfo.length) {
|
if (tenentInfo.length) {
|
||||||
props.showTenantName(
|
dispatch(
|
||||||
tenentInfo[0].tenentName || ""
|
showTenant(tenentInfo[0].tenentName || "")
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenantName",
|
"TenantName",
|
||||||
@@ -351,15 +330,15 @@ const PgSignUp = (props) => {
|
|||||||
tenentName: x.TenantId.TenantName || ""
|
tenentName: x.TenantId.TenantName || ""
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenetId",
|
"TenantId",
|
||||||
x.TenantId.objectId
|
x.TenantId.objectId
|
||||||
);
|
);
|
||||||
tenentInfo.push(obj);
|
tenentInfo.push(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (tenentInfo.length) {
|
if (tenentInfo.length) {
|
||||||
props.showTenantName(
|
dispatch(
|
||||||
tenentInfo[0].tenentName || ""
|
showTenant(tenentInfo[0].tenentName || "")
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenantName",
|
"TenantName",
|
||||||
@@ -433,21 +412,8 @@ const PgSignUp = (props) => {
|
|||||||
<div className="bg-white">
|
<div className="bg-white">
|
||||||
<Title title={"Pgsignup page"} />
|
<Title title={"Pgsignup page"} />
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="flex justify-center items-center h-[100vh]">
|
||||||
style={{
|
<Loader />
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form id="signup" className="pgsignup-content" onSubmit={handleSubmit}>
|
<form id="signup" className="pgsignup-content" onSubmit={handleSubmit}>
|
||||||
@@ -537,15 +503,4 @@ const PgSignUp = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
export default PgSignUp;
|
||||||
if (Object.keys(state.appInfo).length !== 0) {
|
|
||||||
return { appInfo: state.appInfo, isloginVisible: true };
|
|
||||||
} else {
|
|
||||||
return { appInfo: state.appInfo, isloginVisible: false };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
|
||||||
fetchAppInfo,
|
|
||||||
showTenantName
|
|
||||||
})(PgSignUp);
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate } from "react-router";
|
import { NavLink, useNavigate } from "react-router-dom";
|
||||||
import { NavLink } from "react-router-dom";
|
|
||||||
import checkmark from "../assets/images/checkmark.png";
|
import checkmark from "../assets/images/checkmark.png";
|
||||||
import plansArr from "../json/plansArr.json";
|
import plansArr from "../json/plansArr";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
|
import Parse from "parse";
|
||||||
|
import { openInNewTab } from "../constant/Utils";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
const listItemStyle = {
|
const listItemStyle = {
|
||||||
paddingLeft: "20px", // Add padding to create space for the image
|
paddingLeft: "20px", // Add padding to create space for the image
|
||||||
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
|
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
|
||||||
@@ -16,18 +18,34 @@ const PlanSubscriptions = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [yearlyVisible, setYearlyVisible] = useState(false);
|
const [yearlyVisible, setYearlyVisible] = useState(false);
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
|
const extUser =
|
||||||
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
|
localStorage.getItem("Extand_Class") &&
|
||||||
|
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||||
|
const user = {
|
||||||
|
name: extUser?.Name,
|
||||||
|
email: extUser?.Email,
|
||||||
|
company: extUser?.Company,
|
||||||
|
phone: extUser?.Phone
|
||||||
|
};
|
||||||
|
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
|
||||||
|
// console.log("userDetails ", userDetails);
|
||||||
const name =
|
const name =
|
||||||
userDetails && userDetails.name ? "first_name=" + userDetails.name : "";
|
userDetails && userDetails.name
|
||||||
|
? "first_name=" + encodeURIComponent(userDetails.name)
|
||||||
|
: "";
|
||||||
const email =
|
const email =
|
||||||
userDetails && userDetails.email ? "&email=" + userDetails.email : "";
|
userDetails && userDetails.email
|
||||||
|
? "&email=" + encodeURIComponent(userDetails.email)
|
||||||
|
: "";
|
||||||
const company =
|
const company =
|
||||||
userDetails && userDetails.company
|
userDetails && userDetails.company
|
||||||
? "&company_name=" + userDetails.company
|
? "&company_name=" + encodeURIComponent(userDetails.company)
|
||||||
: "";
|
: "";
|
||||||
const phone =
|
const phone =
|
||||||
userDetails && userDetails.phone ? "&mobile=" + userDetails.phone : "";
|
userDetails && userDetails.phone
|
||||||
|
? "&mobile=" + encodeURIComponent(userDetails.phone)
|
||||||
|
: "";
|
||||||
|
|
||||||
const details =
|
const details =
|
||||||
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
||||||
name +
|
name +
|
||||||
@@ -35,57 +53,72 @@ const PlanSubscriptions = () => {
|
|||||||
company +
|
company +
|
||||||
phone;
|
phone;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem("accesstoken")) {
|
// if (localStorage.getItem("accesstoken")) {
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
setYearlyVisible(false);
|
// } else {
|
||||||
} else {
|
// navigate("/", { replace: true });
|
||||||
navigate("/", { replace: true });
|
// }
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleFreePlan = async () => {
|
||||||
|
setIsLoader(true);
|
||||||
|
try {
|
||||||
|
const params = { userId: Parse.User.current().id };
|
||||||
|
const res = await Parse.Cloud.run("freesubscription", params);
|
||||||
|
if (res.status === "success" && res.result === "already subscribed!") {
|
||||||
|
setIsLoader(false);
|
||||||
|
alert("You have already subscribed to plan!");
|
||||||
|
} else if (res.status === "success") {
|
||||||
|
setIsLoader(false);
|
||||||
|
navigate("/");
|
||||||
|
} else if (res.status === "error") {
|
||||||
|
setIsLoader(false);
|
||||||
|
alert(res.result);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setIsLoader(false);
|
||||||
|
console.log("err in free subscribe", err.message);
|
||||||
|
alert("Somenthing went wrong, please try again later!");
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title title={"Subscriptions"} />
|
<Title title={"Subscriptions"} />
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="flex items-center justify-center h-screen">
|
||||||
style={{
|
<Loader />
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="overflow-y-auto max-h-full">
|
||||||
style={{
|
<div className="block my-2">
|
||||||
backgroundColor: "white",
|
<div className="flex flex-col justify-center items-center w-full">
|
||||||
overflowY: "auto",
|
<div
|
||||||
maxHeight: "100%",
|
role="tablist"
|
||||||
"--theme-color": "#7952b3",
|
className="op-tabs op-tabs-boxed bg-base-100 my-2 shadow-lg transition-all"
|
||||||
"--plan-width": 30
|
>
|
||||||
}}
|
<a
|
||||||
>
|
onClick={() => setYearlyVisible(false)}
|
||||||
<div
|
role="tab"
|
||||||
id="monthlyPlans"
|
className={`${!yearlyVisible ? "op-tab-active" : ""} op-tab`}
|
||||||
className={`${yearlyVisible ? "none" : "block my-2"}`}
|
>
|
||||||
>
|
Monthly
|
||||||
<div className="flex justify-center w-full my-2">
|
</a>
|
||||||
<ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300">
|
<a
|
||||||
|
onClick={() => setYearlyVisible(true)}
|
||||||
|
role="tab"
|
||||||
|
className={`${yearlyVisible ? "op-tab-active" : ""} op-tab`}
|
||||||
|
>
|
||||||
|
Yearly (10% off)
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
|
||||||
{plansArr.map((item) => (
|
{plansArr.map((item) => (
|
||||||
<li
|
<li
|
||||||
className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]"
|
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
|
||||||
key={item.planName}
|
key={item.planName}
|
||||||
>
|
>
|
||||||
<div className="p-2 flex flex-col justify-center items-center">
|
<div className="p-2 flex flex-col justify-center items-center max-h-[310px]">
|
||||||
<h3 className="text-[#002862] uppercase">
|
<h3 className="text-[#002862] uppercase">
|
||||||
{item.planName}
|
{item.planName}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -96,20 +129,20 @@ const PlanSubscriptions = () => {
|
|||||||
alt="freeimg"
|
alt="freeimg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="">
|
<div>
|
||||||
<span className="text-3xl">
|
<span className="text-3xl">
|
||||||
{item.currency && <small>{item.currency}</small>}
|
{item.currency && <small>{item.currency}</small>}
|
||||||
{item.price}
|
{yearlyVisible
|
||||||
|
? item?.yearlyPrice
|
||||||
|
: item.monthlyPrice}
|
||||||
</span>
|
</span>
|
||||||
<div
|
<p className="font-semibold pt-2 text-sm">
|
||||||
className={`${
|
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
|
||||||
item.subtitle.length <= 32
|
</p>
|
||||||
? "w-[150px] text-center"
|
<div className="max-w-[250px] h-[40px] text-center text-sm my-2">
|
||||||
: ""
|
<div
|
||||||
} text-sm text-center my-2`}
|
|
||||||
>
|
|
||||||
<p
|
|
||||||
style={{
|
style={{
|
||||||
|
textAlign: "center",
|
||||||
backgroundColor: item.subtitlecolor
|
backgroundColor: item.subtitlecolor
|
||||||
? item.subtitlecolor
|
? item.subtitlecolor
|
||||||
: "white"
|
: "white"
|
||||||
@@ -124,29 +157,37 @@ const PlanSubscriptions = () => {
|
|||||||
) : (
|
) : (
|
||||||
<span>{item.subtitle}</span>
|
<span>{item.subtitle}</span>
|
||||||
)}
|
)}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{item.url ? (
|
||||||
<NavLink
|
<NavLink
|
||||||
to={
|
to={
|
||||||
item.btnText === "Subscribe"
|
item.btnText === "Subscribe"
|
||||||
? item.url + details
|
? yearlyVisible
|
||||||
: item.url
|
? item.yearlyUrl + details
|
||||||
}
|
: item.url + details
|
||||||
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white"
|
: item.url
|
||||||
target={item.target}
|
}
|
||||||
>
|
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
|
||||||
{item.btnText}
|
target={item.target}
|
||||||
</NavLink>
|
>
|
||||||
|
{item.btnText}
|
||||||
|
</NavLink>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:text-white cursor-pointer"
|
||||||
|
onClick={() => handleFreePlan()}
|
||||||
|
>
|
||||||
|
{item.btnText}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<hr className="w-full bg-gray-300 p-[.5px]" />
|
<hr className="w-full bg-gray-300 h-[0.5px]" />
|
||||||
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
|
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
|
||||||
{item.benefits.map((subitem, index) => (
|
{item.benefits.map((subitem, index) => (
|
||||||
<li style={listItemStyle} key={index} className="m-1">
|
<li style={listItemStyle} key={index} className="m-1">
|
||||||
<span style={{ position: "relative" }}>
|
<span className="relative">{subitem}</span>
|
||||||
{subitem}
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -154,6 +195,24 @@ const PlanSubscriptions = () => {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm flex justify-center items-center mt-3 mb-2">
|
||||||
|
<hr className="border-[2px] border-gray-350 w-[20%]" />
|
||||||
|
<span className="px-2 text-base-content cursor-default">or</span>
|
||||||
|
<hr className="border-[2px] border-gray-350 w-[20%]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-center items-center">
|
||||||
|
<h3 className="text-[#002862] mt-1 mb-2">
|
||||||
|
Host it yourself for free
|
||||||
|
</h3>
|
||||||
|
<div
|
||||||
|
className="op-btn op-btn-primary w-[200px]"
|
||||||
|
onClick={() =>
|
||||||
|
openInNewTab("https://github.com/OpenSignLabs/OpenSign")
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Visit Github
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,17 +6,22 @@ import reportJson from "../json/ReportJson";
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import PageNotFound from "./PageNotFound";
|
import PageNotFound from "./PageNotFound";
|
||||||
|
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
const Report = () => {
|
const Report = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
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 [reporthelp, setReportHelp] = useState("");
|
||||||
const [actions, setActions] = useState([]);
|
const [actions, setActions] = useState([]);
|
||||||
const [heading, setHeading] = useState([]);
|
const [heading, setHeading] = useState([]);
|
||||||
const [isNextRecord, setIsNextRecord] = useState(false);
|
const [isNextRecord, setIsNextRecord] = useState(false);
|
||||||
const [isMoreDocs, setIsMoreDocs] = useState(true);
|
const [isMoreDocs, setIsMoreDocs] = useState(true);
|
||||||
const [form, setForm] = useState("");
|
const [form, setForm] = useState("");
|
||||||
|
const [tourData, setTourData] = useState([]);
|
||||||
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
const docPerPage = 10;
|
const docPerPage = 10;
|
||||||
|
|
||||||
@@ -45,6 +50,9 @@ const Report = () => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [isNextRecord]);
|
}, [isNextRecord]);
|
||||||
|
|
||||||
|
const handleDontShow = (isChecked) => {
|
||||||
|
setIsDontShow(isChecked);
|
||||||
|
};
|
||||||
const getReportData = async (skipUserRecord = 0, limit = 200) => {
|
const getReportData = async (skipUserRecord = 0, limit = 200) => {
|
||||||
// setIsLoader(true);
|
// setIsLoader(true);
|
||||||
const json = reportJson(id);
|
const json = reportJson(id);
|
||||||
@@ -53,6 +61,7 @@ const Report = () => {
|
|||||||
setHeading(json.heading);
|
setHeading(json.heading);
|
||||||
setReportName(json.reportName);
|
setReportName(json.reportName);
|
||||||
setForm(json.form);
|
setForm(json.form);
|
||||||
|
setReportHelp(json?.helpMsg);
|
||||||
const currentUser = Parse.User.current().id;
|
const currentUser = Parse.User.current().id;
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
@@ -67,6 +76,42 @@ const Report = () => {
|
|||||||
headers: headers,
|
headers: headers,
|
||||||
signal: abortController.signal // is used to cancel fetch query
|
signal: abortController.signal // is used to cancel fetch query
|
||||||
});
|
});
|
||||||
|
if (id === "6TeaPr321t") {
|
||||||
|
const tourConfig = [
|
||||||
|
{
|
||||||
|
selector: "[data-tut=reactourFirst]",
|
||||||
|
content: () => (
|
||||||
|
<TourContentWithBtn
|
||||||
|
message={
|
||||||
|
"Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like ‘HR Manager’ and ‘New Employee’. Each time you use the template, you can assign the ‘New Employee’ role to different incoming staff members, while the ‘HR Manager’ role remains constant, facilitating a seamless onboarding process for each recruit. "
|
||||||
|
}
|
||||||
|
isChecked={handleDontShow}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
position: "top",
|
||||||
|
style: { fontSize: "13px" }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (res.data.result && res.data.result?.length > 0) {
|
||||||
|
json.actions.map((data) => {
|
||||||
|
const newConfig = {
|
||||||
|
selector: `[data-tut="${data?.selector}"]`,
|
||||||
|
content: () => (
|
||||||
|
<TourContentWithBtn
|
||||||
|
message={data?.message}
|
||||||
|
isChecked={handleDontShow}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
position: "top",
|
||||||
|
style: { fontSize: "13px" }
|
||||||
|
};
|
||||||
|
tourConfig.push(newConfig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setTourData(tourConfig);
|
||||||
|
}
|
||||||
if (id === "4Hhwbp482K") {
|
if (id === "4Hhwbp482K") {
|
||||||
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
||||||
let arr = [];
|
let arr = [];
|
||||||
@@ -104,12 +149,14 @@ const Report = () => {
|
|||||||
} else {
|
} else {
|
||||||
setIsMoreDocs(false);
|
setIsMoreDocs(false);
|
||||||
}
|
}
|
||||||
setIsNextRecord(false);
|
if (!res.data.result.error) {
|
||||||
setList((prevRecord) =>
|
setIsNextRecord(false);
|
||||||
prevRecord.length > 0
|
setList((prevRecord) =>
|
||||||
? [...prevRecord, ...res.data.result]
|
prevRecord.length > 0
|
||||||
: res.data.result
|
? [...prevRecord, ...res.data.result]
|
||||||
);
|
: res.data.result
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -127,21 +174,8 @@ const Report = () => {
|
|||||||
<>
|
<>
|
||||||
<Title title={reportName} />
|
<Title title={reportName} />
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="h-[100vh] 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>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -156,6 +190,9 @@ const Report = () => {
|
|||||||
isMoreDocs={isMoreDocs}
|
isMoreDocs={isMoreDocs}
|
||||||
docPerPage={docPerPage}
|
docPerPage={docPerPage}
|
||||||
form={form}
|
form={form}
|
||||||
|
report_help={reporthelp}
|
||||||
|
tourData={tourData}
|
||||||
|
isDontShow={isDontShow}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PageNotFound prefix={"Report"} />
|
<PageNotFound prefix={"Report"} />
|
||||||
|
|||||||
@@ -0,0 +1,384 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import Parse from "parse";
|
||||||
|
import axios from "axios";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import { fetchSubscription } from "../constant/Utils";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
|
const SSOVerify = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isModal, setIsModal] = useState(false);
|
||||||
|
const [message, setMessage] = useState("Verifying SSO...");
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [userDetails, setUserDetails] = useState({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
Destination: "",
|
||||||
|
Company: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
linkUserWithSSO();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// `linkUserWithSSO` is used to sign in or sign up a user using an SSO code and check if the user is present in the extended class
|
||||||
|
const linkUserWithSSO = async () => {
|
||||||
|
const param = new URLSearchParams(location.search);
|
||||||
|
const code = param?.get("code");
|
||||||
|
const state = param?.get("state");
|
||||||
|
try {
|
||||||
|
// The `ssosign` cloud function is used to sign in or sign up a user
|
||||||
|
const ssosign = await Parse.Cloud.run("ssosign", {
|
||||||
|
code: code,
|
||||||
|
email: state
|
||||||
|
});
|
||||||
|
localStorage.setItem("accesstoken", ssosign.sessiontoken);
|
||||||
|
// `checkExtUser` checks if the user is present in the extended class `contracts_Users` and if not, initiates the new user flow
|
||||||
|
await checkExtUser(ssosign);
|
||||||
|
} catch (err) {
|
||||||
|
setMessage("Error: " + err.message);
|
||||||
|
console.log("err", err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const checkExtUser = async (ssosign) => {
|
||||||
|
const params = { email: ssosign?.email };
|
||||||
|
try {
|
||||||
|
// `isextenduser` checks if the current user is present in the extended class
|
||||||
|
const extRes = await Parse.Cloud.run("isextenduser", params);
|
||||||
|
if (extRes?.isUserExist) {
|
||||||
|
// `getUserDetails` retrieves the current user's details from the extended class
|
||||||
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
|
if (ssosign && ssosign.sessiontoken) {
|
||||||
|
const LocalUserDetails = {
|
||||||
|
name: extRes.Name,
|
||||||
|
email: extRes.email,
|
||||||
|
phone: extRes?.get("Phone") || "",
|
||||||
|
company: extRes.get("Company")
|
||||||
|
};
|
||||||
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
|
thirdpartyLoginfn(ssosign.sessiontoken);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsModal(true);
|
||||||
|
setUserDetails((prev) => ({
|
||||||
|
...prev,
|
||||||
|
name: ssosign.name,
|
||||||
|
email: ssosign.email,
|
||||||
|
phone: ssosign?.phone || ""
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in isextenduser or getuserdetails cloud function", err);
|
||||||
|
setMessage("Error: " + err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleSubmitbtn` is used to create a user in the extended class
|
||||||
|
const handleSubmitbtn = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsLoader(true);
|
||||||
|
let phone;
|
||||||
|
if (userDetails?.phone) {
|
||||||
|
phone = validateInput(userDetails?.phone);
|
||||||
|
} else {
|
||||||
|
phone = true;
|
||||||
|
}
|
||||||
|
if (userDetails.Destination && userDetails.Company && phone) {
|
||||||
|
const payload = { sessionToken: localStorage.getItem("accesstoken") };
|
||||||
|
if (payload && payload.sessionToken) {
|
||||||
|
const params = {
|
||||||
|
userDetails: {
|
||||||
|
name: userDetails.name,
|
||||||
|
email: userDetails.email,
|
||||||
|
phone: userDetails?.phone || "",
|
||||||
|
role: "contracts_User",
|
||||||
|
company: userDetails.Company,
|
||||||
|
jobTitle: userDetails.Destination
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const userSignUp = await Parse.Cloud.run("usersignup", params);
|
||||||
|
if (userSignUp && userSignUp.sessionToken) {
|
||||||
|
const LocalUserDetails = params.userDetails;
|
||||||
|
localStorage.setItem(
|
||||||
|
"userDetails",
|
||||||
|
JSON.stringify(LocalUserDetails)
|
||||||
|
);
|
||||||
|
await thirdpartyLoginfn(userSignUp.sessionToken);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else {
|
||||||
|
alert(userSignUp.message);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("error in usersignup", err);
|
||||||
|
localStorage.removeItem("accesstoken");
|
||||||
|
alert("Something went wrong.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("accesstoken");
|
||||||
|
alert("Internal server error.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Please fill required details correctly.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
|
||||||
|
const thirdpartyLoginfn = async (sessionToken) => {
|
||||||
|
const baseUrl = localStorage.getItem("baseUrl");
|
||||||
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
|
try {
|
||||||
|
const validUser = await Parse.User.become(sessionToken);
|
||||||
|
if (validUser) {
|
||||||
|
localStorage.setItem("accesstoken", sessionToken);
|
||||||
|
const _user = JSON.parse(JSON.stringify(validUser));
|
||||||
|
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||||
|
if (_user.ProfilePic) {
|
||||||
|
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("profileImg", "");
|
||||||
|
}
|
||||||
|
// Check extended class user role and tenentId
|
||||||
|
try {
|
||||||
|
let userRoles = [];
|
||||||
|
const userSettings = appInfo.settings;
|
||||||
|
//Get Current user roles
|
||||||
|
const url = `${baseUrl}functions/UserGroups`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: _user.sessionToken
|
||||||
|
};
|
||||||
|
const body = { appname: appInfo.appname };
|
||||||
|
const UserGroupsRes = await axios.post(url, JSON.stringify(body), {
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || [];
|
||||||
|
if (userRoles) {
|
||||||
|
let _currentRole = "";
|
||||||
|
const excludeRoles = ["contracts_Guest", `contracts_appeditor`];
|
||||||
|
const filteredRole = userRoles.filter(
|
||||||
|
(x) => !excludeRoles.includes(x)
|
||||||
|
);
|
||||||
|
if (filteredRole?.length > 1) {
|
||||||
|
console.log("user has two roles");
|
||||||
|
// _currentRole = filteredRole.filter((x) => x === "contracts_User");
|
||||||
|
} else {
|
||||||
|
_currentRole = filteredRole?.[0] || "";
|
||||||
|
}
|
||||||
|
if (_currentRole) {
|
||||||
|
const roleSetting = userSettings?.find(
|
||||||
|
(setting) => setting.role === _currentRole
|
||||||
|
);
|
||||||
|
const redirectUrl =
|
||||||
|
location?.state?.from ||
|
||||||
|
`/${roleSetting.pageType}/${roleSetting.pageId}`;
|
||||||
|
const _role = _currentRole.replace(`${appInfo.appname}_`, "");
|
||||||
|
localStorage.setItem("_user_role", _role);
|
||||||
|
// Get TenentID from Extendend Class
|
||||||
|
localStorage.setItem(
|
||||||
|
"extended_class",
|
||||||
|
roleSetting.extended_class
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||||
|
email: _user.email
|
||||||
|
});
|
||||||
|
if (extUser) {
|
||||||
|
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||||
|
localStorage.setItem("userEmail", _extUser.Email);
|
||||||
|
localStorage.setItem("username", _extUser.Name);
|
||||||
|
localStorage.setItem("scriptId", true);
|
||||||
|
let tenentInfo = [];
|
||||||
|
const results = [extUser];
|
||||||
|
if (results) {
|
||||||
|
let extendedInfo_stringify = JSON.stringify(results);
|
||||||
|
localStorage.setItem(
|
||||||
|
"Extand_Class",
|
||||||
|
extendedInfo_stringify
|
||||||
|
);
|
||||||
|
if (_extUser.TenantId) {
|
||||||
|
const obj = {
|
||||||
|
tenentId: _extUser.TenantId.objectId,
|
||||||
|
tenentName: _extUser.TenantId.TenantName || ""
|
||||||
|
};
|
||||||
|
localStorage.setItem("TenantId", obj.tenentId);
|
||||||
|
tenentInfo.push(obj);
|
||||||
|
dispatch(showTenant(obj.tenentName || ""));
|
||||||
|
localStorage.setItem("TenantName", obj.tenentName || "");
|
||||||
|
}
|
||||||
|
localStorage.setItem("PageLanding", roleSetting.pageId);
|
||||||
|
localStorage.setItem("defaultmenuid", roleSetting.menuId);
|
||||||
|
localStorage.setItem("pageType", roleSetting.pageType);
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const res = await fetchSubscription();
|
||||||
|
const plan = res.plan;
|
||||||
|
const billingDate = res.billingDate;
|
||||||
|
if (plan === "freeplan") {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else if (billingDate) {
|
||||||
|
if (new Date(billingDate) > new Date()) {
|
||||||
|
localStorage.removeItem("userDetails");
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert("user not exist.");
|
||||||
|
setMessage("Error: User not exist.");
|
||||||
|
console.log("err in get extUser", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Role does not exists.");
|
||||||
|
setMessage("Error: Role does not exists.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Role does not exists.");
|
||||||
|
setMessage("Error: Role does not exists.");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in usergroups", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in become method", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModal(false);
|
||||||
|
if (Parse?.User?.current()) {
|
||||||
|
Parse.User.logOut();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `validateInput` is used to verify the phone pattern
|
||||||
|
function validateInput(input) {
|
||||||
|
if (input) {
|
||||||
|
const pattern = /^(?!.*\+.*\+)[\d+-]*$/;
|
||||||
|
return pattern.test(input);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="w-full h-screen flex flex-col justify-center items-center text-sm md:text-xl ">
|
||||||
|
{message === "Verifying SSO..." && <Loader />}
|
||||||
|
<div className="text-base-content">{message}</div>
|
||||||
|
</div>
|
||||||
|
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
|
||||||
|
<div className="relative">
|
||||||
|
{isLoader && (
|
||||||
|
<div className="absolute w-full h-full bg-black bg-opacity-25 flex justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form
|
||||||
|
className="px-4 py-3 text-base-content"
|
||||||
|
onSubmit={handleSubmitbtn}
|
||||||
|
>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label htmlFor="Company" className="block text-xs font-semibold">
|
||||||
|
Phone <span className="text-[13px] text-[red]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
|
id="Phone"
|
||||||
|
value={userDetails.phone}
|
||||||
|
onChange={(e) =>
|
||||||
|
setUserDetails({
|
||||||
|
...userDetails,
|
||||||
|
phone: e.target.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<p className="text-[10px] text-[red] ml-2">
|
||||||
|
{!validateInput(userDetails.phone) && "Invalid phone value"}
|
||||||
|
</p>
|
||||||
|
</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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="op-btn op-btn-ghost ml-2"
|
||||||
|
onClick={handleCloseModal}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SSOVerify;
|
||||||
+145
-106
@@ -1,23 +1,30 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { connect } from "react-redux";
|
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import { fetchAppInfo, showTenantName } from "../redux/actions";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
import { useNavigate, NavLink } from "react-router-dom";
|
|
||||||
import login_img from "../assets/images/login_img.svg";
|
import login_img from "../assets/images/login_img.svg";
|
||||||
import { useWindowSize } from "../hook/useWindowSize";
|
import { useWindowSize } from "../hook/useWindowSize";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
const Signup = (props) => {
|
import { useDispatch } from "react-redux";
|
||||||
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import { getAppLogo, openInNewTab } from "../constant/Utils";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
const Signup = () => {
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [company, setCompany] = useState("");
|
const [company, setCompany] = useState("");
|
||||||
const [jobTitle, setJobTitle] = useState("");
|
const [jobTitle, setJobTitle] = useState("");
|
||||||
|
const [image, setImage] = useState();
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
loading: false,
|
loading: false,
|
||||||
alertType: "success",
|
alertType: "success",
|
||||||
@@ -27,9 +34,8 @@ const Signup = (props) => {
|
|||||||
const [lengthValid, setLengthValid] = useState(false);
|
const [lengthValid, setLengthValid] = useState(false);
|
||||||
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
||||||
const [specialCharValid, setSpecialCharValid] = useState(false);
|
const [specialCharValid, setSpecialCharValid] = useState(false);
|
||||||
|
const [isAuthorize, setIsAuthorize] = useState(false);
|
||||||
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
||||||
const image = props.appInfo.applogo;
|
|
||||||
|
|
||||||
const clearStorage = async () => {
|
const clearStorage = async () => {
|
||||||
if (Parse.User.current()) {
|
if (Parse.User.current()) {
|
||||||
@@ -65,6 +71,19 @@ const Signup = (props) => {
|
|||||||
localStorage.setItem("baseUrl", baseUrl);
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
localStorage.setItem("parseAppId", appid);
|
localStorage.setItem("parseAppId", appid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFreePlan = async (id) => {
|
||||||
|
try {
|
||||||
|
const params = { userId: id };
|
||||||
|
const res = await Parse.Cloud.run("freesubscription", params);
|
||||||
|
if (res.status === "error") {
|
||||||
|
alert(res.result);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in free subscribe", err.message);
|
||||||
|
alert("Somenthing went wrong, please try again later!");
|
||||||
|
}
|
||||||
|
};
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||||
@@ -90,7 +109,7 @@ const Signup = (props) => {
|
|||||||
res
|
res
|
||||||
.then(async (r) => {
|
.then(async (r) => {
|
||||||
if (r) {
|
if (r) {
|
||||||
let roleData = props.appInfo.settings;
|
let roleData = appInfo.settings;
|
||||||
if (roleData && roleData.length > 0) {
|
if (roleData && roleData.length > 0) {
|
||||||
const params = {
|
const params = {
|
||||||
userDetails: {
|
userDetails: {
|
||||||
@@ -99,7 +118,7 @@ const Signup = (props) => {
|
|||||||
name: name,
|
name: name,
|
||||||
email: email,
|
email: email,
|
||||||
phone: phone,
|
phone: phone,
|
||||||
role: props.appInfo.defaultRole
|
role: appInfo.defaultRole
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -108,7 +127,13 @@ const Signup = (props) => {
|
|||||||
params
|
params
|
||||||
);
|
);
|
||||||
if (usersignup) {
|
if (usersignup) {
|
||||||
handleNavigation(r.getSessionToken());
|
const param = new URLSearchParams(location.search);
|
||||||
|
const isFreeplan =
|
||||||
|
param?.get("subscription") === "freeplan";
|
||||||
|
if (isFreeplan) {
|
||||||
|
await handleFreePlan(r.id);
|
||||||
|
}
|
||||||
|
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.message);
|
alert(err.message);
|
||||||
@@ -153,7 +178,7 @@ const Signup = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNavigation = async (sessionToken) => {
|
const handleNavigation = async (sessionToken, isFreeplan = false) => {
|
||||||
const baseUrl = localStorage.getItem("baseUrl");
|
const baseUrl = localStorage.getItem("baseUrl");
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
const res = await axios.get(baseUrl + "users/me", {
|
const res = await axios.get(baseUrl + "users/me", {
|
||||||
@@ -181,8 +206,8 @@ const Signup = (props) => {
|
|||||||
// Check extended class user role and tenentId
|
// Check extended class user role and tenentId
|
||||||
try {
|
try {
|
||||||
let userRoles = [];
|
let userRoles = [];
|
||||||
if (props.appInfo.settings) {
|
if (appInfo.settings) {
|
||||||
let userSettings = props.appInfo.settings;
|
let userSettings = appInfo.settings;
|
||||||
|
|
||||||
//Get Current user roles
|
//Get Current user roles
|
||||||
let url = `${baseUrl}functions/UserGroups`;
|
let url = `${baseUrl}functions/UserGroups`;
|
||||||
@@ -256,8 +281,8 @@ const Signup = (props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (tenentInfo.length) {
|
if (tenentInfo.length) {
|
||||||
props.showTenantName(
|
dispatch(
|
||||||
tenentInfo[0].tenentName || ""
|
showTenant(tenentInfo[0].tenentName || "")
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenantName",
|
"TenantName",
|
||||||
@@ -293,15 +318,15 @@ const Signup = (props) => {
|
|||||||
tenentName: x.TenantId.TenantName || ""
|
tenentName: x.TenantId.TenantName || ""
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenetId",
|
"TenantId",
|
||||||
x.TenantId.objectId
|
x.TenantId.objectId
|
||||||
);
|
);
|
||||||
tenentInfo.push(obj);
|
tenentInfo.push(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (tenentInfo.length) {
|
if (tenentInfo.length) {
|
||||||
props.showTenantName(
|
dispatch(
|
||||||
tenentInfo[0].tenentName || ""
|
showTenant(tenentInfo[0].tenentName || "")
|
||||||
);
|
);
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"TenantName",
|
"TenantName",
|
||||||
@@ -321,8 +346,14 @@ const Signup = (props) => {
|
|||||||
element.pageType
|
element.pageType
|
||||||
);
|
);
|
||||||
setState({ loading: false });
|
setState({ loading: false });
|
||||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
if (isEnableSubscription) {
|
||||||
navigate(`/subscription`, { replace: true });
|
if (isFreeplan) {
|
||||||
|
navigate(
|
||||||
|
`/${element.pageType}/${element.pageId}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, { replace: true });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
alert("Registered user successfully");
|
alert("Registered user successfully");
|
||||||
navigate(
|
navigate(
|
||||||
@@ -339,8 +370,14 @@ const Signup = (props) => {
|
|||||||
);
|
);
|
||||||
localStorage.setItem("pageType", element.pageType);
|
localStorage.setItem("pageType", element.pageType);
|
||||||
setState({ loading: false });
|
setState({ loading: false });
|
||||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
if (isEnableSubscription) {
|
||||||
navigate(`/subscription`, { replace: true });
|
if (isFreeplan) {
|
||||||
|
navigate(
|
||||||
|
`/${element.pageType}/${element.pageId}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, { replace: true });
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
navigate(
|
navigate(
|
||||||
`/${element.pageType}/${element.pageId}`
|
`/${element.pageType}/${element.pageId}`
|
||||||
@@ -433,10 +470,24 @@ const Signup = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.fetchAppInfo();
|
dispatch(fetchAppInfo());
|
||||||
|
saveLogo();
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const saveLogo = async () => {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const logo = await getAppLogo();
|
||||||
|
if (logo) {
|
||||||
|
setImage(logo);
|
||||||
|
} else {
|
||||||
|
setImage(appInfo?.applogo || undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setImage(appInfo?.applogo || undefined);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlePasswordChange = (e) => {
|
const handlePasswordChange = (e) => {
|
||||||
const newPassword = e.target.value;
|
const newPassword = e.target.value;
|
||||||
setPassword(newPassword);
|
setPassword(newPassword);
|
||||||
@@ -451,78 +502,58 @@ const Signup = (props) => {
|
|||||||
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
|
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="bg-white">
|
<div className="">
|
||||||
{state.loading && (
|
{state.loading && (
|
||||||
<div
|
<div className="fixed w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-50">
|
||||||
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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Title title={"Signup page"} />
|
<Title title={"Signup page"} />
|
||||||
|
{appInfo && appInfo.applogo ? (
|
||||||
{props.isloginVisible && props.isloginVisible ? (
|
<div className="md:p-10 lg:p-16">
|
||||||
<div>
|
<div className="md:p-4 lg:p-10 p-4 bg-base-100 text-base-content op-card">
|
||||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||||
<div className="w-[250px] h-[66px] inline-block">
|
{image && (
|
||||||
<img src={image} width="100%" alt="" />
|
<img src={image} className="object-contain h-full" alt="logo" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
||||||
<div className="">
|
<div>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<h2 className="text-[30px] mt-6">Create Account !</h2>
|
<h2 className="text-[30px] mt-6">Create Account !</h2>
|
||||||
<div className="outline outline-1 outline-slate-300/50 shadow-md rounded my-4">
|
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
|
||||||
<div className="px-6 py-4 text-xs">
|
<div className="px-6 py-4 text-xs">
|
||||||
<label className="block ">
|
<label className="block ">
|
||||||
Name{" "}
|
Name <span className="text-[red] text-[13px]">*</span>
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
<label>
|
<label>
|
||||||
Email{" "}
|
Email <span className="text-[red] text-[13px]">*</span>
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
type="text"
|
type="email"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) =>
|
||||||
|
setEmail(e.target.value?.toLowerCase())
|
||||||
|
}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
<label>
|
<label>
|
||||||
Phone{" "}
|
Phone <span className="text-[red] text-[13px]">*</span>
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="tel"
|
type="tel"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
required
|
||||||
@@ -530,11 +561,11 @@ const Signup = (props) => {
|
|||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
<label>
|
<label>
|
||||||
Company{" "}
|
Company{" "}
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
<span className="text-[red] text-[13px]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
value={company}
|
value={company}
|
||||||
onChange={(e) => setCompany(e.target.value)}
|
onChange={(e) => setCompany(e.target.value)}
|
||||||
required
|
required
|
||||||
@@ -542,11 +573,11 @@ const Signup = (props) => {
|
|||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
<label>
|
<label>
|
||||||
Job Title{" "}
|
Job Title{" "}
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
<span className="text-[red] text-[13px]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
value={jobTitle}
|
value={jobTitle}
|
||||||
onChange={(e) => setJobTitle(e.target.value)}
|
onChange={(e) => setJobTitle(e.target.value)}
|
||||||
required
|
required
|
||||||
@@ -554,21 +585,19 @@ const Signup = (props) => {
|
|||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
<label>
|
<label>
|
||||||
Password{" "}
|
Password{" "}
|
||||||
<span style={{ color: "red", fontSize: 13 }}>*</span>
|
<span className="text-[red] text-[13px]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type={showPassword ? "text" : "password"}
|
type={showPassword ? "text" : "password"}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
name="password"
|
name="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => handlePasswordChange(e)}
|
onChange={(e) => handlePasswordChange(e)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
|
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
|
||||||
showPassword ? "text-[#007bff]" : "text-black"
|
|
||||||
}`}
|
|
||||||
onClick={togglePasswordVisibility}
|
onClick={togglePasswordVisibility}
|
||||||
>
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
@@ -606,34 +635,62 @@ const Signup = (props) => {
|
|||||||
{specialCharValid ? "✓" : "✗"} Password should
|
{specialCharValid ? "✓" : "✗"} Password should
|
||||||
contain special character
|
contain special character
|
||||||
</p>
|
</p>
|
||||||
{/* </>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="mt-2.5 ml-1 flex flex-row items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="op-checkbox op-checkbox-sm"
|
||||||
|
id="termsandcondition"
|
||||||
|
checked={isAuthorize}
|
||||||
|
onChange={(e) => setIsAuthorize(e.target.checked)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="text-xs cursor-pointer ml-1 mb-0"
|
||||||
|
htmlFor="termsandcondition"
|
||||||
|
>
|
||||||
|
I agree to the
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
className="underline cursor-pointer ml-1"
|
||||||
|
onClick={() =>
|
||||||
|
openInNewTab(
|
||||||
|
"https://www.opensignlabs.com/terms-and-conditions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Terms of Service
|
||||||
|
</span>
|
||||||
|
<span>.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow uppercase"
|
className="op-btn op-btn-primary"
|
||||||
disabled={state.loading}
|
disabled={state.loading}
|
||||||
>
|
>
|
||||||
{state.loading ? "Loading..." : "Register"}
|
{state.loading ? "Loading..." : "Register"}
|
||||||
</button>
|
</button>
|
||||||
<NavLink
|
<button
|
||||||
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
|
type="button"
|
||||||
to="/"
|
className="op-btn op-btn-secondary"
|
||||||
style={width < 768 ? { textAlign: "center" } : {}}
|
disabled={state.loading}
|
||||||
|
onClick={() =>
|
||||||
|
navigate(location.search ? "/" + location.search : "/")
|
||||||
|
}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</NavLink>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{width >= 768 && (
|
{width >= 768 && (
|
||||||
<div className="self-center">
|
<div className="self-center">
|
||||||
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
|
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
|
||||||
<img src={login_img} alt="bisec" width="100%" />
|
<img src={login_img} alt="loader" width="100%" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -642,30 +699,12 @@ const Signup = (props) => {
|
|||||||
<Alert type={state.alertType}>{state.alertMsg}</Alert>
|
<Alert type={state.alertType}>{state.alertMsg}</Alert>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="fixed w-full h-full flex justify-center items-center z-50">
|
||||||
style={{
|
<Loader />
|
||||||
position: "fixed",
|
</div>
|
||||||
fontSize: "50px",
|
|
||||||
color: "#3ac9d6",
|
|
||||||
top: "50%",
|
|
||||||
left: "45%"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) => {
|
export default Signup;
|
||||||
if (Object.keys(state.appInfo).length !== 0) {
|
|
||||||
return { appInfo: state.appInfo, isloginVisible: true };
|
|
||||||
} else {
|
|
||||||
return { appInfo: state.appInfo, isloginVisible: false };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
|
||||||
fetchAppInfo,
|
|
||||||
showTenantName
|
|
||||||
})(Signup);
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
import "../styles/signature.css";
|
import "../styles/signature.css";
|
||||||
import { themeColor } from "../constant/const";
|
import Parse from "parse";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import Confetti from "react-confetti";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Loader from "../primitives/LoaderWithMsg";
|
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||||
import loader from "../assets/images/loader2.gif";
|
|
||||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||||
import { DndProvider } from "react-dnd";
|
import { DndProvider } from "react-dnd";
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
@@ -26,7 +27,13 @@ import {
|
|||||||
contactBook,
|
contactBook,
|
||||||
randomId,
|
randomId,
|
||||||
getDate,
|
getDate,
|
||||||
textInputWidget
|
textWidget,
|
||||||
|
getTenantDetails,
|
||||||
|
checkIsSubscribed,
|
||||||
|
convertPdfArrayBuffer,
|
||||||
|
fetchImageBase64,
|
||||||
|
changeImageWH,
|
||||||
|
handleSendOTP
|
||||||
} from "../constant/Utils";
|
} from "../constant/Utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import Tour from "reactour";
|
import Tour from "reactour";
|
||||||
@@ -39,7 +46,8 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
|||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||||
|
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||||
function SignYourSelf() {
|
function SignYourSelf() {
|
||||||
const [pdfDetails, setPdfDetails] = useState([]);
|
const [pdfDetails, setPdfDetails] = useState([]);
|
||||||
@@ -62,12 +70,10 @@ function SignYourSelf() {
|
|||||||
const [dragKey, setDragKey] = useState();
|
const [dragKey, setDragKey] = useState();
|
||||||
const [signKey, setSignKey] = useState();
|
const [signKey, setSignKey] = useState();
|
||||||
const [imgWH, setImgWH] = useState({});
|
const [imgWH, setImgWH] = useState({});
|
||||||
const [isCeleb, setIsCeleb] = useState(false);
|
|
||||||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||||||
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
||||||
const [successEmail, setSuccessEmail] = useState(false);
|
const [successEmail, setSuccessEmail] = useState(false);
|
||||||
const imageRef = useRef(null);
|
const imageRef = useRef(null);
|
||||||
const [documentStatus, setDocumentStatus] = useState({ isCompleted: false });
|
|
||||||
const [myInitial, setMyInitial] = useState("");
|
const [myInitial, setMyInitial] = useState("");
|
||||||
const [isInitial, setIsInitial] = useState(false);
|
const [isInitial, setIsInitial] = useState(false);
|
||||||
const [isUiLoading, setIsUiLoading] = useState(false);
|
const [isUiLoading, setIsUiLoading] = useState(false);
|
||||||
@@ -87,10 +93,11 @@ function SignYourSelf() {
|
|||||||
const [containerWH, setContainerWH] = useState({});
|
const [containerWH, setContainerWH] = useState({});
|
||||||
const [isPageCopy, setIsPageCopy] = useState(false);
|
const [isPageCopy, setIsPageCopy] = useState(false);
|
||||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||||
|
const [otpLoader, setOtpLoader] = useState(false);
|
||||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||||
status: false
|
status: false
|
||||||
});
|
});
|
||||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
|
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||||
const [widgetType, setWidgetType] = useState("");
|
const [widgetType, setWidgetType] = useState("");
|
||||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||||
@@ -99,6 +106,14 @@ function SignYourSelf() {
|
|||||||
});
|
});
|
||||||
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||||||
const [isDontShow, setIsDontShow] = useState(false);
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
|
const [extUserId, setExtUserId] = useState("");
|
||||||
|
const [isCompleted, setIsCompleted] = useState(false);
|
||||||
|
const [isCelebration, setIsCelebration] = useState(false);
|
||||||
|
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||||
|
const [activeMailAdapter, setActiveMailAdapter] = useState("");
|
||||||
|
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||||
|
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||||
|
const [otp, setOtp] = useState("");
|
||||||
const divRef = useRef(null);
|
const divRef = useRef(null);
|
||||||
const nodeRef = useRef(null);
|
const nodeRef = useRef(null);
|
||||||
const [, drop] = useDrop({
|
const [, drop] = useDrop({
|
||||||
@@ -180,19 +195,29 @@ function SignYourSelf() {
|
|||||||
|
|
||||||
//function for get document details for perticular signer with signer'object id
|
//function for get document details for perticular signer with signer'object id
|
||||||
const getDocumentDetails = async (showComplete) => {
|
const getDocumentDetails = async (showComplete) => {
|
||||||
|
let isCompleted;
|
||||||
//getting document details
|
//getting document details
|
||||||
const documentData = await contractDocument(documentId);
|
const documentData = await contractDocument(documentId);
|
||||||
|
|
||||||
if (documentData && documentData.length > 0) {
|
if (documentData && documentData.length > 0) {
|
||||||
setPdfDetails(documentData);
|
setPdfDetails(documentData);
|
||||||
|
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||||
|
const url = documentData[0] && documentData[0]?.URL;
|
||||||
|
if (url) {
|
||||||
|
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||||||
|
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||||
|
if (arrayBuffer === "Error") {
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
} else {
|
||||||
|
setPdfArrayBuffer(arrayBuffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
}
|
||||||
|
|
||||||
const isCompleted =
|
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||||
documentData[0].IsCompleted && documentData[0].IsCompleted;
|
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
const docStatus = {
|
setIsCompleted(true);
|
||||||
isCompleted: isCompleted
|
|
||||||
};
|
|
||||||
setDocumentStatus(docStatus);
|
|
||||||
setPdfUrl(documentData[0].SignedUrl);
|
setPdfUrl(documentData[0].SignedUrl);
|
||||||
const alreadySign = {
|
const alreadySign = {
|
||||||
status: true,
|
status: true,
|
||||||
@@ -204,8 +229,12 @@ function SignYourSelf() {
|
|||||||
setIsUiLoading(false);
|
setIsUiLoading(false);
|
||||||
setIsSignPad(false);
|
setIsSignPad(false);
|
||||||
setIsEmail(true);
|
setIsEmail(true);
|
||||||
|
setIsCelebration(true);
|
||||||
setXyPostion([]);
|
setXyPostion([]);
|
||||||
setSignBtnPosition([]);
|
setSignBtnPosition([]);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsCelebration(false);
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -255,16 +284,20 @@ function SignYourSelf() {
|
|||||||
setHandleError("Error: Something went wrong!");
|
setHandleError("Error: Something went wrong!");
|
||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
});
|
});
|
||||||
|
|
||||||
const contractUsersRes = await contractUsers(jsonSender.email);
|
const contractUsersRes = await contractUsers(jsonSender.email);
|
||||||
|
if (contractUsersRes === "Error: Something went wrong!") {
|
||||||
if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
const loadObj = {
|
||||||
|
isLoad: false
|
||||||
|
};
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
setIsLoading(loadObj);
|
||||||
|
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
||||||
|
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
|
||||||
setContractName("_Users");
|
setContractName("_Users");
|
||||||
setSignerUserId(contractUsersRes[0].objectId);
|
setSignerUserId(contractUsersRes[0].objectId);
|
||||||
const tourstatuss =
|
const tourstatuss =
|
||||||
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
|
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
|
||||||
|
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
|
||||||
if (tourstatuss && tourstatuss.length > 0) {
|
|
||||||
setTourStatus(tourstatuss);
|
setTourStatus(tourstatuss);
|
||||||
const checkTourRecipients = tourstatuss.filter(
|
const checkTourRecipients = tourstatuss.filter(
|
||||||
(data) => data.signyourself
|
(data) => data.signyourself
|
||||||
@@ -272,17 +305,13 @@ function SignYourSelf() {
|
|||||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||||
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setCheckTourStatus(true);
|
||||||
}
|
}
|
||||||
const loadObj = {
|
const loadObj = {
|
||||||
isLoad: false
|
isLoad: false
|
||||||
};
|
};
|
||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
} else if (contractUsersRes === "Error: Something went wrong!") {
|
|
||||||
const loadObj = {
|
|
||||||
isLoad: false
|
|
||||||
};
|
|
||||||
setHandleError("Error: Something went wrong!");
|
|
||||||
setIsLoading(loadObj);
|
|
||||||
} else if (contractUsersRes.length === 0) {
|
} else if (contractUsersRes.length === 0) {
|
||||||
const contractContactBook = await contactBook(jsonSender.objectId);
|
const contractContactBook = await contactBook(jsonSender.objectId);
|
||||||
if (contractContactBook && contractContactBook.length > 0) {
|
if (contractContactBook && contractContactBook.length > 0) {
|
||||||
@@ -291,7 +320,8 @@ function SignYourSelf() {
|
|||||||
const tourstatuss =
|
const tourstatuss =
|
||||||
contractContactBook[0].TourStatus &&
|
contractContactBook[0].TourStatus &&
|
||||||
contractContactBook[0].TourStatus;
|
contractContactBook[0].TourStatus;
|
||||||
if (tourstatuss && tourstatuss.length > 0) {
|
|
||||||
|
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
|
||||||
setTourStatus(tourstatuss);
|
setTourStatus(tourstatuss);
|
||||||
const checkTourRecipients = tourstatuss.filter(
|
const checkTourRecipients = tourstatuss.filter(
|
||||||
(data) => data.signyourself
|
(data) => data.signyourself
|
||||||
@@ -299,6 +329,8 @@ function SignYourSelf() {
|
|||||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||||
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setCheckTourStatus(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setHandleError("No Data Found!");
|
setHandleError("No Data Found!");
|
||||||
@@ -342,8 +374,7 @@ function SignYourSelf() {
|
|||||||
return {
|
return {
|
||||||
name: "checkbox"
|
name: "checkbox"
|
||||||
};
|
};
|
||||||
|
case textWidget:
|
||||||
case textInputWidget:
|
|
||||||
return {
|
return {
|
||||||
name: "text"
|
name: "text"
|
||||||
};
|
};
|
||||||
@@ -514,89 +545,213 @@ function SignYourSelf() {
|
|||||||
setSignKey(key);
|
setSignKey(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//`handleResend` function is used to resend otp for email verification
|
||||||
|
const handleResend = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
setOtpLoader(false);
|
||||||
|
alert("OTP sent on you email");
|
||||||
|
};
|
||||||
|
//`handleVerifyEmail` function is used to verify email with otp
|
||||||
|
const handleVerifyEmail = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
try {
|
||||||
|
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||||
|
otp: otp,
|
||||||
|
email: Parse.User.current().getEmail()
|
||||||
|
});
|
||||||
|
if (resEmail?.message === "Email is verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
} else if (resEmail?.message === "Email is already verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
}
|
||||||
|
setOtp("");
|
||||||
|
alert(resEmail.message);
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
// handleRecipientSign();
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
} finally {
|
||||||
|
setOtpLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//`handleVerifyBtn` function is used to send otp on user mail
|
||||||
|
const handleVerifyBtn = async () => {
|
||||||
|
setIsVerifyModal(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
};
|
||||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||||
async function embedWidgetsData() {
|
async function embedWidgetsData() {
|
||||||
let showAlert = false;
|
//check current user email is verified or not
|
||||||
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
for (let i = 0; i < xyPostion?.length; i++) {
|
let isEmailVerified;
|
||||||
const requiredWidgets = xyPostion[i].pos.filter(
|
isEmailVerified = currentUser?.emailVerified;
|
||||||
(position) => position.type !== "checkbox"
|
if (isEmailVerified) {
|
||||||
);
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (requiredWidgets && requiredWidgets?.length > 0) {
|
} else {
|
||||||
let checkSigned;
|
try {
|
||||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
if (!checkSigned) {
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
});
|
||||||
|
if (user) {
|
||||||
let checkDefaultSigned = requiredWidgets[i]?.options?.defaultValue;
|
isEmailVerified = user?.get("emailVerified");
|
||||||
if (!checkSignUrl) {
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (!checkDefaultSigned) {
|
}
|
||||||
if (!showAlert) {
|
} catch (e) {
|
||||||
showAlert = true;
|
setHandleError("Error: Something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isEmailVerified) {
|
||||||
|
let showAlert = false,
|
||||||
|
isSignatureExist = false;
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < xyPostion?.length; i++) {
|
||||||
|
const requiredWidgets = xyPostion[i].pos.filter(
|
||||||
|
(position) => position.type !== "checkbox"
|
||||||
|
);
|
||||||
|
if (requiredWidgets && requiredWidgets?.length > 0) {
|
||||||
|
let checkSigned;
|
||||||
|
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||||
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
|
if (!checkSigned) {
|
||||||
|
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||||
|
let checkDefaultSigned =
|
||||||
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
|
if (!checkSignUrl) {
|
||||||
|
if (!checkDefaultSigned) {
|
||||||
|
if (!showAlert) {
|
||||||
|
showAlert = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//condition to check exist signature widget or not
|
||||||
|
if (!isSignatureExist) {
|
||||||
|
isSignatureExist = xyPostion[i].pos.some(
|
||||||
|
(data) => data?.type === "signature"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (xyPostion.length === 0 || !isSignatureExist) {
|
||||||
|
setIsAlert({
|
||||||
|
header: "Fields required",
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure there's at least one signature widget added"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (showAlert) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure all field is accurately filled and meets all requirements."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setIsUiLoading(true);
|
||||||
|
const existingPdfBytes = pdfArrayBuffer;
|
||||||
|
// Load a PDFDocument from the existing PDF bytes
|
||||||
|
try {
|
||||||
|
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||||
|
const isSignYourSelfFlow = true;
|
||||||
|
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||||
|
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||||
|
//embed document's object id to all pages in pdf document
|
||||||
|
if (!HeaderDocId) {
|
||||||
|
await embedDocId(pdfDoc, documentId, allPages);
|
||||||
|
}
|
||||||
|
//embed multi signature in pdf
|
||||||
|
const pdfBytes = await multiSignEmbed(
|
||||||
|
xyPostion,
|
||||||
|
pdfDoc,
|
||||||
|
pdfOriginalWidth,
|
||||||
|
isSignYourSelfFlow,
|
||||||
|
containerWH
|
||||||
|
);
|
||||||
|
// console.log("pdf", pdfBytes);
|
||||||
|
//function for call to embed signature in pdf and get digital signature pdf
|
||||||
|
await signPdfFun(pdfBytes, documentId);
|
||||||
|
} catch (err) {
|
||||||
|
setIsUiLoading(false);
|
||||||
|
if (err && err.message.includes("is encrypted.")) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Currently encrypted pdf files are not supported.`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("err in signing", err);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Something went wrong.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in embedselfsign ", err);
|
||||||
|
setIsUiLoading(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong, please try again later."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xyPostion.length === 0) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else if (showAlert) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setIsCeleb(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsCeleb(false);
|
|
||||||
}, 3000);
|
|
||||||
setIsUiLoading(true);
|
|
||||||
const url = pdfDetails[0] && pdfDetails[0].URL;
|
|
||||||
|
|
||||||
const existingPdfBytes = await fetch(url).then((res) =>
|
|
||||||
res.arrayBuffer()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Load a PDFDocument from the existing PDF bytes
|
|
||||||
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
|
|
||||||
ignoreEncryption: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const flag = true;
|
|
||||||
|
|
||||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
|
||||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
|
||||||
|
|
||||||
//embed document's object id to all pages in pdf document
|
|
||||||
if (!HeaderDocId) {
|
|
||||||
await embedDocId(pdfDoc, documentId, allPages);
|
|
||||||
}
|
|
||||||
//embed multi signature in pdf
|
|
||||||
const pdfBytes = await multiSignEmbed(
|
|
||||||
xyPostion,
|
|
||||||
pdfDoc,
|
|
||||||
pdfOriginalWidth,
|
|
||||||
flag,
|
|
||||||
containerWH
|
|
||||||
);
|
|
||||||
// console.log('pdf',pdfBytes)
|
|
||||||
//function for call to embed signature in pdf and get digital signature pdf
|
|
||||||
await signPdfFun(pdfBytes, documentId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//function for get digital signature
|
//function for get digital signature
|
||||||
const signPdfFun = async (base64Url, documentId) => {
|
const signPdfFun = async (base64Url, documentId) => {
|
||||||
|
let isCustomCompletionMail = false;
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
const tenantDetails = await getTenantDetails(jsonSender.objectId);
|
||||||
|
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||||
|
alert("User does not exist");
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
tenantDetails?.CompletionBody &&
|
||||||
|
tenantDetails?.CompletionSubject &&
|
||||||
|
(!isEnableSubscription || getIsSubscribe)
|
||||||
|
) {
|
||||||
|
isCustomCompletionMail = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// below for loop is used to get first signature of user to send if to signpdf
|
||||||
|
// for adding it in completion certificate
|
||||||
|
let getSignature;
|
||||||
|
for (let item of xyPostion) {
|
||||||
|
if (!getSignature) {
|
||||||
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let base64Sign = getSignature.SignUrl;
|
||||||
|
//check https type signature (default signature exist) then convert in base64
|
||||||
|
const isUrl = base64Sign.includes("https");
|
||||||
|
if (isUrl) {
|
||||||
|
try {
|
||||||
|
base64Sign = await fetchImageBase64(base64Sign);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//change image width and height to 104/44 in png base64
|
||||||
|
const getNewse64 = await changeImageWH(base64Sign);
|
||||||
|
//remove suffiix of base64
|
||||||
|
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||||
|
|
||||||
let singleSign = {
|
let singleSign = {
|
||||||
pdfFile: base64Url,
|
pdfFile: base64Url,
|
||||||
docId: documentId
|
docId: documentId,
|
||||||
|
isCustomCompletionMail: isCustomCompletionMail,
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
signature: suffixbase64
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios
|
await axios
|
||||||
@@ -604,7 +759,8 @@ function SignYourSelf() {
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
// sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((Listdata) => {
|
.then((Listdata) => {
|
||||||
@@ -705,7 +861,7 @@ function SignYourSelf() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//function for save button to save signature or image url
|
//function for save button to save signature or image url
|
||||||
const saveSign = (isDefaultSign, width, height) => {
|
const saveSign = (type, isDefaultSign, width, height) => {
|
||||||
const isTypeText = width && height ? true : false;
|
const isTypeText = width && height ? true : false;
|
||||||
const signatureImg = isDefaultSign
|
const signatureImg = isDefaultSign
|
||||||
? isDefaultSign === "initials"
|
? isDefaultSign === "initials"
|
||||||
@@ -728,6 +884,7 @@ function SignYourSelf() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const getUpdatePosition = onSaveSign(
|
const getUpdatePosition = onSaveSign(
|
||||||
|
type,
|
||||||
xyPostion,
|
xyPostion,
|
||||||
index,
|
index,
|
||||||
signKey,
|
signKey,
|
||||||
@@ -764,6 +921,7 @@ function SignYourSelf() {
|
|||||||
|
|
||||||
//function for delete signature block
|
//function for delete signature block
|
||||||
const handleDeleteSign = (key) => {
|
const handleDeleteSign = (key) => {
|
||||||
|
setCurrWidgetsDetails({});
|
||||||
const updateResizeData = [];
|
const updateResizeData = [];
|
||||||
|
|
||||||
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
|
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
|
||||||
@@ -934,38 +1092,41 @@ function SignYourSelf() {
|
|||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<Title title={"Self Sign"} />
|
<Title title={"Self Sign"} />
|
||||||
{isLoading.isLoad ? (
|
{isLoading.isLoad ? (
|
||||||
<Loader isLoading={isLoading} />
|
<LoaderWithMsg isLoading={isLoading} />
|
||||||
) : handleError ? (
|
) : handleError ? (
|
||||||
<HandleError handleError={handleError} />
|
<HandleError handleError={handleError} />
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{isUiLoading && (
|
{isUiLoading && (
|
||||||
<div
|
<div className="absolute h-[100vh] w-full z-[999] flex flex-col justify-center items-start bg-[#e6f2f2] bg-opacity-80">
|
||||||
style={{
|
<Loader />
|
||||||
position: "absolute",
|
<span className="font-bold text-[13px]">
|
||||||
height: "100vh",
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
flexDirection: "column",
|
|
||||||
alignItems: "center",
|
|
||||||
zIndex: "999",
|
|
||||||
backgroundColor: "#e6f2f2",
|
|
||||||
opacity: 0.8
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "100px", height: "100px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "13px", fontWeight: "bold" }}>
|
|
||||||
This might take some time
|
This might take some time
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isCelebration && (
|
||||||
|
<div className="relative z-[999]">
|
||||||
|
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="signatureContainer" ref={divRef}>
|
<div
|
||||||
|
className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"
|
||||||
|
ref={divRef}
|
||||||
|
>
|
||||||
|
{!isEmailVerified && (
|
||||||
|
<VerifyEmail
|
||||||
|
isVerifyModal={isVerifyModal}
|
||||||
|
setIsVerifyModal={setIsVerifyModal}
|
||||||
|
handleVerifyEmail={handleVerifyEmail}
|
||||||
|
setOtp={setOtp}
|
||||||
|
otp={otp}
|
||||||
|
otpLoader={otpLoader}
|
||||||
|
handleVerifyBtn={handleVerifyBtn}
|
||||||
|
handleResend={handleResend}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* this component used for UI interaction and show their functionality */}
|
{/* this component used for UI interaction and show their functionality */}
|
||||||
{pdfLoadFail && !checkTourStatus && (
|
{pdfLoadFail && !checkTourStatus && (
|
||||||
<Tour
|
<Tour
|
||||||
@@ -980,8 +1141,9 @@ function SignYourSelf() {
|
|||||||
{/* this component used to render all pdf pages in left side */}
|
{/* this component used to render all pdf pages in left side */}
|
||||||
|
|
||||||
<RenderAllPdfPage
|
<RenderAllPdfPage
|
||||||
pdfUrl={pdfUrl}
|
signPdfUrl={
|
||||||
signPdfUrl={pdfDetails[0] && pdfDetails[0].URL}
|
pdfDetails[0] && (pdfDetails[0].SignedUrl || pdfDetails[0].URL)
|
||||||
|
}
|
||||||
allPages={allPages}
|
allPages={allPages}
|
||||||
setAllPages={setAllPages}
|
setAllPages={setAllPages}
|
||||||
setPageNumber={setPageNumber}
|
setPageNumber={setPageNumber}
|
||||||
@@ -997,9 +1159,8 @@ function SignYourSelf() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
|
||||||
isOpen={isAlert.isShow}
|
isOpen={isAlert.isShow}
|
||||||
title={"Alert"}
|
title={isAlert?.header || "Alert"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setIsAlert({
|
setIsAlert({
|
||||||
isShow: false,
|
isShow: false,
|
||||||
@@ -1007,55 +1168,24 @@ function SignYourSelf() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="p-[20px] h-full">
|
||||||
<p>{isAlert.alertMessage}</p>
|
<p>{isAlert.alertMessage}</p>
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: false,
|
|
||||||
alertMessage: ""
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="finishBtn cancelBtn"
|
|
||||||
>
|
|
||||||
Ok
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
|
|
||||||
{/* this modal is used show this document is already sign */}
|
{/* this modal is used show this document is already sign */}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={showAlreadySignDoc.status}
|
isOpen={showAlreadySignDoc.status}
|
||||||
title={"Sign Documents"}
|
title={"Document signed"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setShowAlreadySignDoc({ status: false });
|
setShowAlreadySignDoc({ status: false });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="p-[20px] h-full">
|
||||||
<p>{showAlreadySignDoc.mssg}</p>
|
<p>{showAlreadySignDoc.mssg}</p>
|
||||||
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
<button
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost shadow-md"
|
||||||
onClick={() => setShowAlreadySignDoc({ status: false })}
|
onClick={() => setShowAlreadySignDoc({ status: false })}
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
@@ -1102,17 +1232,20 @@ function SignYourSelf() {
|
|||||||
setIsInitial={setIsInitial}
|
setIsInitial={setIsInitial}
|
||||||
setIsStamp={setIsStamp}
|
setIsStamp={setIsStamp}
|
||||||
widgetType={widgetType}
|
widgetType={widgetType}
|
||||||
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
/>
|
/>
|
||||||
{/*render email component to send email after finish signature on document */}
|
{/*render email component to send email after finish signature on document */}
|
||||||
<EmailComponent
|
<EmailComponent
|
||||||
isEmail={isEmail}
|
isEmail={isEmail}
|
||||||
pdfUrl={pdfUrl}
|
pdfUrl={pdfUrl}
|
||||||
isCeleb={isCeleb}
|
|
||||||
setIsEmail={setIsEmail}
|
setIsEmail={setIsEmail}
|
||||||
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
||||||
setSuccessEmail={setSuccessEmail}
|
setSuccessEmail={setSuccessEmail}
|
||||||
sender={jsonSender}
|
sender={jsonSender}
|
||||||
setIsAlert={setIsAlert}
|
setIsAlert={setIsAlert}
|
||||||
|
extUserId={extUserId}
|
||||||
|
activeMailAdapter={activeMailAdapter}
|
||||||
/>
|
/>
|
||||||
{/* pdf header which contain funish back button */}
|
{/* pdf header which contain funish back button */}
|
||||||
<Header
|
<Header
|
||||||
@@ -1120,7 +1253,6 @@ function SignYourSelf() {
|
|||||||
allPages={allPages}
|
allPages={allPages}
|
||||||
changePage={changePage}
|
changePage={changePage}
|
||||||
pdfUrl={pdfUrl}
|
pdfUrl={pdfUrl}
|
||||||
documentStatus={documentStatus}
|
|
||||||
embedWidgetsData={embedWidgetsData}
|
embedWidgetsData={embedWidgetsData}
|
||||||
pdfDetails={pdfDetails}
|
pdfDetails={pdfDetails}
|
||||||
isShowHeader={true}
|
isShowHeader={true}
|
||||||
@@ -1128,6 +1260,7 @@ function SignYourSelf() {
|
|||||||
alreadySign={pdfUrl ? true : false}
|
alreadySign={pdfUrl ? true : false}
|
||||||
isSignYourself={true}
|
isSignYourself={true}
|
||||||
setIsEmail={setIsEmail}
|
setIsEmail={setIsEmail}
|
||||||
|
isCompleted={isCompleted}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div data-tut="reactourSecond">
|
<div data-tut="reactourSecond">
|
||||||
@@ -1178,9 +1311,9 @@ function SignYourSelf() {
|
|||||||
maxHeight: window.innerHeight - 70 + "px",
|
maxHeight: window.innerHeight - 70 + "px",
|
||||||
backgroundColor: "white"
|
backgroundColor: "white"
|
||||||
}}
|
}}
|
||||||
className="autoSignScroll"
|
className="overflow-y-auto hide-scrollbar"
|
||||||
>
|
>
|
||||||
{!documentStatus.isCompleted ? (
|
{!isCompleted ? (
|
||||||
<div>
|
<div>
|
||||||
<WidgetComponent
|
<WidgetComponent
|
||||||
dataTut="reactourFirst"
|
dataTut="reactourFirst"
|
||||||
@@ -1190,7 +1323,6 @@ function SignYourSelf() {
|
|||||||
handleDivClick={handleDivClick}
|
handleDivClick={handleDivClick}
|
||||||
handleMouseLeave={handleMouseLeave}
|
handleMouseLeave={handleMouseLeave}
|
||||||
isDragSign={isDragSign}
|
isDragSign={isDragSign}
|
||||||
themeColor={themeColor}
|
|
||||||
dragStamp={dragStamp}
|
dragStamp={dragStamp}
|
||||||
dragRef={dragRef}
|
dragRef={dragRef}
|
||||||
isDragStamp={isDragStamp}
|
isDragStamp={isDragStamp}
|
||||||
@@ -1211,31 +1343,22 @@ function SignYourSelf() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
|
||||||
isOpen={validateAlert}
|
isOpen={validateAlert}
|
||||||
title={"Validation alert"}
|
title={"Validation alert"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setValidateAlert(false);
|
setValidateAlert(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="p-[20px] h-full">
|
||||||
<p>
|
<p>
|
||||||
The input does not meet the criteria set by the regular expression.
|
The input does not meet the criteria set by the regular expression.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setValidateAlert(false)}
|
onClick={() => setValidateAlert(false)}
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-ghost shadow-md"
|
||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
|||||||
import { useParams, useNavigate } from "react-router-dom";
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import "../styles/signature.css";
|
import "../styles/signature.css";
|
||||||
import { themeColor } from "../constant/const";
|
import { isEnableSubscription } from "../constant/const";
|
||||||
import { DndProvider } from "react-dnd";
|
import { DndProvider } from "react-dnd";
|
||||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||||
import { useDrag, useDrop } from "react-dnd";
|
import { useDrag, useDrop } from "react-dnd";
|
||||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||||
import Tour from "reactour";
|
import Tour from "reactour";
|
||||||
import Loader from "../primitives/LoaderWithMsg";
|
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||||
import HandleError from "../primitives/HandleError";
|
import HandleError from "../primitives/HandleError";
|
||||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||||
import Header from "../components/pdf/PdfHeader";
|
import Header from "../components/pdf/PdfHeader";
|
||||||
@@ -23,7 +23,8 @@ import {
|
|||||||
defaultWidthHeight,
|
defaultWidthHeight,
|
||||||
addWidgetOptions,
|
addWidgetOptions,
|
||||||
textInputWidget,
|
textInputWidget,
|
||||||
radioButtonWidget
|
radioButtonWidget,
|
||||||
|
fetchSubscription
|
||||||
} from "../constant/Utils";
|
} from "../constant/Utils";
|
||||||
import RenderPdf from "../components/pdf/RenderPdf";
|
import RenderPdf from "../components/pdf/RenderPdf";
|
||||||
import "../styles/AddUser.css";
|
import "../styles/AddUser.css";
|
||||||
@@ -35,6 +36,7 @@ import AddRoleModal from "../components/pdf/AddRoleModal";
|
|||||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||||
|
import Parse from "parse";
|
||||||
const TemplatePlaceholder = () => {
|
const TemplatePlaceholder = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { templateId } = useParams();
|
const { templateId } = useParams();
|
||||||
@@ -52,6 +54,7 @@ const TemplatePlaceholder = () => {
|
|||||||
const [isSelectListId, setIsSelectId] = useState();
|
const [isSelectListId, setIsSelectId] = useState();
|
||||||
const [isSendAlert, setIsSendAlert] = useState(false);
|
const [isSendAlert, setIsSendAlert] = useState(false);
|
||||||
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
|
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState({
|
const [isLoading, setIsLoading] = useState({
|
||||||
isLoad: true,
|
isLoad: true,
|
||||||
message: "This might take some time"
|
message: "This might take some time"
|
||||||
@@ -77,6 +80,7 @@ const TemplatePlaceholder = () => {
|
|||||||
const [showDropdown, setShowDropdown] = useState(false);
|
const [showDropdown, setShowDropdown] = useState(false);
|
||||||
const [widgetType, setWidgetType] = useState("");
|
const [widgetType, setWidgetType] = useState("");
|
||||||
const [isRadio, setIsRadio] = useState(false);
|
const [isRadio, setIsRadio] = useState(false);
|
||||||
|
const [blockColor, setBlockColor] = useState("");
|
||||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||||
const [isNameModal, setIsNameModal] = useState(false);
|
const [isNameModal, setIsNameModal] = useState(false);
|
||||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||||
@@ -142,6 +146,7 @@ const TemplatePlaceholder = () => {
|
|||||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
|
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
|
||||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||||
const [widgetName, setWidgetName] = useState(false);
|
const [widgetName, setWidgetName] = useState(false);
|
||||||
|
const [isAddRole, setIsAddRole] = useState(false);
|
||||||
const senderUser =
|
const senderUser =
|
||||||
localStorage.getItem(
|
localStorage.getItem(
|
||||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||||
@@ -167,7 +172,23 @@ const TemplatePlaceholder = () => {
|
|||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [divRef.current]);
|
}, [divRef.current]);
|
||||||
|
async function checkIsSubscribed() {
|
||||||
|
const res = await fetchSubscription();
|
||||||
|
const freeplan = res.plan;
|
||||||
|
const billingDate = res.billingDate;
|
||||||
|
if (freeplan === "freeplan") {
|
||||||
|
return true;
|
||||||
|
} else if (billingDate) {
|
||||||
|
if (new Date(billingDate) > new Date()) {
|
||||||
|
setIsSubscribe(true);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`);
|
||||||
|
}
|
||||||
|
}
|
||||||
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
||||||
const fetchTemplate = async () => {
|
const fetchTemplate = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -189,6 +210,9 @@ const TemplatePlaceholder = () => {
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (documentData && documentData.length > 0) {
|
if (documentData && documentData.length > 0) {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
|
||||||
|
}
|
||||||
setPdfDetails(documentData);
|
setPdfDetails(documentData);
|
||||||
setIsSigners(true);
|
setIsSigners(true);
|
||||||
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
|
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
|
||||||
@@ -223,6 +247,7 @@ const TemplatePlaceholder = () => {
|
|||||||
});
|
});
|
||||||
setSignersData(updatedSigners);
|
setSignersData(updatedSigners);
|
||||||
setUniqueId(updatedSigners[0].Id);
|
setUniqueId(updatedSigners[0].Id);
|
||||||
|
setBlockColor(updatedSigners[0].blockColor);
|
||||||
} else {
|
} else {
|
||||||
const updatedSigners = documentData[0].Signers.map((x, index) => ({
|
const updatedSigners = documentData[0].Signers.map((x, index) => ({
|
||||||
...x,
|
...x,
|
||||||
@@ -231,6 +256,7 @@ const TemplatePlaceholder = () => {
|
|||||||
}));
|
}));
|
||||||
setSignersData(updatedSigners);
|
setSignersData(updatedSigners);
|
||||||
setUniqueId(updatedSigners[0].Id);
|
setUniqueId(updatedSigners[0].Id);
|
||||||
|
setBlockColor(updatedSigners[0].blockColor);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setRoleName("User 1");
|
setRoleName("User 1");
|
||||||
@@ -249,6 +275,7 @@ const TemplatePlaceholder = () => {
|
|||||||
setUniqueId(updatedSigners[0].Id);
|
setUniqueId(updatedSigners[0].Id);
|
||||||
setSignersData(updatedSigners);
|
setSignersData(updatedSigners);
|
||||||
setIsSelectId(0);
|
setIsSelectId(0);
|
||||||
|
setBlockColor(updatedSigners[0].blockColor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -283,10 +310,10 @@ const TemplatePlaceholder = () => {
|
|||||||
if (tourstatus && tourstatus.length > 0) {
|
if (tourstatus && tourstatus.length > 0) {
|
||||||
setTourStatus(tourstatus);
|
setTourStatus(tourstatus);
|
||||||
const checkTourRecipients = tourstatus.filter(
|
const checkTourRecipients = tourstatus.filter(
|
||||||
(data) => data.templatetour
|
(data) => data.templateTour
|
||||||
);
|
);
|
||||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||||
setCheckTourStatus(checkTourRecipients[0].templatetour);
|
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const loadObj = {
|
const loadObj = {
|
||||||
@@ -446,7 +473,7 @@ const TemplatePlaceholder = () => {
|
|||||||
setIsCheckbox(true);
|
setIsCheckbox(true);
|
||||||
} else if (dragTypeValue === radioButtonWidget) {
|
} else if (dragTypeValue === radioButtonWidget) {
|
||||||
setIsRadio(true);
|
setIsRadio(true);
|
||||||
}
|
}
|
||||||
setCurrWidgetsDetails({});
|
setCurrWidgetsDetails({});
|
||||||
setWidgetType(dragTypeValue);
|
setWidgetType(dragTypeValue);
|
||||||
setSignKey(key);
|
setSignKey(key);
|
||||||
@@ -456,9 +483,20 @@ const TemplatePlaceholder = () => {
|
|||||||
} else {
|
} else {
|
||||||
setIsReceipent(false);
|
setIsReceipent(false);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setIsAddRole(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tourAddRole = [
|
||||||
|
{
|
||||||
|
selector: '[data-tut="reactourAddbtn"]',
|
||||||
|
content: "You need to add a role before you can add fields for it. ",
|
||||||
|
position: "top",
|
||||||
|
style: { fontSize: "13px" }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
//function for get pdf page details
|
//function for get pdf page details
|
||||||
const pageDetails = async (pdf) => {
|
const pageDetails = async (pdf) => {
|
||||||
const load = {
|
const load = {
|
||||||
@@ -667,35 +705,29 @@ const TemplatePlaceholder = () => {
|
|||||||
Name: pdfDetails[0]?.Name || "",
|
Name: pdfDetails[0]?.Name || "",
|
||||||
Note: pdfDetails[0]?.Note || "",
|
Note: pdfDetails[0]?.Note || "",
|
||||||
Description: pdfDetails[0]?.Description || "",
|
Description: pdfDetails[0]?.Description || "",
|
||||||
SendinOrder: pdfDetails[0]?.SendinOrder || false
|
SendinOrder: pdfDetails[0]?.SendinOrder || false,
|
||||||
|
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
|
||||||
|
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||||
|
NextReminderDate: pdfDetails[0]?.NextReminderDate
|
||||||
};
|
};
|
||||||
|
const updateTemplate = new Parse.Object("contracts_Template");
|
||||||
|
updateTemplate.id = templateId;
|
||||||
|
for (const key in data) {
|
||||||
|
updateTemplate.set(key, data[key]);
|
||||||
|
}
|
||||||
|
await updateTemplate.save(null, {
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
});
|
||||||
|
|
||||||
await axios
|
setIsCreateDocModal(true);
|
||||||
.put(
|
setIsMailSend(true);
|
||||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
const loadObj = {
|
||||||
"_appName"
|
isLoad: false
|
||||||
)}_Template/${templateId}`,
|
};
|
||||||
data,
|
setIsLoading(loadObj);
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
setIsCreateDocModal(true);
|
|
||||||
setIsMailSend(true);
|
|
||||||
const loadObj = {
|
|
||||||
isLoad: false
|
|
||||||
};
|
|
||||||
setIsLoading(loadObj);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setIsLoading(false);
|
||||||
|
alert("Something went wrong, please try again later.");
|
||||||
console.log("error", e);
|
console.log("error", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -737,7 +769,7 @@ const TemplatePlaceholder = () => {
|
|||||||
selector: '[data-tut="reactourSecond"]',
|
selector: '[data-tut="reactourSecond"]',
|
||||||
content: () => (
|
content: () => (
|
||||||
<TourContentWithBtn
|
<TourContentWithBtn
|
||||||
message={`Drag a widget placeholder onto the PDF to choose your desired signing location.`}
|
message={`Drag or click on a field to add it to the document.`}
|
||||||
isChecked={handleDontShow}
|
isChecked={handleDontShow}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -759,7 +791,7 @@ const TemplatePlaceholder = () => {
|
|||||||
selector: '[data-tut="reactourFour"]',
|
selector: '[data-tut="reactourFour"]',
|
||||||
content: () => (
|
content: () => (
|
||||||
<TourContentWithBtn
|
<TourContentWithBtn
|
||||||
message={`Clicking "Save" button will save the template and will ask you if you want to create a new document using this template.`}
|
message={`Clicking ‘Save’ will store the current template. After saving, you’ll be prompted to create a new document from this template if you wish.`}
|
||||||
isChecked={handleDontShow}
|
isChecked={handleDontShow}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@@ -777,15 +809,15 @@ const TemplatePlaceholder = () => {
|
|||||||
if (tourStatus.length > 0) {
|
if (tourStatus.length > 0) {
|
||||||
updatedTourStatus = [...tourStatus];
|
updatedTourStatus = [...tourStatus];
|
||||||
const templatetourIndex = tourStatus.findIndex(
|
const templatetourIndex = tourStatus.findIndex(
|
||||||
(obj) => obj["templatetour"] === false || obj["templatetour"] === true
|
(obj) => obj["templateTour"] === false || obj["templateTour"] === true
|
||||||
);
|
);
|
||||||
if (templatetourIndex !== -1) {
|
if (templatetourIndex !== -1) {
|
||||||
updatedTourStatus[templatetourIndex] = { templatetour: true };
|
updatedTourStatus[templatetourIndex] = { templateTour: true };
|
||||||
} else {
|
} else {
|
||||||
updatedTourStatus.push({ templatetour: true });
|
updatedTourStatus.push({ templateTour: true });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedTourStatus = [{ templatetour: true }];
|
updatedTourStatus = [{ templateTour: true }];
|
||||||
}
|
}
|
||||||
await axios
|
await axios
|
||||||
.put(
|
.put(
|
||||||
@@ -854,6 +886,7 @@ const TemplatePlaceholder = () => {
|
|||||||
setIsModalRole(false);
|
setIsModalRole(false);
|
||||||
setRoleName("");
|
setRoleName("");
|
||||||
setUniqueId(Id);
|
setUniqueId(Id);
|
||||||
|
setBlockColor(color[index]);
|
||||||
setIsSelectId(index);
|
setIsSelectId(index);
|
||||||
setIsMailSend(false);
|
setIsMailSend(false);
|
||||||
};
|
};
|
||||||
@@ -870,9 +903,11 @@ const TemplatePlaceholder = () => {
|
|||||||
if (index === signersdata.length - 1) {
|
if (index === signersdata.length - 1) {
|
||||||
setUniqueId(updateSigner[updateSigner.length - 1]?.Id || "");
|
setUniqueId(updateSigner[updateSigner.length - 1]?.Id || "");
|
||||||
setIsSelectId(index - 1 || 0);
|
setIsSelectId(index - 1 || 0);
|
||||||
|
setBlockColor(color[index - 1 || 0]);
|
||||||
} else {
|
} else {
|
||||||
setUniqueId(updateSigner[index]?.Id || "");
|
setUniqueId(updateSigner[index]?.Id || "");
|
||||||
setIsSelectId(index);
|
setIsSelectId(index);
|
||||||
|
setBlockColor(color[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSignerPos(updatePlaceholderUser);
|
setSignerPos(updatePlaceholderUser);
|
||||||
@@ -1092,8 +1127,6 @@ const TemplatePlaceholder = () => {
|
|||||||
inputype = options.includes(defaultdata.textvalidate)
|
inputype = options.includes(defaultdata.textvalidate)
|
||||||
? defaultdata.textvalidate
|
? defaultdata.textvalidate
|
||||||
: "regex";
|
: "regex";
|
||||||
} else {
|
|
||||||
inputype = "text";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
|
const filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
|
||||||
@@ -1118,11 +1151,14 @@ const TemplatePlaceholder = () => {
|
|||||||
status: defaultdata?.status || "required",
|
status: defaultdata?.status || "required",
|
||||||
hint: defaultdata?.hint || "",
|
hint: defaultdata?.hint || "",
|
||||||
defaultValue: defaultdata?.defaultValue || "",
|
defaultValue: defaultdata?.defaultValue || "",
|
||||||
validation: {
|
validation:
|
||||||
type: inputype,
|
(isSubscribe || !isEnableSubscription) && inputype
|
||||||
pattern:
|
? {
|
||||||
inputype === "regex" ? defaultdata.textvalidate : ""
|
type: inputype,
|
||||||
}
|
pattern:
|
||||||
|
inputype === "regex" ? defaultdata.textvalidate : ""
|
||||||
|
}
|
||||||
|
: {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@@ -1171,11 +1207,14 @@ const TemplatePlaceholder = () => {
|
|||||||
<Title title={"Template"} />
|
<Title title={"Template"} />
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
{isLoading.isLoad ? (
|
{isLoading.isLoad ? (
|
||||||
<Loader isLoading={isLoading} />
|
<LoaderWithMsg isLoading={isLoading} />
|
||||||
) : handleError ? (
|
) : handleError ? (
|
||||||
<HandleError handleError={handleError} />
|
<HandleError handleError={handleError} />
|
||||||
) : (
|
) : (
|
||||||
<div className="signatureContainer" ref={divRef}>
|
<div
|
||||||
|
className="op-card overflow-hidden flex flex-row justify-between bg-base-300 relative"
|
||||||
|
ref={divRef}
|
||||||
|
>
|
||||||
{/* this component used for UI interaction and show their functionality */}
|
{/* this component used for UI interaction and show their functionality */}
|
||||||
{!checkTourStatus && (
|
{!checkTourStatus && (
|
||||||
//this tour component used in your html component where you want to put
|
//this tour component used in your html component where you want to put
|
||||||
@@ -1190,6 +1229,15 @@ const TemplatePlaceholder = () => {
|
|||||||
closeWithMask={false}
|
closeWithMask={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isAddRole && (
|
||||||
|
<Tour
|
||||||
|
onRequestClose={() => setIsAddRole(false)}
|
||||||
|
steps={tourAddRole}
|
||||||
|
isOpen={isAddRole}
|
||||||
|
rounded={5}
|
||||||
|
closeWithMask={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* this component used to render all pdf pages in left side */}
|
{/* this component used to render all pdf pages in left side */}
|
||||||
<RenderAllPdfPage
|
<RenderAllPdfPage
|
||||||
signPdfUrl={pdfDetails[0].URL}
|
signPdfUrl={pdfDetails[0].URL}
|
||||||
@@ -1209,23 +1257,21 @@ const TemplatePlaceholder = () => {
|
|||||||
>
|
>
|
||||||
{/* this modal is used show alert set placeholder for all signers before send mail */}
|
{/* this modal is used show alert set placeholder for all signers before send mail */}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
|
||||||
isOpen={isSendAlert}
|
isOpen={isSendAlert}
|
||||||
title={"Fields required"}
|
title={"Fields required"}
|
||||||
handleClose={() => setIsSendAlert(false)}
|
handleClose={() => setIsSendAlert(false)}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px]">
|
||||||
<p>Please add field for all recipients.</p>
|
<p>Please add at least one signature field for all roles.</p>
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
|
||||||
isOpen={!IsReceipent}
|
isOpen={!IsReceipent}
|
||||||
title={"Roles"}
|
title={"Roles"}
|
||||||
handleClose={() => setIsReceipent(true)}
|
handleClose={() => setIsReceipent(true)}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px] text-center font-medium">
|
||||||
<p>Please add a role</p>
|
<p>Please add roles first</p>
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
{/* this modal is used show send mail message and after send mail success message */}
|
{/* this modal is used show send mail message and after send mail success message */}
|
||||||
@@ -1234,32 +1280,18 @@ const TemplatePlaceholder = () => {
|
|||||||
title={"Create Document"}
|
title={"Create Document"}
|
||||||
handleClose={() => setIsCreateDocModal(false)}
|
handleClose={() => setIsCreateDocModal(false)}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px]">
|
||||||
<p>
|
<p>
|
||||||
Do you want to create a document using the template you just
|
Do you want to create a document using the template you just
|
||||||
created ?
|
created ?
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
{currentEmail.length > 0 && (
|
{currentEmail.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => handleCreateDocModal()}
|
||||||
handleCreateDocModal();
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn"
|
className="op-btn op-btn-primary"
|
||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</button>
|
</button>
|
||||||
@@ -1268,7 +1300,7 @@ const TemplatePlaceholder = () => {
|
|||||||
setIsCreateDocModal(false);
|
setIsCreateDocModal(false);
|
||||||
}}
|
}}
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-secondary ml-2"
|
||||||
>
|
>
|
||||||
No
|
No
|
||||||
</button>
|
</button>
|
||||||
@@ -1276,32 +1308,21 @@ const TemplatePlaceholder = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
{isCreateDoc && <Loader isLoading={isLoading} />}
|
{isCreateDoc && <LoaderWithMsg isLoading={isLoading} />}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
|
||||||
isOpen={isShowEmail}
|
isOpen={isShowEmail}
|
||||||
title={"signers alert"}
|
title={"signers alert"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setIsShowEmail(false);
|
setIsShowEmail(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div className="h-full p-[20px]">
|
||||||
<p>Please select signer for add placeholder!</p>
|
<p>Please select signer for add placeholder!</p>
|
||||||
<div
|
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => setIsShowEmail(false)}
|
||||||
setIsShowEmail(false);
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="finishBtn cancelBtn"
|
className="op-btn op-btn-primary"
|
||||||
>
|
>
|
||||||
Ok
|
Ok
|
||||||
</button>
|
</button>
|
||||||
@@ -1316,6 +1337,7 @@ const TemplatePlaceholder = () => {
|
|||||||
currWidgetsDetails={currWidgetsDetails}
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
handleClose={handleNameModal}
|
handleClose={handleNameModal}
|
||||||
|
isSubscribe={isSubscribe}
|
||||||
/>
|
/>
|
||||||
<DropdownWidgetOption
|
<DropdownWidgetOption
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -1326,6 +1348,7 @@ const TemplatePlaceholder = () => {
|
|||||||
currWidgetsDetails={currWidgetsDetails}
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
handleClose={handleNameModal}
|
handleClose={handleNameModal}
|
||||||
|
isSubscribe={isSubscribe}
|
||||||
/>
|
/>
|
||||||
<DropdownWidgetOption
|
<DropdownWidgetOption
|
||||||
type="dropdown"
|
type="dropdown"
|
||||||
@@ -1336,6 +1359,7 @@ const TemplatePlaceholder = () => {
|
|||||||
currWidgetsDetails={currWidgetsDetails}
|
currWidgetsDetails={currWidgetsDetails}
|
||||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||||
handleClose={handleNameModal}
|
handleClose={handleNameModal}
|
||||||
|
isSubscribe={isSubscribe}
|
||||||
/>
|
/>
|
||||||
<PlaceholderCopy
|
<PlaceholderCopy
|
||||||
isPageCopy={isPageCopy}
|
isPageCopy={isPageCopy}
|
||||||
@@ -1348,7 +1372,7 @@ const TemplatePlaceholder = () => {
|
|||||||
// signerObjId={signerObjId}
|
// signerObjId={signerObjId}
|
||||||
Id={uniqueId}
|
Id={uniqueId}
|
||||||
/>
|
/>
|
||||||
{/* pdf header which contain funish back button */}
|
{/* pdf header which contain finish, back button */}
|
||||||
<Header
|
<Header
|
||||||
completeBtnTitle={"Save"}
|
completeBtnTitle={"Save"}
|
||||||
isPlaceholder={true}
|
isPlaceholder={true}
|
||||||
@@ -1443,16 +1467,21 @@ const TemplatePlaceholder = () => {
|
|||||||
title={"Roles"}
|
title={"Roles"}
|
||||||
initial={true}
|
initial={true}
|
||||||
isTemplateFlow={true}
|
isTemplateFlow={true}
|
||||||
|
sendInOrder={pdfDetails[0].SendinOrder}
|
||||||
|
setSignersData={setSignersData}
|
||||||
|
blockColor={blockColor}
|
||||||
|
setBlockColor={setBlockColor}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<div className="signerComponent">
|
<div className="hidden md:block w-[180px] h-full bg-base-100">
|
||||||
<div
|
<div
|
||||||
style={{ maxHeight: window.innerHeight - 70 + "px" }}
|
style={{ maxHeight: window.innerHeight - 70 + "px" }}
|
||||||
className="autoSignScroll"
|
className="overflow-y-auto hide-scrollbar"
|
||||||
>
|
>
|
||||||
<SignerListPlace
|
<SignerListPlace
|
||||||
|
isMailSend={isMailSend}
|
||||||
signerPos={signerPos}
|
signerPos={signerPos}
|
||||||
signersdata={signersdata}
|
signersdata={signersdata}
|
||||||
isSelectListId={isSelectListId}
|
isSelectListId={isSelectListId}
|
||||||
@@ -1466,10 +1495,14 @@ const TemplatePlaceholder = () => {
|
|||||||
handleRoleChange={handleRoleChange}
|
handleRoleChange={handleRoleChange}
|
||||||
handleOnBlur={handleOnBlur}
|
handleOnBlur={handleOnBlur}
|
||||||
title={"Roles"}
|
title={"Roles"}
|
||||||
|
sendInOrder={pdfDetails[0]?.SendinOrder}
|
||||||
|
setSignersData={setSignersData}
|
||||||
|
blockColor={blockColor}
|
||||||
|
setBlockColor={setBlockColor}
|
||||||
/>
|
/>
|
||||||
<div data-tut="reactourSecond">
|
<div data-tut="reactourSecond">
|
||||||
<WidgetComponent
|
<WidgetComponent
|
||||||
pdfUrl={isMailSend}
|
isMailSend={isMailSend}
|
||||||
dragSignature={dragSignature}
|
dragSignature={dragSignature}
|
||||||
signRef={signRef}
|
signRef={signRef}
|
||||||
handleDivClick={handleDivClick}
|
handleDivClick={handleDivClick}
|
||||||
@@ -1508,6 +1541,7 @@ const TemplatePlaceholder = () => {
|
|||||||
isAddUser={isAddUser}
|
isAddUser={isAddUser}
|
||||||
uniqueId={uniqueId}
|
uniqueId={uniqueId}
|
||||||
closePopup={closePopup}
|
closePopup={closePopup}
|
||||||
|
signersData={signersdata}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ModalUi
|
<ModalUi
|
||||||
@@ -1526,6 +1560,7 @@ const TemplatePlaceholder = () => {
|
|||||||
isOpen={isNameModal}
|
isOpen={isNameModal}
|
||||||
handleClose={handleNameModal}
|
handleClose={handleNameModal}
|
||||||
handleData={handleWidgetdefaultdata}
|
handleData={handleWidgetdefaultdata}
|
||||||
|
isSubscribe={isSubscribe}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, {
|
||||||
|
useState,
|
||||||
|
useEffect
|
||||||
|
// useRef
|
||||||
|
} from "react";
|
||||||
import { Navigate, useNavigate } from "react-router-dom";
|
import { Navigate, useNavigate } from "react-router-dom";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import { SaveFileSize } from "../constant/saveFileSize";
|
import { SaveFileSize } from "../constant/saveFileSize";
|
||||||
@@ -6,10 +10,18 @@ import dp from "../assets/images/dp.png";
|
|||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import sanitizeFileName from "../primitives/sanitizeFileName";
|
import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||||
|
import Tooltip from "../primitives/Tooltip";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
|
||||||
|
import Upgrade from "../primitives/Upgrade";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
function UserProfile() {
|
function UserProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
|
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
|
||||||
|
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||||
const [editmode, setEditMode] = useState(false);
|
const [editmode, setEditMode] = useState(false);
|
||||||
@@ -19,54 +31,125 @@ function UserProfile() {
|
|||||||
const [isLoader, setIsLoader] = useState(false);
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
const [percentage, setpercentage] = useState(0);
|
const [percentage, setpercentage] = useState(0);
|
||||||
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
// const [publicUserName, setPublicUserName] = useState(
|
||||||
|
// extendUser && extendUser?.[0]?.UserName
|
||||||
|
// );
|
||||||
|
// const previousPublicUserName = useRef(publicUserName);
|
||||||
|
const [company, setCompany] = useState(
|
||||||
|
extendUser && extendUser?.[0]?.Company
|
||||||
|
);
|
||||||
|
const [jobTitle, setJobTitle] = useState(
|
||||||
|
extendUser && extendUser?.[0]?.JobTitle
|
||||||
|
);
|
||||||
|
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||||
|
const [otp, setOtp] = useState("");
|
||||||
|
const [otpLoader, setOtpLoader] = useState(false);
|
||||||
|
const [isEmailVerified, setIsEmailVerified] = useState(false);
|
||||||
|
// const [userNameError, setUserNameError] = useState("");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
getUserDetail();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getUserDetail = async () => {
|
||||||
|
setIsLoader(true);
|
||||||
const extClass = localStorage.getItem("Extand_Class");
|
const extClass = localStorage.getItem("Extand_Class");
|
||||||
const jsonSender = JSON.parse(extClass);
|
const jsonSender = JSON.parse(extClass);
|
||||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
if (HeaderDocId) {
|
if (HeaderDocId) {
|
||||||
setIsDisableDocId(HeaderDocId);
|
setIsDisableDocId(HeaderDocId);
|
||||||
}
|
}
|
||||||
}, []);
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
|
let isEmailVerified = currentUser?.emailVerified || false;
|
||||||
|
if (isEmailVerified) {
|
||||||
|
setIsEmailVerified(isEmailVerified);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
});
|
||||||
|
if (user) {
|
||||||
|
isEmailVerified = user?.get("emailVerified");
|
||||||
|
setIsEmailVerified(isEmailVerified);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function to check public username already exist
|
||||||
|
// const handleCheckPublicUserName = async () => {
|
||||||
|
// try {
|
||||||
|
// const res = await Parse.Cloud.run("getpublicusername", {
|
||||||
|
// username: publicUserName
|
||||||
|
// });
|
||||||
|
// if (res) {
|
||||||
|
// setIsLoader(false);
|
||||||
|
// setUserNameError("user name already exist");
|
||||||
|
// setTimeout(() => {
|
||||||
|
// setUserNameError("");
|
||||||
|
// }, 3000);
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log("error in getpublicusername cloud function");
|
||||||
|
// }
|
||||||
|
// };
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoader(true);
|
let phn = Phone,
|
||||||
let phn = Phone;
|
res = "";
|
||||||
|
//condition to call cloud function when user change publicUserName
|
||||||
try {
|
// if (previousPublicUserName.current !== publicUserName) {
|
||||||
const tour = Parse.Object.extend("_User");
|
// res = await handleCheckPublicUserName();
|
||||||
const query = new Parse.Query(tour);
|
// }
|
||||||
|
if (!res) {
|
||||||
await query.get(UserProfile.objectId).then((object) => {
|
setIsLoader(true);
|
||||||
object.set("name", name);
|
try {
|
||||||
object.set("ProfilePic", Image);
|
const userQuery = Parse.Object.extend("_User");
|
||||||
object.set("phone", phn);
|
const query = new Parse.Query(userQuery);
|
||||||
|
await query.get(UserProfile.objectId).then((object) => {
|
||||||
object.save().then(
|
object.set("name", name);
|
||||||
async (response) => {
|
object.set("ProfilePic", Image);
|
||||||
if (response) {
|
object.set("phone", phn || "");
|
||||||
let res = response.toJSON();
|
object.save().then(
|
||||||
let rr = JSON.stringify(res);
|
async (response) => {
|
||||||
localStorage.setItem("UserInformation", rr);
|
if (response) {
|
||||||
SetName(res.name);
|
let res = response.toJSON();
|
||||||
SetPhone(res.phone);
|
let rr = JSON.stringify(res);
|
||||||
setImage(res.ProfilePic);
|
localStorage.setItem("UserInformation", rr);
|
||||||
localStorage.setItem("username", res.name);
|
SetName(res.name);
|
||||||
await updateExtUser({ Name: res.name, Phone: res.phone });
|
SetPhone(res?.phone || "");
|
||||||
alert("Profile updated successfully.");
|
setImage(res.ProfilePic);
|
||||||
setEditMode(false);
|
localStorage.setItem("username", res.name);
|
||||||
navigate("/dashboard/35KBoSgoAK");
|
localStorage.setItem("profileImg", res.ProfilePic);
|
||||||
|
await updateExtUser({
|
||||||
|
Name: res.name,
|
||||||
|
Phone: res?.phone || ""
|
||||||
|
});
|
||||||
|
alert("Profile updated successfully.");
|
||||||
|
setEditMode(false);
|
||||||
|
setIsLoader(false);
|
||||||
|
//navigate("/dashboard/35KBoSgoAK");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
alert("Something went wrong.");
|
||||||
|
console.error("Error while updating tour", error);
|
||||||
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
(error) => {
|
});
|
||||||
alert("Something went wrong.");
|
} catch (error) {
|
||||||
console.error("Error while updating tour", error);
|
console.log("err", error);
|
||||||
setIsLoader(false);
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log("err", error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,12 +158,15 @@ function UserProfile() {
|
|||||||
const extClass = localStorage.getItem("extended_class");
|
const extClass = localStorage.getItem("extended_class");
|
||||||
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
const ExtUserId = extData[0].objectId;
|
const ExtUserId = extData[0].objectId;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
Phone: obj.Phone,
|
Phone: obj?.Phone || "",
|
||||||
Name: obj.Name,
|
Name: obj.Name,
|
||||||
HeaderDocId: isDisableDocId
|
HeaderDocId: isDisableDocId,
|
||||||
|
JobTitle: jobTitle,
|
||||||
|
Company: company
|
||||||
|
// UserName: publicUserName || ""
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios.put(
|
await axios.put(
|
||||||
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
||||||
body,
|
body,
|
||||||
@@ -98,7 +184,6 @@ function UserProfile() {
|
|||||||
|
|
||||||
const json = JSON.parse(JSON.stringify([res]));
|
const json = JSON.parse(JSON.stringify([res]));
|
||||||
const extRes = JSON.stringify(json);
|
const extRes = JSON.stringify(json);
|
||||||
|
|
||||||
localStorage.setItem("Extand_Class", extRes);
|
localStorage.setItem("Extand_Class", extRes);
|
||||||
// console.log("updateRes ", updateRes);
|
// console.log("updateRes ", updateRes);
|
||||||
};
|
};
|
||||||
@@ -133,9 +218,9 @@ function UserProfile() {
|
|||||||
// console.log("File URL:", response.url());
|
// console.log("File URL:", response.url());
|
||||||
if (response.url()) {
|
if (response.url()) {
|
||||||
setImage(response.url());
|
setImage(response.url());
|
||||||
localStorage.setItem("profileImg", response.url());
|
|
||||||
setpercentage(0);
|
setpercentage(0);
|
||||||
SaveFileSize(size, response.url());
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, response.url(), tenantId);
|
||||||
return response.url();
|
return response.url();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -153,30 +238,84 @@ function UserProfile() {
|
|||||||
const handleDisableDocId = () => {
|
const handleDisableDocId = () => {
|
||||||
setIsDisableDocId((prevChecked) => !prevChecked);
|
setIsDisableDocId((prevChecked) => !prevChecked);
|
||||||
};
|
};
|
||||||
|
//`handleVerifyBtn` function is used to send otp on user mail
|
||||||
|
const handleVerifyBtn = async () => {
|
||||||
|
setIsVerifyModal(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
};
|
||||||
|
const handleCloseVerifyModal = async () => {
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
};
|
||||||
|
//`handleVerifyEmail` function is used to verify email with otp
|
||||||
|
const handleVerifyEmail = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
try {
|
||||||
|
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||||
|
otp: otp,
|
||||||
|
email: Parse.User.current().getEmail()
|
||||||
|
});
|
||||||
|
if (resEmail?.message === "Email is verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
} else if (resEmail?.message === "Email is already verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
}
|
||||||
|
setOtp("");
|
||||||
|
alert(resEmail.message);
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
} finally {
|
||||||
|
setOtpLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function to use resend otp for email verification
|
||||||
|
const handleResend = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
await handleSendOTP();
|
||||||
|
setOtpLoader(false);
|
||||||
|
alert("OTP sent on you email");
|
||||||
|
};
|
||||||
|
//function to handle onchange username and restrict 6-characters username for free users
|
||||||
|
// const handleOnchangeUserName = (e) => {
|
||||||
|
// const value = e.target.value;
|
||||||
|
// if (value.length > 6 && !isSubscribe) {
|
||||||
|
// setUserNameError("Please upgrade to allow more than 6 characters.");
|
||||||
|
// setTimeout(() => {
|
||||||
|
// setUserNameError("");
|
||||||
|
// }, 2000);
|
||||||
|
// } else {
|
||||||
|
// setPublicUserName(e.target.value);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
const handleCancel = () => {
|
||||||
|
setEditMode(false);
|
||||||
|
SetName(localStorage.getItem("username"));
|
||||||
|
SetPhone(UserProfile && UserProfile.phone);
|
||||||
|
setImage(localStorage.getItem("profileImg"));
|
||||||
|
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
|
||||||
|
setCompany(extendUser && extendUser?.[0]?.Company);
|
||||||
|
setJobTitle(extendUser?.[0]?.JobTitle);
|
||||||
|
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title={"Profile"} />
|
<Title title={"Profile"} />
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="h-[100vh] 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>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center w-full">
|
<div className="flex justify-center items-center w-full relative">
|
||||||
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
|
{/* {userNameError && (
|
||||||
|
<div
|
||||||
|
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
|
||||||
|
>
|
||||||
|
{userNameError}
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
|
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded-xl w-[450px]">
|
||||||
<div className="flex flex-col justify-center items-center my-4">
|
<div className="flex flex-col justify-center items-center my-4">
|
||||||
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
||||||
<img
|
<img
|
||||||
@@ -188,7 +327,7 @@ function UserProfile() {
|
|||||||
{editmode && (
|
{editmode && (
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
className="max-w-[270px] text-sm py-1 px-2 mt-4 border-[1px] border-[#15b4e9] text-black rounded"
|
className="op-file-input op-file-input-bordered op-file-input-sm max-w-[270px] mt-4 text-sm"
|
||||||
accept="image/png, image/gif, image/jpeg"
|
accept="image/png, image/gif, image/jpeg"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
let files = e.target.files;
|
let files = e.target.files;
|
||||||
@@ -214,7 +353,7 @@ function UserProfile() {
|
|||||||
<ul className="w-full flex flex-col p-2 text-sm">
|
<ul className="w-full flex flex-col p-2 text-sm">
|
||||||
<li
|
<li
|
||||||
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||||
editmode ? "py-1" : "py-2"
|
editmode ? "py-1.5" : "py-2"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="font-semibold">Name:</span>{" "}
|
<span className="font-semibold">Name:</span>{" "}
|
||||||
@@ -222,7 +361,7 @@ function UserProfile() {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={name}
|
value={name}
|
||||||
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||||
onChange={(e) => SetName(e.target.value)}
|
onChange={(e) => SetName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -231,14 +370,14 @@ function UserProfile() {
|
|||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||||
editmode ? "py-1" : "py-2"
|
editmode ? "py-1.5" : "py-2"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="font-semibold">Phone:</span>{" "}
|
<span className="font-semibold">Phone:</span>{" "}
|
||||||
{editmode ? (
|
{editmode ? (
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||||
onChange={(e) => SetPhone(e.target.value)}
|
onChange={(e) => SetPhone(e.target.value)}
|
||||||
value={Phone}
|
value={Phone}
|
||||||
/>
|
/>
|
||||||
@@ -250,68 +389,194 @@ function UserProfile() {
|
|||||||
<span className="font-semibold">Email:</span>{" "}
|
<span className="font-semibold">Email:</span>{" "}
|
||||||
<span>{UserProfile && UserProfile.email}</span>
|
<span>{UserProfile && UserProfile.email}</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
|
<li
|
||||||
|
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||||
|
editmode ? "py-1.5" : "py-2"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="font-semibold">Company:</span>{" "}
|
||||||
|
{editmode ? (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={company}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||||
|
onChange={(e) => setCompany(e.target.value)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>{extendUser?.[0].Company}</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||||
|
editmode ? "py-1.5" : "py-2"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span className="font-semibold">Job title:</span>{" "}
|
||||||
|
{editmode ? (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={jobTitle}
|
||||||
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||||
|
onChange={(e) => setJobTitle(e.target.value)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>{extendUser?.[0]?.JobTitle}</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||||
<span className="font-semibold">Is Email verified:</span>{" "}
|
<span className="font-semibold">Is Email verified:</span>{" "}
|
||||||
<span>
|
<span>
|
||||||
{UserProfile && UserProfile.emailVerified
|
{isEmailVerified ? (
|
||||||
? "Verified"
|
"Verified"
|
||||||
: "Not verified"}
|
) : (
|
||||||
|
<span>
|
||||||
|
Not verified(
|
||||||
|
<span
|
||||||
|
onClick={() => handleVerifyBtn()}
|
||||||
|
className="hover:underline text-blue-600 cursor-pointer"
|
||||||
|
>
|
||||||
|
verify
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
|
{/* {isEnableSubscription && (
|
||||||
<span className="font-semibold">Disable DocumentId :</span>{" "}
|
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||||
<span>
|
<span className="font-semibold">
|
||||||
<label className="relative inline-flex items-center cursor-pointer mb-0">
|
Public profile :{" "}
|
||||||
|
<Tooltip
|
||||||
|
message={`this is your public URL. Copy or share it
|
||||||
|
with the signer, and you will be able to see
|
||||||
|
all your publicly set templates.`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div className="flex text-xs items-center">
|
||||||
|
<span>opensign-me.vercel.app/</span>
|
||||||
|
{editmode ? (
|
||||||
|
<input
|
||||||
|
onChange={handleOnchangeUserName}
|
||||||
|
value={publicUserName}
|
||||||
|
disabled={!editmode}
|
||||||
|
placeholder="enter user name"
|
||||||
|
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
value={extendUser?.[0]?.UserName}
|
||||||
|
disabled
|
||||||
|
placeholder="enter user name"
|
||||||
|
className="op-input op-input-bordered op-input-xs"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)} */}
|
||||||
|
<li className="border-y-[1px] border-gray-300 break-all">
|
||||||
|
<div className="flex justify-between items-center py-2">
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "font-semibold"
|
||||||
|
: "font-semibold text-gray-300"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Disable DocumentId :{" "}
|
||||||
|
<Tooltip
|
||||||
|
url={
|
||||||
|
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</span>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? `${editmode ? "cursor-pointer" : ""}`
|
||||||
|
: "pointer-events-none opacity-50"
|
||||||
|
} relative block items-center mb-0`}
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
disabled={editmode ? false : true}
|
disabled={editmode ? false : true}
|
||||||
|
type="checkbox"
|
||||||
|
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||||
checked={isDisableDocId}
|
checked={isDisableDocId}
|
||||||
onChange={handleDisableDocId}
|
onChange={handleDisableDocId}
|
||||||
type="checkbox"
|
|
||||||
value=""
|
|
||||||
className="sr-only peer"
|
|
||||||
/>
|
/>
|
||||||
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
|
||||||
</label>
|
</label>
|
||||||
</span>
|
</div>
|
||||||
|
{!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Disable documentId is free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
|
<div className="flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4">
|
||||||
{editmode ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleSubmit}
|
|
||||||
className="rounded shadow focus:outline-none border-[2px] border-[#15b4e9] bg-white text-[#15b4e9] px-4 py-2 mr-4"
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => {
|
|
||||||
setEditMode(true);
|
|
||||||
}}
|
|
||||||
className="rounded shadow focus:outline-none text-white bg-[#e7505a] px-4 py-2 mr-4"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={(e) =>
|
||||||
if (editmode) {
|
editmode ? handleSubmit(e) : setEditMode(true)
|
||||||
setEditMode(false);
|
}
|
||||||
} else {
|
className="op-btn op-btn-primary"
|
||||||
navigate("/changepassword");
|
>
|
||||||
}
|
{editmode ? "Save" : "Edit"}
|
||||||
}}
|
</button>
|
||||||
className={`rounded shadow focus:outline-none text-white bg-[#3598dc] ${
|
<button
|
||||||
editmode ? "px-4 py-2 " : "p-2"
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
editmode ? handleCancel() : navigate("/changepassword")
|
||||||
|
}
|
||||||
|
className={`op-btn ${
|
||||||
|
editmode ? "op-btn-ghost" : "op-btn-secondary"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{editmode ? "Cancel" : "Change Password"}
|
{editmode ? "Cancel" : "Change Password"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{isVerifyModal && (
|
||||||
|
<ModalUi
|
||||||
|
isOpen
|
||||||
|
title={"OTP verification"}
|
||||||
|
handleClose={handleCloseVerifyModal}
|
||||||
|
>
|
||||||
|
{otpLoader ? (
|
||||||
|
<div className="h-[150px] flex justify-center items-center">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={(e) => 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={otp}
|
||||||
|
onChange={(e) => setOtp(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="px-6 mb-3">
|
||||||
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
|
Verify
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="op-btn op-btn-secondary ml-2"
|
||||||
|
onClick={(e) => handleResend(e)}
|
||||||
|
>
|
||||||
|
Resend
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</ModalUi>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
import Alert from "../primitives/Alert";
|
import Alert from "../primitives/Alert";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import { rejectBtn, submitBtn } from "../constant/const";
|
import { isEnableSubscription } from "../constant/const";
|
||||||
import { openInNewTab } from "../constant/Utils";
|
import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||||
|
import Tooltip from "../primitives/Tooltip";
|
||||||
|
import Loader from "../primitives/Loader";
|
||||||
|
|
||||||
function Webhook() {
|
function Webhook() {
|
||||||
|
const navigation = useNavigate();
|
||||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||||
const [webhook, setWebhook] = useState();
|
const [webhook, setWebhook] = useState();
|
||||||
@@ -15,6 +20,8 @@ function Webhook() {
|
|||||||
const [isGenerate, setIsGenerate] = useState(false);
|
const [isGenerate, setIsGenerate] = useState(false);
|
||||||
const [isErr, setIsErr] = useState(false);
|
const [isErr, setIsErr] = useState(false);
|
||||||
const [isModal, setIsModal] = useState(false);
|
const [isModal, setIsModal] = useState(false);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchWebhook();
|
fetchWebhook();
|
||||||
@@ -23,6 +30,10 @@ function Webhook() {
|
|||||||
|
|
||||||
const fetchWebhook = async () => {
|
const fetchWebhook = async () => {
|
||||||
const email = Parse.User.current().getEmail();
|
const email = Parse.User.current().getEmail();
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
const params = { email: email };
|
const params = { email: email };
|
||||||
try {
|
try {
|
||||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
@@ -38,41 +49,48 @@ function Webhook() {
|
|||||||
};
|
};
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoader(true);
|
if (webhook && webhook.startsWith("https://")) {
|
||||||
setIsModal(false);
|
setIsLoader(true);
|
||||||
try {
|
setIsModal(false);
|
||||||
const params = { url: webhook };
|
try {
|
||||||
const url = parseBaseUrl + "functions/savewebhook";
|
const params = { url: webhook };
|
||||||
const headers = {
|
const url = parseBaseUrl + "functions/savewebhook";
|
||||||
"Content-Type": "application/json",
|
const headers = {
|
||||||
"X-Parse-Application-Id": parseAppId,
|
"Content-Type": "application/json",
|
||||||
sessiontoken: localStorage.getItem("accesstoken")
|
"X-Parse-Application-Id": parseAppId,
|
||||||
};
|
sessiontoken: localStorage.getItem("accesstoken")
|
||||||
await axios.post(url, params, { headers: headers }).then((res) => {
|
};
|
||||||
if (res.data && res.data.result && res.data.result.Webhook) {
|
await axios.post(url, params, { headers: headers }).then((res) => {
|
||||||
setWebhook(res.data.result.Webhook);
|
if (res.data && res.data.result && res.data.result.Webhook) {
|
||||||
setIsGenerate(true);
|
setWebhook(res.data.result.Webhook);
|
||||||
setTimeout(() => {
|
setIsGenerate(true);
|
||||||
setIsGenerate(false);
|
setTimeout(() => {
|
||||||
}, 1500);
|
setIsGenerate(false);
|
||||||
setIsLoader(false);
|
}, 1500);
|
||||||
} else {
|
setIsLoader(false);
|
||||||
console.error("Error while generating webhook");
|
} else {
|
||||||
setIsLoader(false);
|
console.error("Error while generating webhook");
|
||||||
setIsErr(true);
|
setIsLoader(false);
|
||||||
setTimeout(() => {
|
setIsErr(true);
|
||||||
setIsErr(false);
|
setTimeout(() => {
|
||||||
}, 1500);
|
setIsErr(false);
|
||||||
}
|
}, 1500);
|
||||||
});
|
}
|
||||||
} catch (error) {
|
});
|
||||||
setIsLoader(false);
|
} catch (error) {
|
||||||
setIsErr(true);
|
setIsLoader(false);
|
||||||
setTimeout(() => {
|
setIsErr(true);
|
||||||
setIsErr(false);
|
setTimeout(() => {
|
||||||
}, 1500);
|
setIsErr(false);
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
console.log("err", error);
|
console.log("err", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setError("The webhook url should always start with https://");
|
||||||
|
setTimeout(() => {
|
||||||
|
setError("");
|
||||||
|
}, 1500);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -80,46 +98,50 @@ function Webhook() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title={"Webhook"} />
|
<Title title={"Webhook"} />
|
||||||
{isGenerate && <Alert type="success">Webhook added successfully!</Alert>}
|
{isGenerate && <Alert type="success">Webhook added successfully.</Alert>}
|
||||||
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
{isErr && <Alert type="danger">Something went wrong.</Alert>}
|
||||||
|
|
||||||
{isLoader ? (
|
{isLoader ? (
|
||||||
<div
|
<div className="h-[100vh] 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>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white flex flex-col justify-center shadow rounded">
|
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
|
||||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">Webhook</h1>
|
{!isEnableSubscription && <PremiumAlertHeader />}
|
||||||
<ul className="w-full flex flex-col p-2 text-sm">
|
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||||
|
OpenSign™ Webhook{" "}
|
||||||
|
<Tooltip
|
||||||
|
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
|
||||||
|
isSubscribe={true}
|
||||||
|
/>
|
||||||
|
</h1>
|
||||||
|
<ul
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "w-full flex flex-col p-2 text-sm "
|
||||||
|
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||||
|
}
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||||
>
|
>
|
||||||
<span className="w-[40%]">Webhook:</span>{" "}
|
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
|
||||||
<span id="token" className="w-[60%] md:text-end cursor-pointer">
|
<span className="">Webhook:</span>{" "}
|
||||||
{webhook && webhook}
|
<span id="token" className=" md:text-end cursor-pointer">
|
||||||
</span>
|
{webhook ? webhook : "_____"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleModal}
|
||||||
|
className="op-btn op-btn-primary "
|
||||||
|
>
|
||||||
|
{webhook ? "Update Webhook" : "Add Webhook"}
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
|
|
||||||
<button
|
<div className="flex items-center justify-center ">
|
||||||
type="button"
|
|
||||||
onClick={handleModal}
|
|
||||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
|
||||||
>
|
|
||||||
{webhook ? "Update Webhook" : "Add Webhook"}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -127,36 +149,69 @@ function Webhook() {
|
|||||||
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
|
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
className="op-btn op-btn-secondary my-2"
|
||||||
>
|
>
|
||||||
View Docs
|
View Docs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!isSubscribe && isEnableSubscription && (
|
||||||
|
<>
|
||||||
|
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||||
|
Upgrade to PRO Plan
|
||||||
|
</h1>
|
||||||
|
<ul className={"w-full flex flex-col p-2 text-sm "}>
|
||||||
|
<li
|
||||||
|
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||||
|
>
|
||||||
|
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
|
||||||
|
<span className="">$29.99/month:</span>{" "}
|
||||||
|
<span id="token" className=" md:text-end cursor-pointer">
|
||||||
|
First 100 documents included, then just $0.15 per
|
||||||
|
document.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{!isSubscribe && isEnableSubscription && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => navigation("/subscription")}
|
||||||
|
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||||
|
>
|
||||||
|
Upgrade Now
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={isModal}
|
isOpen={isModal}
|
||||||
title={"Regenerate Token"}
|
title={"Regenerate Token"}
|
||||||
handleClose={handleModal}
|
handleClose={handleModal}
|
||||||
>
|
>
|
||||||
|
{error && <Alert type="danger">{error}</Alert>}
|
||||||
<div className="m-[20px]">
|
<div className="m-[20px]">
|
||||||
<div className="text-lg font-normal text-black">
|
<div className="text-lg font-normal text-base-content">
|
||||||
<label className="text-sm ml-2">Webhook</label>
|
<label className="text-sm ml-2">Webhook</label>
|
||||||
<input
|
<input
|
||||||
value={webhook}
|
value={webhook}
|
||||||
onChange={(e) => setWebhook(e.target.value)}
|
onChange={(e) => setWebhook(e.target.value)}
|
||||||
placeholder="Enter webhook url"
|
placeholder="Enter webhook url"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<hr className="bg-[#ccc] mt-4 " />
|
<hr className="bg-[#ccc] mt-3" />
|
||||||
<div className="flex items-center mt-3 gap-2 text-white">
|
<div className="flex items-center mt-3 gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
className={submitBtn + "ml-[2px]"}
|
className="op-btn op-btn-primary ml-[2px]"
|
||||||
>
|
>
|
||||||
Yes
|
Yes
|
||||||
</button>
|
</button>
|
||||||
<button onClick={handleModal} className={rejectBtn}>
|
<button
|
||||||
|
onClick={handleModal}
|
||||||
|
className="op-btn op-btn-secondary"
|
||||||
|
>
|
||||||
No
|
No
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// below code is used to handle undefined Promise.withResolvers for safari
|
||||||
|
if (typeof Promise.withResolvers === "undefined") {
|
||||||
|
Promise.withResolvers = function () {
|
||||||
|
let resolve, reject;
|
||||||
|
const promise = new Promise((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
return { promise, resolve, reject };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// const { promise, resolve, reject } = Promise.withResolvers()
|
||||||
|
// console.log(promise, resolve, reject) // Promise { <pending> } [Function (anonymous)] [Function (anonymous)]
|
||||||
|
// ... Do something async and then call resolve or reject!
|
||||||
+40
-41
@@ -2,7 +2,8 @@ import React, { useState, useEffect } from "react";
|
|||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import "../styles/AddUser.css";
|
import "../styles/AddUser.css";
|
||||||
const AddUser = (props) => {
|
import Loader from "./Loader";
|
||||||
|
const AddContact = (props) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
@@ -21,7 +22,7 @@ const AddUser = (props) => {
|
|||||||
);
|
);
|
||||||
if (savedUserDetails && addYourself) {
|
if (savedUserDetails && addYourself) {
|
||||||
setName(savedUserDetails.name);
|
setName(savedUserDetails.name);
|
||||||
setPhone(savedUserDetails.phone);
|
setPhone(savedUserDetails?.phone || "");
|
||||||
setEmail(savedUserDetails.email);
|
setEmail(savedUserDetails.email);
|
||||||
}
|
}
|
||||||
}, [addYourself]);
|
}, [addYourself]);
|
||||||
@@ -50,15 +51,17 @@ const AddUser = (props) => {
|
|||||||
try {
|
try {
|
||||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
contactQuery.set("Name", name);
|
contactQuery.set("Name", name);
|
||||||
contactQuery.set("Phone", phone);
|
if (phone) {
|
||||||
|
contactQuery.set("Phone", phone);
|
||||||
|
}
|
||||||
contactQuery.set("Email", email);
|
contactQuery.set("Email", email);
|
||||||
contactQuery.set("UserRole", "contracts_Guest");
|
contactQuery.set("UserRole", "contracts_Guest");
|
||||||
|
|
||||||
if (localStorage.getItem("TenetId")) {
|
if (localStorage.getItem("TenantId")) {
|
||||||
contactQuery.set("TenantId", {
|
contactQuery.set("TenantId", {
|
||||||
__type: "Pointer",
|
__type: "Pointer",
|
||||||
className: "partners_Tenant",
|
className: "partners_Tenant",
|
||||||
objectId: localStorage.getItem("TenetId")
|
objectId: localStorage.getItem("TenantId")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +71,10 @@ const AddUser = (props) => {
|
|||||||
_user.set("name", name);
|
_user.set("name", name);
|
||||||
_user.set("username", email);
|
_user.set("username", email);
|
||||||
_user.set("email", email);
|
_user.set("email", email);
|
||||||
_user.set("phone", phone);
|
_user.set("password", email);
|
||||||
_user.set("password", phone);
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await _user.save();
|
const user = await _user.save();
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -191,40 +196,36 @@ const AddUser = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="addusercontainer">
|
<div className="h-full px-[20px] py-[10px]">
|
||||||
{isLoader && (
|
{isLoader && (
|
||||||
<div className="loaderdiv">
|
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||||
<div
|
<Loader />
|
||||||
style={{
|
|
||||||
fontSize: "45px",
|
|
||||||
color: "#3dd3e0"
|
|
||||||
}}
|
|
||||||
className="loader-37"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="form-wrapper">
|
<div className="w-full mx-auto p-[8px]">
|
||||||
<div style={{ fontSize: 14, fontWeight: "700" }}>Add User</div>
|
<div className="text-[14px] font-[700]">Add User</div>
|
||||||
|
|
||||||
{isUserExist && (
|
{isUserExist && (
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem] flex items-center mt-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="addYourself"
|
id="addYourself"
|
||||||
checked={addYourself}
|
checked={addYourself}
|
||||||
onChange={handleAddYourselfChange}
|
onChange={handleAddYourselfChange}
|
||||||
className="form-checkbox"
|
className="op-checkbox op-checkbox-sm"
|
||||||
/>
|
/>
|
||||||
<label htmlFor="addYourself" className="checkbox-label ">
|
<label
|
||||||
|
htmlFor="addYourself"
|
||||||
|
className="ml-[0.5rem] text-base-content mb-0"
|
||||||
|
>
|
||||||
Add Yourself
|
Add Yourself
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<form onSubmit={handleSubmit}>
|
<form className="text-base-content" onSubmit={handleSubmit}>
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
<label htmlFor="name" className="text-[13px]">
|
||||||
Name
|
Name
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[13px] text-[red]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
@@ -233,48 +234,46 @@ const AddUser = (props) => {
|
|||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="email" style={{ fontSize: 13 }}>
|
<label htmlFor="email" className="text-[13px]">
|
||||||
Email
|
Email
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
<span className="text-[13px] text-[red]"> *</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs lowercase"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="mb-[0.75rem]">
|
||||||
<label htmlFor="phone" style={{ fontSize: 13 }}>
|
<label htmlFor="phone" className="text-[13px]">
|
||||||
Phone
|
Phone
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="phone"
|
id="phone"
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="addUserInput"
|
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="buttoncontainer">
|
<div className="mt-6 flex justify-start gap-2">
|
||||||
<button type="submit" className="submitbutton">
|
<button type="submit" className="op-btn op-btn-primary">
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleReset()}
|
onClick={() => handleReset()}
|
||||||
className="resetbutton"
|
className="op-btn op-btn-secondary"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</button>
|
</button>
|
||||||
@@ -285,4 +284,4 @@ const AddUser = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddUser;
|
export default AddContact;
|
||||||
@@ -5,25 +5,25 @@ const Alert = ({ children, type }) => {
|
|||||||
function theme(color) {
|
function theme(color) {
|
||||||
switch (color) {
|
switch (color) {
|
||||||
case "success":
|
case "success":
|
||||||
return "border-[#c3e6cb] bg-[#d4edda] text-[#155724]";
|
return "op-alert-success";
|
||||||
case "info":
|
case "info":
|
||||||
return "border-[#adcdeb] bg-[#c1daf0] text-[#153756]";
|
return "op-alert-info";
|
||||||
case "danger":
|
case "danger":
|
||||||
return "border-[#f0a8a8] bg-[#f4bebe] text-[#c42121]";
|
return "op-alert-error";
|
||||||
|
case "warning":
|
||||||
|
return "op-alert-warning";
|
||||||
default:
|
default:
|
||||||
return "border-[#d6d6d6] bg-[#d9d9d9] text-[#575757]";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
children && (
|
||||||
{children && (
|
<div className="z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm">
|
||||||
<div
|
<div className={`op-alert ${textcolor}`}>
|
||||||
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] z-50`}
|
<span>{children}</span>
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</>
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,15 +2,8 @@ import React from "react";
|
|||||||
|
|
||||||
function HandleError({ handleError }) {
|
function HandleError({ handleError }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex justify-center items-center h-[100vh]">
|
||||||
style={{
|
<span className="text-[20px] text-[gray]">{handleError}</span>
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ fontSize: "20px", color: "gray" }}>{handleError}</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import React, { Suspense } from "react";
|
||||||
|
import Loader from "./Loader";
|
||||||
|
|
||||||
|
const LazyPage = ({ Page }) => {
|
||||||
|
return (
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="flex justify-center items-center h-[100vh]">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Page />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LazyPage;
|
||||||
@@ -1,45 +1,25 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import SelectSigners from "../components/shared/fields/SelectSigners";
|
import SelectSigners from "../components/shared/fields/SelectSigners";
|
||||||
import AddUser from "./AddUser";
|
import AddContact from "./AddContact";
|
||||||
import ModalUi from "./ModalUi";
|
import ModalUi from "./ModalUi";
|
||||||
|
|
||||||
const LinkUserModal = (props) => {
|
const LinkUserModal = (props) => {
|
||||||
return (
|
return (
|
||||||
<ModalUi
|
<ModalUi
|
||||||
title={"Add/Choose Signer"}
|
title={"Add/Choose Signer"}
|
||||||
isOpen={props.isAddUser[props.uniqueId]}
|
isOpen={
|
||||||
|
props?.isAddSigner ||
|
||||||
|
(props?.isAddUser && props?.isAddUser[props?.uniqueId])
|
||||||
|
}
|
||||||
handleClose={props.closePopup}
|
handleClose={props.closePopup}
|
||||||
>
|
>
|
||||||
<SelectSigners
|
<SelectSigners
|
||||||
details={props.handleAddUser}
|
details={props.handleAddUser}
|
||||||
closePopup={props.closePopup}
|
closePopup={props.closePopup}
|
||||||
|
signersData={props?.signersData}
|
||||||
/>
|
/>
|
||||||
<div
|
<div className="op-divider text-base-content mx-[25%] my-1">or</div>
|
||||||
style={{
|
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 5,
|
|
||||||
margin: "0 30%",
|
|
||||||
color: "#808080"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
height: 1,
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: "#ccc"
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
<span>or</span>
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
height: 1,
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: "#ccc"
|
|
||||||
}}
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<AddUser details={props.handleAddUser} closePopup={props.closePopup} />
|
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Loader = () => {
|
||||||
|
return (
|
||||||
|
<div className="op-loading op-loading-infinity w-[5rem] text-accent"></div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Loader;
|
||||||
@@ -1,27 +1,14 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import loader from "../assets/images/loader2.gif";
|
import Loader from "./Loader";
|
||||||
|
|
||||||
function Loader({ isLoading }) {
|
function LoaderWithMsg({ isLoading }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="flex flex-col justify-center items-center h-[100vh]">
|
||||||
style={{
|
{/* <img alt="loader" src={loader} className="w-[80px] h-[80px]" /> */}
|
||||||
display: "flex",
|
<Loader />
|
||||||
justifyContent: "center",
|
<span className="text-[13px] text-base-cotent">{isLoading.message}</span>
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "80px", height: "80px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "13px", color: "gray" }}>
|
|
||||||
{isLoading.message}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Loader;
|
export default LoaderWithMsg;
|
||||||
|
|||||||
@@ -1,45 +1,51 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { isEnableSubscription } from "../constant/const";
|
||||||
|
import PremiumAlertHeader from "./PremiumAlertHeader";
|
||||||
|
|
||||||
const ModalUi = ({
|
const ModalUi = ({
|
||||||
children,
|
children,
|
||||||
title,
|
title,
|
||||||
isOpen,
|
isOpen,
|
||||||
headColor,
|
|
||||||
handleClose,
|
handleClose,
|
||||||
showHeader = true,
|
showHeader = true,
|
||||||
showClose = true,
|
showClose = true,
|
||||||
styleClass
|
reduceWidth,
|
||||||
|
showHeaderMessage
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
<dialog className="op-modal op-modal-open">
|
||||||
<div
|
<div
|
||||||
className={
|
className={`${
|
||||||
styleClass
|
reduceWidth || "md:min-w-[500px]"
|
||||||
? styleClass
|
} op-modal-box p-0 max-h-90 min-w-[90%] overflow-y-auto hide-scrollbar text-sm`}
|
||||||
: "fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] md:min-w-[500px] overflow-y-auto hide-scrollbar"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{showHeader && (
|
{showHeader && (
|
||||||
<div
|
<>
|
||||||
className="flex justify-between rounded-t items-center py-[15px] px-[20px] text-white"
|
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||||
style={{ background: headColor ? headColor : "#32a3ac" }}
|
{title}
|
||||||
>
|
</h3>
|
||||||
<div className="text-[1.2rem] font-normal">{title}</div>
|
|
||||||
{showClose && (
|
{showClose && (
|
||||||
<div
|
<button
|
||||||
className="text-[1.5rem] cursor-pointer"
|
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
|
||||||
onClick={() => handleClose && handleClose()}
|
onClick={() => handleClose && handleClose()}
|
||||||
>
|
>
|
||||||
×
|
✕
|
||||||
</div>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
|
)}
|
||||||
|
{!isEnableSubscription && showHeaderMessage && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Cutomize Email is free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<div>{children}</div>
|
<div>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</dialog>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user