mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-03 08:18:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ccb1a3e5 | ||
|
|
f3d34b1d2c | ||
|
|
3f56aa0860 | ||
|
|
4eeacbaaf1 | ||
|
|
096a9e5736 | ||
|
|
4650660a26 | ||
|
|
9e6309644a | ||
|
|
14477a17ee | ||
|
|
7d3d618ff3 | ||
|
|
284cd277ea | ||
|
|
df61d752c8 | ||
|
|
9def956929 | ||
|
|
53aa5a26aa | ||
|
|
86cb5ccf78 | ||
|
|
c8e91e5205 | ||
|
|
87cfb643f9 | ||
|
|
2f7bd75608 | ||
|
|
930bac71a0 | ||
|
|
fcad7dc020 | ||
|
|
bada731d16 | ||
|
|
fd5e86edf2 | ||
|
|
1a886261ef | ||
|
|
41c3b4746d | ||
|
|
f52d013b06 | ||
|
|
0045603cb0 | ||
|
|
4235bc077d | ||
|
|
58b3cc6ba2 | ||
|
|
97f5d09306 | ||
|
|
fa98972b27 | ||
|
|
76848b7e09 | ||
|
|
c3faaa65b9 | ||
|
|
e819513650 | ||
|
|
dd0e2f0e58 | ||
|
|
fa08efeac6 | ||
|
|
f3ba02496c | ||
|
|
5cbbb00f66 | ||
|
|
7050df8b4c | ||
|
|
4fb27f4896 | ||
|
|
145aa5da97 | ||
|
|
1a455415f1 | ||
|
|
44ef1fe825 | ||
|
|
cb07e376d5 | ||
|
|
9b472a6b8b | ||
|
|
9840fe9412 | ||
|
|
d1a99d5211 | ||
|
|
5f7736882b | ||
|
|
d8095e077a | ||
|
|
c8fae00ce5 | ||
|
|
7c6a03a2d2 | ||
|
|
657ab13a04 | ||
|
|
40c21321dd | ||
|
|
476dbb555d | ||
|
|
19b4bfb9ef | ||
|
|
e9a85162e7 | ||
|
|
960c297995 | ||
|
|
50e525f448 | ||
|
|
c86f048498 | ||
|
|
39fd2f697f | ||
|
|
3f2cf3ddf4 | ||
|
|
5a53a70dcf | ||
|
|
66323f9347 | ||
|
|
c65f17a216 | ||
|
|
9bf93fb231 | ||
|
|
f418e02270 | ||
|
|
403254a5c6 | ||
|
|
c893d96ad3 | ||
|
|
3971118af8 | ||
|
|
4db0d93f41 | ||
|
|
affbfc5061 | ||
|
|
886ede2007 | ||
|
|
a3936c66aa | ||
|
|
d98453ffc2 | ||
|
|
7424912455 | ||
|
|
e095495081 | ||
|
|
eb4ce5544f | ||
|
|
8eef17834d | ||
|
|
40ed5e2353 | ||
|
|
2aa4c8c361 | ||
|
|
e5b120fa36 | ||
|
|
1e026f8ebd | ||
|
|
e5c7da0847 | ||
|
|
2e67002768 | ||
|
|
9c03649d48 | ||
|
|
df436b3b20 | ||
|
|
34acf4170e | ||
|
|
f9a13c880a | ||
|
|
df7cd45be6 | ||
|
|
b22d70c833 | ||
|
|
1ef8e8aa6a | ||
|
|
2aa58b7eb3 | ||
|
|
9fb61509c4 | ||
|
|
b99dc875ed | ||
|
|
7da31f00c3 | ||
|
|
894ccd1244 | ||
|
|
8d90e6e808 | ||
|
|
79771f725e | ||
|
|
786f56941a | ||
|
|
8fae910d46 | ||
|
|
8b5920bba3 | ||
|
|
03f1451e64 | ||
|
|
1d2a975b5f | ||
|
|
3db32979dc | ||
|
|
110431dae2 | ||
|
|
ade228f756 | ||
|
|
9b18a66f22 | ||
|
|
c88de90a24 | ||
|
|
737d66d97d | ||
|
|
b77b57d3fd | ||
|
|
240c7766c1 | ||
|
|
508b2e43d3 | ||
|
|
d6129e21e9 | ||
|
|
a11a2444f1 | ||
|
|
db1fb1c0e1 | ||
|
|
7f326b19aa | ||
|
|
2a1d7d2502 | ||
|
|
01edfce6e7 | ||
|
|
9176b8f99c | ||
|
|
0ff2e57424 | ||
|
|
567966866c | ||
|
|
e03f07a8ef | ||
|
|
9915a700bb | ||
|
|
f0a406a397 | ||
|
|
851ea24be4 | ||
|
|
50c4eff790 | ||
|
|
e69e6998c9 | ||
|
|
c87873cfb6 | ||
|
|
b09becf3eb | ||
|
|
0673242f41 | ||
|
|
1776931df5 | ||
|
|
124fbfd798 | ||
|
|
893e433805 | ||
|
|
a2c0921345 | ||
|
|
ad0a974c14 | ||
|
|
2d4a7d336c | ||
|
|
8d2df87de3 | ||
|
|
873e7e2068 | ||
|
|
3fcb1d0138 | ||
|
|
ce41f9a0ce | ||
|
|
34d8ed19bf | ||
|
|
ea9f34f0b3 | ||
|
|
4e9cbd23f2 | ||
|
|
bbd65c01e9 | ||
|
|
5dc30b7702 | ||
|
|
eb3b31bd59 | ||
|
|
57597dfef0 | ||
|
|
8cb7a610c7 | ||
|
|
2c464978b4 | ||
|
|
8d0bf6abe8 | ||
|
|
82c724a768 | ||
|
|
6146fe5f86 | ||
|
|
24ce6eae3b | ||
|
|
8db934a2c2 | ||
|
|
dc90d412ca | ||
|
|
b8415c8995 | ||
|
|
0d86f39ba3 | ||
|
|
0bab033673 | ||
|
|
72a0ce3f51 | ||
|
|
c3a14b82b3 | ||
|
|
b3252e276b | ||
|
|
10c63a66f6 | ||
|
|
f44205b90d | ||
|
|
cd087d410f | ||
|
|
c5812ba938 | ||
|
|
2e4c7f7690 | ||
|
|
a7389efb00 | ||
|
|
532062687c | ||
|
|
d3a603d115 | ||
|
|
26fece78c5 | ||
|
|
b8fb626591 | ||
|
|
732786067f | ||
|
|
45c0cd48c7 | ||
|
|
50d7982dd5 | ||
|
|
ff085b1284 | ||
|
|
c3564b1f8f | ||
|
|
32eba21e7a | ||
|
|
9952afcc38 | ||
|
|
b3feccab0d | ||
|
|
86ebd4903b | ||
|
|
0533bb9c30 | ||
|
|
185e90494c | ||
|
|
3f77b497ce | ||
|
|
87a8333cdf | ||
|
|
3d12261aa7 | ||
|
|
19c6cbbcd3 | ||
|
|
39c191db7c | ||
|
|
58c36266e0 | ||
|
|
0ebdfffdeb | ||
|
|
ed840f3048 | ||
|
|
1e13b509df | ||
|
|
d76a9a4628 | ||
|
|
f70e20498e | ||
|
|
1715b39629 | ||
|
|
3c63db8c9c | ||
|
|
7af78c95dd | ||
|
|
1c4a377641 | ||
|
|
bf56937585 | ||
|
|
e97442b0c8 | ||
|
|
d029ce5380 | ||
|
|
20049921ad | ||
|
|
05a0db75db | ||
|
|
a7b8100a18 | ||
|
|
112369b283 | ||
|
|
f9b915b728 | ||
|
|
8acdbc928e | ||
|
|
9665e14e6d | ||
|
|
b45e05cc9e | ||
|
|
116f4d94ea | ||
|
|
f18b5b49c5 | ||
|
|
09a4db7c03 | ||
|
|
2616a5776b | ||
|
|
36cfc4a867 | ||
|
|
631f118afb | ||
|
|
b43e3a61e6 | ||
|
|
04100adece | ||
|
|
a103f93925 | ||
|
|
8467340fb5 | ||
|
|
3a8e41de27 | ||
|
|
97c2f183ca | ||
|
|
f1ab35e71e | ||
|
|
240d892b94 | ||
|
|
364d96e7f6 | ||
|
|
03dfd58ac5 | ||
|
|
716023b534 | ||
|
|
67b3417ac9 | ||
|
|
f3667f6aaf | ||
|
|
7b35e71f3d | ||
|
|
51031878eb | ||
|
|
19d96230cd | ||
|
|
dbee7e2894 | ||
|
|
656241da54 | ||
|
|
2a3947ea1d | ||
|
|
9c1b4261dc | ||
|
|
7c37d8128e | ||
|
|
404785bf29 | ||
|
|
dc7fd34b07 | ||
|
|
86a2975b3c | ||
|
|
f824fc7c81 | ||
|
|
244cc30eb6 | ||
|
|
a833ed8060 | ||
|
|
1e869d2d2b | ||
|
|
64df5cfff3 | ||
|
|
d8b2a80e90 | ||
|
|
10061c061e | ||
|
|
6a4124fdf0 | ||
|
|
faaa915163 | ||
|
|
3fb911298d | ||
|
|
761f81d27c | ||
|
|
d4b7c8ae97 | ||
|
|
23679e58e1 | ||
|
|
f3e39f18ef | ||
|
|
55a4cda351 | ||
|
|
f0f270b44d | ||
|
|
529de2bd90 | ||
|
|
51dd83df09 | ||
|
|
9abe4ffd8e | ||
|
|
cdd92f6990 | ||
|
|
b08fa9bf60 | ||
|
|
f0bcc3c3e7 | ||
|
|
b9c45f24f5 | ||
|
|
d5f3967558 | ||
|
|
d3fccc9b87 | ||
|
|
7e6c03faf5 | ||
|
|
02b2a52280 | ||
|
|
6eb708b155 | ||
|
|
83e6444ddc | ||
|
|
168fe4d680 | ||
|
|
0a10c9a7c9 | ||
|
|
204c530712 | ||
|
|
5e55f0debf | ||
|
|
f8bd5a6c86 | ||
|
|
101e7a9a49 | ||
|
|
271e79d6a2 | ||
|
|
2ca8f8aff8 | ||
|
|
bc6891e746 | ||
|
|
26aaeccf11 | ||
|
|
91b512b693 | ||
|
|
88d7917a84 | ||
|
|
7ae0964dd1 | ||
|
|
edb0c305dd | ||
|
|
0321f35869 | ||
|
|
1870f8e90e | ||
|
|
6e112b904f | ||
|
|
dca8d2533e | ||
|
|
c57957b56d | ||
|
|
1f5ab87653 | ||
|
|
d5513e90bd | ||
|
|
00fcd17285 | ||
|
|
356ab4e7cb | ||
|
|
572cb55bf8 | ||
|
|
08b83469e3 | ||
|
|
6939150d2b | ||
|
|
cf9a4d8a26 | ||
|
|
a5f8e4fc58 | ||
|
|
775e325503 | ||
|
|
0c472ba22c | ||
|
|
4c0d15d6ef | ||
|
|
28cea81f61 | ||
|
|
d975dae88f | ||
|
|
30297584d5 | ||
|
|
1105f8e608 | ||
|
|
eeae097a54 | ||
|
|
8199e5a253 | ||
|
|
08c7f4b491 | ||
|
|
634744c22d | ||
|
|
45f4bdd1c5 | ||
|
|
c8f5c80d5b | ||
|
|
5556fafa94 | ||
|
|
20c21cb964 | ||
|
|
d2de889995 | ||
|
|
05cf3ef7fe | ||
|
|
6bd8a8fea8 | ||
|
|
4cba1a2fd2 | ||
|
|
86f0622c4a | ||
|
|
85ad7ca187 | ||
|
|
879279a382 | ||
|
|
74df84e449 | ||
|
|
2862ae3302 | ||
|
|
7d0dfe480c | ||
|
|
c47748ed4d | ||
|
|
396504138c | ||
|
|
6e067a160c | ||
|
|
66a56757d1 | ||
|
|
088ec5580a | ||
|
|
63b2693fe0 | ||
|
|
76a69d8f3b | ||
|
|
5cd0f55cb2 | ||
|
|
db640b04a6 | ||
|
|
0665bcf30a | ||
|
|
7e8ed42f46 | ||
|
|
8d7b1a5b9b | ||
|
|
c6829a7d5e | ||
|
|
bef8fbf183 | ||
|
|
fca95f09cc | ||
|
|
4d6cdf8823 | ||
|
|
e34f52dcc5 | ||
|
|
91cc4e6f3d | ||
|
|
429b7dcb0e | ||
|
|
5ffa681a80 | ||
|
|
91364db608 | ||
|
|
2433c68f27 | ||
|
|
6ea254d9ac | ||
|
|
a4d1df8324 | ||
|
|
ba15611903 | ||
|
|
eda6113792 | ||
|
|
4f1509c836 | ||
|
|
dfcf32ef3e | ||
|
|
4a95257ee2 | ||
|
|
ee1e13ef61 | ||
|
|
c9c099ffc5 | ||
|
|
a80f4fb3c9 | ||
|
|
941f3ac47f | ||
|
|
80d3200d57 | ||
|
|
55926e25d2 | ||
|
|
a31f3ebad7 | ||
|
|
dc9c32c6ee | ||
|
|
dac3aac2a1 | ||
|
|
40196e823c | ||
|
|
7708b7e938 | ||
|
|
8bbc5a9bc6 | ||
|
|
4dbdfb1b77 | ||
|
|
05c51edce3 | ||
|
|
379e8cd95e | ||
|
|
9fd00d4427 | ||
|
|
ca114cf124 | ||
|
|
75128784db | ||
|
|
722f0e277e | ||
|
|
7089998966 | ||
|
|
52211ddfa4 | ||
|
|
2b8b762d35 | ||
|
|
04c228cd36 | ||
|
|
915f8e7700 | ||
|
|
218cea00b5 | ||
|
|
f38f5ea670 | ||
|
|
a9c08c8a9f | ||
|
|
9406c4f718 | ||
|
|
791d8beb06 | ||
|
|
57a9f3aca0 | ||
|
|
ae8bf187c8 | ||
|
|
88f2ab1f8e | ||
|
|
76f5136073 | ||
|
|
0f84cd8949 | ||
|
|
adfdf7606f | ||
|
|
ce671d4294 | ||
|
|
2e7cf7a9df | ||
|
|
f699d93e44 | ||
|
|
531ef4d0b2 | ||
|
|
4c6dbb6690 | ||
|
|
0beb60d1b5 | ||
|
|
893c9d7c42 | ||
|
|
b7a22d94db | ||
|
|
3d854756d1 | ||
|
|
251e7058f1 | ||
|
|
368eb24217 | ||
|
|
f89ef54b82 | ||
|
|
b9d9f8772b | ||
|
|
f2ee03e850 | ||
|
|
92e59a0072 | ||
|
|
5394c0b520 | ||
|
|
e4a9c6f8c2 | ||
|
|
e559d037c3 | ||
|
|
c9909a0869 | ||
|
|
988fc1bbfd | ||
|
|
1831bbcd70 | ||
|
|
ced9bbdbd5 | ||
|
|
15d57d937b | ||
|
|
555979869d | ||
|
|
4e6477297e | ||
|
|
e9e06fc53b | ||
|
|
ecb2818589 | ||
|
|
c442689573 | ||
|
|
88919f2634 | ||
|
|
1a1bc41283 | ||
|
|
4188caed33 | ||
|
|
50557d8bc5 | ||
|
|
0f1ef0406e | ||
|
|
ffc742b460 | ||
|
|
0c04cc6ce6 | ||
|
|
4e6dfed838 | ||
|
|
fdc0524ad2 | ||
|
|
096d5962a3 | ||
|
|
9ea36f94a7 | ||
|
|
2b66df992f | ||
|
|
ff3c31975e | ||
|
|
beaa5eca69 | ||
|
|
b98040faac | ||
|
|
fbbff0df61 | ||
|
|
07d5f393c0 | ||
|
|
8220b15a29 | ||
|
|
7cf638b2be | ||
|
|
9852c87be7 | ||
|
|
30fb8f31e3 | ||
|
|
ec4d875547 | ||
|
|
1268f65516 | ||
|
|
6551b2537a | ||
|
|
43eee63df2 | ||
|
|
4caf9f598a | ||
|
|
107eaca8f0 | ||
|
|
cc11848346 | ||
|
|
b138ccf625 | ||
|
|
27ff7baa6d | ||
|
|
c8ecd34bd3 | ||
|
|
d4a4eb3248 | ||
|
|
f782733397 | ||
|
|
7720eddc47 | ||
|
|
71170a373a | ||
|
|
8f080080a7 | ||
|
|
71b2958c14 | ||
|
|
f69aafcbfa | ||
|
|
5f6a423697 | ||
|
|
5d245927c4 | ||
|
|
17c96fb849 | ||
|
|
b7c28cdf36 | ||
|
|
d05aa0d350 | ||
|
|
875dc086bf | ||
|
|
4719ebd52a | ||
|
|
c8a436a7df | ||
|
|
8d6f4dd64e | ||
|
|
63486927d6 | ||
|
|
c36a70f8d3 | ||
|
|
39472d6874 | ||
|
|
6207e99cc0 | ||
|
|
bdc163199d | ||
|
|
e7a04ede65 | ||
|
|
f6aee4148d | ||
|
|
6cb30a6597 | ||
|
|
34fcfc8f30 | ||
|
|
bc89b33468 | ||
|
|
af9af347e6 | ||
|
|
6f46279b52 | ||
|
|
8f93ed8b8e | ||
|
|
df91363ff4 | ||
|
|
e40e59714d | ||
|
|
a43d95afc3 | ||
|
|
8fa4b5ab22 | ||
|
|
68a030145f | ||
|
|
4f48657623 | ||
|
|
3fc41f4255 | ||
|
|
ff2f7f6586 | ||
|
|
93f33d73d9 | ||
|
|
402c176277 | ||
|
|
79b05b4a66 | ||
|
|
fb9e37e71c | ||
|
|
cd7a741d53 | ||
|
|
5fdc90b4f9 | ||
|
|
e775840abb | ||
|
|
d7df54dfa1 | ||
|
|
646d165d37 | ||
|
|
ba0a80cca4 | ||
|
|
9d59858542 | ||
|
|
aaff0a5697 | ||
|
|
1bb7cee238 | ||
|
|
d330e662d3 | ||
|
|
b530ff76c4 | ||
|
|
b20aa33a04 | ||
|
|
e59a166445 | ||
|
|
980e713735 | ||
|
|
7a5228bbab | ||
|
|
5a26e5c2ee | ||
|
|
a64da4cd85 | ||
|
|
c2dccf17ce | ||
|
|
181d6bf1e4 | ||
|
|
e80bcfc586 | ||
|
|
a07be3b5dc | ||
|
|
af2666c83d | ||
|
|
cad9929b56 | ||
|
|
3b79da527d | ||
|
|
3eaba55823 | ||
|
|
e2eddb6362 | ||
|
|
de5b8160a2 | ||
|
|
83df969751 | ||
|
|
d885570dd8 | ||
|
|
f3de883e39 | ||
|
|
6da22d23a8 | ||
|
|
49461a43bb | ||
|
|
d4414f99af | ||
|
|
52f37b0a37 | ||
|
|
ce949e4029 | ||
|
|
19d3465a13 | ||
|
|
a0e1425c74 | ||
|
|
a5d8379b18 | ||
|
|
9ac487ea5b | ||
|
|
9cc9a93c3d | ||
|
|
4f2b0313de | ||
|
|
806894b24b | ||
|
|
cbb737f96b | ||
|
|
5407f8156a | ||
|
|
a0c2fd09ae | ||
|
|
48b744c8ba | ||
|
|
812b946d85 | ||
|
|
27eca2b470 | ||
|
|
ab6c88bebd | ||
|
|
d547e0552a | ||
|
|
03fc0b84cb | ||
|
|
b0825634ee | ||
|
|
9675e051bd | ||
|
|
981654a2aa | ||
|
|
8c97ea0da0 | ||
|
|
6c80911c90 | ||
|
|
740b5ea719 | ||
|
|
76c958fb7a | ||
|
|
28c53fd115 | ||
|
|
6d9fc8b3df | ||
|
|
a044f4eceb | ||
|
|
1f35efcd51 | ||
|
|
c587abddae | ||
|
|
d7f4c9a329 | ||
|
|
8a3e6f5781 | ||
|
|
cfbebc4989 | ||
|
|
99ec772d64 | ||
|
|
1f60b0bbcb | ||
|
|
c8fb9a5b09 | ||
|
|
d6f117c7bf | ||
|
|
a765668354 | ||
|
|
da053658e8 | ||
|
|
cb5bcd72e4 | ||
|
|
d4d53623ef | ||
|
|
8a31b008d2 | ||
|
|
fa4541b59f | ||
|
|
aae6b32828 | ||
|
|
7158d6735b | ||
|
|
f04fd16c9e | ||
|
|
f4b896d5e6 | ||
|
|
d2f814e6f8 | ||
|
|
87cec67e60 | ||
|
|
041c46f38e | ||
|
|
54a7628f75 | ||
|
|
7cbbc16390 | ||
|
|
14532bf6db | ||
|
|
7bfe18e718 | ||
|
|
07903a07a8 | ||
|
|
14fe3b2b98 | ||
|
|
fa2756ff3f | ||
|
|
9d4841b4dc | ||
|
|
4b74b08825 | ||
|
|
6457cfc958 | ||
|
|
30081c893b | ||
|
|
e69890a47e | ||
|
|
9d31162eec | ||
|
|
0031cc7418 | ||
|
|
57132892b5 | ||
|
|
be084d1570 | ||
|
|
03d29a9ebd | ||
|
|
e9a837b534 | ||
|
|
36c601f9fa | ||
|
|
af41234484 | ||
|
|
bcaf03a770 | ||
|
|
9429b009ce | ||
|
|
3395785cbc | ||
|
|
97c048c7af | ||
|
|
726eb2ffe6 | ||
|
|
a9f22d3a10 | ||
|
|
5a6437e371 | ||
|
|
4ffc98b371 | ||
|
|
16c73d4f91 | ||
|
|
96aff5230f | ||
|
|
b298571be8 | ||
|
|
7b7b10c9a5 | ||
|
|
56beed5ad5 | ||
|
|
8c8ef0ea53 | ||
|
|
90377d98c0 | ||
|
|
d167e474d8 | ||
|
|
0cfacfe057 | ||
|
|
fbda5231d0 | ||
|
|
bd4d333d59 | ||
|
|
07878207de | ||
|
|
b2352877b9 | ||
|
|
669ba9e8cb | ||
|
|
5fa8a76219 | ||
|
|
476dcf051c | ||
|
|
db96dbba6a | ||
|
|
e9c6509970 | ||
|
|
b26c7dfa8f | ||
|
|
3f251761d6 | ||
|
|
bc9d489ee8 | ||
|
|
271563ba9f | ||
|
|
82aba20e83 | ||
|
|
12f60c9be5 | ||
|
|
1c98a354c7 | ||
|
|
9ca7aee859 | ||
|
|
937b306128 | ||
|
|
69a2ab0ce2 | ||
|
|
b720711217 | ||
|
|
bf56348c62 | ||
|
|
f993817cb6 | ||
|
|
1c2b4794f3 | ||
|
|
523d210503 | ||
|
|
39da8bd636 | ||
|
|
fbea98bbe6 | ||
|
|
5d89f724f4 | ||
|
|
0313b6f3ab | ||
|
|
fe92111dc9 | ||
|
|
9a03bfb9e7 | ||
|
|
882db6acc9 | ||
|
|
5596468b03 | ||
|
|
eb66de1220 | ||
|
|
f97896f6a6 | ||
|
|
cfb47f880e | ||
|
|
eba87776a0 | ||
|
|
dda0e6d676 | ||
|
|
8d514644c1 | ||
|
|
0870a34391 | ||
|
|
38118986a3 | ||
|
|
1b4e5247ad | ||
|
|
3ef2eeef93 | ||
|
|
4d5682e79b | ||
|
|
24100d203f | ||
|
|
86946a2c21 | ||
|
|
54f3e4cc6f | ||
|
|
d9360b2d8c | ||
|
|
abb47118c0 | ||
|
|
9e4e55bb8d | ||
|
|
498f720507 | ||
|
|
b9bc9cb75e | ||
|
|
3b65917422 | ||
|
|
862dd78cd5 | ||
|
|
d0bc48d119 | ||
|
|
37462ba282 | ||
|
|
99e7f5862a | ||
|
|
048e529bb5 | ||
|
|
5b8b06f3a1 | ||
|
|
3c7d025802 | ||
|
|
4f8b46da14 | ||
|
|
0b386a882e | ||
|
|
6329d58a67 | ||
|
|
af0d780ac0 | ||
|
|
45c2c863ef | ||
|
|
3372e16409 | ||
|
|
a04fad9c1f | ||
|
|
a8a89dc9bd | ||
|
|
a0305a745a | ||
|
|
f06b702360 | ||
|
|
6f104c4ee6 | ||
|
|
c721580516 | ||
|
|
1fe1381fe2 | ||
|
|
870a25dad1 | ||
|
|
e4e5d5c6d7 | ||
|
|
7e45f23b6c | ||
|
|
719f61f612 | ||
|
|
908edda14b |
@@ -232,6 +232,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "1024mb",
|
||||
"name": "1024mb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9301204?v=4",
|
||||
"profile": "https://github.com/1024mb",
|
||||
"contributions": [
|
||||
"translation"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||
|
||||
# Set it to the URL form where the app home page will be accessed
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
PUBLIC_URL=https://localhost:3001
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
GENERATE_SOURCEMAP=false
|
||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
|
||||
+2
-2
@@ -77,6 +77,6 @@ Engage with the community on our [Gitter Channel](https://gitter.im/OpenSignLabs
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under its [MIT License](LICENSE).
|
||||
By contributing, you agree that your contributions will be licensed under its [AGPL-V3 License](LICENSE).
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
* All content that resides under the "apps/OpenSignServer/cloud/customRoute" directory of this repository, if that directory exists, is licensed under the license defined in "apps/OpenSignServer/cloud/customRoute".
|
||||
* All third party components incorporated into the OpenSign Software are licensed under the original license provided by the owner of the applicable component.
|
||||
* Content outside of the above mentioned directories or restrictions above is available under the "GNU AFFERO GENERAL PUBLIC LICENSE" as defined below -
|
||||
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
|
||||
@@ -147,7 +147,8 @@ We would like to thank all our contributors and users for their support and feed
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/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>
|
||||
<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></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/1024mb"><img src="https://avatars.githubusercontent.com/u/9301204?v=4?s=100" width="100px;" alt="1024mb"/><br /><sub><b>1024mb</b></sub></a><br /><a href="#translation-1024mb" title="Translation">🌍</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -157,3 +158,5 @@ We would like to thank all our contributors and users for their support and feed
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project is tested with BrowserStack.
|
||||
|
||||
|
||||
Generated
+3159
-2070
File diff suppressed because it is too large
Load Diff
+39
-34
@@ -4,16 +4,17 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@lottiefiles/dotlottie-react": "^0.8.7",
|
||||
"@lottiefiles/dotlottie-react": "^0.10.0",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@radix-ui/themes": "^3.1.1",
|
||||
"@react-pdf/renderer": "^3.4.4",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"axios": "^1.7.3",
|
||||
"@radix-ui/themes": "^3.1.6",
|
||||
"@react-pdf/renderer": "^4.1.4",
|
||||
"@reduxjs/toolkit": "^2.3.0",
|
||||
"axios": "^1.7.7",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^23.12.2",
|
||||
"i18next": "^23.16.5",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next-http-backend": "^2.5.2",
|
||||
"i18next-http-backend": "^2.6.2",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"nth-check": "^2.1.1",
|
||||
@@ -22,10 +23,10 @@
|
||||
"print-js": "^1.6.0",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
"react-bootstrap": "^2.10.5",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-datepicker": "^7.3.0",
|
||||
"react-cookie": "^7.2.2",
|
||||
"react-datepicker": "^7.5.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
@@ -33,33 +34,34 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-i18next": "^15.1.1",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-quill-new": "^3.2.2",
|
||||
"react-pdf": "^9.1.1",
|
||||
"react-quill-new": "^3.3.3",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-rnd": "^10.4.11",
|
||||
"react-router-dom": "^6.26.0",
|
||||
"react-rnd": "^10.4.13",
|
||||
"react-router-dom": "^6.28.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-select": "^5.8.3",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-tooltip": "^5.28.0",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
"serve": "^14.2.3",
|
||||
"serve": "^14.2.4",
|
||||
"styled-components": "^5.3.0",
|
||||
"web-vitals": "^4.2.2",
|
||||
"web-vitals": "^4.2.4",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build-webpack": "webpack --config webpack.config.js",
|
||||
"build": "npm run version && npm run build-webpack && react-scripts build",
|
||||
"build-watch": "webpack --config webpack.config.js --watch",
|
||||
"start-dev": "concurrently \"npm run build-watch\" \"react-scripts start\"",
|
||||
"start-dev": "react-scripts start",
|
||||
"start": "serve -s build",
|
||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||
@@ -93,33 +95,36 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/runtime-corejs2": "^7.25.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"babel-loader": "^9.1.3",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/preset-react": "^7.25.9",
|
||||
"@babel/runtime-corejs2": "^7.26.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-loader": "^9.2.1",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"commitizen": "^4.3.1",
|
||||
"concurrently": "^9.1.0",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"daisyui": "^4.12.14",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"lint-staged": "^15.2.8",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"postcss": "^8.4.41",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"lint-staged": "^15.2.10",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"postcss": "^8.4.49",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"tailwindcss": "^3.4.9",
|
||||
"tailwindcss": "^3.4.15",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"overrides": {
|
||||
"nth-check": "$nth-check",
|
||||
"ws": "$ws"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || 22"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,713 @@
|
||||
{
|
||||
"header-news": "Nueva característica: los usuarios del plan Teams ahora pueden integrar sus propios depósitos de AWS S3 para el almacenamiento de archivos",
|
||||
"header-news-btn": "Configurar ahora",
|
||||
"create-account": "Crear cuenta",
|
||||
"login": "Iniciar sesión",
|
||||
"language": "Idioma",
|
||||
"name": "Nombre",
|
||||
"phone": "Teléfono",
|
||||
"phone-optional": "opcional",
|
||||
"email": "Correo",
|
||||
"company": "Compañía",
|
||||
"job-title": "Título del puesto",
|
||||
"profile": "Perfil",
|
||||
"log-out": "Cerrar sesión",
|
||||
"is-email-verified": "El correo está verificado",
|
||||
"public-profile": "Perfil público",
|
||||
"tagline": "Eslogan ",
|
||||
"has-requested-you-to-sign": "ha solicitado que firmes",
|
||||
"has-requested-you-to-review-and-sign": " ha solicitado que revises y firmes",
|
||||
"input-required": "Por favor, rellena este campo.",
|
||||
"sort-order": {
|
||||
"Ascending": "Ascendente",
|
||||
"Descending": "Descendente",
|
||||
"Name": "Nombre",
|
||||
"Date": "Fecha"
|
||||
},
|
||||
"context-menu": {
|
||||
"Download": "Descargar",
|
||||
"Rename": "Renombrar",
|
||||
"Move": "Mover",
|
||||
"Delete": "Eliminar"
|
||||
},
|
||||
"action": "Acción",
|
||||
"add": "Agregar",
|
||||
"add-new-folder": "Agregar nueva carpeta",
|
||||
"disable-documentId": "Desactivar ID de documento",
|
||||
"edit": "Editar",
|
||||
"change-password": "Cambiar contraseña",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"upgrade-now": "Mejorar ahora",
|
||||
"upgrade-to": "Mejorar a",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
||||
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
||||
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
||||
"tour-content": "No mostrar esto otra vez",
|
||||
"pro": "PRO",
|
||||
"team": "EQUIPO",
|
||||
"user": "Usuario",
|
||||
"docs": "Documentación",
|
||||
"session-expired": "Tu sesión ha expirado.",
|
||||
"public-profile-help": "Esta es tu URL pública. Cópiala o compártela con el firmante, y podrás ver todas tus plantillas públicas.",
|
||||
"tagline-help": "Este eslogan aparecerá en tu perfil público de OpenSign, por ejem. https://opensign.me/alex",
|
||||
"welcome": "¡Bienvenido de nuevo!",
|
||||
"Login-to-your-account": "Ingresa a tu cuenta",
|
||||
"password": "Contraseña",
|
||||
"forgot-password": "¿Olvidaste la contraseña?",
|
||||
"loading": "Cargando...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Ingresar con SSO",
|
||||
"login-page": "Página de inicio de sesión",
|
||||
"or": "O",
|
||||
"signup-page": "Página de registro",
|
||||
"password-length": "La contraseña debe tener 8 caracteres",
|
||||
"password-case": "La contraseña debe incluir mayúsculas, minúsculas y números",
|
||||
"password-special-char": " La contraseña debe incluir caracteres especiales",
|
||||
"agreee": "Estoy de acuerdo con los",
|
||||
"term": "Términos de Servicio",
|
||||
"subscribe-to-opensign": "Suscribirse al boletín de OpenSign",
|
||||
"register": "Registrarse",
|
||||
"sidebar": {
|
||||
"Dashboard": "Panel",
|
||||
"Sign yourself": "Firma tu mismo",
|
||||
"Request signatures": "Solicitar firmas",
|
||||
"Templates": "Plantillas",
|
||||
"Templates-Children": {
|
||||
"Create template": "Crear plantilla",
|
||||
"Manage templates": "Administrar plantillas"
|
||||
},
|
||||
"OpenSign™ Drive": "OpenSign™ Drive",
|
||||
"Reports": "Reportes",
|
||||
"Reports-Children": {
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In Progress": "En progreso",
|
||||
"Completed": "Completado",
|
||||
"Drafts": "Borradores",
|
||||
"Declined": "Rechazados",
|
||||
"Expired": "Expirados",
|
||||
"Contactbook": "Agenda de contactos"
|
||||
},
|
||||
"Settings": "Ajustes",
|
||||
"Settings-Children": {
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferencias",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
}
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necesitan tu firma",
|
||||
"Out for signatures": "En espera de ser firmados"
|
||||
},
|
||||
"report-name": {
|
||||
"Draft Documents": "Documentos borradores",
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In-progress documents": "Documentos en progreso",
|
||||
"Completed Documents": "Documentos completados",
|
||||
"Declined Documents": "Documentos rechazados",
|
||||
"Expired Documents": "Documentos expirados",
|
||||
"Recently sent for signatures": "Recientemente enviados para ser firmados",
|
||||
"Recent signature requests": "Solicitudes de firma recientes",
|
||||
"Drafts": "Borradores",
|
||||
"Contactbook": "Agenda de contactos",
|
||||
"Templates": "Plantillas",
|
||||
"Teams": "Equipos",
|
||||
"Users": "Usuarios"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "firmar",
|
||||
"Sign": "FIRMAR",
|
||||
"Resend": "Reenviar",
|
||||
"Revoke": "Revocar",
|
||||
"Delete": "Eliminar",
|
||||
"Use": "Usar",
|
||||
"Quick send": "Envío rápido",
|
||||
"Edit": "Editar",
|
||||
"Share with team": "Compartir con el equipo",
|
||||
"Share": "Compartir",
|
||||
"View": "Ver",
|
||||
"option": "Opción",
|
||||
"Embed": "Incrustar",
|
||||
"Copy TemplateId": "Copiar ID de plantilla",
|
||||
"Copy Public URL": "Copiar URL pública",
|
||||
"extend-expiry-date": "Ampliar fecha de caducidad"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
"Title": "Título",
|
||||
"Email": "Correo",
|
||||
"Phone": "Teléfono",
|
||||
"File": "Archivo",
|
||||
"Owner": "Propietario",
|
||||
"Signers": "Firmantes",
|
||||
"Note": "Nota",
|
||||
"Folder": "Carpeta",
|
||||
"Reason": "Razón",
|
||||
"Parent Team": "Equipo padre",
|
||||
"Active": "Activo",
|
||||
"Role": "Rol",
|
||||
"Team": "Equipo",
|
||||
"Name": "Nombre",
|
||||
"Status": "Estado",
|
||||
"created-date": "Fecha de creación",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Registros",
|
||||
"Expiry-date": "Date d'expiration"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
|
||||
"Need your sign": "Esta es una lista de documentos que esperan tu firma.",
|
||||
"In-progress documents": "Esta es una lista de documentos que has enviado a otras partes para su firma.",
|
||||
"Completed Documents": "Esta es una lista de documentos en los que todas las partes han firmado.",
|
||||
"Declined Documents": "Esta es una lista de documentos que han sido rechazados por uno de los firmantes.",
|
||||
"Expired Documents": "Esta es una lista de documentos que han alcanzado su fecha de caducidad.",
|
||||
"Contactbook": "Esta es una lista de contactos/firmantes añadidos por ti. Aparecerán como sugerencias cuando intentes añadir firmantes a un nuevo documento.",
|
||||
"Templates": "Esta es una lista de plantillas que están a tu disposición para crear documentos. Puedes hacer clic en el botón «usar» para crear un nuevo documento utilizando una plantilla, modifica el documento y añade firmantes en el siguiente paso."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla"
|
||||
},
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "archivo seleccionado",
|
||||
"template-title": "Título de la plantilla",
|
||||
"document-title": "Título del documento",
|
||||
"description": "Descripción",
|
||||
"time-to-complete": "Plazo de finalización (días)",
|
||||
"send-in-order": "Enviar en orden",
|
||||
"send-in-order-help": {
|
||||
"p1": "Elige cómo quieres que se envíen las solicitudes de firma a los firmantes del documento:",
|
||||
"p2": "Seleccionando esta opción se enviará inicialmente la solicitud de firma al primer firmante. Una vez que el primer firmante complete su parte, el siguiente firmante en la secuencia recibirá la solicitud. Este proceso continúa hasta que todos los firmantes hayan firmado el documento. Este método garantiza que el documento se firme en un orden específico.",
|
||||
"p3": "Seleccionando esta opción se enviarán los enlaces para la firma a todos los firmantes simultáneamente. Cada firmante puede firmar el documento cuando le convenga, independientemente de si otros firmantes han completado sus firmas. Este método es más rápido, pero no impone ningún orden de firma entre los participantes.",
|
||||
"p4": "Selecciona la opción que mejor se adapte a las necesidades de tu tramitación de documentos."
|
||||
},
|
||||
"no": "No",
|
||||
"auto-reminder": "Recordatorio automático",
|
||||
"remind-once": " Recordar una vez cada (Días)",
|
||||
"next": "Siguiente",
|
||||
"select-folder": "Seleccionar carpeta",
|
||||
"OpenSign-drive": "OpenSign™ Drive",
|
||||
"drive-document-status": {
|
||||
"Completed": "Completado",
|
||||
"Declined": "Rechazado",
|
||||
"Draft": "Borrador",
|
||||
"Expired": "Expirado",
|
||||
"In Progress": "En progreso"
|
||||
},
|
||||
"select-folder-help": "Si no seleccionas una carpeta, tu documento firmado se guardará en la carpeta principal de OpenSign Drive.",
|
||||
"no-data": "No se encontraron datos",
|
||||
"save-here": "Guardar aquí",
|
||||
"back": "Atrás",
|
||||
"add-folder": "Añadir carpeta",
|
||||
"create-folder": "Crear carpeta",
|
||||
"parent-folder": "Carpeta superior",
|
||||
"create": "Crear",
|
||||
"signers": "Firmantes",
|
||||
"signers-help": "Empieza a escribir el nombre de un contacto para ver los firmantes sugeridos entre tus contactos guardados o añade otros nuevos. Organiza el orden de firma añadiendo firmantes en la secuencia deseada. Utiliza el botón «+» para añadir nuevos firmantes y la «X» para quitar todos. Cada firmante recibirá una solicitud por correo electrónico para que firme el documento según el orden indicado.",
|
||||
"add-signer": "Añadir firmante",
|
||||
"contact-not-found": "Contacto no encontrado",
|
||||
"add-yourself": "Añadirte a ti mismo",
|
||||
"submit": "Enviar",
|
||||
"reset": "Volver a empezar",
|
||||
"my-signature": "Mi firma",
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen",
|
||||
"initials": "Iniciales",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
"regenerate-token": "Regenerar token activo",
|
||||
"generate-token": "Generar token activo",
|
||||
"view-docs": "Ver documentación",
|
||||
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
||||
"yes": "Sí",
|
||||
"copied": "Copiado",
|
||||
"something-went-wrong-mssg": "Algo salió mal, por favor, intenta de nuevo más tarde.",
|
||||
"token-generated": "Token generado exitosamente.",
|
||||
"webhook": "Webhook",
|
||||
"update-webhook": "Actualizar webhook",
|
||||
"add-webhook": "Agregar webhook",
|
||||
"webhook-added": "Webhook agregado exitosamente.",
|
||||
"team-disabled": "Equipo deshabilitado",
|
||||
"Team enabled": "Equipo habilitado",
|
||||
"are-you-sure": "¿En definitiva quieres ",
|
||||
"disable": "deshabilitar",
|
||||
"enable": "habilitar",
|
||||
"this-team": " este equipo?",
|
||||
"edit-team": "Editar equipo",
|
||||
"name-of-team": "Nombre del equipo",
|
||||
"prev": "Anterior",
|
||||
"no-data-avaliable": "No hay datos disponibles",
|
||||
"add-team": "Agregar equipo",
|
||||
"page-not-found": "Página no encontrada",
|
||||
"users-from-teams": "usuarios de equipos",
|
||||
"user-status": "Estado del usuario",
|
||||
"activate": "activar",
|
||||
"deactivate": "desactivar",
|
||||
"this-user": " este usuario",
|
||||
"delete-user": "Eliminar usuario",
|
||||
"delete": "eliminar",
|
||||
"add-user": "Agregar usuario",
|
||||
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
|
||||
"Team status": "Estado del equipo",
|
||||
"user-deactivated": "Usuario desactivado.",
|
||||
"user-activated": "Usuario activado.",
|
||||
"public": "Público",
|
||||
"download": "Descargar",
|
||||
"view": "Ver",
|
||||
"make-template-public": "Convertir la plantilla en pública",
|
||||
"make-template-private": "Convertir la plantilla en privada",
|
||||
"make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?",
|
||||
"make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.",
|
||||
"public-role": "Rol público",
|
||||
"public-url": "Perfil publico",
|
||||
"embed-template": "Insertar plantilla",
|
||||
"public-url-copy": "Aquí está tu URL pública: ",
|
||||
"public-url-copy-mssg": "Cópiala o compártela con el firmante y podrás ver todas tus plantillas públicas.",
|
||||
"add-public-url-alert": "Por favor, completa la configuración de tu perfil público OpenSign. Una vez terminado, podrás crear plantillas públicas. Este proceso solo necesita ser completado una vez.",
|
||||
"share-with-alert": "No puedes compartir una plantilla si alguno de los roles ya tiene contactos asignados. Elimina todas las asignaciones de contactos de los roles antes de compartir la plantilla.",
|
||||
"share-with": "Compartir con",
|
||||
"team-not-found": "Equipo no encontrado",
|
||||
"roles": "Roles",
|
||||
"delete-document": "Eliminar documento",
|
||||
"delete-document-alert": "¿En definitiva quieres eliminar este documento?",
|
||||
"quick-send": "Envío rápido",
|
||||
"add-new": "Agregar nuevo",
|
||||
"send": "Enviar",
|
||||
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
||||
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
||||
"quick-send-alert-3": "Por favor, añade al menos un rol a esta plantilla para poder hacer un «envío rápido» de copias a varios firmantes.",
|
||||
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
||||
"copy-link": "Copiar enlace",
|
||||
"copy": "Copiar",
|
||||
"revoke-document": "Revocar documento",
|
||||
"revoke-document-alert": "¿En definitiva quieres revocar este documento?",
|
||||
"resend-mail": "Reenviar correo",
|
||||
"resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
"document-sent-alert": "¡Documento enviado exitosamente!",
|
||||
"template-share-alert": "Plantilla compartida exitosamente.",
|
||||
"template-public-alert-1": "Has convertido exitosamente la plantilla en pública.",
|
||||
"template-public-alert-2": "Has convertido exitosamente la plantilla en privada.",
|
||||
"template-public-alert-3": "Tienes que seleccionar un rol para los firmantes públicos.",
|
||||
"template-public-alert-4": "Por favor, asegúrate de que has añadido al menos un widget de firma para cada firmante.",
|
||||
"template-public-alert-5": "Dado que esta plantilla tiene «enviar en orden» habilitado, el rol público debe estar posicionado arriba al inicio.",
|
||||
"template-public-alert-6": "Por favor, enlaza firmantes a todos los roles excepto al usuario de rol público.",
|
||||
"template-public-alert-7": "Por favor, asigna al menos un rol público para convertir está plantilla en pública.",
|
||||
"loader": " Esto podría llevar algún tiempo",
|
||||
"pages": "Páginas",
|
||||
"document-signed": "Documento firmado",
|
||||
"close": "Cerrar",
|
||||
"validation-alert": "Alerta de validación",
|
||||
"validate-alert-mssg": " Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"otp-verification": "Verificación OTP",
|
||||
"enter-otp": "Ingresar OTP",
|
||||
"verify": "Verificar",
|
||||
"verify-email-1": "Verificar correo",
|
||||
"resend": "Reenviar",
|
||||
"contact-details": "Detalles del contacto",
|
||||
"verify-email": "¡Por favor, verifica tu correo!",
|
||||
"send-otp": " Enviar OTP",
|
||||
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
||||
"loading-doc": "Cargando documento...",
|
||||
"widgets-name": {
|
||||
"signature": "firma",
|
||||
"stamp": "sello",
|
||||
"initials": "iniciales",
|
||||
"name": "nombre",
|
||||
"job title": "título del puesto",
|
||||
"company": "compañía",
|
||||
"date": "fecha",
|
||||
"text": "texto",
|
||||
"text input": "entrada de texto",
|
||||
"checkbox": "casilla",
|
||||
"dropdown": "desplegable",
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
"please-add": "Por favor, agrega un",
|
||||
"signed-by": "Firmado por",
|
||||
"more": "más",
|
||||
"sent": "enviado",
|
||||
"print": "Imprimir",
|
||||
"certificate": "Certificado",
|
||||
"decline": "Rechazar",
|
||||
"finish": "Finalizar",
|
||||
"mail": "Correo",
|
||||
"sign-now": "Firmar ahora",
|
||||
"successfully-signed": "¡Firmado exitosamente!",
|
||||
"email-mssg": " Los destinatarios agregados aquí recibirán una copia del documento firmado.",
|
||||
"email-error-1": " por favor, proporciona una dirección de correo correcta",
|
||||
"email-error-2": "Solo puedes enviar hasta a 10 destinatarios a la vez.",
|
||||
"Email-verified-alert-1": "El correo está verificado.",
|
||||
"Email-verified-alert-2": "El correo ya ha sido verificado.",
|
||||
"upload-stamp-image": "Subir imagen del sello",
|
||||
"draw": "Dibujar",
|
||||
"type": "Escribir",
|
||||
"color-type": {
|
||||
"red": "rojo",
|
||||
"blue": "azul",
|
||||
"black": "negro",
|
||||
"yellow": "amarillo"
|
||||
},
|
||||
"my-initials": "Mis iniciales",
|
||||
"upload": "Subir",
|
||||
"initial-teb": "Iniciales",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "Tu firma",
|
||||
"copy-title": "Copiar widget a",
|
||||
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?",
|
||||
"reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
|
||||
"reset-password-alert-2": "Por favor, configura un adaptador de correo ",
|
||||
"reset-password-alert-3": "Restablece tu contraseña",
|
||||
"faild-animation": "Error al cargar la animación",
|
||||
"apply": "Aplicar",
|
||||
"copy-type": {
|
||||
"All pages": "Todas las páginas",
|
||||
"All pages but last": "Todas las páginas menos la última",
|
||||
"All pages but first": "Todas las páginas menos la primera",
|
||||
"Next to current widget": "Junto al widget actual"
|
||||
},
|
||||
"options": "Opciones",
|
||||
"minimun-check": "Chequeo mínimo",
|
||||
"maximum-check": "Chequeo máximo",
|
||||
"default-value": "Valor por defecto",
|
||||
"select": "Seleccionar",
|
||||
"read-only": "Es de solo lectura",
|
||||
"hide-labels": "Esconder etiquetas",
|
||||
"checkbox": "Casilla",
|
||||
"alert": "Alerta",
|
||||
"zoom-in": "Acercar",
|
||||
"zoom-out": "Alejar",
|
||||
"document-signed-alert": "¡Felicitaciones! 🎉 ¡Este documento ha sido firmado por ti exitosamente!",
|
||||
"otp-sent-alert": "OTP enviado a tu correo",
|
||||
"fields-required": "Campos requeridos",
|
||||
"signature-widget-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma agregado",
|
||||
"signature-widget-alert-2": "Por favor, asegúrate de que todos los campos están correctamente rellenados y de que cumplen todos los requisitos.",
|
||||
"encrypted-pdf-alert": "Actualmente los archivos PDF encriptados no están soportados.",
|
||||
"encrypted-pdf-alert-1": "La contraseña del archivo PFX es inválida.",
|
||||
"user-not-exist": "El usuario no existe",
|
||||
"template-signature-field": "Por favor, agrega al menos un campo de firma para todos los roles.",
|
||||
"template-role-alert": "Por favor, primero agrega roles",
|
||||
"create-document": "Crear documento",
|
||||
"template-created-alert": "¿Quieres crear un documento usando la plantilla que acabas de crear?",
|
||||
"signers-alert": "alerta de firmantes",
|
||||
"template-creation-alert-1": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"ok": "OK",
|
||||
"radio-group": "Grupo de radio",
|
||||
"dropdown-options": "Opciones del desplegable",
|
||||
"add-role": "Agregar rol",
|
||||
"role-ex": " por ejem.: Cliente, RRHH, Director, Administrador, Estudiante, etc.",
|
||||
"add/choose-signer": "Agregar/Elegir firmante",
|
||||
"choose-from-contacts": "Elegir desde los contactos",
|
||||
"select-signer": " Por favor, selecciona el firmante",
|
||||
"edit-template": "Editar plantilla",
|
||||
"widget-info": "Información del widget",
|
||||
"validation": "Validación",
|
||||
"invalid-default-value": "valor por defecto inválido",
|
||||
"hint": "Pista",
|
||||
"color": "color",
|
||||
"role-help": {
|
||||
"p1": "¿Qué son los roles de una plantilla?",
|
||||
"p2": "Empieza por especificar cada uno de los roles necesarios para completar el documento. Piensa en las partes implicadas en el proceso de firma y cuáles son sus responsabilidades. Los roles más habituales son RRHH para los documentos internos, Cliente para los contratos o Proveedor para los convenios comerciales.",
|
||||
"p3": "¿Por qué preasignar usuarios a algunos roles?",
|
||||
"p4": "Para los roles que consistentemente involucran al mismo individuo (por ejem., la firma del Gerente en las ofertas de empleo), puedes preasignar un usuario a un rol dentro de una plantilla. Este paso es opcional pero recomendado para la eficiencia y consistencia entre documentos",
|
||||
"p5": "¿Cuando especifico el usuario asignado a cada rol?",
|
||||
"p6": "Cuando crees un documento usando tu plantilla, se te pedirá que asignes usuarios a cada rol definido. Si un rol ya tiene un usuario asignado, este será rellenado con anticipación, pero puedes modificarlo según lo necesites antes de enviar el documento."
|
||||
},
|
||||
"add-recipients": "Agregar destinatarios",
|
||||
"loading-mssg": "Esto podría llevar algún tiempo",
|
||||
"send-mail": "Enviar correo",
|
||||
"placeholder-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma para cada destinatario.",
|
||||
"placeholder-alert-2": "Por favor, confirma que hayas rellenado el campo de texto.",
|
||||
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
|
||||
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
|
||||
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"email-subject": "asunto del correo",
|
||||
"email-body": "cuerpo del correo",
|
||||
"email-placeholder": "agregar cuerpo del correo",
|
||||
"reset-to-default": "Restablecer a por defecto",
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"mails-sent": "Correos enviados",
|
||||
"mail-not-delivered": "correo no entregado",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
"alert-message": "Mensaje de alerta",
|
||||
"document-decline": "Rechazar documento",
|
||||
"decline-alert-1": "¿En definitiva quieres rechazar este documento?",
|
||||
"decline-alert-2": "¡Has rechazado este documento!",
|
||||
"decline-alert-3": "No puedes firmar este documento ya que ha sido rechazado/revocado.",
|
||||
"sign here": "Firma aquí",
|
||||
"guest-email-alert": "el código de verificación ha sido enviado a tu correo",
|
||||
"get-otp-alert": "recibirás un código de verificación por correo",
|
||||
"user-already-exist": "El usuario ya existe.",
|
||||
"server-error": "¡Error interno del servidor!",
|
||||
"fill-required-details!": "¡Por favor, rellena los detalles requeridos!",
|
||||
"sign-up-form": "Formulario de registro",
|
||||
"sign-up": "Registrarse",
|
||||
"folder-already-exist!": "¡la carpeta ya existe!",
|
||||
"please-select-position!": "¡por favor, selecciona una posición!",
|
||||
"default-sign-alert": "¿En definitiva quieres firmar automáticamente en todos los lugares solicitados?",
|
||||
"Your-Signature": "Tu firma",
|
||||
"auto-sign-all": "Firmar todo automáticamente",
|
||||
"generating-certificate": "Generando certificado",
|
||||
"pdf-download": "Descargar PDF",
|
||||
"generate-certificate-alert": " Tu certificado de finalización se está generando. Por favor, espera un momento. Si la descarga no inicia pronto, presiona el botón otra vez.",
|
||||
"folder-already-exist": "¡La carpeta ya existe!",
|
||||
"folder-created-successfully": "¡Carpeta creada exitosamente!",
|
||||
"fill-folder-name": "Por favor, rellena el nombre de la carpeta",
|
||||
"already-exists-this-username": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"verification-code-sent": "¡El correo de verificación ha sido enviado a tu correo!",
|
||||
"registered-user-successfully": "Usuario registrado exitosamente.",
|
||||
"role-not-found": "Rol no encontrado.",
|
||||
"do-not-access": "No tienes acceso, por favor, contacta al administrador.",
|
||||
"add-admin": "Agregar administrador",
|
||||
"opensign-setup": "Instalación de OpenSign",
|
||||
"join-discord": "Únete a nuestro servidor de Discord",
|
||||
"admin-already-exist": "El administrador ya existe.",
|
||||
"password-update-alert-1": "Contraseña actualizada exitosamente.",
|
||||
"password-update-alert-2": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-3": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-4": "Tu contraseña y su confirmación no son iguales.",
|
||||
"current-password": "Contraseña actual",
|
||||
"new-password": "Contraseña nueva",
|
||||
"confirm-password": "Confirmar contraseña",
|
||||
"file-alert-1": "El tamaño del archivo seleccionado es muy grande. Por favor, selecciona un archivo con un tamaño menor a",
|
||||
"file-alert-2": "Por favor, selecciona un archivo.",
|
||||
"file-alert-3": "Por favor, espera mientras se sube el documento.",
|
||||
"enter-pdf-password": "Ingresa la contraseña del PDF",
|
||||
"correct-password": "Por favor, proporciona la contraseña correcta",
|
||||
"decrypting-pdf": " Desencriptando PDF, por favor, espera...",
|
||||
"invalid-otp": "OTP inválido",
|
||||
"user-not-found": "¡Usuario no encontrado!",
|
||||
"enter-otp-alert": "¡Por favor, ingresa el OTP!",
|
||||
"get-verification-code": "Obtener código de verificación",
|
||||
"get-verification-code-2": "Obtendrás un código de verificación por correo",
|
||||
"enter-verification-code": "Ingresar código de verificación",
|
||||
"provide-your-details": "Proporciona tus detalles",
|
||||
"provide-email": "Por favor, proporciona un correo.",
|
||||
"additional-info": "Información adicional",
|
||||
"signature-saved-alert": "Firma guardada exitosamente.",
|
||||
"fill-field": "Por favor, rellena este campo",
|
||||
"error-template": "Error: ¡Plantilla no encontrada!",
|
||||
"error": "Error",
|
||||
"invalid-document": "Error: ¡Documento inválido!",
|
||||
"document-not-found": "¡Documento no encontrado!",
|
||||
"document-signed-alert-1": "Ha firmado exitosamente el documento. Puede descargar o imprimir una copia del documento parcialmente firmado. Se enviará una copia del documento firmado digitalmente y del certificado de finalización al propietario y a todos los firmantes por correo electrónico una vez que todos los firmantes lo hayan firmado.",
|
||||
"encrypted-pdf-not-support": "Los archivos PDF encriptados no están soportados actualmente.",
|
||||
"document-signed-alert-4": " ¡Felicitaciones! 🎉 ¡Este documento ha sido firmado exitosamente por todos los participantes!",
|
||||
"document-signed-alert-5": "Este documento ha sido firmado por todos los firmantes.",
|
||||
"document-signed-alert-6": "Este documento ha sido rechazado por uno o más destinatarios.",
|
||||
"document-signed-alert-7": "Este documento ya no está disponible.",
|
||||
"document-signed-alert-8": "Este documento ya ha sido enviado para ser firmado.",
|
||||
"yet-to-sign": "Todavía por firmar",
|
||||
"validation-alert-1": "Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"account-already-exists": "¡La cuenta ya existe!",
|
||||
"choose-password": "Elegir contraseña",
|
||||
"enter-password": "Ingresar contraseña",
|
||||
"sure": "claro",
|
||||
"subscribed-alert": "¡Ya te has suscrito al plan!",
|
||||
"monthly": "Mensual",
|
||||
"yearly-upto": "Anual (hasta 66% de descuento)",
|
||||
"billed-yearly": "Facturado anualmente",
|
||||
"billed-monthly": "Facturado mensualmente",
|
||||
"plansubscription-1": "Alójalo por tu cuenta gratuitamente",
|
||||
"visit-github": "Visitar GitHub",
|
||||
"user-already-exist-name": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"do-not-access-contact-admin": "No tienes acceso, por favor, contacta a un administrador.",
|
||||
"filed-required-correctly": "Por favor, rellena correctamente los detalles requeridos.",
|
||||
"admin-created": "Administrador creado",
|
||||
"invalid-masterkey": "Llave maestra inválida",
|
||||
"opensign-Setup": "Instalación de OpenSign",
|
||||
"master-key": " Llave maestra",
|
||||
"profile-update-alert": "Perfil actualizado exitosamente.",
|
||||
"date": "Fecha",
|
||||
"report-not-found": "Reporte no encontrado",
|
||||
"please-wait": "por favor, espera",
|
||||
"select-foler": "Seleccionar carpeta",
|
||||
"generated-on": "Generado el",
|
||||
"text-field": "Campo de texto",
|
||||
"font-size": "Tamaño de la fuente",
|
||||
"widget-status": {
|
||||
"Required": "Requerido",
|
||||
"Optional": "Opcional"
|
||||
},
|
||||
"all-right": "Todos los derechos reservados",
|
||||
"version": "versión",
|
||||
"social-media": {
|
||||
"github": "GitHub",
|
||||
"linked-in": "LinkedIn",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord"
|
||||
},
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.",
|
||||
"home-layout-2": "Para subir documentos para firmar tú mismo o para solicitar la firma de otros, simplemente selecciona los botones correspondientes.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar OpenSign! Si necesitas soporte siéntete libre de contactarnos.",
|
||||
"generate-token": "Mejora ahora para generar un token API de producción.",
|
||||
"opensign-drive-1": "Haz clic en los enlaces para navegar fácilmente por la estructura jerárquica de carpetas y ver los documentos de cada carpeta.",
|
||||
"opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
|
||||
"opensign-drive-3": "Ordena tus documentos por «Fecha» o «Nombre» usando este menú.",
|
||||
"opensign-drive-4": "Haz clic en este menú para mostrar los documentos en vista de lista.",
|
||||
"opensign-drive-5": "La lista de documentos es mostrada de acuerdo a la opción de ordenamiento seleccionada. Los íconos situados junto a cada documento indican su estado actual.",
|
||||
"opensign-drive-6": "Haz clic con el botón derecho en un documento para ver opciones como «Descargar», «Renombrar», «Mover» y «Eliminar». Haz clic en el documento para abrirlo.",
|
||||
"opensign-drive-7": "Haz clic con el botón derecho en cualquier carpeta para ver las opciones. Elige «Renombrar» para cambiar el nombre de la carpeta o haz clic en ella para navegar por su contenido.",
|
||||
"pdf-request-file-1": "Lista de firmantes que todavía tienen que firmar este documento.",
|
||||
"pdf-request-file-2": "Haz clic en cualquiera de los marcadores de posición que aparecen en el documento para firmar. A continuación, verás opciones para dibujar tu firma, escribirla o subir una imagen .",
|
||||
"pdf-request-file-3": "Haz clic en los botones «Atrás», «Rechazar» o «Finalizar» para navegar por el documento. Utiliza el menú de tres puntos para ver otras opciones, como el botón «Descargar».",
|
||||
"pdf-request-file-4": "Lista de firmantes que ya han firmado el documento.",
|
||||
"pdf-request-file-5": "Puedes hacer clic en «Firmar todo automáticamente» para firmar de manera automática en todos los lugares que deban ser firmados por ti. Asegúrate de revisar correctamente el documento antes de hacer clic en ese botón.",
|
||||
"placeholder-sign-1": "Selecciona un destinatario de esta lista para agregar un marcador de posición dónde este destinatario tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del destinatario una vez lo sueltes en el documento.",
|
||||
"placeholder-sign-2": "Hacer clic en el botón «Agregar destinatarios» te permitirá añadir más firmantes.",
|
||||
"placeholder-sign-3": "Haz clic en este botón para agregar más destinatarios/firmantes al documento.",
|
||||
"placeholder-sign-4": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los marcadores de posición existentes en la plantilla. Para tu comodidad, estos marcadores de posición coincidirán con el color del nombre del destinatario, haciéndolos fácilmente identificables.",
|
||||
"placeholder-sign-6": "Hacer clic en «Enviar» guardará el documento. En el siguiente paso podrás personalizar los correos que serán enviados a los destinatarios o copiar los enlaces para firmar y compartirlos con los destinatarios por tu cuenta.",
|
||||
"report-1": "Haz clic en el botón «Agregar» para crear una nueva plantilla. Las plantillas son documentos reusables diseñados para generar rápidamente nuevos documentos con la misma estructura pero diferentes firmantes. Por ejemplo, una plantilla de RR.HH. para el proceso de incorporación podría tener roles predefinidos como «Director de RR.HH.» y «Nuevo empleado». Cada vez que utilices la plantilla, puedes asignar el rol de «Nuevo empleado» a los distintos miembros del personal que se incorporen, mientras que el rol de « Director de RR.HH.» permanece constante, lo que facilita un proceso de incorporación fluido para cada persona contratada.",
|
||||
"redirect": "Haz clic en el botón «Usar» para crear un nuevo documento desde una plantilla existente.",
|
||||
"bulksend": "Para crear y enviar rápidamente múltiples documentos usando una plantilla existente, haz clic en el botón «Envío rápido».",
|
||||
"option": "Este menú revela más opciones como «Editar» y «Eliminar». Usa el botón «Editar» para agregar roles, modificar campos y actualizar tu plantilla. Los cambios aplicarán para todos los documentos futuros que sean creados usando esta plantilla pero no afectarán a los documentos ya existentes. Con el botón «Eliminar» puedes eliminar la plantilla.",
|
||||
"signyour-self-1": "Para personalizar el documento antes de que sea firmado, selecciona y arrastra al documento los widgets que prefieras. Escoge el espacio perfecto para cada modificación para adaptar el documento a tus necesidades.",
|
||||
"signyour-self-2": "Arrastra y suelta en cualquier lugar de esta área. Después podrás cambiar el tamaño y moverlo.",
|
||||
"template-placeholder-1": "Hacer clic en el botón «Agregar rol» te permitirá agregar varios roles de firmantes. Puedes asociar usuarios a cada rol en los siguientes pasos.",
|
||||
"template-placeholder-2": "Una vez que los roles hayan sido agregados, selecciona un rol de la lista para agregar un marcador de posición donde éste tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del rol una vez lo sueltes en el documento.",
|
||||
"template-placeholder-3": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"template-placeholder-4": "Arrastra el marcador de posición para un rol a cualquier lugar del documento. Recuerda, aparecerá con el mismo color que el nombre del rol para poder referenciarlo fácilmente.",
|
||||
"template-placeholder-5": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"webhook-1": "Mejora ahora para configurar un webhook",
|
||||
"Need your Signature": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan por tu revisión.",
|
||||
"Out for signatures": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan tu firma.",
|
||||
"Recent signature requests": "Esta es una lista de documentos que esperan por tu firma.",
|
||||
"Recently sent for signatures": "Esta es una lista de documentos que has enviado a otras partes para que sea firmado.",
|
||||
"Drafts": "Estos son documentos que has empezado pero que todavía no finalizas para su envío.",
|
||||
"public-template": "Este video muestra cómo puedes configurar tu perfil público personalizado, como «https://opensign.me/tu-nombre-de-usuario». También aprenderás cómo personalizar tu eslogan y cómo hacer tus plantillas disponibles para su firmado público."
|
||||
},
|
||||
"enter-email-plaholder": "Agrega una dirección de correo y presiona «Enter»",
|
||||
"success-email-alert": "¡Correo enviado exitosamente!",
|
||||
"expired-doc-title": "Documento expirado",
|
||||
"expired-on-mssg": "Este documento expiro el {{expiredDate}} y ya no está disponible para ser firmado.",
|
||||
"signature-validate-alert": "Por favor, confirma que has seleccionado al menos {{minRequiredCount}} casillas.",
|
||||
"signature-validate-alert-2": "Asegúrate de que este campo esté correctamente rellenado y que cumpla con todos los requisitos.",
|
||||
"remaing-users": "Usuarios restantes",
|
||||
"Role": "Rol",
|
||||
"additional-users": "Por favor, compra el complemento Usuarios.",
|
||||
"Quantity-of-users": "Cantidad de usuarios",
|
||||
"Price": "Precio",
|
||||
"Total-price-for-next-time": "Precio total para la próxima vez",
|
||||
"Proceed": "Proceder",
|
||||
"Select": "Seleccionar",
|
||||
"Add-seats": "Agregar plazas",
|
||||
"user-name-exist": "el nombre de usuario ya existe",
|
||||
"format": "formato",
|
||||
"select-date-format": "Selecciona un formato de fecha",
|
||||
"quantity-of-credits": "Cantidad de créditos premium",
|
||||
"remaining-credits": "Créditos premium disponibles:",
|
||||
"remaining-credits-help": "Usa créditos premium para la API para la firma de documentos, envío en masa, o para incorporar la integración de OpenSign en tu página web. Te quedan {{allowedcredits}} créditos incluidos y {{addoncredits}} créditos comprados adicionalmente.",
|
||||
"additional-credits": "Por favor, compra créditos premium",
|
||||
"quota-err-quicksend": "Cuota alcanzada, no tienes créditos suficientes.",
|
||||
"buy-credits": "Comprar créditos premium",
|
||||
"rotate-right": "Rotar a la derecha",
|
||||
"rotate-left": "Rotar a la izquierda",
|
||||
"rotate-alert-mssg": "Todos los widgets de esta página se perderán. ¿En definitiva quieres proceder?",
|
||||
"templateid": "ID de plantilla",
|
||||
"bulk-send-subcription-alert": "Por favor, mejora al plan a Professional o Team para usar envío en masa.",
|
||||
"generate-test-token": "Generar token de prueba",
|
||||
"regenerate-test-token": "Regenerar token de prueba",
|
||||
"help-test-token": "Este token puede ser usado para probar las API en el punto de acceso «https://sandbox.opensignlabs.com/api/v1», permitiéndote realizar firmas de documentos ilimitadas. Por favor, ten en cuenta que el API de pruebas firmará tus documentos con certificados autofirmados, que podrían no ser reconocidos por Adobe como válidos. Una vez hayas completado tus pruebas, puedes mejorar a uno de nuestros planes de pago para generar un token de producción.",
|
||||
"help-api-token": "Este token puede ser usado para acceder a las API de producción en el punto de acceso «{{origin}}/api/v1». Solo puede ser generado con uno de nuestros planes de pago.",
|
||||
"reason": "Razón",
|
||||
"decline-by": "Rechazado/revocado por",
|
||||
"document-declined": "Documento rechazado",
|
||||
"public-template-mssg-1": "Para integrar OpenSign a tu proyecto React o Next.js, simplemente ejecuta los siguientes comandos:",
|
||||
"public-template-mssg-2": "Asegúrate de tener «npm» o «yarn» configurado en tu proyecto. Si estás usando «yarn», puedes reemplazar «npm install» con «yarn add @opensign/react».",
|
||||
"public-template-mssg-3": "¿Necesitas más detalles o ejemplos?",
|
||||
"public-template-mssg-4": "Visita la",
|
||||
"public-template-mssg-5": " «npm» para las últimas actualizaciones, documentación detallada e historial de versiones.",
|
||||
"public-template-mssg-6": "Antes de que puedas usar este fragmento de código, debes convertir esta plantilla en pública.",
|
||||
"copy-code": "COPIAR",
|
||||
"copied-code": "COPIADO",
|
||||
"Installation": "Instalación",
|
||||
"Usage": "Uso",
|
||||
"insufficient-credits": "Créditos de firma insuficientes",
|
||||
"insufficient-credits-mssg": "El propietario de este documento actualmente carece de los créditos OpenSign necesarios para que puedas firmarlo. Por favor, contacta al propietario si necesitas más detalles.",
|
||||
"angular-npm-mssg-1": "Para integrar OpenSign a tu proyecto Angular, simplemente ejecuta los siguientes comandos:",
|
||||
"quota-mail-info-head": "Límite mensual de solicitudes de firma por correo",
|
||||
"quota-mail-info": "Puedes enviar hasta 15 solicitudes de firma por correo cada mes. Mejora ahora para enviar solicitudes de firma sin límite.",
|
||||
"quota-mail-reset": "Los créditos de tu correo electrónico de solicitud de firma se restablecerán el",
|
||||
"quota-mail": "Has alcanzado tu límite de 15 solicitudes de firma por correo para este mes. Mejora ahora para continuar enviando correos.",
|
||||
"quota-mail-tip": "Consejo: Todavía puedes solciitar la firma de <1>documentos ilimitados</1> compartiendo manualmente los enlaces de solicitud de firma.",
|
||||
"quota-mail-head": "Cuota alcanzada",
|
||||
"unauthorized-modal": "No tienes permisos para realizar esta acción, por favor, contacta a {{adminEmail}}.",
|
||||
"sent-this-month": "Enviados este mes",
|
||||
"available-seats": "Plazas disponibles",
|
||||
"buy-users": "Comprar más usuarios",
|
||||
"isenable-otp": "Habilitar verificación con OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "¿Te gustaría habilitar el proceso de verificación usando una contraseña de un solo uso (OTP)?",
|
||||
"p2": "Seleccionar esta opción habilitará la verificación con OTP. Los usuarios recibirán un código de verificación por correo, la cual tendrán que ingresar para poder firmar el documento.",
|
||||
"p3": "Seleccionar esta opción deshabilitará la verificación con OTP, lo cual permitirá a los usuarios firmar el documento directamente sin pasos adicionales.",
|
||||
"p4": "Por favor, selecciona la opción que mejor se adapte a tus requisitos para la firma de documentos."
|
||||
},
|
||||
"advanced-options": "Opciones avanzadas",
|
||||
"hide-advanced-options": "Ocultar las opciones avanzadas",
|
||||
"download-files": "Descargar archivos",
|
||||
"download-pdf": "Descargar PDF",
|
||||
"pdf-certificate": "Descargar PDF + Certificado",
|
||||
"document-logs": "Registros de documentos",
|
||||
"server-down": "No se pudo conectar al servidor de OpenSign. Si estás alojando OpenSign por tu cuenta, por favor, asegúrate de que todos los pasos de la documentación han sido seguidos correctamente. Si estás ejecutando OpenSign localmente, puedes estar accediendo a OpenSign por un número de puerto incorrecto.",
|
||||
"admin-exists": "El administrador ya existe. Por favor, inicia sesión en la aplicación usando las credenciales de administrador para poder administrar los usuarios.",
|
||||
"public-tour-message": "Por favor, convierte la plantilla en pública para copiar la URL pública",
|
||||
"add-user-template": "Es necesario agregar un rol antes de poder agregarle campos.",
|
||||
"pdf-uncompatible": "Este pdf no es compatible con opensign, comuníquese con <support@opensignlabs.com>",
|
||||
"enable-tour": "Habilitar recorrido",
|
||||
"istourenabled-help":{
|
||||
"p1": "Esto permitirá realizar una visita guiada para los firmantes, brindando instrucciones durante el proceso de firma.",
|
||||
"p2": "Esto desactivará la visita guiada, lo que garantizará una experiencia de firma más rápida e ininterrumpida.",
|
||||
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
|
||||
},
|
||||
"form-title-1": "Configuración del flujo de documentos",
|
||||
"form-title-2": "Configuración de seguridad",
|
||||
"text-field-tour":"Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
|
||||
"attach-signer-tour" :"Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
|
||||
"allowed-signature-types": "Tipos de firma permitidos",
|
||||
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
|
||||
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
|
||||
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
|
||||
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
|
||||
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
|
||||
"l3": "Cargue una versión digital existente de su firma.",
|
||||
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
|
||||
},
|
||||
"notify-on-signatures": "Notificar sobre firmas",
|
||||
"notify-on-signatures-help": {
|
||||
"p1": "El creador del documento recibirá una notificación por correo electrónico cada vez que un firmante firme el documento.",
|
||||
"note": "Nota: No se enviará un correo electrónico de notificación al último firmante, ya que el creador del documento recibirá un correo electrónico de finalización."
|
||||
},
|
||||
"expiry-date": "Date d'expiration",
|
||||
"expiry-date-updated": "La fecha de vencimiento se ha extendido con éxito hasta el {{newexpirydate}}",
|
||||
"expiry-date-error": "Proporcione una nueva fecha de vencimiento que sea posterior a la actual"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -35,6 +35,7 @@ const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||
const Preferences = lazy(() => import("./pages/Preferences"));
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||
const AppLoader = () => {
|
||||
@@ -198,6 +199,10 @@ function App() {
|
||||
</>
|
||||
)}
|
||||
<Route path="/users" element={<UserList />} />
|
||||
<Route
|
||||
path="/preferences"
|
||||
element={<LazyPage Page={Preferences} />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/sso" element={<SSOVerify />} />
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddSigner = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -47,139 +48,143 @@ const AddSigner = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
try {
|
||||
const user = Parse.User.current();
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", email);
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
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.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
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 currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
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);
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
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 currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
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 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("");
|
||||
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(t("add-signer-alert"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
alert(t("add-signer-alert"));
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in fetch contact", err);
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -206,6 +211,7 @@ const AddSigner = (props) => {
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto p-2 text-base-content">
|
||||
<p className="mb-3">{t("add-signer-note")}</p>
|
||||
{isUserExist && (
|
||||
<div className="mb-3 flex items-center">
|
||||
<input
|
||||
@@ -268,6 +274,7 @@ const AddSigner = (props) => {
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
disabled={addYourself}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 flex gap-x-2 justify-start">
|
||||
|
||||
@@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { copytoData } from "../constant/Utils";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { copytoData, fetchSubscriptionInfo } from "../constant/Utils";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
@@ -27,15 +27,64 @@ const AddUser = (props) => {
|
||||
password: "",
|
||||
role: ""
|
||||
});
|
||||
const [amount, setAmount] = useState({ quantity: 1, price: 0 });
|
||||
const [planInfo, setPlanInfo] = useState({
|
||||
priceperUser: 0,
|
||||
price: 0,
|
||||
totalAllowedUser: 0,
|
||||
adminId: ""
|
||||
});
|
||||
const [isFormLoader, setIsFormLoader] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [teamList, setTeamList] = useState([]);
|
||||
const [allowedUser, setAllowedUser] = useState(0);
|
||||
const [err, setErr] = useState("");
|
||||
const role = ["OrgAdmin", "Editor", "User"];
|
||||
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
useEffect(() => {
|
||||
getTeamList();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const getTeamList = async () => {
|
||||
if (isEnableSubscription) {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const resSub = await fetchSubscriptionInfo();
|
||||
if (!resSub.error) {
|
||||
setPlanInfo((prev) => ({
|
||||
...prev,
|
||||
priceperUser: resSub.price,
|
||||
totalAllowedUser: resSub.totalAllowedUser,
|
||||
adminId: resSub.adminId
|
||||
}));
|
||||
setAmount((prev) => ({ ...prev, price: resSub.price }));
|
||||
const res = await Parse.Cloud.run("allowedusers");
|
||||
if (props.setFormHeader) {
|
||||
if (res > 0) {
|
||||
props.setFormHeader(t("add-user"));
|
||||
} else {
|
||||
props.setFormHeader(
|
||||
resSub.adminId !== extUser?.objectId
|
||||
? "Unauthorized"
|
||||
: t("Add-seats")
|
||||
);
|
||||
}
|
||||
}
|
||||
setAllowedUser(res);
|
||||
} else {
|
||||
setAllowedUser(0);
|
||||
}
|
||||
} catch (err) {
|
||||
setAllowedUser(0);
|
||||
console.log("Err in alloweduser", err);
|
||||
setErr(err.message);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
|
||||
const teamRes = await Parse.Cloud.run("getteams", { active: true });
|
||||
if (teamRes.length > 0) {
|
||||
@@ -66,176 +115,179 @@ const AddUser = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
|
||||
setIsLoader(false);
|
||||
setTimeout(() => {
|
||||
props.setIsAlert({ type: "success", msg: "" });
|
||||
}, 1000);
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
try {
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsFormLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
props.setIsAlert({ type: "danger", msg: t("user-already-exist") });
|
||||
setIsFormLoader(false);
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1000);
|
||||
} else {
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
extUser.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", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.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);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsFormLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const currentUser = Parse.User.current();
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
extUser.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);
|
||||
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
console.log("err", err);
|
||||
setIsFormLoader(false);
|
||||
props.setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("something-went-wrong-mssg")
|
||||
});
|
||||
} finally {
|
||||
setTimeout(
|
||||
() => props.setIsAlert({ type: "success", msg: "" }),
|
||||
1500
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
setIsLoader(false);
|
||||
props.setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("something-went-wrong-mssg")
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
setFormdata({ name: "", email: "", phone: "", team: "", role: "" });
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
@@ -245,10 +297,7 @@ const AddUser = (props) => {
|
||||
if (name === "email") {
|
||||
value = value?.toLowerCase()?.replace(/\s/g, "");
|
||||
}
|
||||
setFormdata((prev) => ({
|
||||
...prev,
|
||||
[name]: value
|
||||
}));
|
||||
setFormdata((prev) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
@@ -256,163 +305,271 @@ const AddUser = (props) => {
|
||||
props.setIsAlert({ type: "success", msg: t("copied") });
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handlePricePerUser = (e) => {
|
||||
const quantity = e.target.value;
|
||||
const price = e.target?.value > 0 ? planInfo.priceperUser * quantity : 0;
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleAddOnSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsFormLoader(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buyaddonusers", {
|
||||
users: amount.quantity
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setAllowedUser(amount.quantity);
|
||||
setPlanInfo((obj) => ({ ...obj, totalAllowedUser: _resAddon.addon }));
|
||||
}
|
||||
if (props.handleBuyUsers) {
|
||||
props.handleBuyUsers(amount.quantity, _resAddon.addon);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
props.setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
setIsFormLoader(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
|
||||
<Title title={"Add User"} />
|
||||
{isLoader && (
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box">
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-base-100 relative">
|
||||
<Title title={t("add-user")} />
|
||||
{isFormLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("password")}
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
{!isLoader ? (
|
||||
<>
|
||||
{err ? (
|
||||
<div className="flex justify-center items-center h-[70px] text-[18px]">
|
||||
{err}
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
{t("password-generateed")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
placeholder="optional"
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
// required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
{isEnableSubscription && (
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Team<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.team}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="team"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
select
|
||||
</option>
|
||||
{teamList.length > 0 &&
|
||||
teamList.map((x) => (
|
||||
<option key={x.objectId} value={x.objectId}>
|
||||
{x.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : (
|
||||
<div className="w-full mx-auto">
|
||||
{!isEnableSubscription || allowedUser > 0 ? (
|
||||
<form onSubmit={handleSubmit}>
|
||||
{isEnableSubscription && (
|
||||
<p className="flex justify-center mb-1">
|
||||
{t("remaing-users")}{" "}
|
||||
<span
|
||||
className={`${
|
||||
allowedUser < 2 ? "op-text-accent" : "op-text-primary"
|
||||
} font-medium ml-1`}
|
||||
>
|
||||
{allowedUser} {t("of")} {planInfo.totalAllowedUser}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("name")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("email")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("password")}
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
{t("password-generateed")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("phone")}
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
{/* added hint for phone number being optional */}
|
||||
<input
|
||||
type="text"
|
||||
name="phone"
|
||||
placeholder={t("phone-optional")}
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
{isEnableSubscription && (
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("team")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
name="team"
|
||||
value={formdata.team}
|
||||
onChange={(e) => handleChange(e)}
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
{t("Select")}
|
||||
</option>
|
||||
{teamList.length > 0 &&
|
||||
teamList.map((x) => (
|
||||
<option key={x.objectId} value={x.objectId}>
|
||||
{x.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Role")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(t("input-required"))
|
||||
}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
{t("Select")}
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{t("cancel")}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
) : (
|
||||
<>
|
||||
{planInfo.adminId !== extUser?.objectId ? (
|
||||
<div className="mb-3 mt-1 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", {
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleAddOnSubmit}>
|
||||
<p className="flex justify-center text-center mx-2 mb-3 text-base op-text-accent font-medium">
|
||||
{t("additional-users")}
|
||||
</p>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label
|
||||
htmlFor="quantity"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Quantity-of-users")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value={amount.quantity}
|
||||
onChange={(e) => handlePricePerUser(e)}
|
||||
className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("Price")} (1 * {planInfo.priceperUser})
|
||||
</label>
|
||||
<div className="w-1/4 flex justify-center items-center text-sm">
|
||||
USD {amount.price}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-base-content mb-3" />
|
||||
<button className="op-btn op-btn-primary w-full">
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Role<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
select
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
Submit
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-full h-[200px] inset-0 flex justify-center items-center z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,9 +3,14 @@ import axios from "axios";
|
||||
import SuggestionInput from "./shared/fields/SuggestionInput";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
const BulkSendUi = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [forms, setForms] = useState([]);
|
||||
const [formId, setFormId] = useState(2);
|
||||
const formRef = useRef(null);
|
||||
@@ -13,6 +18,22 @@ const BulkSendUi = (props) => {
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [allowedForm, setAllowedForm] = useState(0);
|
||||
const [isSignatureExist, setIsSignatureExist] = useState();
|
||||
const [isBulkAvailable, setIsBulkAvailable] = useState(false);
|
||||
const quantityList = [500, 1000, 5000, 50000];
|
||||
const [amount, setAmount] = useState({
|
||||
price: (75.0).toFixed(2),
|
||||
quantity: 500,
|
||||
priceperbulksend: 0.15,
|
||||
totalcredits: 0
|
||||
});
|
||||
const [isQuotaReached, setIsQuotaReached] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isFreePlan, setIsFreePlan] = useState(false);
|
||||
const [admin, setAdmin] = useState({
|
||||
objectId: "",
|
||||
email: "",
|
||||
isAdmin: true
|
||||
});
|
||||
const allowedSigners = 50;
|
||||
useEffect(() => {
|
||||
signatureExist();
|
||||
@@ -20,14 +41,85 @@ const BulkSendUi = (props) => {
|
||||
}, []);
|
||||
|
||||
//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);
|
||||
const signatureExist = async () => {
|
||||
if (isEnableSubscription) {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const subscription = await fetchSubscription();
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (
|
||||
subscription?.adminId &&
|
||||
extUser?.objectId === subscription?.adminId
|
||||
) {
|
||||
setAdmin((obj) => ({
|
||||
...obj,
|
||||
objectId: subscription?.adminId,
|
||||
isAdmin: true
|
||||
}));
|
||||
} else {
|
||||
setAdmin((obj) => ({
|
||||
...obj,
|
||||
isAdmin: false,
|
||||
email: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
}
|
||||
setAdmin((obj) => ({ ...obj, objectId: subscription?.adminId }));
|
||||
if (subscription?.plan === "freeplan") {
|
||||
setIsFreePlan(true);
|
||||
}
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const axiosres = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/allowedcredits`,
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
}
|
||||
}
|
||||
);
|
||||
const resCredits = axiosres.data && axiosres.data.result;
|
||||
if (resCredits) {
|
||||
const allowedcredits = resCredits?.allowedcredits || 0;
|
||||
const addoncredits = resCredits?.addoncredits || 0;
|
||||
const totalcredits = allowedcredits + addoncredits;
|
||||
if (totalcredits > 0) {
|
||||
if (subscription?.plan !== "freeplan") {
|
||||
setIsBulkAvailable(true);
|
||||
}
|
||||
}
|
||||
setAmount((obj) => ({ ...obj, totalcredits: totalcredits }));
|
||||
}
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
)
|
||||
);
|
||||
setIsSignatureExist(checkIsSignatureExistt);
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
console.log("Err", err);
|
||||
}
|
||||
} else {
|
||||
setIsBulkAvailable(true);
|
||||
setAdmin((obj) => ({ ...obj, isAdmin: true }));
|
||||
const getPlaceholder = props?.Placeholders;
|
||||
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||
placeholderObj?.placeHolder?.some((holder) =>
|
||||
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||
)
|
||||
);
|
||||
setIsSignatureExist(checkIsSignatureExistt);
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (scrollOnNextUpdate && formRef.current) {
|
||||
@@ -76,29 +168,33 @@ const BulkSendUi = (props) => {
|
||||
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);
|
||||
if (isEnableSubscription && forms.length >= amount.totalcredits) {
|
||||
setIsQuotaReached(true);
|
||||
} else {
|
||||
// If the limit has been reached, throw an error with the appropriate message
|
||||
alert(t("quick-send-alert-4"));
|
||||
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(t("quick-send-alert-4"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,83 +202,101 @@ const BulkSendUi = (props) => {
|
||||
const updatedForms = forms.filter((_, i) => i !== index);
|
||||
setForms(updatedForms);
|
||||
};
|
||||
|
||||
function validateEmails(data) {
|
||||
for (const item of data) {
|
||||
for (const field of item.fields) {
|
||||
if (!emailRegex.test(field.email)) {
|
||||
alert(`Invalid email found: ${field.email}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
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) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
});
|
||||
// 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) {
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...placeholder,
|
||||
email: field.email,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
if (validateEmails(forms)) {
|
||||
// Create a copy of Placeholders array from props.item
|
||||
let Placeholders = [...props.Placeholders];
|
||||
// Initialize an empty array to store updated documents
|
||||
let Documents = [];
|
||||
// Loop through each form
|
||||
forms.forEach((form) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
}
|
||||
// If no matching field is found, keep the placeholder as is
|
||||
return placeholder;
|
||||
});
|
||||
});
|
||||
// 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) {
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
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
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders
|
||||
});
|
||||
}
|
||||
});
|
||||
//console.log("Documents ", Documents);
|
||||
await batchQuery(Documents);
|
||||
// Push a new document object with updated Placeholders into the Documents array
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
SignatureType: props.signatureType
|
||||
});
|
||||
}
|
||||
});
|
||||
await batchQuery(Documents);
|
||||
} else {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const batchQuery = async (Documents) => {
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const functionsUrl = `${serverUrl}functions/batchdocuments`;
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const functionsUrl = `${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}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)
|
||||
...token
|
||||
};
|
||||
const params = { Documents: JSON.stringify(Documents) };
|
||||
try {
|
||||
const res = await axios.post(functionsUrl, params, { headers: headers });
|
||||
// console.log("res ", res);
|
||||
@@ -196,83 +310,232 @@ const BulkSendUi = (props) => {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddOnQuickSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmit(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buycredits", {
|
||||
credits: amount.quantity
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setIsBulkAvailable(true);
|
||||
handleCloseQuotaReached();
|
||||
setAmount((obj) => ({
|
||||
...obj,
|
||||
quantity: 500,
|
||||
priceperbulksend: 0.15,
|
||||
price: (75.0).toFixed(2),
|
||||
totalcredits: obj?.totalcredits + _resAddon.addon
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
} finally {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
const handlePricePerQuick = async (e) => {
|
||||
const quantity = e.target?.value;
|
||||
const price =
|
||||
quantity > 0
|
||||
? (Math.round(quantity * amount.priceperbulksend * 100) / 100).toFixed(
|
||||
2
|
||||
)
|
||||
: 500 * amount.priceperbulksend;
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleCloseQuotaReached = () => {
|
||||
setIsQuotaReached(false);
|
||||
};
|
||||
const handleNavigation = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isSubmit && (
|
||||
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
|
||||
{isLoader ? (
|
||||
<div className="w-full h-[100px] flex justify-center items-center z-[999]">
|
||||
<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-light 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-light fa-plus"></i>{" "}
|
||||
<span>{t("add-new")}</span>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-secondary focus:outline-none"
|
||||
>
|
||||
<i className="fa-light fa-paper-plane"></i>{" "}
|
||||
<span>{t("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">
|
||||
{t("quick-send-alert-1")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-2")}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-3")}
|
||||
</div>
|
||||
<>
|
||||
{isSubmit && (
|
||||
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isBulkAvailable ? (
|
||||
<>
|
||||
{props.Placeholders?.length > 0 ? (
|
||||
isSignatureExist ? (
|
||||
<>
|
||||
{props.Placeholders?.some((x) => !x.signerObjId) ? (
|
||||
<div>
|
||||
<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
|
||||
)
|
||||
}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
</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-light fa-trash"></i>
|
||||
</button>
|
||||
)}
|
||||
<div ref={formRef}></div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col mx-4 mb-4 gap-3">
|
||||
{isEnableSubscription && (
|
||||
<button
|
||||
onClick={handleAddForm}
|
||||
className="op-btn op-btn-primary focus:outline-none"
|
||||
>
|
||||
<i className="fa-light fa-plus"></i>{" "}
|
||||
<span>{t("add-new")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-accent focus:outline-none"
|
||||
>
|
||||
<i className="fa-light fa-paper-plane"></i>{" "}
|
||||
<span>{t("send")}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<ModalUi
|
||||
isOpen={isQuotaReached}
|
||||
handleClose={() => handleCloseQuotaReached()}
|
||||
>
|
||||
<div className="p-4 flex justify-center items-center flex-col gap-y-3">
|
||||
<p className="text-center text-base-content">
|
||||
{t("quota-err-quicksend")}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsBulkAvailable(false)}
|
||||
className=" op-btn op-btn-primary w-[200px]"
|
||||
>
|
||||
{t("buy-credits")}
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-1")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-2")}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||
{t("quick-send-alert-3")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{isFreePlan ? (
|
||||
<div className="w-full h-[130px] flex flex-col justify-center items-center text-center p-4">
|
||||
<p className="text-base font-medium mb-2.5">
|
||||
{t("bulk-send-subcription-alert")}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => handleNavigation()}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{admin?.isAdmin ? (
|
||||
<form onSubmit={handleAddOnQuickSubmit} className="p-3">
|
||||
<p className="flex justify-center text-center mx-2 mb-3 text-base op-text-accent font-medium">
|
||||
{t("additional-credits")}
|
||||
</p>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label
|
||||
htmlFor="quantity"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("quantity-of-credits")}
|
||||
<span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<select
|
||||
value={amount.quantity}
|
||||
onChange={(e) => handlePricePerQuick(e)}
|
||||
name="quantity"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-1/4 text-xs"
|
||||
required
|
||||
>
|
||||
{quantityList.length > 0 &&
|
||||
quantityList.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("Price")} (1 * {amount.priceperbulksend})
|
||||
</label>
|
||||
<div className="w-1/4 flex justify-center items-center text-sm">
|
||||
USD {amount.price}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-base-content mb-3" />
|
||||
<button className="op-btn op-btn-primary w-full mt-2">
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="mx-8 mt-4 mb-8 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", { adminEmail: admin?.email })}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -6,30 +6,44 @@ import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
checkIsSubscribedTeam,
|
||||
formatDate,
|
||||
getAppLogo,
|
||||
openInNewTab,
|
||||
saveLanguageInLocal
|
||||
} from "../constant/Utils";
|
||||
import { isEnableSubscription, isStaging } from "../constant/const";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import QuotaCard from "../primitives/QuotaCard";
|
||||
|
||||
const Header = ({ showSidebar }) => {
|
||||
const Header = ({ showSidebar, setIsMenu }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
let username = localStorage.getItem("username");
|
||||
const current_notification_version = "1"; // Update this with each new feature release
|
||||
// Get the dismissed version from local storage
|
||||
const dismissedVersion = localStorage.getItem("notificationDismissedVersion");
|
||||
const username = localStorage.getItem("username") || "";
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||
const [isPro, setIsPro] = useState(false);
|
||||
const [isTeam, setIsTeam] = useState(false);
|
||||
const [isTeam, setIsTeam] = useState({ plan: "", isValid: false });
|
||||
const [applogo, setAppLogo] = useState(
|
||||
localStorage.getItem("appLogo") || " "
|
||||
);
|
||||
|
||||
const [emailUsed, setEmailUsed] = useState(0);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [emailResetDate, setEmailResetDate] = useState("");
|
||||
const [showNotification, setShowNotification] = useState(
|
||||
(!dismissedVersion || dismissedVersion !== current_notification_version) &&
|
||||
true
|
||||
);
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (width <= 768) {
|
||||
setIsMenu(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
checkSubscription();
|
||||
@@ -37,9 +51,8 @@ const Header = ({ showSidebar }) => {
|
||||
}, []);
|
||||
async function checkSubscription() {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
const getIsTeam = await checkIsSubscribedTeam();
|
||||
if (getIsSubscribe) {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
if (subscribe.isValid) {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
@@ -47,20 +60,39 @@ const Header = ({ showSidebar }) => {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
setIsPro(getIsSubscribe);
|
||||
setIsTeam(getIsTeam);
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
setIsTeam(subscribe);
|
||||
setIsSubscribe(subscribe.isValid);
|
||||
try {
|
||||
const extUser = await Parse.Cloud.run("getUserDetails");
|
||||
const MonthlyFreeEmails = extUser?.get("MonthlyFreeEmails") || 0;
|
||||
setEmailUsed(MonthlyFreeEmails);
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
if (_extUser?.LastEmailCountReset?.iso) {
|
||||
const resetDate = formatDate(_extUser?.LastEmailCountReset?.iso);
|
||||
setEmailResetDate(resetDate);
|
||||
}
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in while fetching monthlyfreeEmails", err);
|
||||
}
|
||||
} else {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
} else {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const closeDropdown = () => {
|
||||
const closeDropdown = async () => {
|
||||
setIsOpen(false);
|
||||
if (Parse?.User?.current()) {
|
||||
try {
|
||||
Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
}
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
@@ -103,129 +135,185 @@ const Header = ({ showSidebar }) => {
|
||||
window.open("https://console.opensignlabs.com/");
|
||||
}
|
||||
};
|
||||
const handleNavigation = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
const handleMailUsed = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
// Handle dismissing the notification
|
||||
const dismissNotification = () => {
|
||||
// Store the current version as dismissed in local storage
|
||||
localStorage.setItem(
|
||||
"notificationDismissedVersion",
|
||||
current_notification_version
|
||||
);
|
||||
setShowNotification(false);
|
||||
};
|
||||
const handleSetupNowNavigation = () => {
|
||||
const url = isStaging
|
||||
? "https://staging-console.opensignlabs.com/fileadapter"
|
||||
: "https://console.opensignlabs.com/fileadapter";
|
||||
|
||||
openInNewTab(url);
|
||||
};
|
||||
return (
|
||||
<div className="op-navbar bg-base-100 shadow">
|
||||
<div className="flex-none">
|
||||
<button
|
||||
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
|
||||
onClick={showSidebar}
|
||||
>
|
||||
<i className="fa-light fa-bars text-xl text-base-content"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 ml-2">
|
||||
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
||||
<img
|
||||
className="object-contain h-full w-auto"
|
||||
src={applogo}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="profile-menu" className="flex-none gap-2">
|
||||
{!isSubscribe && (
|
||||
<div>
|
||||
<button
|
||||
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
|
||||
onClick={() => navigate("/subscription")}
|
||||
<div>
|
||||
{isEnableSubscription && showNotification && (
|
||||
<div className="flex justify-between items-center shadow py-1 bg-[#CAE4FA] p-1">
|
||||
<div className="text-center text-[14px] ml-auto">
|
||||
{t("header-news")} —
|
||||
<span
|
||||
className="cursor-pointer font-medium underline text-blue-800"
|
||||
onClick={() => handleSetupNowNavigation()}
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
</button>
|
||||
{" " + t("header-news-btn") + "."}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!isTeam && 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">
|
||||
{t("pro")}
|
||||
</div>
|
||||
)}
|
||||
{isTeam && (
|
||||
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||
{t("team")}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
|
||||
className="ml-auto mr-1 cursor-pointer"
|
||||
onClick={() => dismissNotification()}
|
||||
>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="op-navbar bg-base-100 shadow">
|
||||
<div className="flex-none">
|
||||
<button
|
||||
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
|
||||
onClick={showSidebar}
|
||||
>
|
||||
<i className="fa-light fa-bars text-xl text-base-content"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 ml-2">
|
||||
<div className="h-[25px] md:h-[40px] w-auto overflow-hidden">
|
||||
<img
|
||||
className="w-[35px] h-[35px] object-contain"
|
||||
src={image}
|
||||
className="object-contain h-full w-auto"
|
||||
src={applogo}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer text-base-content text-sm"
|
||||
>
|
||||
{username && username}
|
||||
</div>
|
||||
<div id="profile-menu" className="flex-none gap-2">
|
||||
{!isSubscribe && (
|
||||
<div>
|
||||
<button
|
||||
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
|
||||
onClick={() => handleNavigation()}
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{isTeam.isValid && !validplan[isTeam.plan] && (
|
||||
<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">
|
||||
{t("pro")}
|
||||
</div>
|
||||
)}
|
||||
{validplan[isTeam.plan] && (
|
||||
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||
{t("team")}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
)}
|
||||
<div className="op-dropdown op-dropdown-end" id="profile-menu">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
|
||||
>
|
||||
<i
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-gray-400 overflow-hidden"
|
||||
>
|
||||
<img
|
||||
className="w-[35px] h-[35px] object-contain"
|
||||
src={image}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{width >= 768 && (
|
||||
<div
|
||||
onClick={toggleDropdown}
|
||||
className="cursor-pointer text-base-content text-sm"
|
||||
>
|
||||
{username && username}
|
||||
</div>
|
||||
)}
|
||||
<div className="op-dropdown op-dropdown-end" id="profile-menu">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={toggleDropdown}
|
||||
className="fa-light fa-angle-down text-base-content"
|
||||
></i>
|
||||
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
|
||||
>
|
||||
<i
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={toggleDropdown}
|
||||
className="fa-light fa-angle-down text-base-content"
|
||||
></i>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
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"
|
||||
>
|
||||
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
|
||||
<span>
|
||||
<i className="fa-light fa-book"></i> {t("docs")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/profile");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-user"></i> {t("profile")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/changepassword");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-lock"></i> {t("change-password")}
|
||||
</span>
|
||||
</li>
|
||||
{isEnableSubscription && (
|
||||
<li onClick={() => handleConsoleRedirect()}>
|
||||
<span>
|
||||
<i className="fa-light fa-id-card"></i> Console
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
{isEnableSubscription && isTeam?.plan === "freeplan" && (
|
||||
<li className="cursor-pointer" onClick={handleMailUsed}>
|
||||
<span>
|
||||
<i className="fa-light fa-envelope"></i>
|
||||
{emailUsed}/15 {t("sent-this-month")}
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
<li onClick={closeDropdown}>
|
||||
<span>
|
||||
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
|
||||
{t("log-out")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
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"
|
||||
>
|
||||
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
|
||||
<span>
|
||||
<i className="fa-light fa-book"></i> {t("docs")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/profile");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-user"></i> {t("profile")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigate("/changepassword");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-lock"></i> {t("change-password")}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
handleConsoleRedirect();
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-light fa-id-card"></i> Console
|
||||
</span>
|
||||
</li>
|
||||
<li onClick={closeDropdown}>
|
||||
<span>
|
||||
<i className="fa-light fa-arrow-right-from-bracket"></i>{" "}
|
||||
{t("log-out")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ModalUi isOpen={isModal}>
|
||||
<QuotaCard
|
||||
isPaidInfo={true}
|
||||
handlClose={handleMailUsed}
|
||||
emailResetDate={emailResetDate}
|
||||
/>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,8 +47,10 @@ const GoogleSignInBtn = ({
|
||||
});
|
||||
});
|
||||
const clearStorage = async () => {
|
||||
if (Parse.User.current()) {
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
@@ -160,10 +162,13 @@ const GoogleSignInBtn = ({
|
||||
alert(t("fill-required-details!"));
|
||||
}
|
||||
};
|
||||
const handleCloseModal = () => {
|
||||
const handleCloseModal = async () => {
|
||||
setIsModal(false);
|
||||
Parse.User.logOut();
|
||||
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import React from "react";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function RotateAlert(props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isRotate}
|
||||
title={t("Rotation-alert")}
|
||||
handleClose={() => props.setIsRotate({ status: false, degree: 0 })}
|
||||
>
|
||||
{" "}
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{t("rotate-alert-mssg")}</p>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
onClick={() => props.setIsRotate({ status: false, degree: 0 })}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost shadow-md"
|
||||
>
|
||||
{t("no")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => props.handleRemoveWidgets()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default RotateAlert;
|
||||
@@ -2,13 +2,13 @@ import React, { useState, useEffect, useRef } from "react";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import axios from "axios";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { saveAs } from "file-saver";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Table from "react-bootstrap/Table";
|
||||
import * as HoverCard from "@radix-ui/react-hover-card";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import FolderModal from "../shared/fields/FolderModal";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { handleDownloadPdf } from "../../constant/Utils";
|
||||
|
||||
function DriveBody(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -100,16 +100,17 @@ function DriveBody(props) {
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
const signedUrl = data.SignedUrl;
|
||||
const signerExist = data?.Signers;
|
||||
const isDecline = data?.IsDeclined;
|
||||
const isPlaceholder = data?.Placeholders;
|
||||
const signedUrl = data?.SignedUrl;
|
||||
const isSignYourself = data?.IsSignyourself;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
else if ((!signerExist && !isPlaceholder) || isSignYourself) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
@@ -136,12 +137,10 @@ function DriveBody(props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (selectType, data) => {
|
||||
const handleMenuItemClick = async (selectType, data) => {
|
||||
switch (selectType) {
|
||||
case "Download": {
|
||||
const pdfName = data && data.Name;
|
||||
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
await handleDownloadPdf([data]);
|
||||
break;
|
||||
}
|
||||
case "Rename": {
|
||||
@@ -264,12 +263,6 @@ function DriveBody(props) {
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
@@ -357,7 +350,7 @@ function DriveBody(props) {
|
||||
e.stopPropagation();
|
||||
handleMenuItemClick("Download", data);
|
||||
}}
|
||||
className="fa-light fa-download mr-[8px] op-text-primary"
|
||||
className="fa-light fa-download mr-[8px] op-text-primary cursor-pointer"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</td>
|
||||
@@ -370,7 +363,11 @@ function DriveBody(props) {
|
||||
{/* folder */}
|
||||
<div
|
||||
data-tut={props.dataTutSeventh}
|
||||
onClick={() => handleOnclikFolder(data)}
|
||||
onClick={() => {
|
||||
if (!rename) {
|
||||
handleOnclikFolder(data);
|
||||
}
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
@@ -383,7 +380,6 @@ function DriveBody(props) {
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
onFocus={() => {
|
||||
inputRef.current.setSelectionRange(0, 0);
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
@@ -395,9 +391,8 @@ function DriveBody(props) {
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
// value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName">{data.Name}</span>
|
||||
@@ -423,7 +418,11 @@ function DriveBody(props) {
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
) : (
|
||||
<HoverCard.Root openDelay={0} closeDelay={100}>
|
||||
<HoverCard.Root
|
||||
open={rename ? false : undefined}
|
||||
openDelay={0}
|
||||
closeDelay={100}
|
||||
>
|
||||
<HoverCard.Trigger asChild>
|
||||
<div>
|
||||
<ContextMenu.Root>
|
||||
@@ -432,7 +431,11 @@ function DriveBody(props) {
|
||||
{/* pdf */}
|
||||
<div
|
||||
data-tut={props.dataTutSixth}
|
||||
onClick={() => checkPdfStatus(data)}
|
||||
onClick={() => {
|
||||
if (!rename) {
|
||||
checkPdfStatus(data);
|
||||
}
|
||||
}}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
@@ -447,7 +450,6 @@ function DriveBody(props) {
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
onFocus={() => {
|
||||
inputRef.current.setSelectionRange(0, 0);
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
@@ -457,9 +459,8 @@ function DriveBody(props) {
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
// value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||
className="op-input op-input-bordered op-input-xs w-[100px] focus:outline-none hover:border-base-content text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName">{data.Name}</span>
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import React from "react";
|
||||
|
||||
function BorderResize({ right, top }) {
|
||||
function BorderResize(props) {
|
||||
const getHeight = () => {
|
||||
const height = props.posHeight(props.pos, props.isSignYourself);
|
||||
if (height > 14) {
|
||||
return "14px";
|
||||
} else {
|
||||
return `${height}px`;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${right ? `-right-[12px]` : "-right-[1px]"} ${
|
||||
top ? `-bottom-[11px]` : "-bottom-[1px]"
|
||||
} absolute inline-block w-[14px] h-[14px] hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
style={{
|
||||
width: getHeight() || "14px",
|
||||
height: getHeight() || "14px"
|
||||
}}
|
||||
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${
|
||||
props.top ? `-bottom-[12px]` : "-bottom-[2px] "
|
||||
} absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
function DefaultSignature(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const confirmToaddDefaultSign = () => {
|
||||
if (xyPostion.length > 0) {
|
||||
setDefaultSignAlert({
|
||||
if (props?.xyPostion.length > 0) {
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("default-sign-alert")
|
||||
});
|
||||
} else {
|
||||
setDefaultSignAlert({
|
||||
props?.setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("please-select-position!")
|
||||
});
|
||||
@@ -22,22 +22,30 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
{t("signature")}
|
||||
</div>
|
||||
<div className="flex flex-col items-center mt-[10px] font-semibold">
|
||||
<div className="flex flex-col items-center mt-[10px] font-semibold relative">
|
||||
<p className="text-base-content">{t("Your-Signature")}</p>
|
||||
<div className="op-card shadow-md h-[111px] w-[90%] p-2">
|
||||
<img
|
||||
alt="signature"
|
||||
className="w-full h-full object-contain"
|
||||
src={defaultSignImg}
|
||||
src={props?.defaultSignImg}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mt-[10px]"
|
||||
onClick={() => confirmToaddDefaultSign()}
|
||||
disabled={!props?.isDefault}
|
||||
>
|
||||
{t("auto-sign-all")}
|
||||
</button>
|
||||
{!props.isDefault && (
|
||||
<div className="absolute bg-black/70 text-white w-full h-full flex items-center justify-center text-[11px] cursor-default">
|
||||
<span className="-rotate-45">
|
||||
Document creator has disabled this option
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -54,7 +54,7 @@ function DraftDocument() {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
else if ((!signerExist && !isPlaceholder) || data?.IsSignyourself) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
|
||||
@@ -4,6 +4,8 @@ import ModalUi from "../../primitives/ModalUi";
|
||||
import { radioButtonWidget } from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
|
||||
|
||||
function DropdownWidgetOption(props) {
|
||||
const { t } = useTranslation();
|
||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||
@@ -330,6 +332,55 @@ function DropdownWidgetOption(props) {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center mt-3 mb-3">
|
||||
<span>{t("font-size")} :</span>
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={size} key={ind}>
|
||||
{size}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="flex flex-row gap-1 items-center ml-4 ">
|
||||
<span>{t("color")} : </span>
|
||||
<select
|
||||
value={
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
onChange={(e) => props.setFontColor(e.target.value)}
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
>
|
||||
{fontColorArr.map((color, ind) => {
|
||||
return (
|
||||
<option value={color} key={ind}>
|
||||
{t(`color-type.${color}`)}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span
|
||||
style={{
|
||||
background:
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}}
|
||||
className="w-5 h-[19px] ml-1"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
{["checkbox", radioButtonWidget].includes(props.type) && (
|
||||
<div className="flex flex-row gap-5 my-2 items-center text-center">
|
||||
{!props.isSignYourself && (
|
||||
@@ -362,6 +413,7 @@ function DropdownWidgetOption(props) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${
|
||||
props.type === "checkbox" && !props.isSignYourself
|
||||
|
||||
@@ -3,18 +3,25 @@ import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import { useTranslation } from "react-i18next";
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
const EditTemplate = ({ template, onSuccess, jwttoken }) => {
|
||||
const { t } = useTranslation();
|
||||
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
Name: template?.Name || "",
|
||||
Note: template?.Note || "",
|
||||
Description: template?.Description || "",
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||
AutomaticReminders: template?.AutomaticReminders || false,
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5
|
||||
RemindOnceInEvery: template?.RemindOnceInEvery || 5,
|
||||
IsEnableOTP: template?.IsEnableOTP ? `${template?.IsEnableOTP}` : "false",
|
||||
IsTourEnabled: template?.IsTourEnabled
|
||||
? `${template?.IsTourEnabled}`
|
||||
: "false",
|
||||
NotifyOnSignatures:
|
||||
template?.NotifyOnSignatures !== undefined
|
||||
? template?.NotifyOnSignatures
|
||||
: false
|
||||
});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
useEffect(() => {
|
||||
@@ -23,25 +30,22 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
}, []);
|
||||
const fetchSubscription = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
const subscribe = await checkIsSubscribed(jwttoken);
|
||||
setIsSubscribe(subscribe.isValid);
|
||||
}
|
||||
};
|
||||
const handleStrInput = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
// const handleFolder = (data) => {
|
||||
// console.log("handleFolder ", data)
|
||||
// setFolder(data);
|
||||
// };
|
||||
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||
const isTourEnabled = formData?.IsTourEnabled === "false" ? false : true;
|
||||
const AutoReminder = formData?.AutomaticReminders || false;
|
||||
|
||||
const IsEnableOTP = formData.IsEnableOTP === "true" ? true : false;
|
||||
let reminderDate = {};
|
||||
if (AutoReminder) {
|
||||
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||
@@ -49,7 +53,13 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||
reminderDate = { NextReminderDate: ReminderDate };
|
||||
}
|
||||
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
|
||||
const data = {
|
||||
...formData,
|
||||
SendinOrder: isChecked,
|
||||
IsEnableOTP: IsEnableOTP,
|
||||
IsTourEnabled: isTourEnabled,
|
||||
...reminderDate
|
||||
};
|
||||
onSuccess(data);
|
||||
};
|
||||
const handleAutoReminder = () => {
|
||||
@@ -58,6 +68,11 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
AutomaticReminders: !formData.AutomaticReminders
|
||||
}));
|
||||
};
|
||||
|
||||
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
||||
const handleNotifySignChange = (value) => {
|
||||
setFormData((obj) => ({ ...obj, NotifyOnSignatures: value }));
|
||||
};
|
||||
return (
|
||||
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||
<div className="text-base-content">
|
||||
@@ -72,7 +87,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
</div>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
{t("name")}
|
||||
{t("Title")}
|
||||
<span className="text-[13px] text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
@@ -114,27 +129,29 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
</div>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label className="text-[13px]">{t("send-in-order")}</label>
|
||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-[12px]">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-[12px]">{t("no")}</div>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-[12px]">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-[12px]">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isEnableSubscription && (
|
||||
@@ -145,13 +162,12 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
}
|
||||
>
|
||||
{t("auto-reminder")}
|
||||
{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe
|
||||
? "cursor-pointer "
|
||||
? "cursor-pointer"
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
@@ -184,6 +200,180 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
<span className={isSubscribe ? "" : " text-gray-300"}>
|
||||
{t("isenable-otp")}{" "}
|
||||
<a data-tooltip-id="isenableotp-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<Tooltip id="isenableotp-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("isenable-otp")}</p>
|
||||
<p>{t("isenable-otp-help.p1")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("isenable-otp-help.p2")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("isenable-otp-help.p3")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("isenable-otp-help.p4")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1`}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsEnableOTP"
|
||||
checked={formData.IsEnableOTP === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1 `}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="IsEnableOTP"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.IsEnableOTP === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
<span>
|
||||
{t("enable-tour")}
|
||||
<a data-tooltip-id="istourenabled-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>{" "}
|
||||
</span>
|
||||
<Tooltip id="istourenabled-tooltip" className="z-50">
|
||||
<div className="max-w-[200px] md:max-w-[450px]">
|
||||
<p className="font-bold">{t("enable-tour")}</p>
|
||||
<p className="p-[5px]">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">{t("yes")}: </span>
|
||||
<span>{t("istourenabled-help.p1")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">{t("no")}: </span>
|
||||
<span>{t("istourenabled-help.p2")}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>{t("istourenabled-help.p3")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
className="op-radio op-radio-xs"
|
||||
name="IsTourEnabled"
|
||||
checked={formData.IsTourEnabled === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="IsTourEnabled"
|
||||
className="op-radio op-radio-xs"
|
||||
checked={formData.IsTourEnabled === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-3">
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription ? "" : "text-gray-300"
|
||||
} block`}
|
||||
>
|
||||
{t("notify-on-signatures")}
|
||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
<Tooltip id="nos-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">{t("notify-on-signatures")}</p>
|
||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||
<p>{t("notify-on-signatures-help.note")}</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription
|
||||
? ""
|
||||
: "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1`}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(true)}
|
||||
checked={formData.NotifyOnSignatures === true}
|
||||
/>
|
||||
<div className="text-center">{t("yes")}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription
|
||||
? ""
|
||||
: "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1`}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(false)}
|
||||
checked={formData.NotifyOnSignatures === false}
|
||||
/>
|
||||
<div className="text-center">{t("no")}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-[1rem] flex justify-start">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
{t("submit")}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import axios from "axios";
|
||||
import { handleDownloadPdf, handleToPrint } from "../../constant/Utils";
|
||||
import { handleToPrint } from "../../constant/Utils";
|
||||
import { themeColor, emailRegex } from "../../constant/const";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
@@ -15,7 +15,8 @@ function EmailComponent({
|
||||
sender,
|
||||
setIsAlert,
|
||||
extUserId,
|
||||
activeMailAdapter
|
||||
activeMailAdapter,
|
||||
setIsDownloadModal
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
@@ -29,12 +30,33 @@ function EmailComponent({
|
||||
const pdfName = pdfDetails[0]?.Name;
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const FileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
let presignedUrl = pdfUrl;
|
||||
try {
|
||||
const axiosRes = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
|
||||
{ url: pdfUrl, docId: docId, fileAdapterId: FileAdapterId },
|
||||
{
|
||||
headers: {
|
||||
"content-type": "Application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
presignedUrl = axiosRes.data.result;
|
||||
} catch (err) {
|
||||
console.log("err in getsignedurl", err);
|
||||
}
|
||||
for (let i = 0; i < emailList.length; i++) {
|
||||
try {
|
||||
const imgPng =
|
||||
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
|
||||
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
@@ -46,7 +68,7 @@ function EmailComponent({
|
||||
mailProvider: activeMailAdapter,
|
||||
extUserId: extUserId,
|
||||
pdfName: pdfName,
|
||||
url: pdfUrl,
|
||||
url: presignedUrl,
|
||||
recipient: emailList[i],
|
||||
subject: `${sender.name} has signed the doc - ${pdfName}`,
|
||||
from: sender.email,
|
||||
@@ -74,26 +96,15 @@ function EmailComponent({
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMail && sendMail.data.result.status === "success") {
|
||||
if (sendMail?.data?.result?.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setIsEmail(false);
|
||||
setTimeout(() => {
|
||||
setSuccessEmail(false);
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}, 1500);
|
||||
|
||||
setIsLoading(false);
|
||||
} else if (sendMail && sendMail.data.result.status === "error") {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: t("something-went-wrong-mssg")
|
||||
});
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
@@ -140,6 +151,11 @@ function EmailComponent({
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
@@ -165,7 +181,9 @@ function EmailComponent({
|
||||
<div className="flex flex-row">
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
@@ -174,9 +192,10 @@ function EmailComponent({
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
handleClose();
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
{t("download")}
|
||||
@@ -272,11 +291,7 @@ function EmailComponent({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
onClick={() => handleClose()}
|
||||
>
|
||||
{t("close")}
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
import React, { useState } from "react";
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import { copytoData } from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function EmbedTab(props) {
|
||||
const { t } = useTranslation();
|
||||
const tabName = [
|
||||
{ title: "React/Next.js", icon: "fa-brands fa-react", color: "#61dafb" },
|
||||
{ title: "JavaScript", icon: "fa-brands fa-js", color: "#ffd43b" },
|
||||
{ title: "Angular", icon: "fa-brands fa-angular", color: "#ff5733" }
|
||||
];
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
// State to track if the code has been copied
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const reactCode = [
|
||||
{
|
||||
id: 0,
|
||||
title: "Installation",
|
||||
codeString: `npm install @opensign/react`
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: "Usage",
|
||||
codeString: `
|
||||
import React from "react";
|
||||
import Opensign from "@opensign/react";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="app">
|
||||
<Opensign
|
||||
onLoad={() => console.log("success")}
|
||||
onLoadError={(error) => console.log(error)}
|
||||
templateId= "${props.templateId}"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
`
|
||||
}
|
||||
];
|
||||
|
||||
const angularCode = [
|
||||
{
|
||||
id: 0,
|
||||
title: "Installation",
|
||||
codeString: `npm install @opensign/angular`
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: "Usage",
|
||||
codeString: `
|
||||
import { Component } from '@angular/core';
|
||||
import { OpensignComponent } from "@opensign/angular"
|
||||
|
||||
@Component({
|
||||
selector:'app-root',
|
||||
standalone: true,
|
||||
imports: [OpensignComponent],
|
||||
template:\`<opensign templateId="${props.templateId}"
|
||||
(onLoad)="handleLoad()"
|
||||
(onLoadError)="handleError($event)"
|
||||
></opensign>\`,
|
||||
})
|
||||
export class AppComponent {
|
||||
handleLoad() {
|
||||
console.log("success");
|
||||
}
|
||||
handleError(error: string) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
`
|
||||
}
|
||||
];
|
||||
const jsCodeString = `
|
||||
<script
|
||||
src= "https://app.opensignlabs.com/static/js/public-template.bundle.js"
|
||||
id="opensign-script"
|
||||
templateId=${props.templateId}
|
||||
></script>
|
||||
`;
|
||||
|
||||
const handleCopy = (code, ind) => {
|
||||
copytoData(code);
|
||||
setIsCopied({ ...isCopied, [ind]: true });
|
||||
setTimeout(() => setIsCopied(false), 3000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-4 border-t-[1px]">
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] pb-[5px]">
|
||||
{t("embed-template")}
|
||||
</h3>
|
||||
<div className="flex justify-center items-center mt-2">
|
||||
<div role="tablist" className="op-tabs op-tabs-bordered">
|
||||
{tabName.map((tabData, ind) => (
|
||||
<div
|
||||
onClick={() => setActiveTab(ind)}
|
||||
key={ind}
|
||||
role="tab"
|
||||
className={`${
|
||||
activeTab === ind ? "op-tab-active" : ""
|
||||
} op-tab flex items-center pb-10 md:pb-0`}
|
||||
>
|
||||
<i
|
||||
className={`${tabData.icon}`}
|
||||
style={{ color: tabData.color }}
|
||||
></i>
|
||||
<span className="ml-1 text-[10px] font-medium md:font-normal md:text-[15px]">
|
||||
{tabData.title}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{activeTab === 0 ? (
|
||||
<div className="mt-4">
|
||||
{reactCode.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind}>
|
||||
<p className="font-medium text-[18px]">
|
||||
{t(`${data.title}`)}
|
||||
</p>
|
||||
{ind === 0 && (
|
||||
<p className="text-[13px] mt-2">
|
||||
{t("public-template-mssg-1")}
|
||||
</p>
|
||||
)}
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(data.codeString, ind)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[ind] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{data.codeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<p className="font-medium text-[15px]">
|
||||
{t("public-template-mssg-3")}
|
||||
</p>
|
||||
<p className="my-[6px]">
|
||||
{" "}
|
||||
{t("public-template-mssg-4")}
|
||||
<a
|
||||
href="https://www.npmjs.com/package/@opensign/react"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="cursor-pointer text-blue-700 "
|
||||
>
|
||||
{" "}
|
||||
OpenSign React package{" "}
|
||||
</a>
|
||||
{t("public-template-mssg-5")}
|
||||
</p>
|
||||
</div>
|
||||
) : activeTab === 1 ? (
|
||||
<div className="mt-4">
|
||||
<div>
|
||||
{/* <p className="font-medium text-[18px]">{t(`${data.title}`)}</p> */}
|
||||
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(jsCodeString, 0)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[0] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{jsCodeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
activeTab === 2 && (
|
||||
<div className="mt-4">
|
||||
{angularCode.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind}>
|
||||
<p className="font-medium text-[18px]">
|
||||
{t(`${data.title}`)}
|
||||
</p>
|
||||
{ind === 0 && (
|
||||
<p className="text-[13px] mt-2">
|
||||
{t("angular-npm-mssg-1")}
|
||||
</p>
|
||||
)}
|
||||
<div className="relative p-1">
|
||||
<div
|
||||
onClick={() => handleCopy(data.codeString, ind)}
|
||||
className="absolute top-[20px] right-[20px] cursor-pointer"
|
||||
>
|
||||
<i className="fa-light fa-copy text-white mr-[2px]" />
|
||||
<span className=" text-white">
|
||||
{isCopied[ind] ? t("copied-code") : t("copy-code")}
|
||||
</span>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
customStyle={{
|
||||
borderRadius: "15px"
|
||||
}}
|
||||
language="javascript"
|
||||
style={tomorrow}
|
||||
>
|
||||
{data.codeString}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<p className="font-medium text-[15px]">
|
||||
{t("public-template-mssg-3")}
|
||||
</p>
|
||||
<p className="my-[6px]">
|
||||
{" "}
|
||||
{t("public-template-mssg-4")}
|
||||
<a
|
||||
href="https://www.npmjs.com/package/@opensign/angular"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="cursor-pointer text-blue-700 "
|
||||
>
|
||||
{" "}
|
||||
OpenSign Angular package{" "}
|
||||
</a>
|
||||
{t("public-template-mssg-5")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmbedTab;
|
||||
@@ -34,7 +34,14 @@ function Header({
|
||||
setIsEmail,
|
||||
completeBtnTitle,
|
||||
setIsEditTemplate,
|
||||
isPublicTemplate
|
||||
isPublicTemplate,
|
||||
clickOnZoomIn,
|
||||
clickOnZoomOut,
|
||||
handleRotationFun,
|
||||
isDisableRotate,
|
||||
templateId,
|
||||
setIsDownloadModal,
|
||||
disabledBackBtn
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const filterPrefill =
|
||||
@@ -42,13 +49,13 @@ function Header({
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
const enabledBackBtn = disabledBackBtn === true ? false : true;
|
||||
|
||||
//function for show decline alert
|
||||
const handleDeclinePdfAlert = async () => {
|
||||
const currentDecline = { currnt: "Sure", isDeclined: true };
|
||||
setIsDecline(currentDecline);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex py-[5px]">
|
||||
{isMobile && isShowHeader ? (
|
||||
@@ -73,7 +80,7 @@ function Header({
|
||||
allPages={allPages}
|
||||
changePage={changePage}
|
||||
/>
|
||||
{pdfUrl && alreadySign ? (
|
||||
{isCompleted || alreadySign ? (
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
|
||||
@@ -90,9 +97,13 @@ function Header({
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
@@ -138,7 +149,7 @@ function Header({
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
@@ -184,13 +195,7 @@ function Header({
|
||||
) : (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
} else if (isPdfRequestFiles) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
onClick={() => embedWidgetsData()}
|
||||
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
|
||||
>
|
||||
{t("finish")}
|
||||
@@ -207,30 +212,86 @@ function Header({
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="bg-white shadow-md rounded-full px-3 py-2"
|
||||
className="bg-white shadow-md rounded-md px-3 py-2"
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||
className="DropdownMenuItem"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">{t("download")}</span>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">
|
||||
{t("download")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{!isDisableRotate && (
|
||||
<>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleRotationFun(90)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-right")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => handleRotationFun(-90)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("rotate-left")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)}
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => clickOnZoomIn()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">{t("zoom-in")}</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => clickOnZoomOut()}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("zoom-out")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
)}
|
||||
{isPublicTemplate && (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => embedWidgetsData()}
|
||||
className="border-none font-[650] text-[14px] op-link op-link-primary no-underline"
|
||||
>
|
||||
{t("sign-now")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -273,13 +334,15 @@ function Header({
|
||||
<i className="fa-light fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
{enabledBackBtn && (
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
disabled={isMailSend && true}
|
||||
data-tut="headerArea"
|
||||
@@ -298,7 +361,9 @@ function Header({
|
||||
alreadySign ? (
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
@@ -326,9 +391,13 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
if (isCompleted) {
|
||||
setIsDownloadModal(true);
|
||||
} else {
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
@@ -339,15 +408,28 @@ function Header({
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex" data-tut="reactourFifth">
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
{!templateId && (
|
||||
<button
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
{t("back")}
|
||||
</button>
|
||||
)}
|
||||
{currentSigner && (
|
||||
<>
|
||||
{templateId && (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => handleDeclinePdfAlert()}
|
||||
@@ -361,16 +443,18 @@ function Header({
|
||||
>
|
||||
{t("finish")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
{!templateId && (
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
<span className="hidden lg:block">{t("download")}</span>
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -392,7 +476,9 @@ function Header({
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
onClick={(e) =>
|
||||
handleToPrint(e, pdfUrl, setIsDownloading, pdfDetails)
|
||||
}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
@@ -402,9 +488,9 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
onClick={() => {
|
||||
setIsDownloadModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">{t("download")}</span>
|
||||
@@ -423,9 +509,7 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm shadow"
|
||||
onClick={() => {
|
||||
embedWidgetsData();
|
||||
}}
|
||||
onClick={() => embedWidgetsData()}
|
||||
>
|
||||
{t("sign-now")}
|
||||
</button>
|
||||
@@ -442,11 +526,7 @@ function Header({
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
onClick={() => embedWidgetsData()}
|
||||
>
|
||||
{t("finish")}
|
||||
</button>
|
||||
|
||||
@@ -3,44 +3,37 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
function PdfZoom(props) {
|
||||
const { t } = useTranslation();
|
||||
const onClickZoomIn = () => {
|
||||
props.setScale(props.scale + 0.1 * props.scale);
|
||||
props.setZoomPercent(props.zoomPercent + 10);
|
||||
};
|
||||
const onClickZoomOut = () => {
|
||||
if (props.zoomPercent > 0) {
|
||||
if (props.zoomPercent === 10) {
|
||||
props.setScale(1);
|
||||
} else {
|
||||
props.setScale(props.scale - 0.1 * props.scale);
|
||||
}
|
||||
props.setZoomPercent(props.zoomPercent - 10);
|
||||
}
|
||||
};
|
||||
const handleReset = () => {
|
||||
props.setScale(1);
|
||||
props.setZoomPercent(0);
|
||||
};
|
||||
|
||||
return (
|
||||
// md:mt-[41px] xl:mt-[63px]mt=[]
|
||||
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[50px] 2xl:mt-[63px]">
|
||||
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[43px] 2xl:mt-[63px]">
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={onClickZoomIn}
|
||||
onClick={() => props.clickOnZoomIn()}
|
||||
title={t("zoom-in")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={handleReset}
|
||||
title={t("reset")}
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
{!props?.isDisableRotate && (
|
||||
<>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={() => props.handleRotationFun(90)}
|
||||
title={t("rotate-right")}
|
||||
>
|
||||
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
title={t("rotate-left")}
|
||||
onClick={() => props.handleRotationFun(-90)}
|
||||
>
|
||||
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
|
||||
onClick={onClickZoomOut}
|
||||
onClick={() => props.clickOnZoomOut()}
|
||||
style={{
|
||||
cursor: props.zoomPercent > 0 ? "pointer" : "default"
|
||||
}}
|
||||
|
||||
@@ -4,6 +4,8 @@ import PlaceholderBorder from "./PlaceholderBorder";
|
||||
import { Rnd } from "react-rnd";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
fontColorArr,
|
||||
fontsizeArr,
|
||||
getContainerScale,
|
||||
handleCopyNextToWidget,
|
||||
isTabAndMobile,
|
||||
@@ -15,6 +17,8 @@ import {
|
||||
import PlaceholderType from "./PlaceholderType";
|
||||
import moment from "moment";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const selectFormat = (data) => {
|
||||
switch (data) {
|
||||
@@ -89,9 +93,10 @@ const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
|
||||
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||
|
||||
function Placeholder(props) {
|
||||
const { t } = useTranslation();
|
||||
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
|
||||
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
||||
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
|
||||
const [isDateModal, setIsDateModal] = useState(false);
|
||||
const [containerScale, setContainerScale] = useState();
|
||||
const [selectDate, setSelectDate] = useState({
|
||||
date:
|
||||
@@ -111,6 +116,7 @@ function Placeholder(props) {
|
||||
: ""
|
||||
});
|
||||
const [dateFormat, setDateFormat] = useState([]);
|
||||
const [clickonWidget, setClickonWidget] = useState({});
|
||||
const [startDate, setStartDate] = useState(
|
||||
props.pos.type === "date" &&
|
||||
getDefaultdate(
|
||||
@@ -200,20 +206,6 @@ function Placeholder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectDate]);
|
||||
|
||||
//it detect outside click of date dropdown menu
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (isShowDateFormat && !e.target.closest("#menu-container")) {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
}
|
||||
};
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isShowDateFormat]);
|
||||
|
||||
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
|
||||
const handleWidgetIdandPopup = () => {
|
||||
if (props.setSelectWidgetId) {
|
||||
@@ -338,12 +330,10 @@ function Placeholder(props) {
|
||||
|
||||
//condition for only placeholder and template flow
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
const checkIndex = props.xyPostion
|
||||
.filter((data) => data.Role !== "prefill")
|
||||
.findIndex((data) => data.Id === props.data.Id);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
//condition to handle in placeholder and template flow for text widget signerId for text widgets i have to set uniqueId in tempSignerId because
|
||||
@@ -351,10 +341,6 @@ function Placeholder(props) {
|
||||
else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setWidgetType(props.pos.type);
|
||||
@@ -369,21 +355,14 @@ function Placeholder(props) {
|
||||
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
|
||||
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
const checkIndex = props.xyPostion
|
||||
.filter((data) => data.Role !== "prefill")
|
||||
.findIndex((data) => data.Id === props.data.Id);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
} else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
|
||||
//checking widget's type and open widget copy modal for required widgets
|
||||
@@ -430,7 +409,11 @@ function Placeholder(props) {
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
data?.format
|
||||
data?.format,
|
||||
null,
|
||||
null,
|
||||
props.fontSize || props.pos?.options?.fontSize || 12,
|
||||
props.fontColor || props.pos?.options?.fontColor || "black"
|
||||
);
|
||||
setSelectDate({ date: date, format: data?.format });
|
||||
};
|
||||
@@ -462,17 +445,13 @@ function Placeholder(props) {
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: "29px",
|
||||
top: "-19px"
|
||||
}}
|
||||
style={{ color: "#188ae2", right: "29px", top: "-19px" }}
|
||||
></i>
|
||||
) : (
|
||||
/* condition to add setting icon for placeholder & template flow for all widgets except signature and date */
|
||||
((!props?.pos?.type && props.pos.isStamp) ||
|
||||
(props?.pos?.type &&
|
||||
!["date", "signature"].includes(props.pos.type) &&
|
||||
!["date"].includes(props.pos.type) &&
|
||||
!props.isSignYourself)) && (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
@@ -486,7 +465,7 @@ function Placeholder(props) {
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: props?.pos?.type === textWidget ? "32px" : "47px",
|
||||
right: props?.pos?.type === textWidget ? "32px" : "51px",
|
||||
top: "-19px"
|
||||
}}
|
||||
></i>
|
||||
@@ -522,17 +501,12 @@ function Placeholder(props) {
|
||||
)}
|
||||
{/* setting icon only for date widgets */}
|
||||
{props.pos.type === "date" && selectDate && (
|
||||
<div
|
||||
id="menu-container"
|
||||
style={{
|
||||
zIndex: "99",
|
||||
top: "-19px",
|
||||
right: props.isPlaceholder ? "60px" : "44px"
|
||||
}}
|
||||
className={`${isShowDateFormat ? "show" : ""} dropdown icon`}
|
||||
<i
|
||||
onClick={(e) => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
props.setCurrWidgetsDetails(props.pos);
|
||||
setIsDateModal(!isDateModal);
|
||||
e.stopPropagation();
|
||||
setClickonWidget(props.pos);
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
@@ -543,8 +517,9 @@ function Placeholder(props) {
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
props.setCurrWidgetsDetails(props.pos);
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setIsDateModal(!isDateModal);
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
@@ -554,44 +529,15 @@ function Placeholder(props) {
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
fontSize: "14px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className={
|
||||
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
|
||||
}
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isShowDateFormat ? "true" : "false"}
|
||||
>
|
||||
{dateFormat.map((data, ind) => {
|
||||
return (
|
||||
<span
|
||||
key={ind}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
handleSaveDate(data);
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
handleSaveDate(data);
|
||||
}}
|
||||
className="dropdown-item text-[13px]"
|
||||
>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
style={{
|
||||
zIndex: "99",
|
||||
top: "-18px",
|
||||
right: props.isPlaceholder ? "50px" : "30px",
|
||||
color: "#188ae2",
|
||||
fontSize: "14px"
|
||||
}}
|
||||
className="fa-light fa-gear icon"
|
||||
></i>
|
||||
)}
|
||||
{/* copy icon for all widgets */}
|
||||
<i
|
||||
@@ -691,6 +637,21 @@ function Placeholder(props) {
|
||||
}
|
||||
}
|
||||
};
|
||||
//function to calculate font size
|
||||
const calculateFont = (size, isMinHeight) => {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
const fontSize = (size || 12) * containerScale * props.scale;
|
||||
//isMinHeight to set text box minimum height
|
||||
if (isMinHeight) {
|
||||
return fontSize * 1.2 + "px";
|
||||
} else {
|
||||
return fontSize + "px";
|
||||
}
|
||||
};
|
||||
|
||||
const getCursor = () => {
|
||||
if (props.data && props.isNeedSign) {
|
||||
@@ -705,228 +666,338 @@ function Placeholder(props) {
|
||||
return "all-scroll";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Rnd
|
||||
id={props.pos.key}
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
![
|
||||
textWidget,
|
||||
"email",
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
textInputWidget
|
||||
].includes(props.pos.type) &&
|
||||
(props.pos.Width
|
||||
? props.pos.Width / props.pos.Height
|
||||
: defaultWidthHeight(props.pos.type).width /
|
||||
defaultWidthHeight(props.pos.type).height)
|
||||
}
|
||||
enableResizing={{
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
topRight: false,
|
||||
bottomRight:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.type !== radioButtonWidget
|
||||
? true
|
||||
: false
|
||||
: props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId &&
|
||||
true,
|
||||
bottomLeft: false,
|
||||
topLeft: false
|
||||
}}
|
||||
bounds="parent"
|
||||
className="signYourselfBlock"
|
||||
style={{
|
||||
border: "1px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
textAlign:
|
||||
props.pos.type !== "name" &&
|
||||
props.pos.type !== "company" &&
|
||||
props.pos.type !== "job title" &&
|
||||
"center",
|
||||
cursor: getCursor(),
|
||||
zIndex:
|
||||
props.pos.type === "date"
|
||||
? props.pos.key === props.selectWidgetId
|
||||
? 99 + 1
|
||||
: 99
|
||||
: props?.pos?.zIndex
|
||||
? props.pos.zIndex
|
||||
: "5",
|
||||
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
|
||||
}}
|
||||
onDrag={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||
}}
|
||||
size={{
|
||||
width:
|
||||
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResizeStart={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
containerScale,
|
||||
props.scale,
|
||||
props.data && props.data.Id,
|
||||
props.isResize
|
||||
);
|
||||
}}
|
||||
disableDragging={
|
||||
props.isNeedSign
|
||||
? true
|
||||
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
|
||||
? false
|
||||
: !isDraggingEnabled
|
||||
}
|
||||
onDragStop={(event, dragElement) =>
|
||||
props.handleStop &&
|
||||
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||
}
|
||||
position={{
|
||||
x: xPos(props.pos, props.isSignYourself),
|
||||
y: yPos(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResize={(e, direction, ref) => {
|
||||
setPlaceholderBorder({
|
||||
w: ref.offsetWidth / (props.scale * containerScale),
|
||||
h: ref.offsetHeight / (props.scale * containerScale)
|
||||
});
|
||||
}}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.isShowBorder &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId ? (
|
||||
<BorderResize right={-12} top={-11} />
|
||||
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||
props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" ? (
|
||||
<BorderResize />
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<Rnd
|
||||
id={props.pos.key}
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
![
|
||||
textWidget,
|
||||
"email",
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
textInputWidget
|
||||
].includes(props.pos.type) &&
|
||||
(props.pos.Width
|
||||
? props.pos.Width / props.pos.Height
|
||||
: defaultWidthHeight(props.pos.type).width /
|
||||
defaultWidthHeight(props.pos.type).height)
|
||||
}
|
||||
enableResizing={{
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
topRight: false,
|
||||
bottomRight:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.type !== radioButtonWidget
|
||||
? true
|
||||
: false
|
||||
: props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId &&
|
||||
true,
|
||||
bottomLeft: false,
|
||||
topLeft: false
|
||||
}}
|
||||
bounds="parent"
|
||||
className="signYourselfBlock"
|
||||
style={{
|
||||
border: "1px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
textAlign:
|
||||
props.pos.type !== "name" &&
|
||||
props.pos.type !== "company" &&
|
||||
props.pos.type !== "job title" &&
|
||||
"center",
|
||||
cursor: getCursor(),
|
||||
zIndex:
|
||||
props.pos.type === "date"
|
||||
? props.pos.key === props.selectWidgetId
|
||||
? 99 + 1
|
||||
: 99
|
||||
: props?.pos?.zIndex
|
||||
? props.pos.zIndex
|
||||
: "5",
|
||||
background: props.data
|
||||
? props.data?.blockColor + "b0"
|
||||
: "rgba(203, 233, 237, 0.69)"
|
||||
}}
|
||||
onDrag={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||
}}
|
||||
size={{
|
||||
width:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself)
|
||||
}}
|
||||
minHeight={calculateFont(props.pos.options?.fontSize, true)}
|
||||
maxHeight="auto"
|
||||
onResizeStart={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
setTimeout(() => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
}, 50);
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
containerScale,
|
||||
props.scale,
|
||||
props.data && props.data.Id,
|
||||
props.isResize
|
||||
);
|
||||
}}
|
||||
disableDragging={
|
||||
props.isNeedSign
|
||||
? true
|
||||
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
|
||||
? false
|
||||
: !isDraggingEnabled
|
||||
}
|
||||
onDragStop={(event, dragElement) =>
|
||||
props.handleStop &&
|
||||
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||
}
|
||||
position={{
|
||||
x: xPos(props.pos, props.isSignYourself),
|
||||
y: yPos(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResize={(e, direction, ref) => {
|
||||
setPlaceholderBorder({
|
||||
w: ref.offsetWidth / (props.scale * containerScale),
|
||||
h: ref.offsetHeight / (props.scale * containerScale)
|
||||
});
|
||||
}}
|
||||
onClick={() => !props.isResize && handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.isShowBorder &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId && <BorderResize />
|
||||
)}
|
||||
|
||||
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
|
||||
<PlaceholderBorder
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pos={props.pos}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||
scale={props.scale}
|
||||
containerScale={containerScale}
|
||||
placeholderBorder={placeholderBorder}
|
||||
/>
|
||||
)}
|
||||
{isTabAndMobile ? (
|
||||
<div
|
||||
className="flex items-stretch"
|
||||
style={{
|
||||
left: xPos(props.pos, props.isSignYourself),
|
||||
top: yPos(props.pos, props.isSignYourself),
|
||||
width:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself),
|
||||
zIndex: "10"
|
||||
}}
|
||||
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
|
||||
<PlaceholderType
|
||||
props.pos.key === props.selectWidgetId ? (
|
||||
<BorderResize
|
||||
right={-12}
|
||||
top={-11}
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
posHeight={props.posHeight}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
/>
|
||||
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||
props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" ? (
|
||||
<BorderResize
|
||||
posHeight={props.posHeight}
|
||||
isSignYourself={props.isSignYourself}
|
||||
pos={props.pos}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
) : (
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId && <BorderResize />
|
||||
)}
|
||||
|
||||
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
|
||||
<PlaceholderBorder
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pos={props.pos}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||
scale={props.scale}
|
||||
containerScale={containerScale}
|
||||
placeholderBorder={placeholderBorder}
|
||||
/>
|
||||
)}
|
||||
{isTabAndMobile ? (
|
||||
<div
|
||||
className="flex items-stretch justify-center"
|
||||
style={{
|
||||
left: xPos(props.pos, props.isSignYourself),
|
||||
top: yPos(props.pos, props.isSignYourself),
|
||||
width:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself),
|
||||
zIndex: "10"
|
||||
}}
|
||||
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
calculateFont={calculateFont}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Rnd>
|
||||
<ModalUi isOpen={isDateModal} title={t("widget-info")} showClose={false}>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<div className="flex flex-row items-center">
|
||||
<span>{t("format")} : </span>
|
||||
<div className="flex">
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
defaultValue={""}
|
||||
onChange={(e) => {
|
||||
const selectedIndex = e.target.value;
|
||||
e.stopPropagation();
|
||||
setSelectDate(dateFormat[selectedIndex]);
|
||||
}}
|
||||
>
|
||||
<option value="" disabled>
|
||||
{t("select-date-format")}
|
||||
</option>
|
||||
{dateFormat.map((data, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={ind} key={ind}>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<span className="text-xs text-gray-400 ml-1">
|
||||
{selectDate.format}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center mt-4 md:mt-2">
|
||||
<span>{t("font-size")} :</span>
|
||||
<select
|
||||
className="ml-[3px] md:ml:[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={props.fontSize || clickonWidget.options?.fontSize || 12}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={size} key={ind}>
|
||||
{size}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="flex flex-row gap-1 items-center ml-2 md:ml-4 ">
|
||||
<span>{t("color")}: </span>
|
||||
<select
|
||||
value={
|
||||
props.fontColor || clickonWidget.options?.fontColor || "black"
|
||||
}
|
||||
onChange={(e) => props.setFontColor(e.target.value)}
|
||||
className="ml-[4px] md:ml[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
>
|
||||
{fontColorArr.map((color, ind) => {
|
||||
return (
|
||||
<option value={color} key={ind}>
|
||||
{t(`color-type.${color}`)}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span
|
||||
style={{
|
||||
background:
|
||||
props.fontColor || props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
className="w-5 h-[19px] ml-1"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={() => {
|
||||
setIsDateModal(false);
|
||||
handleSaveDate(selectDate);
|
||||
props.setFontColor("");
|
||||
props.setFontSize("");
|
||||
}}
|
||||
>
|
||||
{t("save")}
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Rnd>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import React, { useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { randomId, textWidget } from "../../constant/Utils";
|
||||
import {
|
||||
handleCopyNextToWidget,
|
||||
randomId,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const { t } = useTranslation();
|
||||
const copyType = ["All pages", "All pages but last", "All pages but first"];
|
||||
const [selectCopyType, setSelectCopyType] = useState("");
|
||||
const copyType = [
|
||||
{ id: 1, type: "All pages" },
|
||||
{ id: 2, type: "All pages but last" },
|
||||
{ id: 3, type: "All pages but first" },
|
||||
{ id: 4, type: "Next to current widget" }
|
||||
];
|
||||
const [selectCopyType, setSelectCopyType] = useState(1);
|
||||
//function for get copy placeholder position
|
||||
const getCopyPlaceholderPosition = (
|
||||
type,
|
||||
@@ -20,7 +29,7 @@ function PlaceholderCopy(props) {
|
||||
(data) => data.xPosition !== currentPlaceholder.xPosition
|
||||
);
|
||||
//copy all page placeholder at requested position except first page
|
||||
if (newPageNumber === 1 && type === "first") {
|
||||
if (newPageNumber === 1 && type === 3) {
|
||||
if (filterPosition && filterPosition.length > 0) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
@@ -29,7 +38,7 @@ function PlaceholderCopy(props) {
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position except last page
|
||||
else if (newPageNumber === props.allPages && type === "last") {
|
||||
else if (newPageNumber === props.allPages && type === 2) {
|
||||
if (existPlaceholderPosition) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
@@ -172,25 +181,65 @@ function PlaceholderCopy(props) {
|
||||
|
||||
//function for getting selected type placeholder copy
|
||||
const handleApplyCopy = () => {
|
||||
if (selectCopyType === "All pages") {
|
||||
copyPlaceholder("all");
|
||||
} else if (selectCopyType === "All pages but last") {
|
||||
copyPlaceholder("last");
|
||||
} else if (selectCopyType === "All pages but first") {
|
||||
copyPlaceholder("first");
|
||||
if (selectCopyType === 4) {
|
||||
const signerPosition = props.xyPostion;
|
||||
let currentXYposition;
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
if (signerId) {
|
||||
const filterSignerPosition = signerPosition.filter(
|
||||
(item) => item?.Id === signerId
|
||||
);
|
||||
currentXYposition = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === props?.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
currentXYposition = currentXYposition[0].pos.find(
|
||||
(position) => position.key === props.signKey
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPostion,
|
||||
props.pageNumber,
|
||||
props.setXyPostion,
|
||||
props?.Id
|
||||
);
|
||||
} else {
|
||||
const getIndex = props?.xyPostion.findIndex(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
const placeholderPosition = props?.xyPostion[getIndex];
|
||||
//get current placeholder position data which user want to copy
|
||||
currentXYposition = placeholderPosition.pos.find(
|
||||
(pos) => pos.key === props.signKey
|
||||
);
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
currentXYposition,
|
||||
props.widgetType,
|
||||
props.xyPostion,
|
||||
getIndex,
|
||||
props.setXyPostion
|
||||
);
|
||||
}
|
||||
} else {
|
||||
copyPlaceholder(selectCopyType);
|
||||
}
|
||||
};
|
||||
const handleUniqueId = () => {
|
||||
if (props.widgetType === textWidget) {
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
if (signerId && props.widgetType === textWidget) {
|
||||
props.setUniqueId(props?.tempSignerId);
|
||||
props.setTempSignerId("");
|
||||
}
|
||||
props.setIsPageCopy(false);
|
||||
setSelectCopyType(1);
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isPageCopy}
|
||||
title={t("copy-to-all-pages")}
|
||||
title={t("copy-title")}
|
||||
handleClose={() => handleUniqueId()}
|
||||
>
|
||||
<div className="h-full p-[20px] text-base-content">
|
||||
@@ -201,11 +250,11 @@ function PlaceholderCopy(props) {
|
||||
<input
|
||||
className="mr-[8px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
value={data}
|
||||
onChange={() => setSelectCopyType(data)}
|
||||
checked={selectCopyType === data}
|
||||
value={data.id}
|
||||
onChange={() => setSelectCopyType(data.id)}
|
||||
checked={selectCopyType === data.id}
|
||||
/>
|
||||
{t(`copy-type.${data}`)}
|
||||
{t(`copy-type.${data.type}`)}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,8 @@ function PlaceholderType(props) {
|
||||
const [textValue, setTextValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
const years = range(1990, getYear(new Date()) + 16, 1);
|
||||
const fontSize = props.calculateFont(props.pos.options?.fontSize);
|
||||
const fontColor = props.pos.options?.fontColor || "black";
|
||||
const months = [
|
||||
"January",
|
||||
"February",
|
||||
@@ -143,7 +145,12 @@ function PlaceholderType(props) {
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className={`${selectWidgetCls} text-[12px] overflow-hidden`}
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
className={`${selectWidgetCls} overflow-hidden`}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
@@ -343,9 +350,13 @@ function PlaceholderType(props) {
|
||||
return (
|
||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||
<input
|
||||
style={{
|
||||
width: fontSize,
|
||||
height: fontSize
|
||||
}}
|
||||
className={`${
|
||||
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||
} flex justify-center op-checkbox op-checkbox-xs rounded-[0.350rem]`}
|
||||
} flex justify-center op-checkbox rounded-[1px] `}
|
||||
onBlur={handleInputBlur}
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
@@ -379,7 +390,15 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<label className="text-xs mb-0 text-center">{data}</label>
|
||||
<label
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
className="text-xs mb-0 text-center"
|
||||
>
|
||||
{data}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -410,20 +429,16 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
@@ -433,6 +448,10 @@ function PlaceholderType(props) {
|
||||
case "dropdown":
|
||||
return props.data?.signerObjId === props.signerObjId ? (
|
||||
<select
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
className={`${selectWidgetCls} text-[12px] bg-inherit`}
|
||||
id="myDropdown"
|
||||
value={selectOption}
|
||||
@@ -450,20 +469,41 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
>
|
||||
{/* Default/Title option */}
|
||||
<option value="" disabled hidden>
|
||||
<option
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
value=""
|
||||
disabled
|
||||
hidden
|
||||
>
|
||||
{props?.pos?.options?.name}
|
||||
</option>
|
||||
|
||||
{props.pos?.options?.values?.map((data, ind) => {
|
||||
return (
|
||||
<option key={ind} value={data}>
|
||||
<option
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
) : (
|
||||
<div className="text-[12px] overflow-hidden">
|
||||
<div
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
className=" overflow-hidden"
|
||||
>
|
||||
{props.pos?.options?.name
|
||||
? props.pos.options.name
|
||||
: widgetTypeTraslation}
|
||||
@@ -511,20 +551,16 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
@@ -555,20 +591,16 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
overflow: "hidden",
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
@@ -599,20 +631,16 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
@@ -680,7 +708,14 @@ function PlaceholderType(props) {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-[12px] text-black items-center overflow-hidden">
|
||||
<div
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
className="items-center overflow-hidden"
|
||||
>
|
||||
<span>
|
||||
{props.selectDate
|
||||
? props.selectDate?.format
|
||||
@@ -732,10 +767,8 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
cols="50"
|
||||
@@ -743,10 +776,8 @@ function PlaceholderType(props) {
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontSize: fontSize,
|
||||
color: fontColor,
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
@@ -761,9 +792,12 @@ function PlaceholderType(props) {
|
||||
return (
|
||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||
<input
|
||||
className={`${
|
||||
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||
} ${"w-[15px] h-[15px]"} flex justify-center op-radio`}
|
||||
style={{
|
||||
width: fontSize,
|
||||
height: fontSize,
|
||||
marginTop: ind > 0 ? "10px" : "0px"
|
||||
}}
|
||||
className={`flex justify-center op-radio`}
|
||||
type="radio"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
@@ -778,7 +812,15 @@ function PlaceholderType(props) {
|
||||
}}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<label className="text-xs mb-0">{data}</label>
|
||||
<label
|
||||
style={{
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
className="text-xs mb-0"
|
||||
>
|
||||
{data}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -808,10 +850,8 @@ function PlaceholderType(props) {
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontFamily: "Arial, sans-serif",
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
fontSize: fontSize,
|
||||
color: fontColor
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
|
||||
@@ -16,7 +16,7 @@ const RecipientList = (props) => {
|
||||
//function for onhover signer name change background color
|
||||
const inputRef = useRef(null);
|
||||
const isWidgetExist = (Id) => {
|
||||
return props.signerPos.some((x) => x.Id === Id);
|
||||
return props.signerPos.some((x) => x.Id === Id && x.placeHolder);
|
||||
};
|
||||
|
||||
//handle drag start
|
||||
@@ -63,9 +63,7 @@ const RecipientList = (props) => {
|
||||
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);
|
||||
@@ -121,9 +119,7 @@ const RecipientList = (props) => {
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
props.setSignerObjId(obj?.objectId || "");
|
||||
props.setIsSelectId(ind);
|
||||
props.setContractName(obj?.className || "");
|
||||
props.setUniqueId(obj.Id);
|
||||
props.setRoleName(obj.Role);
|
||||
props.setBlockColor(obj?.blockColor);
|
||||
|
||||
@@ -12,7 +12,8 @@ function RenderAllPdfPage({
|
||||
pageNumber,
|
||||
signerPos,
|
||||
signerObjectId,
|
||||
containerWH
|
||||
containerWH,
|
||||
pdfRotateBase64
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||
@@ -66,6 +67,7 @@ function RenderAllPdfPage({
|
||||
)
|
||||
);
|
||||
};
|
||||
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
|
||||
return (
|
||||
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
@@ -78,7 +80,7 @@ function RenderAllPdfPage({
|
||||
<Document
|
||||
loading={t("loading-doc")}
|
||||
onLoadSuccess={onDocumentLoad}
|
||||
file={signPdfUrl}
|
||||
file={(pdfRotateBase64 && pdfDataBase64) || signPdfUrl}
|
||||
>
|
||||
{Array.from(new Array(allPages), (el, index) => (
|
||||
<div
|
||||
@@ -95,16 +97,16 @@ function RenderAllPdfPage({
|
||||
>
|
||||
{signerPos && addSignatureBookmark(index)}
|
||||
|
||||
<div className="relative z-[1] overflow-hidden">
|
||||
<Page
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={pageWidth - 60}
|
||||
scale={1}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
/>
|
||||
</div>
|
||||
{/* <div className="relative z-[1] overflow-hidden"> */}
|
||||
<Page
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={pageWidth - 60}
|
||||
scale={1}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
/>
|
||||
{/* </div> */}
|
||||
</div>
|
||||
))}
|
||||
</Document>
|
||||
|
||||
@@ -41,7 +41,6 @@ function RenderPdf({
|
||||
setUniqueId,
|
||||
signersdata,
|
||||
setIsPageCopy,
|
||||
setSignerObjId,
|
||||
setShowDropdown,
|
||||
setIsInitial,
|
||||
setIsValidate,
|
||||
@@ -61,10 +60,17 @@ function RenderPdf({
|
||||
scale,
|
||||
setIsSelectId,
|
||||
ispublicTemplate,
|
||||
handleUserDetails
|
||||
handleUserDetails,
|
||||
pdfRotateBase64,
|
||||
fontSize,
|
||||
setFontSize,
|
||||
fontColor,
|
||||
setFontColor,
|
||||
isResize
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const isMobile = window.innerWidth < 767;
|
||||
|
||||
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
@@ -99,6 +105,7 @@ function RenderPdf({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const posHeight = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
@@ -202,6 +209,7 @@ function RenderPdf({
|
||||
pageNumber={pageNumber}
|
||||
ispublicTemplate={ispublicTemplate}
|
||||
handleUserDetails={handleUserDetails}
|
||||
isResize={isResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -248,155 +256,184 @@ function RenderPdf({
|
||||
);
|
||||
}
|
||||
};
|
||||
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{successEmail && (
|
||||
<Alert type={"success"}>{t("success-email-alert")}</Alert>
|
||||
)}
|
||||
{isMobile ? (
|
||||
<div
|
||||
data-tut="reactourForth"
|
||||
className={`${
|
||||
isGuestSigner ? "30px" : ""
|
||||
} border-[0.1px] border-[#ebe8e8]`}
|
||||
ref={drop}
|
||||
id="container"
|
||||
<RSC
|
||||
style={{
|
||||
position: "relative",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
height: window.innerHeight - 200,
|
||||
zIndex: 0
|
||||
}}
|
||||
className="h-full"
|
||||
noScrollY={false}
|
||||
noScrollX={scale === 1 ? true : false}
|
||||
>
|
||||
{containerWH?.width &&
|
||||
pdfOriginalWH.length > 0 &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSigners(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder // placeholder mobile
|
||||
? signerPos.map((data, ind) => {
|
||||
<div
|
||||
data-tut="reactourForth"
|
||||
className={`${
|
||||
isGuestSigner ? "30px" : ""
|
||||
} border-[0.1px] border-[#ebe8e8] overflow-x-auto`}
|
||||
style={{
|
||||
width: containerWH?.width && containerWH?.width * scale
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
{containerWH?.width &&
|
||||
pdfOriginalWH.length > 0 &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSigners(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
: placeholder // placeholder mobile
|
||||
? signerPos?.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data?.placeHolder &&
|
||||
data?.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={
|
||||
setSelectWidgetId
|
||||
}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: !pdfDetails?.[0]?.IsCompleted &&
|
||||
xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
setPdfLoad(false);
|
||||
}}
|
||||
onLoadError={() => setPdfLoad(false)}
|
||||
loading={t("loading-doc")}
|
||||
onLoadSuccess={pageDetails}
|
||||
// ref={pdfRef}'
|
||||
@@ -405,9 +442,14 @@ function RenderPdf({
|
||||
setSelectWidgetId("");
|
||||
}
|
||||
}}
|
||||
file={pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL}
|
||||
file={
|
||||
(pdfRotateBase64 && pdfDataBase64) ||
|
||||
pdfDetails[0]?.SignedUrl ||
|
||||
pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
<Page
|
||||
scale={scale || 1}
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
width={containerWH.width}
|
||||
@@ -419,7 +461,7 @@ function RenderPdf({
|
||||
/>
|
||||
</Document>
|
||||
</div>
|
||||
</div>
|
||||
</RSC>
|
||||
) : (
|
||||
<RSC
|
||||
style={{
|
||||
@@ -453,71 +495,80 @@ function RenderPdf({
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{data?.placeHolder &&
|
||||
data?.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={
|
||||
setSelectWidgetId
|
||||
}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
: !pdfDetails?.[0]?.IsCompleted &&
|
||||
xyPostion?.map((data, ind) => {
|
||||
// signyourself flow
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
@@ -565,6 +616,12 @@ function RenderPdf({
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -575,9 +632,7 @@ function RenderPdf({
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
setPdfLoad(false);
|
||||
}}
|
||||
onLoadError={() => setPdfLoad(false)}
|
||||
loading={t("loading-doc")}
|
||||
onLoadSuccess={pageDetails}
|
||||
onClick={() => {
|
||||
@@ -586,7 +641,11 @@ function RenderPdf({
|
||||
}
|
||||
}}
|
||||
// ref={pdfRef}
|
||||
file={pdfDetails[0]?.SignedUrl || pdfDetails[0].URL}
|
||||
file={
|
||||
(pdfRotateBase64 && pdfDataBase64) ||
|
||||
pdfDetails[0]?.SignedUrl ||
|
||||
pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
<Page
|
||||
key={index}
|
||||
|
||||
@@ -6,34 +6,31 @@ function SelectLanguage(props) {
|
||||
const { i18n } = useTranslation();
|
||||
const languages = [
|
||||
{ value: "en", text: "English" },
|
||||
{ value: "es", text: "Española" },
|
||||
{ value: "fr", text: "Français" }
|
||||
];
|
||||
const [lang, setLang] = useState(i18next.language || "en");
|
||||
const defaultLanguage = i18next.language || "en";
|
||||
const [lang, setLang] = useState(defaultLanguage);
|
||||
// This function put query that helps to change the language
|
||||
const handleChangeLang = (e) => {
|
||||
setLang(e.target.value);
|
||||
i18n.changeLanguage(e.target.value);
|
||||
props?.updateExtUser &&
|
||||
props.updateExtUser({
|
||||
language: e.target.value
|
||||
});
|
||||
props?.updateExtUser && props.updateExtUser({ language: e.target.value });
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
!props.isProfile && " mt-3 pb-2 md:pb-0 "
|
||||
!props.isProfile && " mt-[9px] pb-2 md:pb-0 "
|
||||
} flex justify-center items-center `}
|
||||
>
|
||||
<select
|
||||
value={lang}
|
||||
onChange={handleChangeLang}
|
||||
className={`${
|
||||
!props.isProfile && " md:w-[15%] w-[50%]"
|
||||
} op-select op-select-bordered bg-white op-select-sm`}
|
||||
!props.isProfile ? " md:w-[15%] w-[50%]" : "w-[180px]"
|
||||
} op-select op-select-bordered bg-white op-select-sm `}
|
||||
>
|
||||
<option disabled selected>
|
||||
select
|
||||
</option>
|
||||
<option disabled>select</option>
|
||||
{languages.map((item) => {
|
||||
return (
|
||||
<option key={item.value} value={item.value}>
|
||||
|
||||
@@ -1,39 +1,20 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
function SignPad({
|
||||
isSignPad,
|
||||
isStamp,
|
||||
setIsImageSelect,
|
||||
onSaveSign,
|
||||
setIsSignPad,
|
||||
setImage,
|
||||
isImageSelect,
|
||||
imageRef,
|
||||
onImageChange,
|
||||
onSaveImage,
|
||||
image,
|
||||
defaultSign,
|
||||
setSignature,
|
||||
myInitial,
|
||||
isInitial,
|
||||
setIsInitial,
|
||||
setIsStamp,
|
||||
widgetType,
|
||||
currWidgetsDetails,
|
||||
setCurrWidgetsDetails
|
||||
}) {
|
||||
|
||||
function SignPad(props) {
|
||||
const { t } = useTranslation();
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const allColor = ["blue", "red", "black"];
|
||||
const canvasRef = useRef(null);
|
||||
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isTab, setIsTab] = useState("");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const [signatureType, setSignatureType] = useState("draw");
|
||||
const [textWidth, setTextWidth] = useState(0);
|
||||
const [textHeight, setTextHeight] = useState(0);
|
||||
const [signatureType, setSignatureType] = useState("");
|
||||
const [isSignTypes, setIsSignTypes] = useState(true);
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
{ value: "Dancing Script" },
|
||||
@@ -51,26 +32,60 @@ function SignPad({
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
const currentUserName = jsonSender && jsonSender?.name;
|
||||
|
||||
useEffect(() => {
|
||||
handleTab();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.signatureTypes]);
|
||||
|
||||
function handleTab() {
|
||||
const signtypes = props?.signatureTypes || [];
|
||||
const index = signtypes?.findIndex((x) => x.enabled === true);
|
||||
if (index !== -1) {
|
||||
setIsSignTypes(true);
|
||||
const tab = props?.signatureTypes[index].name;
|
||||
if (tab === "draw") {
|
||||
setIsTab("draw");
|
||||
setSignatureType("draw");
|
||||
} else if (tab === "upload") {
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
} else if (tab === "typed") {
|
||||
setIsTab("type");
|
||||
} else if (tab === "default") {
|
||||
setIsTab("mysignature");
|
||||
} else {
|
||||
setIsTab(true);
|
||||
}
|
||||
} else {
|
||||
setIsSignTypes(false);
|
||||
}
|
||||
}
|
||||
function isTabEnabled(tabName) {
|
||||
const isEnabled = props?.signatureTypes.find(
|
||||
(x) => x.name === tabName
|
||||
)?.enabled;
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
props?.setCurrWidgetsDetails({});
|
||||
if (isTab === "draw") {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.clear();
|
||||
} else if (isStamp) {
|
||||
setImage("");
|
||||
} else if (props?.isStamp) {
|
||||
props?.setImage("");
|
||||
}
|
||||
setIsSignImg("");
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
props?.setImage("");
|
||||
}
|
||||
// setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = (data) => {
|
||||
setSignature(data);
|
||||
props?.setSignature(data);
|
||||
setIsSignImg(data);
|
||||
};
|
||||
//save button component
|
||||
@@ -88,58 +103,57 @@ function SignPad({
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrWidgetsDetails({});
|
||||
if (!image) {
|
||||
props?.setCurrWidgetsDetails({});
|
||||
if (!props?.image) {
|
||||
if (isTab === "mysignature") {
|
||||
setIsSignImg("");
|
||||
if (isInitial) {
|
||||
onSaveSign(signatureType, "initials");
|
||||
if (props?.isInitial) {
|
||||
props?.onSaveSign(signatureType, "initials");
|
||||
} else {
|
||||
onSaveSign(null, "default");
|
||||
props?.onSaveSign(null, "default");
|
||||
}
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(
|
||||
props?.onSaveSign(
|
||||
null,
|
||||
false,
|
||||
!isInitial && textWidth > 150 ? 150 : textWidth,
|
||||
!isInitial && textHeight > 35 ? 35 : textHeight
|
||||
!props?.isInitial && textWidth > 150 ? 150 : textWidth,
|
||||
!props?.isInitial && textHeight > 35 ? 35 : textHeight
|
||||
);
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
canvasRef.current.clear();
|
||||
onSaveSign(signatureType);
|
||||
props?.onSaveSign(signatureType);
|
||||
}
|
||||
}
|
||||
|
||||
setPenColor("blue");
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveImage(signatureType);
|
||||
props?.onSaveImage(signatureType);
|
||||
}
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
props?.setIsSignPad(false);
|
||||
props?.setIsInitial(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
props?.setImage();
|
||||
handleTab();
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
props?.setIsStamp(false);
|
||||
}}
|
||||
type="button"
|
||||
className={`${
|
||||
isSignImg || image || isDefaultSign || textWidth
|
||||
isSignImg || props?.image || isDefaultSign || textWidth
|
||||
? ""
|
||||
: "pointer-events-none"
|
||||
} op-btn op-btn-primary shadow-lg`}
|
||||
disabled={
|
||||
(isTab === "draw" && isSignImg) ||
|
||||
(isTab === "image" && image) ||
|
||||
(isTab === "image" && props?.image) ||
|
||||
(isTab === "mysignature" && isDefaultSign) ||
|
||||
(isTab === "type" && textWidth)
|
||||
(isTab === "type" && signValue)
|
||||
? false
|
||||
: image
|
||||
: props?.image
|
||||
? false
|
||||
: true
|
||||
}
|
||||
@@ -151,12 +165,12 @@ function SignPad({
|
||||
};
|
||||
//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;
|
||||
if (props?.currWidgetsDetails && canvasRef.current) {
|
||||
const isWidgetType = props?.currWidgetsDetails?.type;
|
||||
const signatureType = props?.currWidgetsDetails?.signatureType;
|
||||
const url = props?.currWidgetsDetails?.SignUrl;
|
||||
//checking widget type and draw type signature url
|
||||
if (isInitial) {
|
||||
if (props?.isInitial) {
|
||||
if (isWidgetType === "initials" && signatureType === "draw" && url) {
|
||||
canvasRef.current.fromDataURL(url);
|
||||
}
|
||||
@@ -169,14 +183,14 @@ function SignPad({
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedName = currentUserName && currentUserName.trim();
|
||||
const trimmedName = currentUserName && currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
const userName = props?.isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName || "");
|
||||
setFontSelect("Fasthand");
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSignPad]);
|
||||
}, [props.isSignPad]);
|
||||
useEffect(() => {
|
||||
const loadFont = async () => {
|
||||
try {
|
||||
@@ -200,9 +214,11 @@ function SignPad({
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
if (isTab === "type") {
|
||||
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
const trimmedName = signValue
|
||||
? signValue?.trim()
|
||||
: currentUserName?.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = props?.isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
}
|
||||
@@ -231,7 +247,7 @@ function SignPad({
|
||||
// Draw the text content on the canvas
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
const addExtraWidth = isInitial ? 10 : 50;
|
||||
const addExtraWidth = props?.isInitial ? 10 : 50;
|
||||
const width = span.offsetWidth + addExtraWidth;
|
||||
const height = span.offsetHeight;
|
||||
setTextWidth(width);
|
||||
@@ -253,7 +269,7 @@ function SignPad({
|
||||
document.body.removeChild(span);
|
||||
// Convert the canvas to image data
|
||||
const dataUrl = canvasElement.toDataURL("image/png");
|
||||
setSignature(dataUrl);
|
||||
props?.setSignature(dataUrl);
|
||||
};
|
||||
|
||||
const PenColorComponent = (props) => {
|
||||
@@ -295,319 +311,365 @@ function SignPad({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// `handleCancelBtn` function trigger when user click on cross button
|
||||
const handleCancelBtn = () => {
|
||||
setPenColor("blue");
|
||||
props?.setIsSignPad(false);
|
||||
props?.setIsInitial(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
props?.setImage();
|
||||
handleTab();
|
||||
setSignValue("");
|
||||
props?.setIsStamp(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isSignPad && (
|
||||
{props?.isSignPad && (
|
||||
<div className="op-modal op-modal-open">
|
||||
<div className="op-modal-box px-[13px] pt-2 pb-0">
|
||||
<div className="flex justify-between text-base-content items-center">
|
||||
<div className="text-[1.2rem]">
|
||||
<div className="flex flex-row justify-between mt-[3px]">
|
||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||
{isStamp ? (
|
||||
<span className="text-base-content font-bold text-lg">
|
||||
{widgetType === "image" ||
|
||||
currWidgetsDetails?.type === "image"
|
||||
? t("upload-image")
|
||||
: t("upload-stamp-image")}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
setImage();
|
||||
if (isSignImg) {
|
||||
setSignature(isSignImg);
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
isTab === "draw"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("draw")}
|
||||
{isSignTypes ? (
|
||||
<>
|
||||
<div className="flex justify-between text-base-content items-center">
|
||||
<div className="text-[1.2rem]">
|
||||
<div className="flex flex-row justify-between mt-[3px]">
|
||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||
{props?.isStamp ? (
|
||||
<span className="text-base-content font-bold text-lg">
|
||||
{props?.widgetType === "image" ||
|
||||
props?.currWidgetsDetails?.type === "image"
|
||||
? t("upload-image")
|
||||
: t("upload-stamp-image")}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
setSignatureType("");
|
||||
}}
|
||||
className={`${
|
||||
isTab === "uploadImage"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("upload-image")}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "type"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("type")}
|
||||
</span>
|
||||
</div>
|
||||
{!isInitial && defaultSign ? (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-signature")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
isInitial &&
|
||||
myInitial && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-initials")}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
<>
|
||||
{isTabEnabled("draw") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
props?.setImage();
|
||||
if (isSignImg) {
|
||||
props?.setSignature(isSignImg);
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
isTab === "draw"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("draw")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isTabEnabled("upload") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
setSignatureType("");
|
||||
}}
|
||||
className={`${
|
||||
isTab === "uploadImage"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("upload-image")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isTabEnabled("typed") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
props?.setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "type"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("type")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{!props?.isInitial &&
|
||||
props?.defaultSign &&
|
||||
isTabEnabled("default") ? (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-signature")}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
props?.isInitial &&
|
||||
props?.myInitial &&
|
||||
isTabEnabled("default") && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
props?.setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
props?.setImage();
|
||||
}}
|
||||
className={`${
|
||||
isTab === "mysignature"
|
||||
? "op-link-primary"
|
||||
: "no-underline"
|
||||
} op-link underline-offset-8 ml-[2px]`}
|
||||
>
|
||||
{t("my-initials")}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={() => {
|
||||
setPenColor("blue");
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignatureType("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-[20px] h-full">
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
className="w-full h-full object-contain bg-white"
|
||||
draggable="false"
|
||||
src={isInitial ? myInitial : defaultSign}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : isImageSelect || isStamp ? (
|
||||
!image ? (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
isInitial ? "intialSignatureCanvas" : "signatureCanvas"
|
||||
}
|
||||
onClick={() => imageRef.current.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="text-[10px]">{t("upload")}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
marginBottom: 6,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
className={
|
||||
isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={imageRef}
|
||||
src={image.src}
|
||||
draggable="false"
|
||||
className=" object-contain h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="mr-[5px] text-[12px]">
|
||||
{isInitial ? t("initial-teb") : t("signature-tab")}:
|
||||
</span>
|
||||
<input
|
||||
maxLength={isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect, color: penColor }}
|
||||
type="text"
|
||||
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value && "rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px",
|
||||
color: penColor
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent convertToImg={convertToImg} />
|
||||
<SaveBtn />
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={handleCancelBtn}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current?.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
<div className="p-[20px] h-full">
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
className="w-full h-full object-contain bg-white"
|
||||
draggable="false"
|
||||
src={
|
||||
props?.isInitial
|
||||
? props?.myInitial
|
||||
: props?.defaultSign
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : props?.isImageSelect || props?.isStamp ? (
|
||||
!props?.image ? (
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginBottom: 6,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
onClick={() => props?.imageRef.current.click()}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
onChange={props?.onImageChange}
|
||||
className="filetype"
|
||||
accept="image/png,image/jpeg"
|
||||
ref={props?.imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="text-[10px]">{t("upload")}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
style={{
|
||||
border: "1.3px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
marginBottom: 6,
|
||||
overflow: "hidden"
|
||||
}}
|
||||
className={
|
||||
props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas"
|
||||
}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
ref={props?.imageRef}
|
||||
src={props?.image.src}
|
||||
draggable="false"
|
||||
className=" object-contain h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="mr-[5px] text-[12px]">
|
||||
{props?.isInitial
|
||||
? t("initial-teb")
|
||||
: t("signature-tab")}
|
||||
:
|
||||
</span>
|
||||
<input
|
||||
maxLength={props?.isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect, color: penColor }}
|
||||
type="text"
|
||||
className="ml-1 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-[20px]"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value &&
|
||||
"rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px",
|
||||
color: penColor
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent convertToImg={convertToImg} />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex justify-center">
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: props?.isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(
|
||||
canvasRef.current?.toDataURL()
|
||||
)
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="">
|
||||
<div className="relative flex flex-row items-center justify-between">
|
||||
<div className="text-base-content font-bold text-lg">
|
||||
Signature
|
||||
</div>
|
||||
<div className="flex flex-row justify-between mt-[10px]">
|
||||
<PenColorComponent />
|
||||
<SaveBtn />
|
||||
<div
|
||||
className="text-[1.5rem] cursor-pointer"
|
||||
onClick={handleCancelBtn}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-3 mb-6 mt-3">
|
||||
<p>{t("at-least-one-signature-type")}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -21,9 +21,11 @@ function TextFontSetting(props) {
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
"12"
|
||||
12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
onChange={(e) => {
|
||||
props.setFontSize(parseInt(e.target.value));
|
||||
}}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -56,9 +56,7 @@ function VerifyEmail(props) {
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
onClick={() => {
|
||||
props.handleVerifyBtn();
|
||||
}}
|
||||
onClick={() => props.handleVerifyBtn()}
|
||||
>
|
||||
{t("send-otp")}
|
||||
</button>
|
||||
|
||||
@@ -4,6 +4,7 @@ import RecipientList from "./RecipientList";
|
||||
import { useDrag } from "react-dnd";
|
||||
import WidgetList from "./WidgetList";
|
||||
import {
|
||||
color,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
@@ -20,9 +21,7 @@ function WidgetComponent({
|
||||
addPositionOfSignature,
|
||||
signersdata,
|
||||
isSelectListId,
|
||||
setSignerObjId,
|
||||
setIsSelectId,
|
||||
setContractName,
|
||||
isSigners,
|
||||
dataTut,
|
||||
isMailSend,
|
||||
@@ -45,163 +44,69 @@ function WidgetComponent({
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 5,
|
||||
text: "dropdown"
|
||||
},
|
||||
|
||||
item: { type: "BOX", id: 5, text: "dropdown" },
|
||||
collect: (monitor) => ({
|
||||
isDragDropdown: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, checkbox] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 6,
|
||||
text: "checkbox"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCheck: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 6, text: "checkbox" },
|
||||
collect: (monitor) => ({ isDragCheck: !!monitor.isDragging() })
|
||||
});
|
||||
const [, textInput] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 7,
|
||||
text: textInputWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragTextInput: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 7, text: textInputWidget },
|
||||
collect: (monitor) => ({ isDragTextInput: !!monitor.isDragging() })
|
||||
});
|
||||
const [, initials] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 8,
|
||||
text: "initials"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragInitial: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 8, text: "initials" },
|
||||
collect: (monitor) => ({ isDragInitial: !!monitor.isDragging() })
|
||||
});
|
||||
const [, name] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 9,
|
||||
text: "name"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragName: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 9, text: "name" },
|
||||
collect: (monitor) => ({ isDragName: !!monitor.isDragging() })
|
||||
});
|
||||
const [, company] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 10,
|
||||
text: "company"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCompany: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 10, text: "company" },
|
||||
collect: (monitor) => ({ isDragCompany: !!monitor.isDragging() })
|
||||
});
|
||||
const [, jobTitle] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 11,
|
||||
text: "job title"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragJobtitle: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 11, text: "job title" },
|
||||
collect: (monitor) => ({ isDragJobtitle: !!monitor.isDragging() })
|
||||
});
|
||||
const [, date] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 12,
|
||||
text: "date"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDate: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 12, text: "date" },
|
||||
collect: (monitor) => ({ isDragDate: !!monitor.isDragging() })
|
||||
});
|
||||
const [, image] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 13,
|
||||
text: "image"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragImage: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 13, text: "image" },
|
||||
collect: (monitor) => ({ isDragImage: !!monitor.isDragging() })
|
||||
});
|
||||
const [, email] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 14,
|
||||
text: "email"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragEmail: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 14, text: "email" },
|
||||
collect: (monitor) => ({ isDragEmail: !!monitor.isDragging() })
|
||||
});
|
||||
const [, radioButton] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 15,
|
||||
text: radioButtonWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragRadiotton: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 15, text: radioButtonWidget },
|
||||
collect: (monitor) => ({ isDragRadiotton: !!monitor.isDragging() })
|
||||
});
|
||||
const [, text] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 16,
|
||||
text: textWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragText: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 16, text: textWidget },
|
||||
collect: (monitor) => ({ isDragText: !!monitor.isDragging() })
|
||||
});
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [widget, setWidget] = useState([]);
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const handleModal = () => {
|
||||
setIsSignersModal(!isSignersModal);
|
||||
};
|
||||
@@ -360,9 +265,7 @@ function WidgetComponent({
|
||||
signerPos={signerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
setSignerObjId={setSignerObjId}
|
||||
setIsSelectId={setIsSelectId}
|
||||
setContractName={setContractName}
|
||||
setUniqueId={setUniqueId}
|
||||
setRoleName={setRoleName}
|
||||
handleDeleteUser={handleDeleteUser}
|
||||
|
||||
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
import { textInputWidget, textWidget } from "../../constant/Utils";
|
||||
import {
|
||||
signatureTypes,
|
||||
textInputWidget,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
@@ -11,6 +15,7 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const signTypes = props?.signatureType || signatureTypes;
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
@@ -21,6 +26,7 @@ const WidgetNameModal = (props) => {
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const statusArr = ["Required", "Optional"];
|
||||
const inputOpt = ["text", "email", "number"];
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.defaultdata) {
|
||||
@@ -40,12 +46,32 @@ const WidgetNameModal = (props) => {
|
||||
name: props.defaultdata?.options?.name || ""
|
||||
});
|
||||
}
|
||||
|
||||
if (signTypes.length > 0) {
|
||||
const defaultSignatureType = signTypes || [];
|
||||
setSignatureType(defaultSignatureType);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.defaultdata]);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
props.handleData(formdata);
|
||||
if (["signature", "initials"].includes(props.defaultdata?.type)) {
|
||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||
const isDefaultSignTypeOnly =
|
||||
enabledSignTypes?.length === 1 &&
|
||||
enabledSignTypes[0]?.name === "default";
|
||||
if (enabledSignTypes.length === 0) {
|
||||
alert(t("at-least-one-signature-type"));
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
alert(t("expect-default-one-more-signature-type"));
|
||||
} else {
|
||||
const data = { ...formdata, signatureType };
|
||||
props.handleData(data, props.defaultdata?.type);
|
||||
}
|
||||
} else {
|
||||
props.handleData(formdata);
|
||||
}
|
||||
setFormdata({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
@@ -53,6 +79,7 @@ const WidgetNameModal = (props) => {
|
||||
hint: "",
|
||||
textvalidate: ""
|
||||
});
|
||||
setSignatureType(signTypes);
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
@@ -102,6 +129,15 @@ const WidgetNameModal = (props) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Create a copy of the signatureType array
|
||||
const updatedSignatureType = [...signatureType];
|
||||
// Toggle the enabled value for the clicked item
|
||||
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// Update the state with the modified array
|
||||
setSignatureType(updatedSignatureType);
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
@@ -111,24 +147,30 @@ const WidgetNameModal = (props) => {
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
props.defaultdata?.type === textInputWidget ? "pt-0" : ""
|
||||
props.defaultdata?.type === textInputWidget
|
||||
? "pt-0"
|
||||
: ["signature", "initials"].includes(props.defaultdata?.type)
|
||||
? "pt-2"
|
||||
: ""
|
||||
} p-[20px] text-base-content`}
|
||||
>
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="name">
|
||||
{t("name")}
|
||||
<span className="text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
<label htmlFor="name">
|
||||
{t("name")}
|
||||
<span className="text-[red]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onInvalid={(e) => e.target.setCustomValidity(t("input-required"))}
|
||||
onInput={(e) => e.target.setCustomValidity("")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<>
|
||||
{isEnableSubscription && (
|
||||
@@ -226,30 +268,63 @@ const WidgetNameModal = (props) => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="flex flex-row gap-[5px] items-center">
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
{!["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex flex-row gap-[10px] mb-[0.5rem]">
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div className="text-[13px] font-medium">
|
||||
{t(`widget-status.${data}`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{["signature", "initials"].includes(props.defaultdata?.type) && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="signaturetype" className="text-[14px] mb-[0.7rem]">
|
||||
{t("allowed-signature-types")}
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => {
|
||||
return (
|
||||
<div key={i} className="flex flex-row gap-[5px] items-center">
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type.name}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="hint" className="text-[13px]">
|
||||
@@ -276,9 +351,9 @@ const WidgetNameModal = (props) => {
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize || props.defaultdata?.options?.fontSize || "12"
|
||||
props.fontSize || props.defaultdata?.options?.fontSize || 12
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
onChange={(e) => props.setFontSize(parseInt(e.target.value))}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
|
||||
@@ -7,10 +7,7 @@ const DROPBOX_SCRIPT_ID = "dropboxjs";
|
||||
|
||||
export default function DropboxChooser({ children, onSuccess, onCancel }) {
|
||||
useDropScript(DROPBOX_SDK_URL, {
|
||||
attrs: {
|
||||
id: DROPBOX_SCRIPT_ID,
|
||||
"data-app-key": DROPBOX_APP_KEY
|
||||
}
|
||||
attrs: { id: DROPBOX_SCRIPT_ID, "data-app-key": DROPBOX_APP_KEY }
|
||||
});
|
||||
|
||||
const options = useMemo(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import AsyncSelect from "react-select/async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
|
||||
const SelectSigners = (props) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -31,17 +31,19 @@ const SelectSigners = (props) => {
|
||||
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (inputValue.length > 1) {
|
||||
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||
}
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsigners`;
|
||||
const token = props.jwttoken
|
||||
? { jwttoken: props.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const searchEmail = inputValue ? inputValue : "";
|
||||
const axiosRes = await axios.post(url, { searchEmail }, { headers });
|
||||
const contactRes = axiosRes?.data?.result || [];
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||
@@ -52,11 +54,9 @@ const SelectSigners = (props) => {
|
||||
!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) => ({
|
||||
@@ -68,7 +68,6 @@ const SelectSigners = (props) => {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full px-[20px] py-[10px] text-base-content">
|
||||
<div className="w-full mx-auto p-[8px]">
|
||||
@@ -85,6 +84,7 @@ const SelectSigners = (props) => {
|
||||
loadOptions={loadOptions}
|
||||
onChange={handleOptions}
|
||||
unstyled
|
||||
onFocus={() => loadOptions()}
|
||||
classNames={{
|
||||
control: () =>
|
||||
"op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Select from "react-select";
|
||||
import AsyncSelect from "react-select/async";
|
||||
import AddSigner from "../../AddSigner";
|
||||
import Parse from "parse";
|
||||
import Tooltip from "../../../primitives/Tooltip";
|
||||
@@ -35,7 +35,6 @@ const AddSignerModal = ({ isOpen, children }) => {
|
||||
const SignersInput = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [state, setState] = useState(undefined);
|
||||
// const [editFormData, setEditFormData] = useState([]);
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [isModal, setIsModel] = useState(false);
|
||||
const onChange = (selectedOptions) => setSelected(selectedOptions);
|
||||
@@ -46,40 +45,6 @@ const SignersInput = (props) => {
|
||||
setSelected(newValue);
|
||||
};
|
||||
|
||||
const GetSelectListData = async () => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
let list = [];
|
||||
|
||||
// let _selected = [];
|
||||
res.forEach((x) => {
|
||||
let obj = {
|
||||
label: x.Name,
|
||||
value: x.objectId,
|
||||
isChecked: true
|
||||
};
|
||||
|
||||
list.push(obj);
|
||||
});
|
||||
setState(list);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
GetSelectListData();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (props.isReset && props.isReset === true) {
|
||||
setSelected([]);
|
||||
@@ -118,7 +83,46 @@ const SignersInput = (props) => {
|
||||
setSelected([data]);
|
||||
}
|
||||
};
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (inputValue.length > 1) {
|
||||
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||
}
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||
//and filter signers from total signer's list which already present in document's signers list
|
||||
const compareArrays = (res, signerObj) => {
|
||||
return res.filter(
|
||||
(item1) =>
|
||||
!signerObj.find((item2) => item2.objectId === item1.objectId)
|
||||
);
|
||||
};
|
||||
|
||||
//get update signer's List if signersdata is present
|
||||
const updateSignersList =
|
||||
props?.signersData && compareArrays(res, props?.signersData);
|
||||
|
||||
const result = updateSignersList ? updateSignersList : res;
|
||||
setState(result);
|
||||
return await result.map((item) => ({
|
||||
label: item.Name,
|
||||
value: item.objectId,
|
||||
isChecked: true
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<label className="block relative">
|
||||
@@ -130,16 +134,20 @@ const SignersInput = (props) => {
|
||||
</label>
|
||||
<div className="flex gap-x-[5px]">
|
||||
<div className="w-full">
|
||||
<Select
|
||||
<AsyncSelect
|
||||
onSortEnd={onSortEnd}
|
||||
distance={4}
|
||||
isMulti
|
||||
cacheOptions
|
||||
defaultOptions
|
||||
options={state || []}
|
||||
value={selected}
|
||||
onChange={onChange}
|
||||
closeMenuOnSelect={false}
|
||||
required={props.required}
|
||||
loadingMessage={() => t("loading")}
|
||||
noOptionsMessage={() => t("contact-not-found")}
|
||||
loadOptions={loadOptions}
|
||||
unstyled
|
||||
classNames={{
|
||||
control: () =>
|
||||
|
||||
@@ -25,7 +25,7 @@ const SuggestionInput = (props) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
(async () => {
|
||||
const res = await findContact(inputValue);
|
||||
const res = await findContact(inputValue, props.jwttoken);
|
||||
if (res?.length > 0) {
|
||||
setSuggestions(res);
|
||||
setShowSuggestions(true);
|
||||
@@ -37,13 +37,14 @@ const SuggestionInput = (props) => {
|
||||
}, 1000);
|
||||
}
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inputValue]);
|
||||
|
||||
const handleInputChange = async (e) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value?.toLowerCase());
|
||||
setInputValue(value?.toLowerCase()?.replace(/\s/g, ""));
|
||||
if (props.onChange) {
|
||||
props.onChange(value);
|
||||
props.onChange(value?.toLowerCase()?.replace(/\s/g, ""));
|
||||
}
|
||||
|
||||
if (value.trim() === "") {
|
||||
|
||||
+794
-468
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ import { serverUrl_fn } from "./appinfo";
|
||||
const parseAppId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const serverUrl = serverUrl_fn()
|
||||
const serverUrl = serverUrl_fn();
|
||||
export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||
//checking server url and save file's size
|
||||
const tenantPtr = {
|
||||
|
||||
@@ -25,7 +25,7 @@ i18n
|
||||
interpolation: {
|
||||
escapeValue: false // Not needed for react as it escapes by default
|
||||
},
|
||||
whitelist: ["en", "fr"] // List of allowed languages
|
||||
whitelist: ["en", "es", "fr"] // List of allowed languages
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -12,23 +12,35 @@ body {
|
||||
}
|
||||
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
@media screen and (max-width: 766px) {
|
||||
.bBofGr {
|
||||
width: 14px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* below css class replace bootstrap background color with daisy */
|
||||
.op-bg-primary {
|
||||
@apply bg-primary;
|
||||
}
|
||||
|
||||
.op-bg-secondary {
|
||||
@apply bg-secondary;
|
||||
}
|
||||
|
||||
.op-bg-info {
|
||||
@apply bg-info;
|
||||
}
|
||||
.op-bg-success{
|
||||
|
||||
.op-bg-success {
|
||||
@apply bg-success;
|
||||
}
|
||||
|
||||
.op-bg-warning {
|
||||
@apply bg-warning;
|
||||
}
|
||||
@@ -37,25 +49,37 @@ body {
|
||||
.op-text-primary {
|
||||
@apply text-primary;
|
||||
}
|
||||
|
||||
.op-text-secondary {
|
||||
@apply text-secondary;
|
||||
}
|
||||
|
||||
.op-text-info {
|
||||
@apply text-info;
|
||||
}
|
||||
.op-text-success{
|
||||
|
||||
.op-text-accent {
|
||||
@apply text-accent;
|
||||
}
|
||||
|
||||
.op-text-success {
|
||||
@apply text-success;
|
||||
}
|
||||
|
||||
.op-text-warning {
|
||||
@apply text-warning;
|
||||
}
|
||||
.op-border-primary{
|
||||
@apply border-primary
|
||||
|
||||
.op-border-primary {
|
||||
@apply border-primary;
|
||||
}
|
||||
|
||||
/* CSS for scrollbar customization */
|
||||
* {
|
||||
scrollbar-width: thin; /* For Firefox */
|
||||
scrollbar-color: gray; /* For Firefox */
|
||||
scrollbar-width: thin;
|
||||
/* For Firefox */
|
||||
scrollbar-color: gray;
|
||||
/* For Firefox */
|
||||
}
|
||||
|
||||
/* For webkit-based browsers like Chrome, Safari */
|
||||
|
||||
@@ -1,18 +1,38 @@
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
|
||||
export default function reportJson(id) {
|
||||
// console.log("json ", json);
|
||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const declineHead = [
|
||||
"Sr.No",
|
||||
"Title",
|
||||
"Reason",
|
||||
"Folder",
|
||||
"File",
|
||||
"Owner",
|
||||
"Signers"
|
||||
];
|
||||
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
|
||||
const head = ["Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const declineHead = ["Title", "Reason", "Folder", "File", "Owner", "Signers"];
|
||||
const iphead = ["Title", "Note", "Folder", "File", "Signers"];
|
||||
const contactbook = ["Name", "Email", "Phone"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Title", "File", "Owner", "Signers"];
|
||||
const templateSubAction = isEnableSubscription
|
||||
? [
|
||||
{
|
||||
btnId: "2435",
|
||||
btnLabel: "Embed",
|
||||
hoverLabel: "Embed",
|
||||
btnIcon: "fa-light fa-code",
|
||||
action: "Embed"
|
||||
},
|
||||
{
|
||||
btnId: "2436",
|
||||
btnLabel: "Copy TemplateId",
|
||||
hoverLabel: "Copy TemplateId",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
action: "CopyTemplateId"
|
||||
},
|
||||
{
|
||||
btnId: "2437",
|
||||
btnLabel: "Copy Public URL",
|
||||
hoverLabel: "Copy Public URL",
|
||||
btnIcon: "fa-light fa-copy",
|
||||
action: "CopyPublicURL"
|
||||
}
|
||||
]
|
||||
: [];
|
||||
switch (id) {
|
||||
// draft documents report
|
||||
case "ByHuevtCFY":
|
||||
@@ -63,7 +83,7 @@ export default function reportJson(id) {
|
||||
case "1MwEuxLEkF":
|
||||
return {
|
||||
reportName: "In-progress documents",
|
||||
heading: head,
|
||||
heading: iphead,
|
||||
actions: [
|
||||
{
|
||||
btnId: "8901",
|
||||
@@ -192,6 +212,25 @@ export default function reportJson(id) {
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
},
|
||||
{
|
||||
btnId: "3234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "2199",
|
||||
btnLabel: "extend-expiry-date",
|
||||
hoverLabel: "Extend expiry date",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-light fa-hourglass-end",
|
||||
redirectUrl: "",
|
||||
action: "extendexpiry"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
@@ -354,6 +393,7 @@ export default function reportJson(id) {
|
||||
redirectUrl: "template",
|
||||
action: "redirect"
|
||||
},
|
||||
...templateSubAction,
|
||||
{
|
||||
btnId: "1834",
|
||||
btnLabel: "Delete",
|
||||
@@ -375,18 +415,15 @@ export default function reportJson(id) {
|
||||
if (item.action === "option") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
newItem.subaction = [
|
||||
{
|
||||
btnId: "1873",
|
||||
btnLabel: "Share with team",
|
||||
hoverLabel: "Share with team",
|
||||
btnIcon: "fa-light fa-share-nodes",
|
||||
redirectUrl: "",
|
||||
action: "sharewith"
|
||||
},
|
||||
...newItem.subaction
|
||||
];
|
||||
|
||||
//splice method used to add `Share with team` option on second index of list
|
||||
newItem.subaction.splice(1, 0, {
|
||||
btnId: "1873",
|
||||
btnLabel: "Share with team",
|
||||
hoverLabel: "Share with team",
|
||||
btnIcon: "fa-light fa-share-nodes",
|
||||
redirectUrl: "",
|
||||
action: "sharewith"
|
||||
});
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
|
||||
@@ -36,7 +36,7 @@ const dashboardJson = [
|
||||
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',
|
||||
'where={"Type":{"#*ne":"Folder"},"Signers":{"#*exists":true,"#*ne":[]},"Placeholders":{"#*ne":null},"SignedUrl":{"#*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",
|
||||
|
||||
@@ -154,6 +154,14 @@ const sidebarList = [
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "webhook"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-sliders",
|
||||
title: "Preferences",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "preferences"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isStaging } from "../constant/const";
|
||||
const plans = [
|
||||
{
|
||||
planName: "OPENSIGN™ FREE",
|
||||
code: { monthly: "freeplan", yearly: "freeplan" },
|
||||
img: "free.png",
|
||||
currency: "",
|
||||
monthlyPrice: "Free",
|
||||
@@ -12,46 +13,49 @@ const plans = [
|
||||
url: "",
|
||||
target: "_blank",
|
||||
benefits: [
|
||||
"Unlimited envelopes",
|
||||
"Unlimited digital signatures",
|
||||
"Sign documents yourself",
|
||||
"Request signatures from others",
|
||||
"Unlimited templates",
|
||||
"14 field types",
|
||||
"Automatic e-signatures",
|
||||
"Completion certificates",
|
||||
"Send in order",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Document templates",
|
||||
"Import from Dropbox",
|
||||
"Contact book",
|
||||
"Document expiry support",
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"Bulk send (upto 5 docs)",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Unlimited envelopes",
|
||||
"Unlimited digital signatures",
|
||||
"Sign documents yourself",
|
||||
"Request signatures from others",
|
||||
"Unlimited templates",
|
||||
"14 field types",
|
||||
"Automatic e-signatures",
|
||||
"Completion certificates",
|
||||
"Send in order",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Document templates",
|
||||
"Import from Dropbox",
|
||||
"Contact book",
|
||||
"Document expiry support",
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"Bulk send (upto 5 docs)",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ PROFESSIONAL",
|
||||
code: isStaging
|
||||
? { monthly: "pro-weekly", yearly: "pro-yearly" }
|
||||
: { monthly: "professional-monthly", yearly: "professional-yearly" },
|
||||
img: "professional.png",
|
||||
currency: "$",
|
||||
monthlyPrice: "29.99",
|
||||
@@ -59,10 +63,10 @@ const plans = [
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btn: { text: "Subscribe", color: "op-btn-primary" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
? "https://billing.zohosecure.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
@@ -73,12 +77,14 @@ const plans = [
|
||||
"Webhooks",
|
||||
"Zapier integration",
|
||||
"API Access",
|
||||
"100 API signatures included",
|
||||
"upto 100 API signatures",
|
||||
"Custom email templates",
|
||||
"Connect your own Gmail or SMTP account for sending emails",
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 20 docs)",
|
||||
"Bulk send (upto 100 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
"Enforce email-based verification to confirm signer identity",
|
||||
"Embedded signing"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Everything in OpenSign™ free",
|
||||
@@ -88,16 +94,19 @@ const plans = [
|
||||
"Webhooks",
|
||||
"Zapier integration",
|
||||
"API Access",
|
||||
"240 API signatures included",
|
||||
"upto 240 API signatures",
|
||||
"Custom email templates",
|
||||
"Connect your own Gmail or SMTP account for sending emails",
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 20 docs)",
|
||||
"Bulk send (upto 240 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
"Enforce email-based verification to confirm signer identity",
|
||||
"Embedded signing"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ TEAMS",
|
||||
code: { monthly: "teams-monthly", yearly: "teams-yearly" },
|
||||
img: "teams.png",
|
||||
currency: "$",
|
||||
monthlyPrice: `39.99<sup style="font-size: 17px;">/user</sup>`,
|
||||
@@ -105,31 +114,33 @@ const plans = [
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btn: { text: "Subscribe", color: "op-btn-accent" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"100 API signatures included",
|
||||
"upto 100 API signatures",
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"BYOC - Store your documents in your own cloud storage",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 50 docs)",
|
||||
"Bulk send (upto 100 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
"Mobile app (coming soon)"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"500 API signatures included",
|
||||
"upto 500 API signatures",
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"BYOC - Store your documents in your own cloud storage",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 50 docs)",
|
||||
"Bulk send (upto 500 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
"Mobile app (coming soon)"
|
||||
]
|
||||
@@ -171,4 +182,74 @@ export const validplan = {
|
||||
"enterprise-monthly": true,
|
||||
"enterprise-yearly": true
|
||||
};
|
||||
export const paidUrl = (plan) => {
|
||||
const teamperiod = {
|
||||
"team-weekly": "monthly",
|
||||
"team-yearly": "yearly",
|
||||
"teams-monthly": "monthly",
|
||||
"teams-yearly": "yearly"
|
||||
};
|
||||
const period = teamperiod[plan] || "";
|
||||
if (period) {
|
||||
const extUser =
|
||||
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
|
||||
};
|
||||
// console.log("userDetails ", userDetails);
|
||||
const fullname = user && user.name ? user.name.split(" ") : "";
|
||||
const firstname = fullname?.[0]
|
||||
? "first_name=" + encodeURIComponent(fullname?.[0])
|
||||
: "";
|
||||
const lastname = fullname?.[1]
|
||||
? "&last_name=" + encodeURIComponent(fullname?.[1])
|
||||
: "";
|
||||
const name = firstname ? firstname + lastname : "";
|
||||
const email =
|
||||
user && user.email ? "&email=" + encodeURIComponent(user.email) : "";
|
||||
const company =
|
||||
user && user.company
|
||||
? "&company_name=" + encodeURIComponent(user.company)
|
||||
: "";
|
||||
const phone =
|
||||
user && user.phone ? "&mobile=" + encodeURIComponent(user.phone) : "";
|
||||
const allowedUsers =
|
||||
localStorage.getItem("allowedUsers") &&
|
||||
localStorage.getItem("allowedUsers") > 0
|
||||
? localStorage.getItem("allowedUsers") - 1
|
||||
: "";
|
||||
const quantity = allowedUsers
|
||||
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
|
||||
: "";
|
||||
|
||||
const details =
|
||||
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
||||
quantity +
|
||||
name +
|
||||
email +
|
||||
company +
|
||||
phone;
|
||||
|
||||
if (user) {
|
||||
localStorage.setItem("userDetails", JSON.stringify(user));
|
||||
}
|
||||
const url = {
|
||||
monthly: isStaging
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-monthly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
|
||||
yearly: isStaging
|
||||
? "https://billing.zohosecure.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/teams-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly"
|
||||
};
|
||||
|
||||
const planurl = url[period] + details;
|
||||
return planurl;
|
||||
} else {
|
||||
return "/subscription";
|
||||
}
|
||||
};
|
||||
export default plans;
|
||||
|
||||
@@ -95,7 +95,9 @@ const HomeLayout = () => {
|
||||
domain: updateDomain
|
||||
});
|
||||
};
|
||||
|
||||
const handleNavigation = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
async function checkIsSubscribed() {
|
||||
if (isEnableSubscription) {
|
||||
const res = await fetchSubscription();
|
||||
@@ -107,10 +109,10 @@ const HomeLayout = () => {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handleNavigation(res.plan);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handleNavigation(res.plan);
|
||||
}
|
||||
} else {
|
||||
setIsUserValid(true);
|
||||
@@ -222,23 +224,13 @@ const HomeLayout = () => {
|
||||
|
||||
async function checkTourStatus() {
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails");
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
// console.log("res ", res.data.TourStatus);
|
||||
if (cloudRes) {
|
||||
const extUser = JSON.parse(JSON.stringify(cloudRes));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||
const tourStatus = extUser?.TourStatus || [];
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter((obj) => obj["loginTour"]);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const loginTour = filteredtourStatus[0]["loginTour"];
|
||||
// console.log("loginTour", loginTour);
|
||||
if (loginTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
const loginTour = tourStatus.find((obj) => obj.loginTour)?.loginTour;
|
||||
setIsTour(!loginTour);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
@@ -250,9 +242,9 @@ const HomeLayout = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
const handleLoginBtn = async () => {
|
||||
try {
|
||||
Parse?.User?.logOut();
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
@@ -263,7 +255,9 @@ const HomeLayout = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-[501]">
|
||||
{!isLoader && <Header showSidebar={showSidebar} />}
|
||||
{!isLoader && (
|
||||
<Header showSidebar={showSidebar} setIsMenu={setIsOpen} />
|
||||
)}
|
||||
</div>
|
||||
{isUserValid ? (
|
||||
<>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
|
||||
const AddAdmin = () => {
|
||||
const { t, i18n } = useTranslation();
|
||||
@@ -45,8 +46,10 @@ const AddAdmin = () => {
|
||||
setState((prev) => ({ ...prev, loading: true }));
|
||||
try {
|
||||
const app = await getAppLogo();
|
||||
if (app?.user === "exist") {
|
||||
setErrMsg(t("admin-already-exist"));
|
||||
if (app?.error === "invalid_json") {
|
||||
setErrMsg(t("server-down"));
|
||||
} else if (app?.user === "exist") {
|
||||
setErrMsg(t("admin-exists"));
|
||||
}
|
||||
} catch (err) {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
@@ -56,8 +59,10 @@ const AddAdmin = () => {
|
||||
}
|
||||
};
|
||||
const clearStorage = async () => {
|
||||
if (Parse.User.current()) {
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const appid = localStorage.getItem("parseAppId");
|
||||
@@ -80,77 +85,81 @@ const AddAdmin = () => {
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
const user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
const userRes = await user.save();
|
||||
if (userRes) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: "contracts_Admin"
|
||||
}
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run("addadmin", params);
|
||||
if (usersignup) {
|
||||
if (isSubscribeNews) {
|
||||
subscribeNewsletter();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
const user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
const userRes = await user.save();
|
||||
if (userRes) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: "contracts_Admin"
|
||||
}
|
||||
handleNavigation(userRes.getSessionToken());
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
if (error.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("already-exists-this-username"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
};
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res) {
|
||||
if (res.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
const usersignup = await Parse.Cloud.run("addadmin", params);
|
||||
if (usersignup) {
|
||||
if (isSubscribeNews) {
|
||||
subscribeNewsletter();
|
||||
}
|
||||
);
|
||||
handleNavigation(userRes.getSessionToken());
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
if (error.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("already-exists-this-username"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res) {
|
||||
if (res.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(error.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(error.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,13 +468,13 @@ const AddAdmin = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-sm"
|
||||
id="termsandcondition"
|
||||
id="subscribetoopensign"
|
||||
checked={isSubscribeNews}
|
||||
onChange={(e) => setIsSubscribeNews(e.target.checked)}
|
||||
/>
|
||||
<label
|
||||
className="text-xs cursor-pointer ml-1 mb-0"
|
||||
htmlFor="termsandcondition"
|
||||
htmlFor="subscribetoopensign"
|
||||
>
|
||||
{t("subscribe-to-opensign")}
|
||||
</label>
|
||||
|
||||
@@ -74,7 +74,7 @@ const DebugPdf = () => {
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(2);
|
||||
const firstPage = await pdf.getPage(pdf?.numPages > 1 ? 2 : 1);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
@@ -215,11 +215,7 @@ const DebugPdf = () => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{copied && (
|
||||
<Alert type="success">
|
||||
<span className="ml-3">Copied</span>
|
||||
</Alert>
|
||||
)}
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
<Title title={"Debug Pdf"} />
|
||||
{width < 800 ? (
|
||||
<HandleError handleError={"Debug PDF only availble for PC"} />
|
||||
|
||||
@@ -7,7 +7,7 @@ import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { getAppLogo } from "../constant/Utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -15,11 +15,7 @@ function ForgotPassword() {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [state, setState] = useState({
|
||||
email: "",
|
||||
password: "",
|
||||
hideNav: ""
|
||||
});
|
||||
const [state, setState] = useState({ email: "", password: "", hideNav: "" });
|
||||
const [sentStatus, setSentStatus] = useState("");
|
||||
const [image, setImage] = useState();
|
||||
|
||||
@@ -40,18 +36,22 @@ function ForgotPassword() {
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
const username = state.email;
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(username);
|
||||
setSentStatus("success");
|
||||
} catch (err) {
|
||||
console.log("err ", err.code);
|
||||
setSentStatus("failed");
|
||||
} finally {
|
||||
setTimeout(() => setSentStatus(""), 1000);
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
const username = state.email;
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(username);
|
||||
setSentStatus("success");
|
||||
} catch (err) {
|
||||
console.log("err ", err.code);
|
||||
setSentStatus("failed");
|
||||
} finally {
|
||||
setTimeout(() => setSentStatus(""), 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -65,6 +65,11 @@ function ForgotPassword() {
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
const saveLogo = async () => {
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("err while logging out ", err);
|
||||
}
|
||||
if (isEnableSubscription) {
|
||||
const app = await getAppLogo();
|
||||
if (app?.logo) {
|
||||
|
||||
+886
-295
File diff suppressed because it is too large
Load Diff
@@ -10,17 +10,39 @@ import Loader from "../primitives/Loader";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import Tour from "reactour";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Parse from "parse";
|
||||
|
||||
function GenerateToken() {
|
||||
const origin = window.location.origin;
|
||||
const { t } = useTranslation();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const [apiToken, SetApiToken] = useState("");
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isModal, setIsModal] = useState({
|
||||
generateapi: false,
|
||||
buyapis: false,
|
||||
unauthorized: false
|
||||
});
|
||||
const [isSubscribe, setIsSubscribe] = useState({
|
||||
plan: "",
|
||||
isValid: false,
|
||||
adminId: "",
|
||||
adminEmail: ""
|
||||
});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [amount, setAmount] = useState({
|
||||
quantity: 500,
|
||||
priceperapi: 0.15,
|
||||
allowedcredits: 0,
|
||||
addoncredits: 0,
|
||||
totalcredits: 0,
|
||||
price: (75.0).toFixed(2)
|
||||
});
|
||||
const [isFormLoader, setIsFormLoader] = useState(false);
|
||||
const [apitestToken, setApiTestToken] = useState("");
|
||||
const quantityList = [500, 1000, 5000, 50000];
|
||||
useEffect(() => {
|
||||
fetchToken();
|
||||
// eslint-disable-next-line
|
||||
@@ -34,8 +56,8 @@ function GenerateToken() {
|
||||
const fetchToken = async () => {
|
||||
try {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
}
|
||||
const url = parseBaseUrl + "functions/getapitoken";
|
||||
const headers = {
|
||||
@@ -45,22 +67,40 @@ function GenerateToken() {
|
||||
};
|
||||
const res = await axios.post(url, {}, { headers: headers });
|
||||
if (res) {
|
||||
SetApiToken(res.data?.result?.result);
|
||||
const resCredits = await Parse.Cloud.run("allowedcredits");
|
||||
const allowedcredits = resCredits?.allowedcredits || 0;
|
||||
const addoncredits = resCredits?.addoncredits || 0;
|
||||
const totalcredits = allowedcredits + addoncredits;
|
||||
setAmount((obj) => ({
|
||||
...obj,
|
||||
allowedcredits: allowedcredits,
|
||||
addoncredits: addoncredits,
|
||||
totalcredits: totalcredits
|
||||
}));
|
||||
SetApiToken(res?.data?.result?.result);
|
||||
}
|
||||
try {
|
||||
const body = { email: Parse?.User?.current()?.getEmail() || "" };
|
||||
const testurl = "https://sandbox.opensignlabs.com/api/gettesttoken";
|
||||
const testRes = await axios.post(testurl, body);
|
||||
setApiTestToken(testRes.data?.token);
|
||||
} catch (err) {
|
||||
console.log("err in fetch test token", err);
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
SetApiToken();
|
||||
setIsLoader(false);
|
||||
console.log("Err", err);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!isSubscribe && isEnableSubscription) {
|
||||
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
setIsModal((obj) => ({ ...obj, generateapi: false }));
|
||||
try {
|
||||
const url = parseBaseUrl + "functions/generateapitoken";
|
||||
const headers = {
|
||||
@@ -91,20 +131,114 @@ function GenerateToken() {
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
setIsAlert({ type: "success", msg: t("copied") });
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleModal = () => {
|
||||
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
setIsModal((obj) => ({ ...obj, generateapi: !obj.generateapi }));
|
||||
}
|
||||
};
|
||||
const handleModal = () => setIsModal(!isModal);
|
||||
|
||||
const handleBuyAPIsModal = () => {
|
||||
if (isSubscribe?.plan === "freeplan" && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (isSubscribe?.adminId && extUser?.objectId === isSubscribe?.adminId) {
|
||||
setIsModal((obj) => ({ ...obj, buyapis: !obj.buyapis }));
|
||||
} else {
|
||||
setIsSubscribe((obj) => ({
|
||||
...obj,
|
||||
adminEmail: extUser?.TenantId?.EmailAddress
|
||||
}));
|
||||
setIsModal((obj) => ({ ...obj, unauthorized: !obj.unauthorized }));
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleCloseModal = () => {
|
||||
setIsModal({ generateapi: false, buyapis: false, unauthorized: false });
|
||||
};
|
||||
const handlePricePerAPIs = (e) => {
|
||||
const quantity = e.target?.value;
|
||||
const price =
|
||||
quantity > 0
|
||||
? (Math.round(quantity * amount.priceperapi * 100) / 100).toFixed(2)
|
||||
: (Math.round(500 * amount.priceperapi * 100) / 100).toFixed(2);
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleAddOnApiSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsFormLoader(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buycredits", {
|
||||
credits: amount.quantity
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setAmount((obj) => ({
|
||||
...obj,
|
||||
quantity: 500,
|
||||
priceperapi: 0.15,
|
||||
price: (75.0).toFixed(2),
|
||||
totalcredits: obj?.totalcredits + _resAddon.addon,
|
||||
addoncredits: obj?.addoncredits + _resAddon.addon
|
||||
}));
|
||||
}
|
||||
}
|
||||
setIsModal((obj) => ({ ...obj, buyapis: false }));
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
setIsFormLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGenerateTestToken = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoader(true);
|
||||
// setIsModal((obj) => ({ ...obj, generateapi: false }));
|
||||
try {
|
||||
const url = "https://sandbox.opensignlabs.com/api/generatetesttoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
token: apitestToken || "1"
|
||||
};
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
const body = {
|
||||
name: extUser?.Name,
|
||||
email: extUser?.Email,
|
||||
phone: extUser?.Phone,
|
||||
company: extUser?.Company,
|
||||
jobTitle: extUser?.JobTitle
|
||||
};
|
||||
const res = await axios.post(url, body, { headers: headers });
|
||||
if (res) {
|
||||
setApiTestToken(res.data?.token);
|
||||
setIsAlert({ type: "success", msg: t("token-generated") });
|
||||
} else {
|
||||
console.error("Error while generating Token");
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
}
|
||||
} catch (error) {
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
console.log("while generating Token", error);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"API Token"} />
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
{isLoader ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<Loader />
|
||||
@@ -115,29 +249,66 @@ function GenerateToken() {
|
||||
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||
OpenSign™ {t("API")}{" "}
|
||||
<Tooltip
|
||||
url={
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
}
|
||||
url="https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</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-t-[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="ml-1">
|
||||
<span className="font-medium">API Test Token : </span>
|
||||
<span className="text-xs">
|
||||
<Tooltip message={t("help-test-token")} />
|
||||
</span>
|
||||
</span>
|
||||
<span id="token" className="md:text-end py-2 md:py-0">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() => copytoclipboard(apitestToken)}
|
||||
>
|
||||
{apitestToken ? apitestToken : "_____"}
|
||||
</span>
|
||||
<button
|
||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
|
||||
onClick={() => copytoclipboard(apitestToken)}
|
||||
>
|
||||
<i className="fa-light fa-copy"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleGenerateTestToken}
|
||||
className="op-btn op-btn-primary w-[210px] md:w-auto"
|
||||
>
|
||||
{apitestToken
|
||||
? t("regenerate-test-token")
|
||||
: t("generate-test-token")}
|
||||
</button>
|
||||
</li>
|
||||
<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="ml-1">{t("api-token")}:</span>{" "}
|
||||
<span className="ml-1">
|
||||
<span className="font-medium">{t("api-token")} : </span>
|
||||
<span className="text-xs">
|
||||
<Tooltip message={t("help-api-token", { origin })} />
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
id="token"
|
||||
className={`${
|
||||
isSubscribe
|
||||
? ""
|
||||
: "bg-white/20 pointer-events-none select-none"
|
||||
isSubscribe?.plan === "freeplan"
|
||||
? "bg-white/20 pointer-events-none select-none"
|
||||
: ""
|
||||
} md:text-end py-2 md:py-0`}
|
||||
>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
{apiToken ? apiToken : "_____"}
|
||||
{isSubscribe?.plan !== "freeplan" && apiToken
|
||||
? apiToken
|
||||
: "_____"}
|
||||
</span>
|
||||
<button
|
||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
|
||||
@@ -149,13 +320,30 @@ function GenerateToken() {
|
||||
</div>
|
||||
<button
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="op-btn op-btn-primary"
|
||||
className="op-btn op-btn-primary w-[210px] md:w-auto"
|
||||
>
|
||||
{apiToken ? t("regenerate-token") : t("generate-token")}
|
||||
</button>
|
||||
</li>
|
||||
<div className="text-xs md:text-[15px] my-3 w-full md:w-[70%] flex-col md:flex-row flex items-center gap-x-5">
|
||||
<span className="font-medium">
|
||||
{t("remaining-credits")}{" "}
|
||||
<span className="text-xs">
|
||||
<Tooltip
|
||||
message={t("remaining-credits-help", {
|
||||
allowedcredits: amount?.allowedcredits || 0,
|
||||
addoncredits: amount?.addoncredits || 0
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span className="md:text-end py-2 md:py-0">
|
||||
{amount.totalcredits}
|
||||
</span>
|
||||
</div>
|
||||
<hr />
|
||||
</ul>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-0 md:gap-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
@@ -163,13 +351,20 @@ function GenerateToken() {
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary mt-2 mb-3 px-8"
|
||||
className="op-btn op-btn-secondary mt-2 md:mb-3 px-8 w-[210px] md:w-auto"
|
||||
>
|
||||
{t("view-docs")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleBuyAPIsModal()}
|
||||
className="op-btn op-btn-accent mt-2 mb-3 px-8 w-[210px] md:w-auto"
|
||||
>
|
||||
{t("buy-credits")}
|
||||
</button>
|
||||
</div>
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
isOpen={isModal.generateapi}
|
||||
title={apiToken ? t("regenerate-token") : t("generate-token")}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
@@ -194,8 +389,68 @@ function GenerateToken() {
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
{/* buy APIs */}
|
||||
<ModalUi
|
||||
isOpen={isModal.buyapis}
|
||||
title={"Buy Credits"}
|
||||
handleClose={handleCloseModal}
|
||||
>
|
||||
{isFormLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleAddOnApiSubmit} className="p-3">
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label
|
||||
htmlFor="quantity"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("quantity-of-credits")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={amount.quantity}
|
||||
onChange={(e) => handlePricePerAPIs(e)}
|
||||
name="quantity"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-1/4 text-xs"
|
||||
required
|
||||
>
|
||||
{quantityList.length > 0 &&
|
||||
quantityList.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("Price")} (1 * {amount.priceperapi})
|
||||
</label>
|
||||
<div className="w-1/4 flex justify-center items-center text-sm">
|
||||
USD {amount.price}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-base-content mb-3" />
|
||||
<button className="op-btn op-btn-primary w-full mt-2">
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isModal.unauthorized}
|
||||
title={"Unauthorized"}
|
||||
handleClose={handleCloseModal}
|
||||
>
|
||||
<div className="m-8 flex justify-center text-center items-center font-medium break-all">
|
||||
{t("unauthorized-modal", {
|
||||
adminEmail: isSubscribe?.adminEmail
|
||||
})}
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
{isEnableSubscription && isSubscribe?.plan === "freeplan" && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard />
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import {
|
||||
contractUsers,
|
||||
getAppLogo,
|
||||
@@ -28,6 +28,28 @@ function GuestLogin() {
|
||||
const [contactId, setContactId] = useState(contactBookId);
|
||||
const [sendmail, setSendmail] = useState();
|
||||
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||
const navigateToDoc = async (docId, contactId) => {
|
||||
try {
|
||||
const docDetails = await Parse.Cloud.run("getDocument", {
|
||||
docId: docId
|
||||
});
|
||||
if (!docDetails.error) {
|
||||
if (sendmail === "false") {
|
||||
navigate(
|
||||
`/load/recipientSignPdf/${docId}/${contactId}?sendmail=${sendmail}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/load/recipientSignPdf/${docId}/${contactId}`);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting doc", err);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
handleServerUrl();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -70,13 +92,14 @@ function GuestLogin() {
|
||||
"linkcontacttodoc",
|
||||
params
|
||||
);
|
||||
// console.log("linkContactRes ", linkContactRes);
|
||||
setContactId(linkContactRes?.contactId);
|
||||
await navigateToDoc(checkSplit[0], linkContactRes?.contactId);
|
||||
} catch (err) {
|
||||
console.log("Err in link ext contact", err);
|
||||
}
|
||||
} else {
|
||||
setContactId(checkSplit[2]);
|
||||
await navigateToDoc(checkSplit[0], checkSplit[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,15 +137,12 @@ function GuestLogin() {
|
||||
if (OTP) {
|
||||
setLoading(true);
|
||||
try {
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail/`;
|
||||
let url = `${serverUrl}functions/AuthLoginAsMail`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email,
|
||||
otp: OTP
|
||||
};
|
||||
let body = { email: email, otp: OTP };
|
||||
let user = await axios.post(url, body, { headers: headers });
|
||||
if (user.data.result === "Invalid Otp") {
|
||||
alert(t("invalid-otp"));
|
||||
@@ -140,7 +160,6 @@ function GuestLogin() {
|
||||
`Parse/${parseId}/currentUser`,
|
||||
JSON.stringify(_user)
|
||||
);
|
||||
// console.log("contractUserDetails ", contractUserDetails);
|
||||
if (contractUserDetails && contractUserDetails.length > 0) {
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
@@ -170,18 +189,30 @@ function GuestLogin() {
|
||||
};
|
||||
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(t("something-went-wrong-mssg"));
|
||||
console.log("Err in link ext contact", err);
|
||||
if (!emailRegex.test(contact.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
const params = { ...contact, docId: documentId };
|
||||
try {
|
||||
setLoading(true);
|
||||
const linkContactRes = await Parse.Cloud.run(
|
||||
"linkcontacttodoc",
|
||||
params
|
||||
);
|
||||
setContactId(linkContactRes.contactId);
|
||||
const IsEnableOTP = await navigateToDoc(
|
||||
documentId,
|
||||
linkContactRes.contactId
|
||||
);
|
||||
if (!IsEnableOTP) {
|
||||
setEnterOtp(true);
|
||||
await SendOtp();
|
||||
}
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
console.log("Err in link ext contact", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleInputChange = (e) => {
|
||||
@@ -208,7 +239,7 @@ function GuestLogin() {
|
||||
<div className="w-full md:w-[50%] text-base-content">
|
||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
|
||||
{t("guest-email-alert")}
|
||||
{t("get-otp-alert")}
|
||||
</legend>
|
||||
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||
<input
|
||||
@@ -236,7 +267,7 @@ function GuestLogin() {
|
||||
>
|
||||
<h1 className="text-2xl md:text-[30px]">{t("welcome")}</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2">
|
||||
{t("get-verification-code-2")}
|
||||
{t("guest-email-alert")}
|
||||
</legend>
|
||||
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
|
||||
<p className="text-sm">{t("enter-verification-code")}</p>
|
||||
@@ -324,6 +355,7 @@ function GuestLogin() {
|
||||
onChange={handleInputChange}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
disabled={loading}
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2 flex justify-start">
|
||||
|
||||
+174
-158
@@ -9,7 +9,7 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
@@ -51,6 +51,7 @@ function Login() {
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [image, setImage] = useState();
|
||||
const [isLoginSSO, setIsLoginSSO] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExt();
|
||||
@@ -59,7 +60,9 @@ function Login() {
|
||||
|
||||
const checkUserExt = async () => {
|
||||
const app = await getAppLogo();
|
||||
if (!isEnableSubscription && app?.user === "not_exist") {
|
||||
if (app?.error === "invalid_json") {
|
||||
setErrMsg(t("server-down"));
|
||||
} else if (!isEnableSubscription && app?.user === "not_exist") {
|
||||
navigate("/addadmin");
|
||||
}
|
||||
if (app?.logo) {
|
||||
@@ -80,171 +83,180 @@ function Login() {
|
||||
}
|
||||
setState({ ...state, [name]: value });
|
||||
};
|
||||
|
||||
const handlePaidRoute = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
const handleSubmit = async (event) => {
|
||||
localStorage.removeItem("accesstoken");
|
||||
event.preventDefault();
|
||||
const { email, password } = state;
|
||||
if (email && password) {
|
||||
try {
|
||||
setState({ ...state, loading: true });
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
// Pass the username and password to logIn function
|
||||
const user = await Parse.User.logIn(email, password);
|
||||
if (user) {
|
||||
let _user = user.toJSON();
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", email);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||
} else {
|
||||
localStorage.setItem("profileImg", "");
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
const userSettings = appInfo.settings;
|
||||
await Parse.Cloud.run("getUserDetails")
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${menu.pageType}/${menu.pageId}`;
|
||||
let _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const checkLanguage = extUser?.get("Language");
|
||||
if (checkLanguage) {
|
||||
checkLanguage && i18n.changeLanguage(checkLanguage);
|
||||
}
|
||||
if (!emailRegex.test(state.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
const { email, password } = state;
|
||||
if (email && password) {
|
||||
try {
|
||||
setState({ ...state, loading: true });
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
// Pass the username and password to logIn function
|
||||
const user = await Parse.User.logIn(email, password);
|
||||
if (user) {
|
||||
let _user = user.toJSON();
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", email);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||
} else {
|
||||
localStorage.setItem("profileImg", "");
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
const userSettings = appInfo.settings;
|
||||
await Parse.Cloud.run("getUserDetails")
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
// console.log("extUser", extUser, extUser?.get("IsDisabled"));
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${menu.pageType}/${menu.pageId}`;
|
||||
let _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const checkLanguage = extUser?.get("Language");
|
||||
if (checkLanguage) {
|
||||
checkLanguage && i18n.changeLanguage(checkLanguage);
|
||||
}
|
||||
|
||||
const results = [extUser];
|
||||
const extUser_str = JSON.stringify(results);
|
||||
const results = [extUser];
|
||||
const extUser_str = JSON.stringify(results);
|
||||
|
||||
localStorage.setItem("Extand_Class", extUser_str);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo.Email);
|
||||
localStorage.setItem("username", extInfo.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0]?.get("Phone") || "",
|
||||
company: results[0].get("Company")
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const res = await fetchSubscription();
|
||||
const freeplan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (freeplan === "freeplan") {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
|
||||
localStorage.setItem("Extand_Class", extUser_str);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo.Email);
|
||||
localStorage.setItem("username", extInfo.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0]?.get("Phone") || "",
|
||||
company: results[0].get("Company")
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const res = await fetchSubscription();
|
||||
const plan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (plan === "freeplan") {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login setState({ ...state, loading: false });
|
||||
setState({ ...state, loading: false });
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(`/subscription`, { replace: true });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
navigate(`/subscription`, { replace: true });
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
setIsModal(true);
|
||||
}
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"You don't have access, please contact the admin."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
} else {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"You don't have access, please contact the admin."
|
||||
});
|
||||
logOutUser();
|
||||
if (isEnableSubscription) {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User not found."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isEnableSubscription) {
|
||||
setState({ ...state, loading: false });
|
||||
setIsModal(true);
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User not found."
|
||||
});
|
||||
logOutUser();
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// const payload = { sessionToken: user.getSessionToken() };
|
||||
// handleSubmitbtn(payload);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `Something went wrong.`
|
||||
})
|
||||
.catch((error) => {
|
||||
// const payload = { sessionToken: user.getSessionToken() };
|
||||
// handleSubmitbtn(payload);
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `Something went wrong.`
|
||||
});
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.error("Error while fetching Follow", error);
|
||||
});
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.error("Error while fetching Follow", error);
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
console.log(error);
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
console.log(error);
|
||||
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "Invalid username or password!"
|
||||
});
|
||||
console.error("Error while logging in user", error);
|
||||
} finally {
|
||||
setTimeout(
|
||||
() => setState((prev) => ({ ...prev, alertMsg: "" })),
|
||||
2000
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "Invalid username or password!"
|
||||
});
|
||||
console.error("Error while logging in user", error);
|
||||
} finally {
|
||||
setTimeout(() => setState((prev) => ({ ...prev, alertMsg: "" })), 2000);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -330,9 +342,9 @@ function Login() {
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const res = await fetchSubscription();
|
||||
const freeplan = res.plan;
|
||||
const plan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (freeplan === "freeplan") {
|
||||
if (plan === "freeplan") {
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
@@ -342,14 +354,14 @@ function Login() {
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -462,8 +474,8 @@ function Login() {
|
||||
localStorage.setItem("userDetails", JSON.stringify(userInfo));
|
||||
const res = await fetchSubscription();
|
||||
const billingDate = res.billingDate;
|
||||
const freeplan = res.plan;
|
||||
if (freeplan === "freeplan") {
|
||||
const plan = res.plan;
|
||||
if (plan === "freeplan") {
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
@@ -471,10 +483,10 @@ function Login() {
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
// Redirect to the appropriate URL after successful login
|
||||
@@ -573,10 +585,12 @@ function Login() {
|
||||
}
|
||||
};
|
||||
|
||||
const logOutUser = () => {
|
||||
const logOutUser = async () => {
|
||||
setIsModal(false);
|
||||
if (Parse?.User?.current()) {
|
||||
Parse.User.logOut();
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
@@ -616,7 +630,11 @@ function Login() {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
return errMsg ? (
|
||||
<div className="h-screen flex justify-center text-center items-center p-4 text-gray-500 text-base">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Title title={"Login Page"} />
|
||||
{state.loading && (
|
||||
@@ -789,9 +807,7 @@ function Login() {
|
||||
</div>
|
||||
<SelectLanguage />
|
||||
{state.alertMsg && (
|
||||
<Alert type={state.alertType}>
|
||||
<div className="ml-3">{state.alertMsg}</div>
|
||||
</Alert>
|
||||
<Alert type={state.alertType}>{state.alertMsg}</Alert>
|
||||
)}
|
||||
</div>
|
||||
<ModalUi
|
||||
|
||||
@@ -2,12 +2,13 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import "../styles/managesign.css";
|
||||
import "../styles/signature.css";
|
||||
import { toDataUrl } from "../constant/Utils";
|
||||
import { generateTitleFromFilename, toDataUrl } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||
const ManageSign = () => {
|
||||
const { t } = useTranslation();
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
@@ -23,9 +24,11 @@ const ManageSign = () => {
|
||||
const [Initials, setInitials] = useState("");
|
||||
const [isInitials, setIsInitials] = useState(false);
|
||||
const [id, setId] = useState("");
|
||||
const [imgInitials, setImgInitials] = useState("");
|
||||
const canvasRef = useRef(null);
|
||||
const imageRef = useRef(null);
|
||||
const initailsRef = useRef(null);
|
||||
const imgInitialsRef = useRef(null);
|
||||
useEffect(() => {
|
||||
fetchUserSign();
|
||||
// eslint-disable-next-line
|
||||
@@ -46,14 +49,23 @@ const ManageSign = () => {
|
||||
if (signRes) {
|
||||
const res = signRes.toJSON();
|
||||
setId(res.objectId);
|
||||
setSignName(res?.SignatureName);
|
||||
setImage(res.ImageURL);
|
||||
if (res?.SignatureName) {
|
||||
const sanitizename = generateTitleFromFilename(res?.SignatureName);
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
setSignName(replaceSpace);
|
||||
}
|
||||
setImage(res?.ImageURL);
|
||||
if (res && res.Initials) {
|
||||
setInitials(res.Initials);
|
||||
// setInitials(res.Initials);
|
||||
setIsInitials(true);
|
||||
setImgInitials(res?.Initials);
|
||||
}
|
||||
} else {
|
||||
setSignName(User?.get("name") || "");
|
||||
if (User?.get("name")) {
|
||||
const sanitizename = generateTitleFromFilename(User?.get("name"));
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
setSignName(replaceSpace);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
@@ -86,8 +98,14 @@ const ManageSign = () => {
|
||||
if (initailsRef.current) {
|
||||
initailsRef.current.clear();
|
||||
}
|
||||
if (imgInitialsRef.current) {
|
||||
imgInitialsRef.current.value = "";
|
||||
}
|
||||
setImgInitials("");
|
||||
setInitials("");
|
||||
setIsValue(true);
|
||||
if (image) {
|
||||
setIsValue(true);
|
||||
}
|
||||
setIsInitials(false);
|
||||
};
|
||||
|
||||
@@ -101,40 +119,54 @@ const ManageSign = () => {
|
||||
const base64Img = await toDataUrl(file);
|
||||
setImage(base64Img);
|
||||
setIsValue(true);
|
||||
} else {
|
||||
setImage("");
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const isUrl = image.includes("https");
|
||||
|
||||
const isUrl = image?.includes("https") || image?.includes("http");
|
||||
if (!isvalue) {
|
||||
setWarning(true);
|
||||
setTimeout(() => setWarning(false), 1000);
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
const replaceSpace = signName.replace(/ /g, "_");
|
||||
const sanitizename = generateTitleFromFilename(signName);
|
||||
const replaceSpace = sanitizeFileName(sanitizename);
|
||||
let file;
|
||||
if (signature) {
|
||||
file = base64StringtoFile(signature, `${replaceSpace}_sign.png`);
|
||||
file = base64StringtoFile(signature, `${replaceSpace}_sign`);
|
||||
} else {
|
||||
if (!isUrl) {
|
||||
file = base64StringtoFile(image, `${replaceSpace}__sign.png`);
|
||||
if (image && !isUrl) {
|
||||
file = base64StringtoFile(image, `${replaceSpace}__sign`);
|
||||
}
|
||||
}
|
||||
let imgUrl;
|
||||
if (!isUrl) {
|
||||
if (file && !isUrl) {
|
||||
imgUrl = await uploadFile(file);
|
||||
} else {
|
||||
imgUrl = image;
|
||||
}
|
||||
let initialsUrl = "";
|
||||
const isInitialsUrl = Initials.includes("https");
|
||||
if (!isInitialsUrl && Initials) {
|
||||
const initialsImg = base64StringtoFile(
|
||||
Initials,
|
||||
`${replaceSpace}_initials.png`
|
||||
);
|
||||
initialsUrl = await uploadFile(initialsImg);
|
||||
const isInitialsUrl =
|
||||
imgInitials?.includes("https") || imgInitials?.includes("http");
|
||||
|
||||
let initialFile;
|
||||
if (Initials) {
|
||||
initialFile = base64StringtoFile(Initials, `${replaceSpace}_sign`);
|
||||
} else {
|
||||
if (imgInitials && !isInitialsUrl) {
|
||||
initialFile = base64StringtoFile(
|
||||
imgInitials,
|
||||
`${replaceSpace}__sign`
|
||||
);
|
||||
}
|
||||
}
|
||||
let initialsUrl;
|
||||
if (initialFile && !isInitialsUrl) {
|
||||
initialsUrl = await uploadFile(initialFile);
|
||||
} else {
|
||||
initialsUrl = imgInitials;
|
||||
}
|
||||
if (imgUrl) {
|
||||
await saveEntry({
|
||||
@@ -146,15 +178,19 @@ const ManageSign = () => {
|
||||
}
|
||||
};
|
||||
function base64StringtoFile(base64String, filename) {
|
||||
var arr = base64String.split(","),
|
||||
let arr = base64String.split(","),
|
||||
// type of uploaded image
|
||||
mime = arr[0].match(/:(.*?);/)[1],
|
||||
// decode base64
|
||||
bstr = atob(arr[1]),
|
||||
n = bstr.length,
|
||||
u8arr = new Uint8Array(n);
|
||||
while (n--) {
|
||||
u8arr[n] = bstr.charCodeAt(n);
|
||||
}
|
||||
return new File([u8arr], filename, { type: mime });
|
||||
const ext = mime.split("/").pop();
|
||||
const name = `${filename}.${ext}`;
|
||||
return new File([u8arr], name, { type: mime });
|
||||
}
|
||||
|
||||
const uploadFile = async (file) => {
|
||||
@@ -175,31 +211,24 @@ const ManageSign = () => {
|
||||
|
||||
const saveEntry = async (obj) => {
|
||||
const signCls = "contracts_Signature";
|
||||
const User = Parse.User.current().id;
|
||||
const User = Parse?.User?.current()?.id;
|
||||
const userId = { __type: "Pointer", className: "_User", objectId: User };
|
||||
if (id) {
|
||||
try {
|
||||
const updateSign = new Parse.Object(signCls);
|
||||
updateSign.id = id;
|
||||
updateSign.set("Initials", obj.initialsUrl ? obj.initialsUrl : "");
|
||||
updateSign.set("ImageURL", obj.url);
|
||||
updateSign.set("ImageURL", obj.url ? obj.url : "");
|
||||
updateSign.set("SignatureName", obj.name);
|
||||
updateSign.set("UserId", userId);
|
||||
const res = await updateSign.save();
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: t("signature-saved-alert")
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setIsAlert({ type: "danger", message: `${err.message}` });
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: `${err.message}`
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
}
|
||||
} else {
|
||||
@@ -210,20 +239,13 @@ const ManageSign = () => {
|
||||
updateSign.set("SignatureName", obj.name);
|
||||
updateSign.set("UserId", userId);
|
||||
const res = await updateSign.save();
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: t("signature-saved-alert")
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
setIsAlert({ type: "success", message: t("signature-saved-alert") });
|
||||
return res;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
setIsAlert({ type: "success", message: `${err.message}` });
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: `${err.message}`
|
||||
});
|
||||
setTimeout(() => setIsAlert({}), 2000);
|
||||
}
|
||||
}
|
||||
@@ -234,7 +256,29 @@ const ManageSign = () => {
|
||||
};
|
||||
const handleInitialsChange = () => {
|
||||
setInitials(initailsRef.current.toDataURL());
|
||||
setIsValue(true);
|
||||
if (image || signature) {
|
||||
setIsValue(true);
|
||||
}
|
||||
};
|
||||
const handleUploadInitials = () => {
|
||||
imgInitialsRef.current.click();
|
||||
};
|
||||
const onImgInitialsChange = async (event) => {
|
||||
if (initailsRef.current) {
|
||||
initailsRef.current.clear();
|
||||
}
|
||||
setInitials("");
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
const file = event.target.files[0];
|
||||
const base64Img = await toDataUrl(file);
|
||||
setImgInitials(base64Img);
|
||||
if (image || signature) {
|
||||
setIsValue(true);
|
||||
}
|
||||
} else {
|
||||
setImgInitials("");
|
||||
setIsValue(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
|
||||
@@ -281,7 +325,7 @@ const ManageSign = () => {
|
||||
height: "180px",
|
||||
className: "signatureCanvas rounded-box"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
@@ -332,7 +376,7 @@ const ManageSign = () => {
|
||||
className="op-link"
|
||||
onClick={() => handleUploadBtn()}
|
||||
>
|
||||
{t("upload-image")}
|
||||
{t("upload")}
|
||||
</div>
|
||||
<div
|
||||
type="button"
|
||||
@@ -356,17 +400,21 @@ const ManageSign = () => {
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
{t("initials")}
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImgInitialsChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
ref={imgInitialsRef}
|
||||
hidden
|
||||
/>
|
||||
<div>
|
||||
{isInitials ? (
|
||||
{imgInitials ? (
|
||||
<div className="intialSignature relative border-[1px] border-[#888] rounded-box overflow-hidden">
|
||||
<img
|
||||
alt="inititals"
|
||||
src={Initials}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
src={imgInitials}
|
||||
className="w-[100%] h-[100%] object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -376,10 +424,8 @@ const ManageSign = () => {
|
||||
canvasProps={{
|
||||
className: "intialSignature rounded-box"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleInitialsChange(initailsRef.current.toDataURL())
|
||||
}
|
||||
// backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() => handleInitialsChange()}
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
@@ -402,6 +448,7 @@ const ManageSign = () => {
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
className="fa-light fa-pen-nib"
|
||||
onClick={() => {
|
||||
if (key === 0) {
|
||||
setInitialPen("blue");
|
||||
@@ -412,7 +459,6 @@ const ManageSign = () => {
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa-light fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
@@ -421,13 +467,22 @@ const ManageSign = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex flex-row gap-1">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
onClick={() => handleClearInitials()}
|
||||
onClick={() => handleUploadInitials()}
|
||||
>
|
||||
{t("clear")}
|
||||
{t("upload")}
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
type="button"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
onClick={() => handleClearInitials()}
|
||||
>
|
||||
{t("clear")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -499,22 +499,13 @@ function Opensigndrive() {
|
||||
//function to use check tour status of open sign drive
|
||||
async function checkTourStatus() {
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails");
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
if (cloudRes) {
|
||||
const extUser = JSON.parse(JSON.stringify(cloudRes));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([extUser]));
|
||||
const tourStatus = extUser?.TourStatus || [];
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter((obj) => obj["driveTour"]);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const driveTour = filteredtourStatus[0]["driveTour"];
|
||||
// console.log("loginTour", loginTour);
|
||||
if (driveTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
const driveTour = tourStatus.find((obj) => obj.driveTour)?.driveTour;
|
||||
setIsTour(!driveTour);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@ import checkmark from "../assets/images/checkmark.png";
|
||||
import plansArr from "../json/plansArr";
|
||||
import Title from "../components/Title";
|
||||
import Parse from "parse";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import { openInNewTab, saveLanguageInLocal } from "../constant/Utils";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const listItemStyle = {
|
||||
@@ -53,12 +53,6 @@ const PlanSubscriptions = () => {
|
||||
? "&mobile=" + encodeURIComponent(userDetails.phone)
|
||||
: "";
|
||||
|
||||
const details =
|
||||
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
||||
name +
|
||||
email +
|
||||
company +
|
||||
phone;
|
||||
useEffect(() => {
|
||||
setIsLoader(false);
|
||||
detectLanguage();
|
||||
@@ -72,6 +66,32 @@ const PlanSubscriptions = () => {
|
||||
|
||||
const handleFreePlan = async (item) => {
|
||||
if (item.url) {
|
||||
const code = yearlyVisible ? item.code.yearly : item.code.monthly;
|
||||
const allowedUsers =
|
||||
localStorage.getItem("allowedUsers") &&
|
||||
localStorage.getItem("allowedUsers") > 0
|
||||
? localStorage.getItem("allowedUsers") - 1
|
||||
: "";
|
||||
const teamperiod = {
|
||||
"team-weekly": "monthly",
|
||||
"team-yearly": "yearly",
|
||||
"teams-monthly": "monthly",
|
||||
"teams-yearly": "yearly"
|
||||
};
|
||||
const period = teamperiod[code] || "";
|
||||
|
||||
const quantity =
|
||||
allowedUsers && period
|
||||
? `addon_code%5B0%5D=extra-teams-users-${period}&addon_quantity%5B0%5D=${allowedUsers}&`
|
||||
: "";
|
||||
|
||||
const details =
|
||||
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
||||
quantity +
|
||||
name +
|
||||
email +
|
||||
company +
|
||||
phone;
|
||||
const url = yearlyVisible ? item.yearlyUrl + details : item.url + details;
|
||||
if (user) {
|
||||
localStorage.setItem("userDetails", JSON.stringify(user));
|
||||
@@ -99,6 +119,30 @@ const PlanSubscriptions = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
saveLanguageInLocal(i18n);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
navigate("/");
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Title title={"Subscriptions"} />
|
||||
@@ -109,7 +153,13 @@ const PlanSubscriptions = () => {
|
||||
) : (
|
||||
<div className="overflow-y-auto max-h-full">
|
||||
<div className="block my-2">
|
||||
<div className="flex flex-col justify-center items-center w-full">
|
||||
<div className="flex flex-col justify-center items-center w-full relative">
|
||||
<button
|
||||
className="md:hidden op-btn op-btn-primary op-btn-sm h-[40px] w-[250px] shadow-lg"
|
||||
onClick={() => handleLogout()}
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
<div
|
||||
role="tablist"
|
||||
className="op-tabs op-tabs-boxed bg-base-100 my-2 shadow-lg transition-all"
|
||||
@@ -130,6 +180,12 @@ const PlanSubscriptions = () => {
|
||||
</a>
|
||||
</div>
|
||||
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
|
||||
<button
|
||||
className="hidden md:block -top-12 right-1 absolute w-[150px] h-[40px] op-btn op-btn-primary op-btn-sm shadow-lg"
|
||||
onClick={() => handleLogout()}
|
||||
>
|
||||
{t("log-out")}
|
||||
</button>
|
||||
{plansArr.map((item) => (
|
||||
<li
|
||||
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
getTenantDetails,
|
||||
handleSignatureType,
|
||||
signatureTypes
|
||||
} from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
const Preferences = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isalert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTopLoader, setIsTopLoader] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
const [isNotifyOnSignatures, setIsNotifyOnSignatures] = useState();
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
useEffect(() => {
|
||||
fetchSignType();
|
||||
}, []);
|
||||
|
||||
const fetchSignType = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe.isValid);
|
||||
} else {
|
||||
setIsSubscribe(true);
|
||||
}
|
||||
setIsTopLoader(true);
|
||||
try {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const tenantSignTypes = signatureType?.filter((x) => x.enabled === true);
|
||||
const getUser = await Parse.Cloud.run("getUserDetails");
|
||||
if (getUser) {
|
||||
const _getUser = JSON.parse(JSON.stringify(getUser));
|
||||
const notifyOnSignatures =
|
||||
_getUser?.NotifyOnSignatures !== undefined
|
||||
? _getUser?.NotifyOnSignatures
|
||||
: "";
|
||||
setIsNotifyOnSignatures(notifyOnSignatures);
|
||||
if (tenantSignTypes?.length > 0) {
|
||||
const signatureType = _getUser?.SignatureType || signatureTypes;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
signatureType
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
} else {
|
||||
const SignatureType = _getUser?.SignatureType || signatureTypes;
|
||||
setSignatureType(SignatureType);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err while getting user details", err);
|
||||
setErrMsg("Something went wrong");
|
||||
} finally {
|
||||
setIsTopLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleCheckboxChange` is trigger when user enable/disable checkbox of respective type
|
||||
const handleCheckboxChange = (index) => {
|
||||
// Create a copy of the signatureType array
|
||||
const updatedSignatureType = [...signatureType];
|
||||
// Toggle the enabled value for the clicked item
|
||||
updatedSignatureType[index].enabled = !updatedSignatureType[index].enabled;
|
||||
// Update the state with the modified array
|
||||
setSignatureType(updatedSignatureType);
|
||||
};
|
||||
|
||||
// `handleSave` is used save updated value signature type
|
||||
const handleSave = async () => {
|
||||
setIsLoader(true);
|
||||
if (signatureType.length > 0 || isNotifyOnSignatures) {
|
||||
let params = {};
|
||||
if (signatureType.length > 0) {
|
||||
const enabledSignTypes = signatureType?.filter((x) => x.enabled);
|
||||
const isDefaultSignTypeOnly =
|
||||
enabledSignTypes?.length === 1 &&
|
||||
enabledSignTypes[0]?.name === "default";
|
||||
if (enabledSignTypes.length === 0) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("at-least-one-signature-type")
|
||||
});
|
||||
} else if (isDefaultSignTypeOnly) {
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
msg: t("expect-default-one-more-signature-type")
|
||||
});
|
||||
} else {
|
||||
params = { ...params, SignatureType: signatureType };
|
||||
}
|
||||
}
|
||||
if (isNotifyOnSignatures) {
|
||||
params = { ...params, NotifyOnSignatures: isNotifyOnSignatures };
|
||||
}
|
||||
try {
|
||||
const updateRes = await Parse.Cloud.run("updatepreferences", params);
|
||||
if (updateRes) {
|
||||
setIsAlert({ type: "success", msg: "Saved successfully." });
|
||||
let extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser && extUser?.objectId) {
|
||||
extUser.NotifyOnSignatures = isNotifyOnSignatures;
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("Extand_Class", JSON.stringify([_extUser]));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Error updating signature type", err);
|
||||
setIsAlert({ type: "danger", msg: err.message });
|
||||
}
|
||||
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleNotifySignChange` is trigger when user change radio of notify on signatures
|
||||
const handleNotifySignChange = (value) => {
|
||||
setIsNotifyOnSignatures(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={t("Preferences")} />
|
||||
{isalert.msg && <Alert type={isalert.type}>{isalert.msg}</Alert>}
|
||||
{isTopLoader ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{errMsg ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
|
||||
{isLoader && (
|
||||
<div className="flex justify-center items-center absolute w-full h-full rounded-box bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
||||
OpenSign™ {t("Preferences")}{" "}
|
||||
<span>
|
||||
<Tooltip
|
||||
message={`OpenSign™ ${t("Preferences")}`}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</span>
|
||||
</h1>
|
||||
<div className="ml-4 mt-1 mb-2 flex flex-col">
|
||||
<div className="mb-[0.75rem]">
|
||||
<label
|
||||
htmlFor="signaturetype"
|
||||
className="text-[14px] mb-[0.7rem] font-medium"
|
||||
>
|
||||
{t("allowed-signature-types")}
|
||||
<a data-tooltip-id="signtypes-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="signtypes-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">
|
||||
{t("allowed-signature-types")}
|
||||
</p>
|
||||
<p>{t("allowed-signature-types-help.p1")}</p>
|
||||
<p className="p-[5px] ml-2">
|
||||
<ol className="list-disc">
|
||||
<li>
|
||||
<span className="font-bold">Draw: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l1")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Typed: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l2")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Upload: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l3")}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="font-bold">Default: </span>
|
||||
<span>
|
||||
{t("allowed-signature-types-help.l4")}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
</label>
|
||||
<div className=" ml-[7px] flex flex-col md:flex-row gap-[10px] mb-[0.7rem]">
|
||||
{signatureType.map((type, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex flex-row gap-[5px] items-center"
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-checkbox op-checkbox-xs"
|
||||
type="checkbox"
|
||||
name="signaturetype"
|
||||
onChange={() => handleCheckboxChange(i)}
|
||||
checked={type.enabled}
|
||||
/>
|
||||
<div
|
||||
className="text-[13px] font-medium hover:underline underline-offset-2 cursor-default capitalize"
|
||||
title={`Enabling this allow signers to ${type.name} signature`}
|
||||
>
|
||||
{type.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe ? "" : "text-gray-300"
|
||||
} text-[14px] mb-[0.7rem] font-medium`}
|
||||
>
|
||||
{t("notify-on-signatures")}
|
||||
<a data-tooltip-id="nos-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id="nos-tooltip" className="z-[999]">
|
||||
<div className="max-w-[200px] md:max-w-[450px] text-[11px]">
|
||||
<p className="font-bold">{t("notify-on-signatures")}</p>
|
||||
<p>{t("notify-on-signatures-help.p1")}</p>
|
||||
<p>{t("notify-on-signatures-help.note")}</p>
|
||||
</div>
|
||||
</ReactTooltip>
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</label>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1`}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(true)}
|
||||
checked={isNotifyOnSignatures === true}
|
||||
/>
|
||||
<div className="text-[13px] font-medium cursor-default capitalize">
|
||||
{t("yes")}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row md:gap-4">
|
||||
<div
|
||||
className={`${
|
||||
isSubscribe ? "" : "pointer-events-none opacity-50"
|
||||
} flex items-center gap-2 ml-2 mb-1`}
|
||||
>
|
||||
<input
|
||||
className="mr-[2px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
onChange={() => handleNotifySignChange(false)}
|
||||
checked={isNotifyOnSignatures === false}
|
||||
/>
|
||||
<div className="text-[13px] font-medium cursor-default capitalize">
|
||||
{t("no")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-[0.75rem]">
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={handleSave}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
export default Preferences;
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
@@ -50,6 +50,9 @@ const SSOVerify = () => {
|
||||
console.log("err", err.message);
|
||||
}
|
||||
};
|
||||
const handlePaidRoute = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
const checkExtUser = async (ssosign) => {
|
||||
const params = { email: ssosign?.email };
|
||||
try {
|
||||
@@ -85,54 +88,58 @@ const SSOVerify = () => {
|
||||
// `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);
|
||||
if (!emailRegex.test(userDetails.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} 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(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(t("something-went-wrong-mssg"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("error in usersignup", err);
|
||||
} else {
|
||||
localStorage.removeItem("accesstoken");
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
alert(t("server-error"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem("accesstoken");
|
||||
alert(t("server-error"));
|
||||
alert(t("filed-required-correctly"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
alert(t("filed-required-correctly"));
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
|
||||
@@ -196,10 +203,10 @@ const SSOVerify = () => {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
handlePaidRoute(plan);
|
||||
}
|
||||
} else {
|
||||
navigate(redirectUrl);
|
||||
@@ -225,10 +232,12 @@ const SSOVerify = () => {
|
||||
}
|
||||
};
|
||||
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
|
||||
const handleCloseModal = () => {
|
||||
const handleCloseModal = async () => {
|
||||
setIsModal(false);
|
||||
if (Parse?.User?.current()) {
|
||||
Parse.User.logOut();
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { emailRegex, isEnableSubscription } from "../constant/const";
|
||||
import {
|
||||
getAppLogo,
|
||||
openInNewTab,
|
||||
@@ -45,8 +45,10 @@ const Signup = () => {
|
||||
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
||||
|
||||
const clearStorage = async () => {
|
||||
if (Parse.User.current()) {
|
||||
try {
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("Err while logging out", err);
|
||||
}
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
@@ -81,92 +83,96 @@ const Signup = () => {
|
||||
};
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
var user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
let res = user.save();
|
||||
res
|
||||
.then(async (r) => {
|
||||
if (r) {
|
||||
let roleData = appInfo.settings;
|
||||
if (roleData && roleData.length > 0) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: appInfo.defaultRole
|
||||
}
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run(
|
||||
"usersignup",
|
||||
params
|
||||
);
|
||||
if (usersignup) {
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan =
|
||||
param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(r.id);
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
var user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
let res = user.save();
|
||||
res
|
||||
.then(async (r) => {
|
||||
if (r) {
|
||||
let roleData = appInfo.settings;
|
||||
if (roleData && roleData.length > 0) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: appInfo.defaultRole
|
||||
}
|
||||
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run(
|
||||
"usersignup",
|
||||
params
|
||||
);
|
||||
if (usersignup) {
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan =
|
||||
param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(r.id);
|
||||
}
|
||||
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("user-already-exist-name"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res1) {
|
||||
if (res1.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(async (err) => {
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert(t("user-already-exist-name"));
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res1) {
|
||||
if (res1.data === undefined) {
|
||||
alert(t("verification-code-sent"));
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,7 +4,6 @@ import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import Confetti from "react-confetti";
|
||||
import axios from "axios";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
@@ -34,25 +33,32 @@ import {
|
||||
changeImageWH,
|
||||
handleSendOTP,
|
||||
getContainerScale,
|
||||
getDefaultSignature
|
||||
getDefaultSignature,
|
||||
onClickZoomIn,
|
||||
onClickZoomOut,
|
||||
rotatePdfPage,
|
||||
signatureTypes
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
import Signedby from "../components/pdf/Signedby";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import { useSelector } from "react-redux";
|
||||
import TextFontSetting from "../components/pdf/TextFontSetting";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import DownloadPdfZip from "../primitives/DownloadPdfZip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
const { t } = useTranslation();
|
||||
@@ -123,52 +129,40 @@ function SignYourSelf() {
|
||||
const [zoomPercent, setZoomPercent] = useState(0);
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
|
||||
const [isDownloadModal, setIsDownloadModal] = useState(false);
|
||||
const [isResize, setIsResize] = useState(false);
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
accept: "BOX",
|
||||
drop: (item, monitor) => addPositionOfSignature(item, monitor),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver()
|
||||
})
|
||||
collect: (monitor) => ({ isOver: !!monitor.isOver() })
|
||||
});
|
||||
const pdfRef = useRef();
|
||||
const [{ isDragSign }, dragSignature] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
id: 1,
|
||||
text: "signature"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragSign: !!monitor.isDragging()
|
||||
})
|
||||
item: { id: 1, text: "signature" },
|
||||
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
|
||||
});
|
||||
|
||||
const [{ isDragStamp }, dragStamp] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
id: 2,
|
||||
text: "stamp"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragStamp: !!monitor.isDragging()
|
||||
})
|
||||
item: { id: 2, text: "stamp" },
|
||||
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
|
||||
});
|
||||
|
||||
const index = xyPostion.findIndex((object) => {
|
||||
const index = xyPostion?.findIndex((object) => {
|
||||
return object.pageNumber === pageNumber;
|
||||
});
|
||||
// rowlevel={JSON.parse(localStorage.getItem("rowlevel"))}
|
||||
const rowLevel =
|
||||
localStorage.getItem("rowlevel") &&
|
||||
JSON.parse(localStorage.getItem("rowlevel"));
|
||||
|
||||
const signObjId =
|
||||
rowLevel && rowLevel?.id
|
||||
? rowLevel.id
|
||||
: rowLevel?.objectId && rowLevel.objectId;
|
||||
|
||||
const documentId = docId ? docId : signObjId && signObjId;
|
||||
const senderUser =
|
||||
localStorage.getItem(
|
||||
@@ -202,12 +196,18 @@ function SignYourSelf() {
|
||||
|
||||
// Use setTimeout to wait for the transition to complete
|
||||
const timer = setTimeout(updateSize, 100); // match the transition duration
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current, isHeader]);
|
||||
|
||||
//function for get document details for perticular signer with signer'object id
|
||||
const getDocumentDetails = async (showComplete) => {
|
||||
try {
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
} catch (err) {
|
||||
console.log("err in fetch sub", err);
|
||||
}
|
||||
try {
|
||||
let isCompleted;
|
||||
//getting document details
|
||||
@@ -216,6 +216,11 @@ function SignYourSelf() {
|
||||
if (documentData && documentData.length > 0) {
|
||||
setPdfDetails(documentData);
|
||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||
const placeholders =
|
||||
documentData[0]?.Placeholders?.length > 0
|
||||
? documentData[0]?.Placeholders
|
||||
: [];
|
||||
setXyPostion(placeholders);
|
||||
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
|
||||
@@ -228,21 +233,17 @@ function SignYourSelf() {
|
||||
} else {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
}
|
||||
isCompleted =
|
||||
documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||
isCompleted = documentData?.[0]?.IsCompleted;
|
||||
if (isCompleted) {
|
||||
setIsCelebration(true);
|
||||
setTimeout(() => {
|
||||
setIsCelebration(false);
|
||||
}, 5000);
|
||||
setTimeout(() => setIsCelebration(false), 5000);
|
||||
setIsCompleted(true);
|
||||
setPdfUrl(documentData[0].SignedUrl);
|
||||
const alreadySign = {
|
||||
status: true,
|
||||
mssg: t("document-signed-alert")
|
||||
};
|
||||
if (showComplete) {
|
||||
setShowAlreadySignDoc(alreadySign);
|
||||
setShowAlreadySignDoc({
|
||||
status: true,
|
||||
mssg: t("document-signed-alert")
|
||||
});
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
setIsSignPad(false);
|
||||
@@ -255,17 +256,11 @@ function SignYourSelf() {
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
} else {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
//function to get default signatur eof current user from `contracts_Signature` class
|
||||
const defaultSignRes = await getDefaultSignature(jsonSender.objectId);
|
||||
@@ -274,17 +269,12 @@ function SignYourSelf() {
|
||||
setMyInitial(defaultSignRes?.res?.defaultInitial);
|
||||
} else if (defaultSignRes?.status === "error") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading({
|
||||
isLoad: false
|
||||
});
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
const contractUsersRes = await contractUsers();
|
||||
if (contractUsersRes === "Error: Something went wrong!") {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
||||
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
|
||||
setContractName("_Users");
|
||||
@@ -364,51 +354,32 @@ function SignYourSelf() {
|
||||
const addWidgetOptions = (type) => {
|
||||
switch (type) {
|
||||
case "signature":
|
||||
return {
|
||||
name: "signature"
|
||||
};
|
||||
return { name: "signature" };
|
||||
case "stamp":
|
||||
return {
|
||||
name: "stamp"
|
||||
};
|
||||
return { name: "stamp" };
|
||||
case "checkbox":
|
||||
return {
|
||||
name: "checkbox"
|
||||
};
|
||||
return { name: "checkbox" };
|
||||
case textWidget:
|
||||
return {
|
||||
name: "text"
|
||||
};
|
||||
return { name: "text" };
|
||||
case "initials":
|
||||
return {
|
||||
name: "initials"
|
||||
};
|
||||
return { name: "initials" };
|
||||
case "name":
|
||||
return {
|
||||
name: "name",
|
||||
defaultValue: getWidgetValue(type),
|
||||
validation: {
|
||||
type: "text",
|
||||
pattern: ""
|
||||
}
|
||||
validation: { type: "text", pattern: "" }
|
||||
};
|
||||
case "company":
|
||||
return {
|
||||
name: "company",
|
||||
defaultValue: getWidgetValue(type),
|
||||
validation: {
|
||||
type: "text",
|
||||
pattern: ""
|
||||
}
|
||||
validation: { type: "text", pattern: "" }
|
||||
};
|
||||
case "job title":
|
||||
return {
|
||||
name: "job title",
|
||||
defaultValue: getWidgetValue(type),
|
||||
validation: {
|
||||
type: "text",
|
||||
pattern: ""
|
||||
}
|
||||
validation: { type: "text", pattern: "" }
|
||||
};
|
||||
case "date":
|
||||
return {
|
||||
@@ -417,17 +388,12 @@ function SignYourSelf() {
|
||||
validation: { format: "MM/dd/yyyy", type: "date-format" }
|
||||
};
|
||||
case "image":
|
||||
return {
|
||||
name: "image"
|
||||
};
|
||||
return { name: "image" };
|
||||
case "email":
|
||||
return {
|
||||
name: "email",
|
||||
defaultValue: getWidgetValue(type),
|
||||
validation: {
|
||||
type: "email",
|
||||
pattern: ""
|
||||
}
|
||||
validation: { type: "email", pattern: "" }
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
@@ -439,7 +405,7 @@ function SignYourSelf() {
|
||||
const key = randomId();
|
||||
let dropData = [];
|
||||
let dropObj = {};
|
||||
let filterDropPos = xyPostion.filter(
|
||||
let filterDropPos = xyPostion?.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||
@@ -454,29 +420,25 @@ function SignYourSelf() {
|
||||
);
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
if (item === "onclick") {
|
||||
// `getBoundingClientRect()` is used to get accurate measurement height of the div
|
||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).width
|
||||
: "";
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).height
|
||||
: "";
|
||||
: defaultWidthHeight(dragTypeValue).width;
|
||||
const getHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
|
||||
dropObj = {
|
||||
xPosition: containerWH.width / 2,
|
||||
yPosition: containerWH.height / 2,
|
||||
xPosition: getWidth / 2 + containerWH.width / 2,
|
||||
yPosition: getHeight + divHeight / 2,
|
||||
isStamp:
|
||||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||||
key: key,
|
||||
type: dragTypeValue,
|
||||
scale: containerScale,
|
||||
Width: getWidth / (containerScale * scale),
|
||||
Height: getHeight / (containerScale * scale),
|
||||
Width: getWidth,
|
||||
Height: getHeight,
|
||||
options: addWidgetOptions(dragTypeValue)
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
} else {
|
||||
//This method returns the offset of the current pointer (mouse) position relative to the client viewport.
|
||||
@@ -494,9 +456,7 @@ function SignYourSelf() {
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getWidth
|
||||
: defaultWidthHeight(dragTypeValue).width;
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getHeight
|
||||
: defaultWidthHeight(dragTypeValue).height;
|
||||
const getHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
dropObj = {
|
||||
xPosition: getXPosition / (containerScale * scale),
|
||||
yPosition: getYPosition / (containerScale * scale),
|
||||
@@ -509,25 +469,18 @@ function SignYourSelf() {
|
||||
options: addWidgetOptions(dragTypeValue),
|
||||
scale: containerScale
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
}
|
||||
if (filterDropPos.length > 0) {
|
||||
const index = xyPostion.findIndex((object) => {
|
||||
if (filterDropPos?.length > 0) {
|
||||
const index = xyPostion?.findIndex((object) => {
|
||||
return object.pageNumber === pageNumber;
|
||||
});
|
||||
const updateData = filterDropPos[0].pos;
|
||||
const updateData = filterDropPos?.[0].pos;
|
||||
const newSignPos = updateData.concat(dropData);
|
||||
let xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
pos: newSignPos
|
||||
};
|
||||
xyPostion.splice(index, 1, xyPos);
|
||||
let xyPos = { pageNumber: pageNumber, pos: newSignPos };
|
||||
xyPostion?.splice(index, 1, xyPos);
|
||||
} else {
|
||||
const xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
pos: dropData
|
||||
};
|
||||
const xyPos = { pageNumber: pageNumber, pos: dropData };
|
||||
setXyPostion((prev) => [...prev, xyPos]);
|
||||
}
|
||||
|
||||
@@ -539,7 +492,6 @@ function SignYourSelf() {
|
||||
) {
|
||||
setIsSignPad(true);
|
||||
}
|
||||
|
||||
if (dragTypeValue === "stamp" || dragTypeValue === "image") {
|
||||
setIsStamp(true);
|
||||
} else if (dragTypeValue === "initials") {
|
||||
@@ -602,6 +554,29 @@ function SignYourSelf() {
|
||||
setIsVerifyModal(true);
|
||||
await handleSendOTP(Parse.User.current().getEmail());
|
||||
};
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (xyPostion?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [xyPostion]);
|
||||
|
||||
// `autosavedetails` is used to save doc details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
|
||||
const autosavedetails = async () => {
|
||||
try {
|
||||
const docCls = new Parse.Object("contracts_Document");
|
||||
docCls.id = documentId;
|
||||
docCls.set("Placeholders", xyPostion);
|
||||
docCls.set("IsSignyourself", true);
|
||||
await docCls.save();
|
||||
} catch (e) {
|
||||
console.log("error", e);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||
async function embedWidgetsData() {
|
||||
//check current user email is verified or not
|
||||
@@ -640,12 +615,8 @@ function SignYourSelf() {
|
||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||
let checkDefaultSigned =
|
||||
requiredWidgets[i]?.options?.defaultValue;
|
||||
if (!checkSignUrl) {
|
||||
if (!checkDefaultSigned) {
|
||||
if (!showAlert) {
|
||||
showAlert = true;
|
||||
}
|
||||
}
|
||||
if (!checkSignUrl && !checkDefaultSigned && !showAlert) {
|
||||
showAlert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -672,10 +643,9 @@ function SignYourSelf() {
|
||||
return;
|
||||
} else {
|
||||
setIsUiLoading(true);
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
try {
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
const pdfDoc = await PDFDocument.load(pdfArrayBuffer);
|
||||
const isSignYourSelfFlow = true;
|
||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||
@@ -688,14 +658,19 @@ function SignYourSelf() {
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
isSignYourSelfFlow,
|
||||
scale,
|
||||
pdfOriginalWH,
|
||||
containerWH
|
||||
scale
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
if (pdfBytes) {
|
||||
if (!pdfBytes?.error) {
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
} else {
|
||||
setIsUiLoading(false);
|
||||
setIsAlert({
|
||||
header: t("error"),
|
||||
isShow: true,
|
||||
alertMessage: t("pdf-uncompatible")
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setIsUiLoading(false);
|
||||
@@ -707,10 +682,7 @@ function SignYourSelf() {
|
||||
});
|
||||
} else {
|
||||
console.log("err in signing", err.message);
|
||||
if (
|
||||
err.message ===
|
||||
"PKCS#12 MAC could not be verified. Invalid password?"
|
||||
) {
|
||||
if (err?.message?.includes("password")) {
|
||||
setIsAlert({
|
||||
header: t("error"),
|
||||
isShow: true,
|
||||
@@ -740,7 +712,7 @@ function SignYourSelf() {
|
||||
//function for get digital signature
|
||||
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(t("user-not-exist"));
|
||||
@@ -748,7 +720,7 @@ function SignYourSelf() {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
getIsSubscribe
|
||||
isSubscribe?.isValid
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
@@ -777,11 +749,10 @@ function SignYourSelf() {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
//change image width and height to 100/40 in png base64
|
||||
const getNewse64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64
|
||||
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||
|
||||
//change image width and height to 300/120 in png base64
|
||||
const imagebase64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64 (without type)
|
||||
const suffixbase64 = imagebase64 && imagebase64.split(",").pop();
|
||||
const params = {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
@@ -794,6 +765,7 @@ function SignYourSelf() {
|
||||
const signedpdf = JSON.parse(JSON.stringify(resSignPdf));
|
||||
setPdfUrl(signedpdf);
|
||||
getDocumentDetails(false);
|
||||
setPdfRotatese64("");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -806,7 +778,7 @@ function SignYourSelf() {
|
||||
const handleStop = (event, dragElement) => {
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
if (isDragging && dragElement) {
|
||||
if (!isResize && isDragging && dragElement) {
|
||||
event.preventDefault();
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
@@ -814,10 +786,10 @@ function SignYourSelf() {
|
||||
containerWH
|
||||
);
|
||||
if (dragKey >= 0) {
|
||||
const filterDropPos = xyPostion.filter(
|
||||
const filterDropPos = xyPostion?.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
if (filterDropPos.length > 0) {
|
||||
if (filterDropPos?.length > 0) {
|
||||
const getXYdata = xyPostion[index].pos;
|
||||
const getPosData = getXYdata;
|
||||
const addSign = getPosData.map((url) => {
|
||||
@@ -888,15 +860,11 @@ function SignYourSelf() {
|
||||
setIsSignPad(false);
|
||||
setIsImageSelect(false);
|
||||
setImage();
|
||||
|
||||
if (isDefaultSign) {
|
||||
const img = new Image();
|
||||
img.src = defaultSignImg;
|
||||
if (img.complete) {
|
||||
imgWH = {
|
||||
width: img.width,
|
||||
height: img.height
|
||||
};
|
||||
imgWH = { width: img.width, height: img.height };
|
||||
}
|
||||
}
|
||||
const getUpdatePosition = onSaveSign(
|
||||
@@ -921,19 +889,11 @@ function SignYourSelf() {
|
||||
const touch = e.touches[0]; // Get the first touch point
|
||||
mouseX = touch.clientX - divRect.left;
|
||||
mouseY = touch.clientY - divRect.top;
|
||||
setSignBtnPosition([
|
||||
{
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
}
|
||||
]);
|
||||
setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]);
|
||||
} else {
|
||||
mouseX = e.clientX - divRect.left;
|
||||
mouseY = e.clientY - divRect.top;
|
||||
const xyPosition = {
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
};
|
||||
const xyPosition = { xPos: mouseX, yPos: mouseY };
|
||||
setXYSignature(xyPosition);
|
||||
}
|
||||
};
|
||||
@@ -951,9 +911,7 @@ function SignYourSelf() {
|
||||
const handleDeleteSign = (key) => {
|
||||
setCurrWidgetsDetails({});
|
||||
const updateResizeData = [];
|
||||
|
||||
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
|
||||
|
||||
//delete and update block position
|
||||
if (filterData.length > 0) {
|
||||
updateResizeData.push(filterData);
|
||||
@@ -1065,7 +1023,6 @@ function SignYourSelf() {
|
||||
const getPageNumer = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (getPageNumer.length > 0) {
|
||||
const getXYdata = getPageNumer[0].pos;
|
||||
const getPosData = getXYdata;
|
||||
@@ -1093,7 +1050,11 @@ function SignYourSelf() {
|
||||
name: dropdownName,
|
||||
values: dropdownOptions,
|
||||
isReadOnly: isReadOnly,
|
||||
isHideLabel: isHideLabel || false
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1111,6 +1072,8 @@ function SignYourSelf() {
|
||||
handleCloseModal();
|
||||
}
|
||||
}
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
};
|
||||
const handleCloseModal = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
@@ -1123,7 +1086,6 @@ function SignYourSelf() {
|
||||
const getPageNumer = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (getPageNumer.length > 0) {
|
||||
const getXYdata = getPageNumer[0].pos;
|
||||
const getPosData = getXYdata;
|
||||
@@ -1133,8 +1095,7 @@ function SignYourSelf() {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize: fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||||
}
|
||||
@@ -1154,6 +1115,56 @@ function SignYourSelf() {
|
||||
handleTextSettingModal(false);
|
||||
}
|
||||
};
|
||||
const clickOnZoomIn = () => {
|
||||
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
|
||||
};
|
||||
const clickOnZoomOut = () => {
|
||||
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
|
||||
};
|
||||
//`handleRotationFun` function is used to roatate pdf particular page
|
||||
const handleRotationFun = async (rotateDegree) => {
|
||||
const isPlaceholder = handleRotateWarning();
|
||||
if (isPlaceholder) {
|
||||
setIsRotate({ status: true, degree: rotateDegree });
|
||||
} else {
|
||||
const urlDetails = await rotatePdfPage(
|
||||
pdfUrl || pdfDetails[0].URL,
|
||||
rotateDegree,
|
||||
pageNumber - 1,
|
||||
pdfRotateBase64
|
||||
);
|
||||
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
|
||||
setPdfRotatese64(urlDetails.base64);
|
||||
}
|
||||
};
|
||||
|
||||
//function to show warning when user rotate page and there are some already widgets on that page
|
||||
const handleRotateWarning = () => {
|
||||
const placeholderData = xyPostion?.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
if (placeholderData && placeholderData.length > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
//function to use remove widgets from current page when user want to rotate page
|
||||
const handleRemoveWidgets = async () => {
|
||||
const updatePlaceholder = xyPostion.filter(
|
||||
(data) => data.pageNumber !== pageNumber
|
||||
);
|
||||
const urlDetails = await rotatePdfPage(
|
||||
pdfUrl || pdfDetails[0].URL,
|
||||
isRotate.degree,
|
||||
pageNumber - 1,
|
||||
pdfRotateBase64
|
||||
);
|
||||
setPdfArrayBuffer && setPdfArrayBuffer(urlDetails.arrayBuffer);
|
||||
setPdfRotatese64(urlDetails.base64);
|
||||
setXyPostion(updatePlaceholder);
|
||||
setIsRotate({ status: false, degree: 0 });
|
||||
};
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Title title={"Self Sign"} />
|
||||
@@ -1173,10 +1184,14 @@ function SignYourSelf() {
|
||||
)}
|
||||
{isCelebration && (
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
<Confetti
|
||||
width={window.innerWidth}
|
||||
height={window.innerHeight}
|
||||
recycle={false} // Prevents confetti from repeating
|
||||
gravity={0.1} // Adjust the gravity to control the speed
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
@@ -1213,14 +1228,13 @@ function SignYourSelf() {
|
||||
setSignBtnPosition={setSignBtnPosition}
|
||||
pageNumber={pageNumber}
|
||||
containerWH={containerWH}
|
||||
pdfRotateBase64={pdfRotateBase64}
|
||||
/>
|
||||
<div className=" w-full md:w-[57%] flex mr-4">
|
||||
<PdfZoom
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
setZoomPercent={setZoomPercent}
|
||||
zoomPercent={zoomPercent}
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
handleRotationFun={handleRotationFun}
|
||||
/>
|
||||
<div className="w-full md:w-[95%]">
|
||||
<ModalUi
|
||||
@@ -1239,9 +1253,7 @@ function SignYourSelf() {
|
||||
<ModalUi
|
||||
isOpen={showAlreadySignDoc.status}
|
||||
title={t("document-signed")}
|
||||
handleClose={() => {
|
||||
setShowAlreadySignDoc({ status: false });
|
||||
}}
|
||||
handleClose={() => setShowAlreadySignDoc({ status: false })}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{showAlreadySignDoc.mssg}</p>
|
||||
@@ -1265,6 +1277,10 @@ function SignYourSelf() {
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
isSignYourself={true}
|
||||
handleClose={handleCloseModal}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
/>
|
||||
<PlaceholderCopy
|
||||
isPageCopy={isPageCopy}
|
||||
@@ -1274,9 +1290,11 @@ function SignYourSelf() {
|
||||
allPages={allPages}
|
||||
pageNumber={pageNumber}
|
||||
signKey={signKey}
|
||||
widgetType={widgetType}
|
||||
/>
|
||||
{/* this is modal of signature pad */}
|
||||
<SignPad
|
||||
signatureTypes={signatureTypes}
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
@@ -1309,6 +1327,7 @@ function SignYourSelf() {
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
activeMailAdapter={activeMailAdapter}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
@@ -1324,12 +1343,13 @@ function SignYourSelf() {
|
||||
isSignYourself={true}
|
||||
setIsEmail={setIsEmail}
|
||||
isCompleted={isCompleted}
|
||||
handleRotationFun={handleRotationFun}
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
widgetsDetails={xyPostion}
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
/>
|
||||
<div
|
||||
ref={divRef}
|
||||
data-tut="reactourSecond"
|
||||
className="h-full md:h-[95%]"
|
||||
>
|
||||
<div ref={divRef} data-tut="reactourSecond" className="h-full">
|
||||
{containerWH?.width && containerWH?.height && (
|
||||
<RenderPdf
|
||||
pageNumber={pageNumber}
|
||||
@@ -1368,6 +1388,13 @@ function SignYourSelf() {
|
||||
handleTextSettingModal={handleTextSettingModal}
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
pdfRotateBase64={pdfRotateBase64}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
setIsResize={setIsResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1407,9 +1434,7 @@ function SignYourSelf() {
|
||||
<ModalUi
|
||||
isOpen={validateAlert}
|
||||
title={t("validation-alert")}
|
||||
handleClose={() => {
|
||||
setValidateAlert(false);
|
||||
}}
|
||||
handleClose={() => setValidateAlert(false)}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{t("validate-alert-mssg")}</p>
|
||||
@@ -1424,6 +1449,17 @@ function SignYourSelf() {
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<RotateAlert
|
||||
isRotate={isRotate.status}
|
||||
setIsRotate={setIsRotate}
|
||||
handleRemoveWidgets={handleRemoveWidgets}
|
||||
/>
|
||||
<DownloadPdfZip
|
||||
setIsDownloadModal={setIsDownloadModal}
|
||||
isDownloadModal={isDownloadModal}
|
||||
pdfDetails={pdfDetails}
|
||||
isDocId={true}
|
||||
/>
|
||||
<TextFontSetting
|
||||
isTextSetting={isTextSetting}
|
||||
setIsTextSetting={setIsTextSetting}
|
||||
|
||||
@@ -8,9 +8,10 @@ import ModalUi from "../primitives/ModalUi";
|
||||
import pad from "../assets/images/pad.svg";
|
||||
import AddTeam from "../components/AddTeam";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribedTeam } from "../constant/Utils";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import Title from "../components/Title";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const heading = ["Sr.No", "Name", "Parent Team", "Active"];
|
||||
@@ -38,7 +39,7 @@ const TeamList = () => {
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: false });
|
||||
const [isEditModal, setIsEditModal] = useState({});
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
@@ -103,8 +104,8 @@ const TeamList = () => {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser) {
|
||||
@@ -134,7 +135,7 @@ const TeamList = () => {
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
// Get current list
|
||||
// to slice out 10 objects from array for current page
|
||||
const indexOfLastDoc = currentPage * recordperPage;
|
||||
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
|
||||
const currentList = teamList?.slice(indexOfFirstDoc, indexOfLastDoc);
|
||||
@@ -145,8 +146,19 @@ const TeamList = () => {
|
||||
};
|
||||
|
||||
// Change page
|
||||
const paginateFront = () => setCurrentPage(currentPage + 1);
|
||||
const paginateBack = () => setCurrentPage(currentPage - 1);
|
||||
const paginateFront = () => {
|
||||
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
|
||||
if (currentPage < lastValue) {
|
||||
setCurrentPage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const paginateBack = () => {
|
||||
if (startIndex > 0) {
|
||||
setCurrentPage(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleActionBtn = (act, item) => {
|
||||
if (act.action === "edit") {
|
||||
setIsEditModal({ [item.objectId]: true });
|
||||
@@ -225,15 +237,11 @@ const TeamList = () => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isSubscribe && isEnableSubscription && !isLoader && (
|
||||
{validplan[isSubscribe.plan] && isEnableSubscription && !isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
{t("report-name.Teams")}
|
||||
@@ -470,9 +478,9 @@ const TeamList = () => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isSubscribe && isEnableSubscription && !isLoader && (
|
||||
{isEnableSubscription && !validplan[isSubscribe.plan] && !isLoader && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard plan={"TEAMS"} price={"20"} />
|
||||
<SubscribeCard plan="TEAMS" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,8 +9,6 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
import WidgetComponent from "../components/pdf/WidgetComponent";
|
||||
import Tour from "reactour";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import SignerListPlace from "../components/pdf/SignerListPlace";
|
||||
import Header from "../components/pdf/PdfHeader";
|
||||
import WidgetNameModal from "../components/pdf/WidgetNameModal";
|
||||
@@ -25,22 +23,36 @@ import {
|
||||
textInputWidget,
|
||||
radioButtonWidget,
|
||||
fetchSubscription,
|
||||
getContainerScale
|
||||
getContainerScale,
|
||||
convertBase64ToFile,
|
||||
rotatePdfPage,
|
||||
onClickZoomOut,
|
||||
onClickZoomIn,
|
||||
handleRemoveWidgets,
|
||||
handleRotateWarning,
|
||||
color,
|
||||
signatureTypes,
|
||||
getTenantDetails,
|
||||
handleSignatureType
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
import EditTemplate from "../components/pdf/EditTemplate";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import AddRoleModal from "../components/pdf/AddRoleModal";
|
||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import Parse from "parse";
|
||||
import { useSelector } from "react-redux";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import RotateAlert from "../components/RotateAlert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
import LinkUserModal from "../primitives/LinkUserModal";
|
||||
|
||||
const TemplatePlaceholder = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -55,12 +67,12 @@ const TemplatePlaceholder = () => {
|
||||
const [xySignature, setXYSignature] = useState({});
|
||||
const [dragKey, setDragKey] = useState();
|
||||
const [signersdata, setSignersData] = useState([]);
|
||||
const [signerObjId, setSignerObjId] = useState();
|
||||
const [signerPos, setSignerPos] = useState([]);
|
||||
const [isSelectListId, setIsSelectId] = useState();
|
||||
const [isSendAlert, setIsSendAlert] = useState(false);
|
||||
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isRotate, setIsRotate] = useState({ status: false, degree: 0 });
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: t("loading-mssg")
|
||||
@@ -73,7 +85,6 @@ const TemplatePlaceholder = () => {
|
||||
const [tourStatus, setTourStatus] = useState([]);
|
||||
const [signerUserId, setSignerUserId] = useState();
|
||||
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
|
||||
const [contractName, setContractName] = useState("");
|
||||
const [containerWH, setContainerWH] = useState();
|
||||
const signRef = useRef(null);
|
||||
const dragRef = useRef(null);
|
||||
@@ -91,50 +102,22 @@ const TemplatePlaceholder = () => {
|
||||
const [isNameModal, setIsNameModal] = useState(false);
|
||||
const [isTextSetting, setIsTextSetting] = useState(false);
|
||||
const [pdfLoad, setPdfLoad] = useState(false);
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const [pdfRotateBase64, setPdfRotatese64] = useState("");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const [, drop] = useDrop({
|
||||
accept: "BOX",
|
||||
drop: (item, monitor) => addPositionOfSignature(item, monitor),
|
||||
collect: (monitor) => ({
|
||||
isOver: !!monitor.isOver()
|
||||
})
|
||||
collect: (monitor) => ({ isOver: !!monitor.isOver() })
|
||||
});
|
||||
const [{ isDragSign }, dragSignature] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 1,
|
||||
text: "signature"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragSign: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 1, text: "signature" },
|
||||
collect: (monitor) => ({ isDragSign: !!monitor.isDragging() })
|
||||
});
|
||||
const [{ isDragStamp }, dragStamp] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 2,
|
||||
text: "stamp"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragStamp: !!monitor.isDragging()
|
||||
})
|
||||
item: { type: "BOX", id: 2, text: "stamp" },
|
||||
collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() })
|
||||
});
|
||||
const [uniqueId, setUniqueId] = useState("");
|
||||
const [isModalRole, setIsModalRole] = useState(false);
|
||||
@@ -155,6 +138,7 @@ const TemplatePlaceholder = () => {
|
||||
const [fontColor, setFontColor] = useState();
|
||||
const [zoomPercent, setZoomPercent] = useState(0);
|
||||
const [scale, setScale] = useState(1);
|
||||
const [signatureType, setSignatureType] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchTemplate();
|
||||
@@ -181,26 +165,59 @@ const TemplatePlaceholder = () => {
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current, isHeader]);
|
||||
|
||||
const handleNavigation = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
async function checkIsSubscribed() {
|
||||
const res = await fetchSubscription();
|
||||
const freeplan = res.plan;
|
||||
const plan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (freeplan === "freeplan") {
|
||||
if (plan === "freeplan") {
|
||||
return true;
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
setIsSubscribe(true);
|
||||
return true;
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handleNavigation(plan);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
handleNavigation(plan);
|
||||
}
|
||||
}
|
||||
|
||||
//function to fetch tenant Details
|
||||
const fetchTenantDetails = async () => {
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
if (user) {
|
||||
try {
|
||||
const tenantDetails = await getTenantDetails(user?.objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert(t("user-not-exist"));
|
||||
} else if (tenantDetails) {
|
||||
const signatureType = tenantDetails?.SignatureType || [];
|
||||
const filterSignTypes = signatureType?.filter(
|
||||
(x) => x.enabled === true
|
||||
);
|
||||
return filterSignTypes;
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
} else {
|
||||
alert(t("user-not-exist"));
|
||||
}
|
||||
};
|
||||
|
||||
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
||||
const fetchTemplate = async () => {
|
||||
try {
|
||||
const tenantSignTypes = await fetchTenantDetails();
|
||||
const params = { templateId: templateId };
|
||||
const templateDeatils = await axios.post(
|
||||
`${localStorage.getItem("baseUrl")}functions/getTemplate`,
|
||||
@@ -222,11 +239,20 @@ const TemplatePlaceholder = () => {
|
||||
if (isEnableSubscription) {
|
||||
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
|
||||
}
|
||||
setPdfDetails(documentData);
|
||||
const userSignatureType =
|
||||
documentData[0]?.ExtUserPtr?.SignatureType || signatureTypes;
|
||||
const docSignTypes =
|
||||
documentData?.[0]?.SignatureType || userSignatureType;
|
||||
const updatedSignatureType = await handleSignatureType(
|
||||
tenantSignTypes,
|
||||
docSignTypes
|
||||
);
|
||||
setSignatureType(updatedSignatureType);
|
||||
const updatedPdfDetails = [...documentData];
|
||||
updatedPdfDetails[0].SignatureType = updatedSignatureType;
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
setIsSigners(true);
|
||||
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
|
||||
setSignerObjId(documentData[0].Signers[0].objectId);
|
||||
setContractName(documentData[0].Signers[0].className);
|
||||
setIsSelectId(0);
|
||||
if (
|
||||
documentData[0].Placeholders &&
|
||||
@@ -247,11 +273,7 @@ const TemplatePlaceholder = () => {
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
Role: x.Role,
|
||||
Id: x.Id,
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
|
||||
}
|
||||
});
|
||||
setSignersData(updatedSigners);
|
||||
@@ -274,11 +296,7 @@ const TemplatePlaceholder = () => {
|
||||
documentData[0].Placeholders.length > 0
|
||||
) {
|
||||
let updatedSigners = documentData[0].Placeholders.map((x) => {
|
||||
return {
|
||||
Role: x.Role,
|
||||
Id: x.Id,
|
||||
blockColor: x.blockColor
|
||||
};
|
||||
return { Role: x.Role, Id: x.Id, blockColor: x.blockColor };
|
||||
});
|
||||
setSignerPos(documentData[0].Placeholders);
|
||||
setUniqueId(updatedSigners[0].Id);
|
||||
@@ -291,17 +309,33 @@ const TemplatePlaceholder = () => {
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
} else {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
const res = await contractUsers();
|
||||
if (res[0] && res.length) {
|
||||
setSignerUserId(res[0].objectId);
|
||||
setCurrentEmail(res[0].Email);
|
||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourstatus && tourstatus.length > 0) {
|
||||
setTourStatus(tourstatus);
|
||||
const checkTourRecipients = tourstatus.filter(
|
||||
(data) => data.templateTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||
}
|
||||
}
|
||||
setIsLoading({ isLoad: false });
|
||||
} else if (res === "Error: Something went wrong!") {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading({ isLoad: false });
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
setIsLoading({ isLoad: false });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
@@ -311,37 +345,6 @@ const TemplatePlaceholder = () => {
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
}
|
||||
}
|
||||
const res = await contractUsers();
|
||||
if (res[0] && res.length) {
|
||||
setSignerUserId(res[0].objectId);
|
||||
setCurrentEmail(res[0].Email);
|
||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||
if (tourstatus && tourstatus.length > 0) {
|
||||
setTourStatus(tourstatus);
|
||||
const checkTourRecipients = tourstatus.filter(
|
||||
(data) => data.templateTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||
}
|
||||
}
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
} else if (res === "Error: Something went wrong!") {
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setHandleError(t("something-went-wrong-mssg"));
|
||||
setIsLoading(loadObj);
|
||||
} else if (res.length === 0) {
|
||||
setHandleError(t("no-data-avaliable"));
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
}
|
||||
};
|
||||
|
||||
//function for setting position after drop signature button over pdf
|
||||
@@ -365,21 +368,21 @@ const TemplatePlaceholder = () => {
|
||||
containerWH
|
||||
);
|
||||
const key = randomId();
|
||||
let filterSignerPos = signerPos.filter((data) => data.Id === uniqueId);
|
||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
|
||||
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
let dropData = [];
|
||||
const widgetWidth =
|
||||
defaultWidthHeight(dragTypeValue).width * containerScale;
|
||||
const widgetHeight =
|
||||
defaultWidthHeight(dragTypeValue).height * containerScale;
|
||||
let dropData = [],
|
||||
currentPagePosition;
|
||||
let placeHolder;
|
||||
if (item === "onclick") {
|
||||
// `getBoundingClientRect()` is used to get accurate measurement height of the div
|
||||
const divHeight = divRef.current.getBoundingClientRect().height;
|
||||
const dropObj = {
|
||||
//onclick put placeholder center on pdf
|
||||
xPosition:
|
||||
(containerWH.width / 2 - widgetWidth / 2) /
|
||||
(containerScale * scale),
|
||||
yPosition:
|
||||
(containerWH.height / 2 - widgetHeight / 2) /
|
||||
(containerScale * scale),
|
||||
xPosition: widgetWidth / 4 + containerWH.width / 2,
|
||||
yPosition: widgetHeight + divHeight / 2,
|
||||
isStamp:
|
||||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||||
key: key,
|
||||
@@ -391,10 +394,7 @@ const TemplatePlaceholder = () => {
|
||||
Height: widgetHeight / (containerScale * scale)
|
||||
};
|
||||
dropData.push(dropObj);
|
||||
placeHolder = {
|
||||
pageNumber: pageNumber,
|
||||
pos: dropData
|
||||
};
|
||||
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
||||
} else {
|
||||
const offset = monitor.getClientOffset();
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
@@ -425,70 +425,39 @@ const TemplatePlaceholder = () => {
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
placeHolder = {
|
||||
pageNumber: pageNumber,
|
||||
pos: dropData
|
||||
};
|
||||
placeHolder = { pageNumber: pageNumber, pos: dropData };
|
||||
}
|
||||
const { blockColor, Role } = signer;
|
||||
//adding placholder in existing signer pos array (placaholder)
|
||||
if (filterSignerPos.length > 0) {
|
||||
const getPlaceHolder = filterSignerPos[0].placeHolder;
|
||||
let filterSignerPos = signerPos.find((data) => data.Id === uniqueId);
|
||||
const getPlaceHolder = filterSignerPos?.placeHolder;
|
||||
if (getPlaceHolder) {
|
||||
//checking exist placeholder on same page
|
||||
currentPagePosition = getPlaceHolder.find(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
}
|
||||
//checking current page has already some placeholders then update that placeholder and add upcoming placehoder position
|
||||
if (getPlaceHolder && currentPagePosition) {
|
||||
const updatePlace = getPlaceHolder.filter(
|
||||
(data) => data.pageNumber !== pageNumber
|
||||
);
|
||||
const getPageNumer = getPlaceHolder.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
const getPos = currentPagePosition?.pos;
|
||||
const newSignPos = getPos.concat(dropData);
|
||||
let xyPos = { pageNumber: pageNumber, pos: newSignPos };
|
||||
updatePlace.push(xyPos);
|
||||
const updatesignerPos = signerPos.map((x) =>
|
||||
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
|
||||
);
|
||||
|
||||
//add entry of position for same signer on multiple page
|
||||
if (getPageNumer.length > 0) {
|
||||
const getPos = getPageNumer[0].pos;
|
||||
const newSignPos = getPos.concat(dropData);
|
||||
let xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
pos: newSignPos
|
||||
};
|
||||
updatePlace.push(xyPos);
|
||||
const updatesignerPos = signerPos.map((x) =>
|
||||
x.Id === uniqueId ? { ...x, placeHolder: updatePlace } : x
|
||||
);
|
||||
setSignerPos(updatesignerPos);
|
||||
} else {
|
||||
const updatesignerPos = signerPos.map((x) =>
|
||||
x.Id === uniqueId
|
||||
? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
|
||||
: x
|
||||
);
|
||||
setSignerPos(updatesignerPos);
|
||||
}
|
||||
setSignerPos(updatesignerPos);
|
||||
} else {
|
||||
//adding new placeholder for selected signer in pos array (placeholder)
|
||||
let placeHolderPos;
|
||||
if (contractName) {
|
||||
placeHolderPos = {
|
||||
signerPtr: {
|
||||
__type: "Pointer",
|
||||
className: `${contractName}`,
|
||||
objectId: signerObjId
|
||||
},
|
||||
signerObjId: signerObjId,
|
||||
blockColor: blockColor ? blockColor : color[isSelectListId],
|
||||
placeHolder: [placeHolder],
|
||||
Role: Role ? Role : roleName,
|
||||
Id: uniqueId
|
||||
};
|
||||
} else {
|
||||
placeHolderPos = {
|
||||
signerPtr: {},
|
||||
signerObjId: "",
|
||||
blockColor: blockColor ? blockColor : color[isSelectListId],
|
||||
placeHolder: [placeHolder],
|
||||
Role: Role ? Role : roleName,
|
||||
Id: uniqueId
|
||||
};
|
||||
}
|
||||
setSignerPos((prev) => [...prev, placeHolderPos]);
|
||||
//else condition to add placeholder widgets on multiple page first time
|
||||
const updatesignerPos = signerPos.map((x) =>
|
||||
x.Id === uniqueId && x?.placeHolder
|
||||
? { ...x, placeHolder: [...x.placeHolder, placeHolder] }
|
||||
: x.Id === uniqueId
|
||||
? { ...x, placeHolder: [placeHolder] }
|
||||
: x
|
||||
);
|
||||
setSignerPos(updatesignerPos);
|
||||
}
|
||||
|
||||
if (dragTypeValue === "dropdown") {
|
||||
@@ -509,7 +478,6 @@ const TemplatePlaceholder = () => {
|
||||
setWidgetType(dragTypeValue);
|
||||
setSignKey(key);
|
||||
setSelectWidgetId(key);
|
||||
|
||||
setIsMailSend(false);
|
||||
} else {
|
||||
setIsReceipent(false);
|
||||
@@ -522,7 +490,7 @@ const TemplatePlaceholder = () => {
|
||||
const tourAddRole = [
|
||||
{
|
||||
selector: '[data-tut="reactourAddbtn"]',
|
||||
content: "You need to add a role before you can add fields for it. ",
|
||||
content: t("add-user-template"),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
@@ -608,17 +576,11 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
}
|
||||
setIsMailSend(false);
|
||||
setTimeout(() => {
|
||||
setIsDragging(false);
|
||||
}, 200);
|
||||
setTimeout(() => setIsDragging(false), 200);
|
||||
};
|
||||
//function for delete signature block
|
||||
const handleDeleteSign = (key, Id) => {
|
||||
const updateData = [];
|
||||
// const filterSignerPos = signerPos.filter(
|
||||
// (data) => data.signerObjId === signerId
|
||||
// );
|
||||
|
||||
const filterSignerPos = signerPos.filter((data) => data.Id === Id);
|
||||
|
||||
if (filterSignerPos.length > 0) {
|
||||
@@ -651,11 +613,11 @@ const TemplatePlaceholder = () => {
|
||||
|
||||
setSignerPos(newUpdateSigner);
|
||||
} else {
|
||||
const updateFilter = signerPos.filter((data) => data.Id !== Id);
|
||||
const getRemainPage = filterSignerPos[0].placeHolder.filter(
|
||||
(data) => data.pageNumber !== pageNumber
|
||||
);
|
||||
|
||||
//condition to check placeholder length is greater than 1 do not need to remove whole placeholder
|
||||
//array only resove particular widgets
|
||||
if (getRemainPage && getRemainPage.length > 0) {
|
||||
const newUpdatePos = filterSignerPos.map((obj) => {
|
||||
if (obj.Id === Id) {
|
||||
@@ -669,7 +631,16 @@ const TemplatePlaceholder = () => {
|
||||
|
||||
setSignerPos(signerupdate);
|
||||
} else {
|
||||
setSignerPos(updateFilter);
|
||||
const updatedData = signerPos.map((item) => {
|
||||
if (item.Id === Id) {
|
||||
// Create a copy of the item object and delete the placeHolder field
|
||||
const updatedItem = { ...item };
|
||||
delete updatedItem.placeHolder;
|
||||
return updatedItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setSignerPos(updatedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,19 +662,11 @@ const TemplatePlaceholder = () => {
|
||||
const touch = e.touches[0]; // Get the first touch point
|
||||
mouseX = touch.clientX - divRect.left;
|
||||
mouseY = touch.clientY - divRect.top;
|
||||
setSignBtnPosition([
|
||||
{
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
}
|
||||
]);
|
||||
setSignBtnPosition([{ xPos: mouseX, yPos: mouseY }]);
|
||||
} else {
|
||||
mouseX = e.clientX - divRect.left;
|
||||
mouseY = e.clientY - divRect.top;
|
||||
const xyPosition = {
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
};
|
||||
const xyPosition = { xPos: mouseX, yPos: mouseY };
|
||||
setXYSignature(xyPosition);
|
||||
}
|
||||
};
|
||||
@@ -712,21 +675,55 @@ const TemplatePlaceholder = () => {
|
||||
const handleMouseLeave = () => {
|
||||
setSignBtnPosition([xySignature]);
|
||||
};
|
||||
|
||||
const alertSendEmail = async () => {
|
||||
if (signerPos.length !== signersdata.length) {
|
||||
setIsSendAlert(true);
|
||||
} else {
|
||||
const isPlaceholderExist = signerPos.every((data) => data.placeHolder);
|
||||
if (isPlaceholderExist) {
|
||||
handleSaveTemplate();
|
||||
} else {
|
||||
setIsSendAlert(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (signerPos?.length > 0) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [signerPos, signatureType]);
|
||||
|
||||
// `autosavedetails` is used to save template details after every 2 sec when changes are happern in placeholder like drag-drop widgets, remove signers
|
||||
const autosavedetails = async () => {
|
||||
let signers = [];
|
||||
if (signersdata?.length > 0) {
|
||||
signersdata.forEach((x) => {
|
||||
if (x.objectId) {
|
||||
const obj = {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Contactbook",
|
||||
objectId: x.objectId
|
||||
};
|
||||
signers.push(obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
try {
|
||||
const templateCls = new Parse.Object("contracts_Template");
|
||||
templateCls.id = templateId;
|
||||
templateCls.set("Placeholders", signerPos);
|
||||
templateCls.set("Signers", signers);
|
||||
templateCls.set("SignatureType", signatureType);
|
||||
await templateCls.save();
|
||||
} catch (err) {
|
||||
console.log("error in autosave template", err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveTemplate = async () => {
|
||||
if (signersdata?.length) {
|
||||
const loadObj = {
|
||||
isLoad: true,
|
||||
message: t("loading-mssg")
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: true, message: t("loading-mssg") });
|
||||
setIsSendAlert(false);
|
||||
let signers = [];
|
||||
if (signersdata?.length > 0) {
|
||||
@@ -741,6 +738,17 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
let pdfUrl = pdfDetails[0]?.URL;
|
||||
if (pdfRotateBase64) {
|
||||
try {
|
||||
pdfUrl = await convertBase64ToFile(
|
||||
pdfDetails[0].Name,
|
||||
pdfRotateBase64
|
||||
);
|
||||
} catch (e) {
|
||||
console.log("error to convertBase64ToFile in placeholder flow", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const data = {
|
||||
Placeholders: signerPos,
|
||||
@@ -751,23 +759,25 @@ const TemplatePlaceholder = () => {
|
||||
SendinOrder: pdfDetails[0]?.SendinOrder || false,
|
||||
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
|
||||
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||
NextReminderDate: pdfDetails[0]?.NextReminderDate
|
||||
NextReminderDate: pdfDetails[0]?.NextReminderDate,
|
||||
IsEnableOTP: pdfDetails[0]?.IsEnableOTP === true ? true : false,
|
||||
IsTourEnabled: pdfDetails[0]?.IsTourEnabled === true ? true : false,
|
||||
URL: pdfUrl,
|
||||
SignatureType: signatureType,
|
||||
NotifyOnSignatures:
|
||||
pdfDetails[0]?.NotifyOnSignatures !== undefined
|
||||
? pdfDetails[0]?.NotifyOnSignatures
|
||||
: false
|
||||
};
|
||||
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 updateTemplate.save();
|
||||
setIsCreateDocModal(true);
|
||||
setIsMailSend(true);
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
};
|
||||
setIsLoading(loadObj);
|
||||
setIsLoading({ isLoad: false });
|
||||
} catch (e) {
|
||||
setIsLoading(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
@@ -866,9 +876,7 @@ const TemplatePlaceholder = () => {
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Users/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{ TourStatus: updatedTourStatus },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -891,8 +899,21 @@ const TemplatePlaceholder = () => {
|
||||
const handleCreateDocModal = async () => {
|
||||
setIsCreateDocModal(false);
|
||||
setIsCreateDoc(true);
|
||||
let pdfUrl = pdfDetails[0]?.URL;
|
||||
if (pdfRotateBase64) {
|
||||
try {
|
||||
pdfUrl = await convertBase64ToFile(pdfDetails[0].Name, pdfRotateBase64);
|
||||
} catch (e) {
|
||||
console.log("error to convertBase64ToFile in placeholder flow", e);
|
||||
}
|
||||
}
|
||||
// handle create document
|
||||
const res = await createDocument(pdfDetails, signerPos, signersdata);
|
||||
const res = await createDocument(
|
||||
pdfDetails,
|
||||
signerPos,
|
||||
signersdata,
|
||||
pdfUrl
|
||||
);
|
||||
if (res.status === "success") {
|
||||
navigate(`/placeHolderSign/${res.id}`, {
|
||||
state: { title: "Use Template" }
|
||||
@@ -903,7 +924,6 @@ const TemplatePlaceholder = () => {
|
||||
setIsCreateDoc(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleAddSigner` is used to open Add Role Modal
|
||||
const handleAddSigner = () => {
|
||||
setIsModalRole(true);
|
||||
@@ -914,8 +934,6 @@ const TemplatePlaceholder = () => {
|
||||
// save Role in entry in signerList and user
|
||||
const handleAddRole = (e) => {
|
||||
e.preventDefault();
|
||||
setSignerObjId("");
|
||||
setContractName("");
|
||||
const count = signersdata.length > 0 ? signersdata.length + 1 : 1;
|
||||
const Id = randomId();
|
||||
const index = signersdata.length;
|
||||
@@ -925,6 +943,15 @@ const TemplatePlaceholder = () => {
|
||||
blockColor: color[index]
|
||||
};
|
||||
setSignersData((prevArr) => [...prevArr, obj]);
|
||||
const signerPosObj = {
|
||||
signerPtr: {},
|
||||
signerObjId: "",
|
||||
blockColor: color[index],
|
||||
Role: roleName || "User " + count,
|
||||
Id: Id
|
||||
};
|
||||
|
||||
setSignerPos((prev) => [...prev, signerPosObj]);
|
||||
setIsModalRole(false);
|
||||
setRoleName("");
|
||||
setUniqueId(Id);
|
||||
@@ -956,11 +983,12 @@ const TemplatePlaceholder = () => {
|
||||
setIsMailSend(false);
|
||||
};
|
||||
|
||||
// `handleLinkUser` is used to open Add/Choose Signer Modal when user can link existing or new User with placeholder
|
||||
// `handleLinkUser` is used to open Add/Choose Signer Modal when user can link existing or new User with placeholder
|
||||
// and update entry in signersList
|
||||
const handleLinkUser = (id) => {
|
||||
setIsAddUser({ [id]: true });
|
||||
};
|
||||
// `handleAddUser` is used to adduser
|
||||
const handleAddUser = (data) => {
|
||||
const signerPtr = {
|
||||
__type: "Pointer",
|
||||
@@ -1089,7 +1117,13 @@ const TemplatePlaceholder = () => {
|
||||
status: status,
|
||||
defaultValue: defaultValue,
|
||||
isReadOnly: isReadOnly || false,
|
||||
isHideLabel: isHideLabel || false
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1121,7 +1155,13 @@ const TemplatePlaceholder = () => {
|
||||
},
|
||||
isReadOnly: isReadOnly || false,
|
||||
defaultValue: defaultValue,
|
||||
isHideLabel: isHideLabel || false
|
||||
isHideLabel: isHideLabel || false,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1133,7 +1173,13 @@ const TemplatePlaceholder = () => {
|
||||
name: dropdownName,
|
||||
status: status,
|
||||
values: dropdownOptions,
|
||||
defaultValue: defaultValue
|
||||
defaultValue: defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1160,9 +1206,19 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
};
|
||||
|
||||
const handleWidgetdefaultdata = (defaultdata) => {
|
||||
const handleWidgetdefaultdata = (defaultdata, isSignWidget) => {
|
||||
if (isSignWidget) {
|
||||
const updatedPdfDetails = [...pdfDetails];
|
||||
const signtypes = defaultdata.signatureType || signatureType;
|
||||
updatedPdfDetails[0].SignatureType = signtypes;
|
||||
// Update the SignatureType with the modified array
|
||||
setPdfDetails(updatedPdfDetails);
|
||||
setSignatureType(signtypes);
|
||||
}
|
||||
const options = ["email", "number", "text"];
|
||||
let inputype;
|
||||
if (defaultdata.textvalidate) {
|
||||
@@ -1202,7 +1258,7 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
: {},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1218,7 +1274,7 @@ const TemplatePlaceholder = () => {
|
||||
status: defaultdata.status,
|
||||
defaultValue: defaultdata.defaultValue,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
fontColor ||
|
||||
currWidgetsDetails?.options?.fontColor ||
|
||||
@@ -1250,6 +1306,7 @@ const TemplatePlaceholder = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
handleNameModal();
|
||||
};
|
||||
|
||||
const handleNameModal = () => {
|
||||
setIsNameModal(false);
|
||||
setCurrWidgetsDetails({});
|
||||
@@ -1258,6 +1315,38 @@ const TemplatePlaceholder = () => {
|
||||
setIsCheckbox(false);
|
||||
};
|
||||
|
||||
const clickOnZoomIn = () => {
|
||||
onClickZoomIn(scale, zoomPercent, setScale, setZoomPercent);
|
||||
};
|
||||
const clickOnZoomOut = () => {
|
||||
onClickZoomOut(zoomPercent, scale, setZoomPercent, setScale);
|
||||
};
|
||||
//`handleRotationFun` function is used to roatate pdf particular page
|
||||
const handleRotationFun = async (rotateDegree) => {
|
||||
const isRotate = handleRotateWarning(signerPos, pageNumber);
|
||||
if (isRotate) {
|
||||
setIsRotate({ status: true, degree: rotateDegree });
|
||||
} else {
|
||||
const urlDetails = await rotatePdfPage(
|
||||
pdfDetails[0].URL,
|
||||
rotateDegree,
|
||||
pageNumber - 1,
|
||||
pdfRotateBase64
|
||||
);
|
||||
setPdfRotatese64(urlDetails.base64);
|
||||
}
|
||||
};
|
||||
const handleRemovePlaceholder = async () => {
|
||||
handleRemoveWidgets(setSignerPos, signerPos, pageNumber, setIsRotate);
|
||||
const urlDetails = await rotatePdfPage(
|
||||
pdfDetails[0].URL,
|
||||
isRotate.degree,
|
||||
pageNumber - 1,
|
||||
pdfRotateBase64
|
||||
);
|
||||
setPdfRotatese64(urlDetails.base64);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title title={"Template"} />
|
||||
@@ -1305,11 +1394,9 @@ const TemplatePlaceholder = () => {
|
||||
{/* pdf render view */}
|
||||
<div className=" w-full md:w-[57%] flex mr-4">
|
||||
<PdfZoom
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
setZoomPercent={setZoomPercent}
|
||||
zoomPercent={zoomPercent}
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
handleRotationFun={handleRotationFun}
|
||||
/>
|
||||
<div className="w-full md:w-[95%]">
|
||||
{/* this modal is used show alert set placeholder for all signers before send mail */}
|
||||
@@ -1391,6 +1478,10 @@ const TemplatePlaceholder = () => {
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
/>
|
||||
<DropdownWidgetOption
|
||||
type="checkbox"
|
||||
@@ -1402,6 +1493,10 @@ const TemplatePlaceholder = () => {
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
/>
|
||||
<DropdownWidgetOption
|
||||
type="dropdown"
|
||||
@@ -1413,6 +1508,10 @@ const TemplatePlaceholder = () => {
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
/>
|
||||
<PlaceholderCopy
|
||||
isPageCopy={isPageCopy}
|
||||
@@ -1440,6 +1539,9 @@ const TemplatePlaceholder = () => {
|
||||
currentSigner={true}
|
||||
setIsEditTemplate={handleEditTemplateModal}
|
||||
dataTut4="reactourFour"
|
||||
handleRotationFun={handleRotationFun}
|
||||
clickOnZoomIn={clickOnZoomIn}
|
||||
clickOnZoomOut={clickOnZoomOut}
|
||||
/>
|
||||
<div
|
||||
ref={divRef}
|
||||
@@ -1471,7 +1573,6 @@ const TemplatePlaceholder = () => {
|
||||
signersdata={signersdata}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
setSignerObjId={setSignerObjId}
|
||||
isDragging={isDragging}
|
||||
setShowDropdown={setShowDropdown}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
@@ -1485,6 +1586,12 @@ const TemplatePlaceholder = () => {
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
setIsSelectId={setIsSelectId}
|
||||
pdfRotateBase64={pdfRotateBase64}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
isResize={isResize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -1509,9 +1616,7 @@ const TemplatePlaceholder = () => {
|
||||
signerPos={signerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
setSignerObjId={setSignerObjId}
|
||||
setIsSelectId={setIsSelectId}
|
||||
setContractName={setContractName}
|
||||
isSigners={isSigners}
|
||||
setIsShowEmail={setIsShowEmail}
|
||||
isMailSend={isMailSend}
|
||||
@@ -1541,10 +1646,8 @@ const TemplatePlaceholder = () => {
|
||||
setSignerPos={setSignerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
setSignerObjId={setSignerObjId}
|
||||
setRoleName={setRoleName}
|
||||
setIsSelectId={setIsSelectId}
|
||||
setContractName={setContractName}
|
||||
handleAddSigner={handleAddSigner}
|
||||
setUniqueId={setUniqueId}
|
||||
handleDeleteUser={handleDeleteUser}
|
||||
@@ -1611,6 +1714,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
</ModalUi>
|
||||
<WidgetNameModal
|
||||
signatureType={signatureType}
|
||||
widgetName={widgetName}
|
||||
defaultdata={currWidgetsDetails}
|
||||
isOpen={isNameModal}
|
||||
@@ -1624,6 +1728,11 @@ const TemplatePlaceholder = () => {
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
/>
|
||||
<RotateAlert
|
||||
isRotate={isRotate.status}
|
||||
setIsRotate={setIsRotate}
|
||||
handleRemoveWidgets={handleRemovePlaceholder}
|
||||
/>
|
||||
</DndProvider>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { NavLink, useNavigate } from "react-router-dom";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Title from "../components/Title";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { emailRegex } from "../constant/const";
|
||||
const UpdateExistUserAdmin = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
@@ -15,14 +16,15 @@ const UpdateExistUserAdmin = () => {
|
||||
const [isAlert, setIsAlert] = useState({ type: "danger", msg: "" });
|
||||
useEffect(() => {
|
||||
checkIsAdminExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const checkIsAdminExist = async () => {
|
||||
try {
|
||||
const isAdminExist = await Parse.Cloud.run("checkadminexist");
|
||||
if (isAdminExist !== "not_exist") {
|
||||
console.log("isAdminExist ", isAdminExist);
|
||||
setErrMsg("Admin already exists.");
|
||||
// console.log("isAdminExist ", isAdminExist);
|
||||
setErrMsg(t("admin-exists"));
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in checkadminexist", err);
|
||||
@@ -34,43 +36,43 @@ const UpdateExistUserAdmin = () => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmitLoading(true);
|
||||
try {
|
||||
const updateUserAsAdmin = await Parse.Cloud.run(
|
||||
"updateuserasadmin",
|
||||
formdata
|
||||
);
|
||||
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
|
||||
if (updateUserAsAdmin === "admin_created") {
|
||||
setIsAlert({ type: "success", msg: t("admin-created") });
|
||||
navigate("/");
|
||||
if (!emailRegex.test(formdata.email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsSubmitLoading(true);
|
||||
try {
|
||||
const updateUserAsAdmin = await Parse.Cloud.run(
|
||||
"updateuserasadmin",
|
||||
formdata
|
||||
);
|
||||
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
|
||||
if (updateUserAsAdmin === "admin_created") {
|
||||
setIsAlert({ type: "success", msg: t("admin-created") });
|
||||
navigate("/");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in updateuserasadmin", err.code);
|
||||
if (err.code === 404) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-exists") }));
|
||||
} else {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitLoading(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert(() => ({ type: "danger", msg: "" }));
|
||||
}, 2000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in updateuserasadmin", err.code);
|
||||
if (err.code === 404) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("invalid-masterkey") }));
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("user-not-found") }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: t("admin-already-exist") }));
|
||||
} else {
|
||||
setErrMsg(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitLoading(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert(() => ({ type: "danger", msg: "" }));
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="h-screen flex justify-center">
|
||||
<Title title="Add admin" />
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
<Title title={"Add Admin"} />
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
{loader ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
<Loader />
|
||||
|
||||
+246
-116
@@ -9,8 +9,9 @@ import Tooltip from "../primitives/Tooltip";
|
||||
import AddUser from "../components/AddUser";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribedTeam } from "../constant/Utils";
|
||||
import { fetchSubscriptionInfo } from "../constant/Utils";
|
||||
import Title from "../components/Title";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
||||
// const actions = [];
|
||||
@@ -18,7 +19,11 @@ const UserList = () => {
|
||||
const { t } = useTranslation();
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isModal, setIsModal] = useState({
|
||||
form: false,
|
||||
addseats: false,
|
||||
options: false
|
||||
});
|
||||
const location = useLocation();
|
||||
const isDashboard =
|
||||
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
|
||||
@@ -26,8 +31,16 @@ const UserList = () => {
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState({
|
||||
plan: "",
|
||||
isValid: false,
|
||||
priceperUser: 0
|
||||
});
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [formHeader, setFormHeader] = useState(t("add-user"));
|
||||
const [usersCount, setUserCounts] = useState({ allowed: 0, totalAllowed: 0 });
|
||||
const [amount, setAmount] = useState({ quantity: 1, price: 0 });
|
||||
const [isBuyLoader, setIsBuyLoader] = useState(false);
|
||||
const recordperPage = 10;
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
|
||||
@@ -79,10 +92,13 @@ const UserList = () => {
|
||||
pages.push(lastPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
const pageNumbers = getPaginationRange();
|
||||
// to slice out 10 objects from array for current page
|
||||
const indexOfLastDoc = currentPage * recordperPage;
|
||||
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
|
||||
const currentList = userList?.slice(indexOfFirstDoc, indexOfLastDoc);
|
||||
useEffect(() => {
|
||||
fetchUserList();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -90,13 +106,43 @@ const UserList = () => {
|
||||
async function fetchUserList() {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
const subscribe = await fetchSubscriptionInfo();
|
||||
if (subscribe?.plan_code?.includes("team")) {
|
||||
const isSupAdmin =
|
||||
subscribe?.adminId && extUser?.objectId === subscribe?.adminId;
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: true,
|
||||
adminId: subscribe.adminId,
|
||||
priceperUser: subscribe.price,
|
||||
isSuperAdmin: isSupAdmin
|
||||
});
|
||||
setAmount((prev) => ({ ...prev, price: subscribe.price }));
|
||||
try {
|
||||
const res = await Parse.Cloud.run("allowedusers");
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(res),
|
||||
totalAllowed: parseInt(subscribe?.totalAllowedUser) || 0
|
||||
}));
|
||||
} catch (err) {
|
||||
console.log("err while get users", err);
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe({
|
||||
plan: subscribe.plan_code,
|
||||
isValid: false,
|
||||
adminId: subscribe.adminId
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsSubscribe(true);
|
||||
setIsSubscribe({ plan: "teams-yearly", isValid: true });
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
|
||||
if (extUser) {
|
||||
const admin =
|
||||
extUser?.UserRole &&
|
||||
@@ -115,26 +161,36 @@ const UserList = () => {
|
||||
console.log("Err in fetch userlist", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
const handleModal = (modalName) => {
|
||||
setIsModal((obj) => ({ ...obj, [modalName]: !obj[modalName] }));
|
||||
};
|
||||
|
||||
// Change page
|
||||
const paginateFront = () => setCurrentPage(currentPage + 1);
|
||||
const paginateBack = () => setCurrentPage(currentPage - 1);
|
||||
// const handleActionBtn = (act, item) => {
|
||||
// if (act.action === "delete") {
|
||||
// setIsDeleteModal({ [item.objectId]: true });
|
||||
// }
|
||||
// };
|
||||
const paginateFront = () => {
|
||||
const lastValue = pageNumbers?.[pageNumbers?.length - 1];
|
||||
if (currentPage < lastValue) {
|
||||
setCurrentPage(currentPage + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const paginateBack = () => {
|
||||
if (startIndex > 0) {
|
||||
setCurrentPage(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUserData = (userData) => {
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
if (userData) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: obj?.allowed > 0 ? obj.allowed - 1 : 1
|
||||
}));
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
}
|
||||
};
|
||||
// `formatRow` is used to show data in poper manner like
|
||||
// if data is of array type then it will join array items with ","
|
||||
@@ -150,9 +206,8 @@ const UserList = () => {
|
||||
return "-";
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsActiveModal({});
|
||||
};
|
||||
const handleClose = () => setIsActiveModal({});
|
||||
|
||||
const handleToggleSubmit = async (user) => {
|
||||
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
|
||||
if (index !== -1) {
|
||||
@@ -184,6 +239,49 @@ const UserList = () => {
|
||||
const handleToggleBtn = (user) => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
|
||||
const handleAddOnSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsBuyLoader(true);
|
||||
try {
|
||||
const resAddon = await Parse.Cloud.run("buyaddonusers", {
|
||||
users: parseInt(amount.quantity)
|
||||
});
|
||||
if (resAddon) {
|
||||
const _resAddon = JSON.parse(JSON.stringify(resAddon));
|
||||
if (_resAddon.status === "success") {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(amount.quantity),
|
||||
totalAllowed: parseInt(_resAddon.addon)
|
||||
}));
|
||||
setAmount((obj) => ({ ...obj, quantity: 1 }));
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in buy addon", err);
|
||||
setIsAlert({ type: "danger", msg: t("something-went-wrong-mssg") });
|
||||
} finally {
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 2000);
|
||||
setIsBuyLoader(false);
|
||||
handleModal("addseats");
|
||||
}
|
||||
};
|
||||
const handlePricePerUser = (e) => {
|
||||
const quantity = e.target.value;
|
||||
const price = e.target?.value > 0 ? isSubscribe.priceperUser * quantity : 0;
|
||||
setAmount((prev) => ({ ...prev, quantity: quantity, price: price }));
|
||||
};
|
||||
const handleBuyUsers = (allowed, totalAllowed) => {
|
||||
if (allowed && totalAllowed) {
|
||||
setUserCounts((obj) => ({
|
||||
...obj,
|
||||
allowed: parseInt(obj.allowed) + parseInt(allowed),
|
||||
totalAllowed: parseInt(totalAllowed)
|
||||
}));
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={isAdmin ? "Users" : "Page not found"} />
|
||||
@@ -193,34 +291,57 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
{Object.keys(isActLoader)?.length > 0 && (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black/30 z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isSubscribe && !isLoader && (
|
||||
{validplan[isSubscribe.plan] && !isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
{t("report-name.Users")}
|
||||
{t("report-name.Users")}{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={t("users-from-teams")} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleFormModal()}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="hidden md:flex op-btn op-btn-sm h-[35px] op-btn-primary op-btn-outline text-xs mb-0.5"
|
||||
onClick={() => handleModal("addseats")}
|
||||
>
|
||||
{t("buy-users")}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModal("form")}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[30px] md:text-[40px]"></i>
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div
|
||||
className="cursor-pointer relative md:hidden mb-0.5"
|
||||
onClick={() => handleModal("options")}
|
||||
>
|
||||
<i className="fa-light fa-ellipsis-vertical fa-xl"></i>
|
||||
{isModal?.options && (
|
||||
<ul className="absolute -right-3 top-auto z-[70] w-max op-menu op-menu-xs shadow bg-base-100 text-base-content rounded-box border">
|
||||
<li onClick={() => handleModal("addseats")}>
|
||||
<span className="text-[13px] capitalize font-medium">
|
||||
{t("buy-users")}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={` overflow-x-auto w-full`}>
|
||||
<div className="overflow-x-auto w-full">
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
@@ -234,7 +355,7 @@ const UserList = () => {
|
||||
<tbody className="text-[12px]">
|
||||
{userList?.length > 0 && (
|
||||
<>
|
||||
{userList.map((item, index) => (
|
||||
{currentList.map((item, index) => (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
{heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
@@ -298,49 +419,6 @@ const UserList = () => {
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
|
||||
{actions?.length > 0 &&
|
||||
actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={act.hoverLabel}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm`}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isDeleteModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={t("delete-user")}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
{t( "are-you-sure")} {t("delete")} {t("this-user")}?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() => handleDelete(item)}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("yes")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
{('no)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</td> */}
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
@@ -348,34 +426,42 @@ const UserList = () => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
<div className="flex flex-row justify-between items-center text-xs font-medium">
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("prev")}
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isEnableSubscription && isSubscribe?.isSuperAdmin && (
|
||||
<div>
|
||||
{t("available-seats")}: {usersCount.allowed}/
|
||||
{usersCount.totalAllowed}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
@@ -397,16 +483,60 @@ const UserList = () => {
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
title={t("add-user")}
|
||||
isOpen={isModal}
|
||||
handleClose={handleFormModal}
|
||||
isOpen={isModal.form}
|
||||
title={formHeader}
|
||||
handleClose={() => handleModal("form")}
|
||||
>
|
||||
<AddUser
|
||||
setIsAlert={setIsAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={handleFormModal}
|
||||
handleBuyUsers={handleBuyUsers}
|
||||
closePopup={() => handleModal("form")}
|
||||
setFormHeader={setFormHeader}
|
||||
/>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
isOpen={isModal.addseats}
|
||||
title="Add Seats"
|
||||
handleClose={() => handleModal("addseats")}
|
||||
>
|
||||
{isBuyLoader && (
|
||||
<div className="absolute w-full h-full inset-0 flex justify-center items-center bg-base-content/30 z-50">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleAddOnSubmit} className=" px-3 pb-6 pt-6">
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label
|
||||
htmlFor="quantity"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
{t("Quantity-of-users")}
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="quantity"
|
||||
value={amount.quantity}
|
||||
onChange={(e) => handlePricePerUser(e)}
|
||||
className="w-1/4 op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3 flex justify-between">
|
||||
<label className="block text-xs text-gray-700 font-semibold">
|
||||
{t("Price")} (1 * {isSubscribe.priceperUser})
|
||||
</label>
|
||||
<div className="w-1/4 flex justify-center items-center text-sm">
|
||||
USD {amount.price}
|
||||
</div>
|
||||
</div>
|
||||
<hr className="text-base-content mb-3" />
|
||||
<button className="op-btn op-btn-primary w-full">
|
||||
{t("Proceed")}
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
@@ -422,9 +552,9 @@ const UserList = () => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isSubscribe && isEnableSubscription && !isLoader && (
|
||||
{isEnableSubscription && !validplan[isSubscribe.plan] && !isLoader && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard plan={"TEAMS"} price={"20"} />
|
||||
<SubscribeCard plan="TEAMS" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import Tooltip from "../primitives/Tooltip";
|
||||
import { isEnableSubscription, isStaging } from "../constant/const";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
checkIsSubscribedTeam,
|
||||
copytoData,
|
||||
handleSendOTP,
|
||||
openInNewTab
|
||||
@@ -18,10 +17,13 @@ import {
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { validplan } from "../json/plansArr";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SelectLanguage from "../components/pdf/SelectLanguage";
|
||||
import { RWebShare } from "react-web-share";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Tour from "reactour";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
|
||||
function UserProfile() {
|
||||
const navigate = useNavigate();
|
||||
@@ -39,7 +41,7 @@ function UserProfile() {
|
||||
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isUpgrade, setIsUpgrade] = useState(false);
|
||||
const [isAlert, setIsAlert] = useState({});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", message: "" });
|
||||
const [publicUserName, setPublicUserName] = useState(
|
||||
extendUser && extendUser?.[0]?.UserName
|
||||
);
|
||||
@@ -57,7 +59,10 @@ function UserProfile() {
|
||||
const [tagLine, setTagLine] = useState(
|
||||
extendUser && extendUser?.[0]?.Tagline
|
||||
);
|
||||
const [isTeam, setIsTeam] = useState(false);
|
||||
const [isPlan, setIsPlan] = useState({ plan: "", isValid: false });
|
||||
const [tourStatus, setTourStatus] = useState([]);
|
||||
const [isProfileTour, setIsProfileTour] = useState(false);
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const getPublicUrl = isStaging
|
||||
? `https://staging.opensign.me/${extendUser?.[0]?.UserName}`
|
||||
: `https://opensign.me/${extendUser?.[0]?.UserName}`;
|
||||
@@ -65,17 +70,27 @@ function UserProfile() {
|
||||
getUserDetail();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const getUserDetail = async () => {
|
||||
setIsLoader(true);
|
||||
const extClass = localStorage.getItem("Extand_Class");
|
||||
const jsonSender = JSON.parse(extClass);
|
||||
const tourstatuss = jsonSender[0]?.TourStatus && jsonSender[0].TourStatus;
|
||||
if (!isEnableSubscription) {
|
||||
setIsProfileTour(true);
|
||||
} else if (tourstatuss && tourstatuss.length > 0) {
|
||||
setTourStatus(tourstatuss);
|
||||
const checkTourRecipients = tourstatuss.filter(
|
||||
(data) => data.profileTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setIsProfileTour(checkTourRecipients[0]?.profileTour);
|
||||
}
|
||||
}
|
||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
const getIsTeam = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
setIsTeam(getIsTeam);
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe.isValid);
|
||||
setIsPlan(subscribe);
|
||||
}
|
||||
if (HeaderDocId) {
|
||||
setIsDisableDocId(HeaderDocId);
|
||||
@@ -109,13 +124,8 @@ function UserProfile() {
|
||||
});
|
||||
if (res) {
|
||||
setIsLoader(false);
|
||||
setIsAlert({
|
||||
type: "danger",
|
||||
message: t("user-name-exist")
|
||||
});
|
||||
setTimeout(() => {
|
||||
setIsAlert({});
|
||||
}, 3000);
|
||||
setIsAlert({ type: "danger", message: t("user-name-exist") });
|
||||
setTimeout(() => setIsAlert({}), 3000);
|
||||
return res;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -320,17 +330,80 @@ function UserProfile() {
|
||||
setJobTitle(extendUser?.[0]?.JobTitle);
|
||||
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
|
||||
};
|
||||
const copytoclipboard = () => {
|
||||
copytoData(getPublicUrl);
|
||||
setIsAlert({
|
||||
type: "success",
|
||||
message: t("copied")
|
||||
});
|
||||
setTimeout(() => {
|
||||
setIsAlert({});
|
||||
}, 3000);
|
||||
const handlePaidRoute = () => {
|
||||
navigate("/subscription");
|
||||
};
|
||||
|
||||
const copytoclipboard = () => {
|
||||
copytoData(getPublicUrl);
|
||||
setIsAlert({ type: "success", message: t("copied") });
|
||||
setTimeout(() => setIsAlert({}), 3000);
|
||||
};
|
||||
|
||||
const tourConfig = [
|
||||
{
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={t("tour-mssg.public-template")}
|
||||
isChecked={handleDontShow}
|
||||
video="https://www.youtube.com/embed/_wB4UA7Jz5Q?si=I40CI3nVUWQzf42Y"
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
//function for update TourStatus
|
||||
const closeTour = async () => {
|
||||
try {
|
||||
setIsProfileTour(true);
|
||||
if (isDontShow) {
|
||||
let updatedTourStatus = [];
|
||||
if (tourStatus.length > 0) {
|
||||
updatedTourStatus = [...tourStatus];
|
||||
const profileIndex = tourStatus.findIndex(
|
||||
(obj) => obj["profileTour"] === false || obj["profileTour"] === true
|
||||
);
|
||||
if (profileIndex !== -1) {
|
||||
updatedTourStatus[profileIndex] = { profileTour: true };
|
||||
} else {
|
||||
updatedTourStatus.push({ profileTour: true });
|
||||
}
|
||||
} else {
|
||||
updatedTourStatus = [{ profileTour: true }];
|
||||
}
|
||||
|
||||
await axios.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Users/${
|
||||
extendUser?.[0]?.objectId
|
||||
}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
);
|
||||
const res = await Parse.Cloud.run("getUserDetails");
|
||||
const json = JSON.parse(JSON.stringify([res]));
|
||||
const extRes = JSON.stringify(json);
|
||||
localStorage.setItem("Extand_Class", extRes);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("axois err ", err);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
const handleDontShow = (isChecked) => {
|
||||
setIsDontShow(isChecked);
|
||||
};
|
||||
const handleOnlickHelp = () => {
|
||||
setIsProfileTour(false);
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"Profile"} />
|
||||
@@ -340,12 +413,19 @@ function UserProfile() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-center items-center w-full relative">
|
||||
{isAlert && (
|
||||
{isAlert.message && (
|
||||
<Alert className="z-[1000] fixed top-[10%]" type={isAlert.type}>
|
||||
{isAlert.message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Tour
|
||||
className="md:!min-w-[650px]"
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfig}
|
||||
isOpen={!isProfileTour}
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box w-[450px]">
|
||||
<div className="flex flex-col justify-center items-center my-4">
|
||||
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
||||
@@ -382,17 +462,6 @@ function UserProfile() {
|
||||
</div>
|
||||
</div>
|
||||
<ul className="w-full flex flex-col p-2 text-sm">
|
||||
<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">{t("language")}:</span>{" "}
|
||||
<SelectLanguage
|
||||
isProfile={true}
|
||||
updateExtUser={updateExtUser}
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||
editmode ? "py-1.5" : "py-2"
|
||||
@@ -403,7 +472,7 @@ function UserProfile() {
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
onChange={(e) => SetName(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
@@ -419,7 +488,7 @@ function UserProfile() {
|
||||
{editmode ? (
|
||||
<input
|
||||
type="text"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
onChange={(e) => SetPhone(e.target.value)}
|
||||
value={Phone}
|
||||
/>
|
||||
@@ -441,7 +510,7 @@ function UserProfile() {
|
||||
<input
|
||||
type="text"
|
||||
value={company}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
@@ -458,7 +527,7 @@ function UserProfile() {
|
||||
<input
|
||||
type="text"
|
||||
value={jobTitle}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-sm"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
onChange={(e) => setJobTitle(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
@@ -486,40 +555,30 @@ function UserProfile() {
|
||||
</li>
|
||||
{isEnableSubscription && (
|
||||
<>
|
||||
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<li className="flex flex-row justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold flex gap-1">
|
||||
{t("public-profile")} :{" "}
|
||||
<Tooltip
|
||||
maxWidth="max-w-[250px]"
|
||||
message={t("public-profile-help")}
|
||||
/>
|
||||
<Tooltip handleOnlickHelp={handleOnlickHelp} />
|
||||
</span>
|
||||
<div className="flex md:flex-row flex-col md:items-center">
|
||||
<div className="flex flex-row items-center gap-1">
|
||||
{editmode || !extendUser?.[0]?.UserName ? (
|
||||
<input
|
||||
maxLength={40}
|
||||
style={{
|
||||
border:
|
||||
!isSubscribe &&
|
||||
publicUserName?.length > 0 &&
|
||||
publicUserName?.length < 9 &&
|
||||
"solid red"
|
||||
}}
|
||||
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
|
||||
maxLength={40}
|
||||
onChange={handleOnchangeUserName}
|
||||
value={publicUserName}
|
||||
disabled={!editmode}
|
||||
placeholder="enter user name"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-row gap-1 items-center justify-between md:justify-start">
|
||||
<span
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
onClick={() => {
|
||||
openInNewTab(getPublicUrl);
|
||||
}}
|
||||
className="cursor-pointer underline hover:text-blue-800 w-[200px] md:w-[150px] whitespace-nowrap overflow-hidden text-ellipsis"
|
||||
onClick={() => openInNewTab(getPublicUrl)}
|
||||
className="cursor-pointer underline hover:text-blue-800 w-[110px] md:w-[150px] whitespace-nowrap overflow-hidden text-ellipsis"
|
||||
>
|
||||
{isStaging
|
||||
? `staging.opensign.me/${extendUser?.[0]?.UserName}`
|
||||
@@ -527,10 +586,7 @@ function UserProfile() {
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<RWebShare
|
||||
data={{
|
||||
url: getPublicUrl,
|
||||
title: "Sign url"
|
||||
}}
|
||||
data={{ url: getPublicUrl, title: "Sign url" }}
|
||||
>
|
||||
<button className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm ">
|
||||
<i className="fa-light fa-share-from-square"></i>{" "}
|
||||
@@ -540,37 +596,30 @@ function UserProfile() {
|
||||
className="op-btn op-btn-primary op-btn-outline op-btn-xs md:op-btn-sm"
|
||||
onClick={() => copytoclipboard()}
|
||||
>
|
||||
<i className="fa-light fa-link"></i>{" "}
|
||||
<i className="fa-light fa-copy" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<li className="flex flex-row justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold flex gap-1">
|
||||
{t("tagline")} :{" "}
|
||||
<Tooltip
|
||||
maxWidth="max-w-[250px]"
|
||||
message={t("tagline-help")}
|
||||
/>
|
||||
<Tooltip handleOnlickHelp={handleOnlickHelp} />
|
||||
</span>
|
||||
<div className="flex md:flex-row flex-col md:items-center">
|
||||
{editmode ? (
|
||||
<div className="flex flex-row md:items-center gap-1">
|
||||
{editmode || !extendUser?.[0]?.Tagline ? (
|
||||
<input
|
||||
maxLength={100}
|
||||
onChange={handleOnchangeTagLine}
|
||||
value={tagLine}
|
||||
disabled={!editmode}
|
||||
placeholder="enter tagline"
|
||||
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
|
||||
className="op-input op-input-bordered op-input-sm w-[180px] focus:outline-none hover:border-base-content text-sm"
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
value={extendUser?.[0]?.Tagline}
|
||||
disabled
|
||||
placeholder="enter tagline"
|
||||
className="op-input op-input-bordered op-input-xs"
|
||||
/>
|
||||
<span>{extendUser?.[0]?.Tagline}</span>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
@@ -581,7 +630,9 @@ function UserProfile() {
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span
|
||||
className={
|
||||
isTeam ? "font-semibold" : "font-semibold text-gray-300"
|
||||
validplan[isPlan.plan]
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
{t("disable-documentId")} :{" "}
|
||||
@@ -590,17 +641,21 @@ function UserProfile() {
|
||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||
}
|
||||
/>
|
||||
{!isTeam && isEnableSubscription && <Upgrade />}
|
||||
{!validplan[isPlan.plan] && isEnableSubscription && (
|
||||
<Upgrade />
|
||||
)}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isTeam
|
||||
validplan[isPlan.plan]
|
||||
? `${editmode ? "cursor-pointer" : ""}`
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
disabled={isTeam ? false : true}
|
||||
disabled={
|
||||
validplan[isPlan.plan] && editmode ? false : true
|
||||
}
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={isDisableDocId}
|
||||
@@ -610,6 +665,17 @@ function UserProfile() {
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
<li
|
||||
className={`flex justify-between items-center border-b-[1px] border-gray-300 break-all ${
|
||||
editmode ? "py-1.5" : "py-2"
|
||||
}`}
|
||||
>
|
||||
<span className="font-semibold">{t("language")}:</span>{" "}
|
||||
<SelectLanguage
|
||||
isProfile={true}
|
||||
updateExtUser={updateExtUser}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className={`${
|
||||
@@ -620,6 +686,7 @@ function UserProfile() {
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
if (
|
||||
editmode &&
|
||||
!isSubscribe &&
|
||||
publicUserName?.length > 0 &&
|
||||
publicUserName?.length < 9
|
||||
@@ -689,6 +756,7 @@ function UserProfile() {
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
|
||||
{isUpgrade && (
|
||||
<div className="op-modal op-modal-open">
|
||||
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
|
||||
@@ -708,7 +776,7 @@ function UserProfile() {
|
||||
<p>{t("user-name-limit-char")}</p>
|
||||
<div className="op-card-actions justify-end">
|
||||
<button
|
||||
onClick={() => navigate("/subscription")}
|
||||
onClick={() => handlePaidRoute()}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
|
||||
@@ -19,7 +19,7 @@ function Webhook() {
|
||||
const [webhook, setWebhook] = useState();
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true });
|
||||
const [error, setError] = useState("");
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
@@ -35,8 +35,8 @@ function Webhook() {
|
||||
];
|
||||
const fetchWebhook = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
const subscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(subscribe);
|
||||
}
|
||||
try {
|
||||
const extRes = await Parse.Cloud.run("getUserDetails");
|
||||
@@ -89,7 +89,7 @@ function Webhook() {
|
||||
};
|
||||
|
||||
const handleModal = () => {
|
||||
if (!isSubscribe && isEnableSubscription) {
|
||||
if (!isSubscribe?.isValid && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
setIsModal(!isModal);
|
||||
@@ -145,7 +145,7 @@ function Webhook() {
|
||||
</div>
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={"Regenerate Token"}
|
||||
title={t("add-webhook")}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
{error && <Alert type="danger">{error}</Alert>}
|
||||
@@ -177,7 +177,7 @@ function Webhook() {
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
{isEnableSubscription && !isSubscribe?.isValid && (
|
||||
<div data-tut="webhooksubscribe">
|
||||
<SubscribeCard />
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import Loader from "./Loader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import axios from "axios";
|
||||
import { getTenantDetails } from "../constant/Utils";
|
||||
import { emailRegex } from "../constant/const";
|
||||
const AddContact = (props) => {
|
||||
const { t } = useTranslation();
|
||||
const [name, setName] = useState("");
|
||||
@@ -12,6 +14,7 @@ const AddContact = (props) => {
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
// Load user details from localStorage when the component mounts
|
||||
useEffect(() => {
|
||||
@@ -26,131 +29,82 @@ const AddContact = (props) => {
|
||||
}, [addYourself]);
|
||||
|
||||
const checkUserExist = async () => {
|
||||
const user = Parse.User.current();
|
||||
try {
|
||||
const query = new Parse.Query("contracts_Contactbook");
|
||||
query.equalTo("CreatedBy", user);
|
||||
query.notEqualTo("IsDeleted", true);
|
||||
query.equalTo("Email", user.getEmail());
|
||||
const res = await query.first();
|
||||
// console.log(res);
|
||||
if (!res) {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/isuserincontactbook`;
|
||||
const token = props?.jwttoken
|
||||
? { jwttoken: props?.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const axiosRes = await axios.post(url, {}, { headers });
|
||||
const contactRes = axiosRes?.data?.result || {};
|
||||
if (!contactRes?.objectId) {
|
||||
setIsUserExist(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
console.log("err ", err);
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
if (phone) {
|
||||
contactQuery.set("Phone", phone);
|
||||
}
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const 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));
|
||||
props.details(parseData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
if (!emailRegex.test(email)) {
|
||||
alert("Please enter a valid email address.");
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
const user = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
const userId = user?.objectId || "";
|
||||
const tenantDetails = await getTenantDetails(userId, props.jwttoken);
|
||||
const tenantId = tenantDetails?.objectId || "";
|
||||
if (tenantId) {
|
||||
try {
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/savecontact`;
|
||||
const token = props?.jwttoken
|
||||
? { jwttoken: props?.jwttoken }
|
||||
: { "X-Parse-Session-Token": localStorage.getItem("accesstoken") };
|
||||
const data = { name, email, phone, tenantId };
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
...token
|
||||
};
|
||||
const axiosRes = await axios.post(url, data, { headers });
|
||||
const contactRes = axiosRes?.data?.result || {};
|
||||
if (contactRes?.objectId) {
|
||||
props.details(contactRes);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log("Err", err);
|
||||
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 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(parseData);
|
||||
if (err?.response?.data?.error?.includes("already exists")) {
|
||||
alert(t("add-signer-alert"));
|
||||
} else {
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
setIsLoader(false);
|
||||
// Reset the form fields
|
||||
setAddYourself(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log("err", err);
|
||||
setIsLoader(false);
|
||||
alert(t("something-went-wrong-mssg"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -243,6 +197,7 @@ const AddContact = (props) => {
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
disabled={addYourself}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
placeholder={t("phone-optional")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ const Alert = ({ children, type, className }) => {
|
||||
: "z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm"
|
||||
} `}
|
||||
>
|
||||
<div className={`op-alert ${textcolor} flex items-center`}>
|
||||
<span>{children}</span>
|
||||
<div className={`op-alert ${textcolor} flex justify-center`}>
|
||||
<span className="px-1">{children}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ModalUi from "./ModalUi";
|
||||
import {
|
||||
getSignedUrl,
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
sanitizeFileName
|
||||
} from "../constant/Utils";
|
||||
import Loader from "./Loader";
|
||||
import JSZip from "jszip";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
function DownloadPdfZip(props) {
|
||||
const { t } = useTranslation();
|
||||
const [selectType, setSelectType] = useState(1);
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const downloadType = [
|
||||
{ id: 1, label: t("download-pdf") },
|
||||
{ id: 2, label: t("pdf-certificate") }
|
||||
];
|
||||
|
||||
const handleDownload = async () => {
|
||||
if (selectType === 1) {
|
||||
await handleDownloadPdf(props.pdfDetails, setIsDownloading);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
} else if (selectType === 2) {
|
||||
setIsDownloading("pdf");
|
||||
const zip = new JSZip();
|
||||
const pdfDetails = props.pdfDetails;
|
||||
const pdfName = pdfDetails?.[0]?.Name || "Document";
|
||||
const pdfUrl = pdfDetails?.[0]?.SignedUrl || "";
|
||||
|
||||
try {
|
||||
// Fetch the first PDF (Signed Document)
|
||||
const docId = pdfDetails?.[0]?.objectId || "";
|
||||
const fileAdapterId = pdfDetails?.[0]?.FileAdapterId
|
||||
? pdfDetails?.[0]?.FileAdapterId
|
||||
: "";
|
||||
console.log("pdfDetails?.[0] ", pdfDetails?.[0]);
|
||||
const signedUrl = await getSignedUrl(pdfUrl, docId, fileAdapterId);
|
||||
console.log("signedUrl ", signedUrl);
|
||||
const pdf1Response = await fetch(signedUrl);
|
||||
if (!pdf1Response.ok) {
|
||||
throw new Error(`Failed to fetch PDF: ${signedUrl}`);
|
||||
}
|
||||
const pdf1Blob = await pdf1Response.blob();
|
||||
const isZip = true;
|
||||
// Fetch the Certificate (or generate its URL dynamically)
|
||||
const certificateUrl = await handleDownloadCertificate(
|
||||
pdfDetails,
|
||||
setIsDownloading,
|
||||
isZip
|
||||
);
|
||||
const pdf2Response = await fetch(certificateUrl);
|
||||
if (!pdf2Response.ok) {
|
||||
throw new Error(`Failed to fetch certificate PDF: ${certificateUrl}`);
|
||||
}
|
||||
const pdf2Blob = await pdf2Response.blob();
|
||||
// Add files to ZIP
|
||||
zip.file(
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`,
|
||||
pdf1Blob
|
||||
);
|
||||
zip.file("Certificate_signed_by_OpenSign™.pdf", pdf2Blob);
|
||||
|
||||
// Generate the ZIP and trigger download
|
||||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||||
saveAs(
|
||||
zipBlob,
|
||||
`${sanitizeFileName(pdfName)}_signed_by_OpenSign™.zip`
|
||||
);
|
||||
setSelectType(1);
|
||||
props.setIsDownloadModal(false);
|
||||
setIsDownloading("");
|
||||
} catch (error) {
|
||||
console.error("Error creating ZIP file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isDownloadModal}
|
||||
title={t("download-files")}
|
||||
handleClose={() => props.setIsDownloadModal(false)}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
{downloadType.map((data, ind) => {
|
||||
return (
|
||||
<label
|
||||
key={ind}
|
||||
className="flex items-center gap-1 mb-2 cursor-pointer"
|
||||
>
|
||||
<input
|
||||
className="mr-[8px] op-radio op-radio-xs"
|
||||
type="radio"
|
||||
value={data.id}
|
||||
onChange={() => setSelectType(data.id)}
|
||||
checked={selectType === data.id}
|
||||
/>
|
||||
{data.label}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
onClick={() => handleDownload()}
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{t("download")}
|
||||
</button>
|
||||
</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"
|
||||
? t("generating-certificate")
|
||||
: t("pdf-download")
|
||||
}
|
||||
handleClose={() => setIsDownloading("")}
|
||||
>
|
||||
<div className="p-3 md:p-5 text-[13px] md:text-base text-center text-base-content">
|
||||
{isDownloading === "certificate"}{" "}
|
||||
<p>{t("generate-certificate-alert")}</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DownloadPdfZip;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,11 +19,16 @@ const LinkUserModal = (props) => {
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
signersData={props?.signersData}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
<div className="op-divider text-base-content mx-[25%] my-1">
|
||||
{t("or")}
|
||||
</div>
|
||||
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
|
||||
<AddContact
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
jwttoken={props?.jwttoken}
|
||||
/>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -21,9 +21,11 @@ const ModalUi = ({
|
||||
>
|
||||
{showHeader && (
|
||||
<>
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
{title}
|
||||
</h3>
|
||||
{title && (
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
{title}
|
||||
</h3>
|
||||
)}
|
||||
{showClose && (
|
||||
<button
|
||||
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2"
|
||||
|
||||
@@ -2,27 +2,65 @@ import React, { useState } from "react";
|
||||
import "../styles/signature.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
function CustomModal({
|
||||
show,
|
||||
headMsg,
|
||||
bodyMssg,
|
||||
footerMessage,
|
||||
declineDoc,
|
||||
setIsDecline
|
||||
}) {
|
||||
function CustomModal(props) {
|
||||
const { t } = useTranslation();
|
||||
const [reason, setReason] = useState("");
|
||||
const [isExtendExpiry, setIsExtendExpiry] = useState(false);
|
||||
const [expiryDate, setExpiryDate] = useState("");
|
||||
const localuser = localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
|
||||
const currentUser = JSON.parse(localuser);
|
||||
const isCreator = props?.doc
|
||||
? props?.doc?.CreatedBy?.objectId === currentUser?.objectId &&
|
||||
localStorage.getItem("_user_role") !== "Guest"
|
||||
: false;
|
||||
const handleExtendBtn = () => setIsExtendExpiry(!isExtendExpiry);
|
||||
|
||||
const handleUpdateExpiry = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (expiryDate) {
|
||||
props.handleExpiry && props.handleExpiry(expiryDate);
|
||||
} else {
|
||||
alert(t("expiry-date-error"));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
show && (
|
||||
props.show && (
|
||||
<dialog className="op-modal op-modal-open absolute z-[448]">
|
||||
<div className="w-[95%] md:w-[60%] lg:w-[40%] op-modal-box p-0 overflow-y-auto hide-scrollbar text-sm">
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
{headMsg && headMsg}
|
||||
{props?.headMsg && props?.headMsg}
|
||||
</h3>
|
||||
<div className="p-[10px] px-[20px]">
|
||||
<p className="text-[15px]">{bodyMssg && bodyMssg}</p>
|
||||
</div>
|
||||
{footerMessage && (
|
||||
{!isExtendExpiry && (
|
||||
<div className="p-[10px] px-[20px]">
|
||||
<p className="text-[15px]">{props.bodyMssg && props.bodyMssg}</p>
|
||||
</div>
|
||||
)}
|
||||
{!isExtendExpiry && (
|
||||
<div className="flex flex-row items-center">
|
||||
{isCreator && (
|
||||
<button
|
||||
className="op-btn op-btn-primary px-6 ml-[20px] mb-3 mt-1"
|
||||
onClick={() => handleExtendBtn()}
|
||||
>
|
||||
Extend
|
||||
</button>
|
||||
)}
|
||||
{props.isDownloadBtn && (
|
||||
<button
|
||||
className="op-btn op-btn-primary ml-[10px] mb-3 mt-1"
|
||||
onClick={() => props.handleDownloadBtn()}
|
||||
>
|
||||
Download
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{props.footerMessage && (
|
||||
<>
|
||||
<div className="mx-3">
|
||||
<textarea
|
||||
@@ -33,14 +71,13 @@ function CustomModal({
|
||||
onChange={(e) => setReason(e.target.value)}
|
||||
></textarea>
|
||||
</div>
|
||||
{/* <div className="h-[1px] bg-[#9f9f9f] w-full"></div> */}
|
||||
<div className="m-[15px]">
|
||||
<button
|
||||
className="op-btn op-btn-primary mr-2 px-6"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
props.declineDoc(reason);
|
||||
setReason("");
|
||||
declineDoc(reason);
|
||||
}}
|
||||
>
|
||||
{t("yes")}
|
||||
@@ -50,7 +87,7 @@ function CustomModal({
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => {
|
||||
setReason("");
|
||||
setIsDecline({ isDeclined: false });
|
||||
props.setIsDecline({ isDeclined: false });
|
||||
}}
|
||||
>
|
||||
{t("close")}
|
||||
@@ -58,6 +95,34 @@ function CustomModal({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{isExtendExpiry && (
|
||||
<form className="mx-3 mb-3" onSubmit={handleUpdateExpiry}>
|
||||
<label className="ml-2 mt-2">
|
||||
{t("expiry-date")} {"(dd-mm-yyyy)"}
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
className="rounded-full bg-base-300 w-full px-4 py-2 text-black border-2 hover:border-spacing-2"
|
||||
defaultValue={props?.doc?.ExpiryDate?.iso?.split("T")?.[0]}
|
||||
onChange={(e) => setExpiryDate(e.target.value)}
|
||||
/>
|
||||
<div className="flex flex-row items-center mt-2">
|
||||
<button type="submit" className="op-btn op-btn-primary mr-2">
|
||||
{t("update")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => {
|
||||
setExpiryDate("");
|
||||
setIsExtendExpiry(false);
|
||||
}}
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</dialog>
|
||||
)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const QuotaCard = ({ isPaidInfo, handlClose, emailResetDate }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
return isPaidInfo ? (
|
||||
<>
|
||||
<div
|
||||
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40"
|
||||
onClick={() => handlClose && handlClose()}
|
||||
>
|
||||
✕
|
||||
</div>
|
||||
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
|
||||
<div className="op-card-body">
|
||||
<h2 className="op-card-title">{t("quota-mail-info-head")}</h2>
|
||||
<p>{t("quota-mail-info")}</p>
|
||||
{emailResetDate && (
|
||||
<p>
|
||||
{t("quota-mail-reset")} {emailResetDate}
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
<Trans i18nKey={"quota-mail-tip"}>
|
||||
Tip: You can still sign <strong>unlimited documents</strong> by
|
||||
manually sharing the signing request links.
|
||||
</Trans>
|
||||
</p>
|
||||
<div className="op-card-actions justify-end">
|
||||
<button
|
||||
onClick={() => navigate("/subscription")}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
|
||||
<div className="op-card-body">
|
||||
<p>{t("quota-mail")}</p>
|
||||
{emailResetDate && (
|
||||
<p>
|
||||
{t("quota-mail-reset")} {emailResetDate}
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
<Trans i18nKey={"quota-mail-tip"}>
|
||||
Tip: You can still sign <strong>unlimited documents</strong> by
|
||||
manually sharing the signing request links.
|
||||
</Trans>
|
||||
</div>
|
||||
<div className="op-card-actions justify-end">
|
||||
<button
|
||||
onClick={() => navigate("/subscription")}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuotaCard;
|
||||
@@ -5,18 +5,24 @@ import { useNavigate } from "react-router-dom";
|
||||
const SubscribeCard = ({ plan, price }) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const handlePaidRoute = () => navigate("/subscription");
|
||||
const premiumPrice = price ? "$" + price : "$9.99";
|
||||
const addonPrice = "$0.15";
|
||||
const plandName = plan ? plan.toLowerCase() : "Paid";
|
||||
return (
|
||||
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
|
||||
<div className="op-card-body">
|
||||
<h2 className="op-card-title">
|
||||
{t("upgrade-to")} {plan ? plan : "PRO"} {t("plan")}
|
||||
{t("upgrade-to")} {plan ? plan : "Paid"} {t("plan")}
|
||||
</h2>
|
||||
<p>
|
||||
${price ? price : "29.99"}/month: {t("subscribe-card-plan")}
|
||||
{plandName?.includes("team")
|
||||
? t("subscribe-card-teamplan")
|
||||
: t("subscribe-card-plan", { premiumPrice, addonPrice })}
|
||||
</p>
|
||||
<div className="op-card-actions justify-end">
|
||||
<button
|
||||
onClick={() => navigate("/subscription")}
|
||||
onClick={() => handlePaidRoute()}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
{t("upgrade-now")}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import React from "react";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
const Tooltip = ({ id, message, url, iconColor, maxWidth }) =>
|
||||
url ? (
|
||||
<button onClick={() => openInNewTab(url)} className={"text-center"}>
|
||||
const Tooltip = ({
|
||||
id,
|
||||
message,
|
||||
url,
|
||||
iconColor,
|
||||
maxWidth,
|
||||
handleOnlickHelp
|
||||
}) =>
|
||||
url || handleOnlickHelp ? (
|
||||
<button
|
||||
onClick={() =>
|
||||
handleOnlickHelp ? handleOnlickHelp() : openInNewTab(url)
|
||||
}
|
||||
className={"text-center cursor-pointer"}
|
||||
>
|
||||
<sup>
|
||||
<i
|
||||
className="fa-light fa-question rounded-full border-[1px] py-[1.5px] px-[4px] text-[13px]"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
export default function TourContentWithBtn({ message, isChecked }) {
|
||||
export default function TourContentWithBtn({ message, isChecked, video }) {
|
||||
const { t } = useTranslation();
|
||||
const [isCheck, setIsCheck] = useState(false);
|
||||
|
||||
@@ -13,7 +13,18 @@ export default function TourContentWithBtn({ message, isChecked }) {
|
||||
return (
|
||||
<div>
|
||||
<p>{message}</p>
|
||||
<label className="flex items-center justify-center mb-0">
|
||||
{video && (
|
||||
<div className="flex items-center justify-center w-full md:h-[300px] my-[10px] border-[1.3px] border-gray-400 rounded-sm">
|
||||
<iframe
|
||||
className="w-full h-full"
|
||||
src={video}
|
||||
title="YouTube video player"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
)}
|
||||
<label className="flex items-center justify-center mt-3 mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-xs mr-1"
|
||||
|
||||
@@ -37,9 +37,9 @@ const Validate = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
const handleLoginBtn = async () => {
|
||||
try {
|
||||
Parse.User.logOut();
|
||||
await Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { combineReducers } from "redux";
|
||||
import infoReducer from "./infoReducer";
|
||||
import ShowTenant from "./ShowTenant";
|
||||
import TourStepsReducer from "./TourStepsReducer";
|
||||
import showHeader from "./showHeader";
|
||||
|
||||
export default combineReducers({
|
||||
appInfo: infoReducer,
|
||||
TourSteps: TourStepsReducer,
|
||||
ShowTenant,
|
||||
showHeader
|
||||
});
|
||||
@@ -11,28 +11,37 @@ import { isPublicStaging } from "../constant/const";
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.mjs`;
|
||||
|
||||
// Wrapper component to manage props
|
||||
const PublicScriptFileWrapper = ({ initialProps }) => {
|
||||
const [props, setProps] = useState(initialProps);
|
||||
const PublicScriptFileWrapper = () => {
|
||||
const [props, setProps] = useState("");
|
||||
|
||||
// Make the setProps function globally accessible
|
||||
useEffect(() => {
|
||||
// Assigns the function 'setProps' to the global 'window' object as 'updatePublicTemplateProps'.
|
||||
// This allows 'setProps' to be accessed and called globally across different scripts or components,
|
||||
// enabling dynamic updates to the public template's properties.
|
||||
window.updatePublicTemplateProps = setProps;
|
||||
|
||||
const publicScript = document.getElementById("opensign-script");
|
||||
// condition for using plan js load opensign-script then get templateId
|
||||
if (publicScript) {
|
||||
// Get the custom templateId attribute
|
||||
const templateId = publicScript.getAttribute("templateId");
|
||||
if (templateId) {
|
||||
setProps({ templateId: templateId });
|
||||
} else {
|
||||
const error = "error: TemplateId is missing";
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
window.updatePublicTemplateProps = undefined;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return <PdfRequestFiles {...props} />;
|
||||
};
|
||||
|
||||
const appId = isPublicStaging ? "opensign" : "legadranaxn";
|
||||
//"opensign"
|
||||
|
||||
const serverUrl = isPublicStaging
|
||||
? "https://staging-app.opensignlabs.com/api/app"
|
||||
: "https://app.opensignlabs.com/api/app";
|
||||
// "https://staging-app.opensignlabs.com/api/app"
|
||||
|
||||
localStorage.setItem("baseUrl", `${serverUrl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
@@ -56,9 +65,9 @@ document.head.appendChild(tailwindCssLink);
|
||||
// Create a root and render the wrapper component with initial props
|
||||
const root = ReactDOM.createRoot(document.getElementById("script-component"));
|
||||
root.render(
|
||||
<div>
|
||||
<div data-theme="opensigncss">
|
||||
<Provider store={store}>
|
||||
<PublicScriptFileWrapper initialProps={{ text: "templateId" }} />
|
||||
<PublicScriptFileWrapper />
|
||||
</Provider>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,6 +2,7 @@ import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import en from "./locales/en/translation.json";
|
||||
import es from "./locales/es/translation.json";
|
||||
import fr from "./locales/fr/translation.json";
|
||||
|
||||
i18n
|
||||
@@ -12,6 +13,9 @@ i18n
|
||||
en: {
|
||||
translation: en
|
||||
},
|
||||
es: {
|
||||
translation: es
|
||||
},
|
||||
fr: {
|
||||
translation: fr
|
||||
}
|
||||
@@ -27,7 +31,7 @@ i18n
|
||||
interpolation: {
|
||||
escapeValue: false // React already does escaping
|
||||
},
|
||||
whitelist: ["en", "fr"] // List of allowed languages
|
||||
whitelist: ["en", "es", "fr"] // List of allowed languages
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"header-news": "New feature: Teams plan users can now integrate their own AWS S3 buckets for file storage",
|
||||
"header-news-btn": "Setup Now",
|
||||
"create-account": "Create Account",
|
||||
"login": "Login",
|
||||
"language": "Language",
|
||||
"name": "Name",
|
||||
"phone": "Phone",
|
||||
"phone-optional": "optional",
|
||||
"email": "Email",
|
||||
"company": "Company",
|
||||
"job-title": "Job title",
|
||||
@@ -38,7 +41,8 @@
|
||||
"upgrade-now": "Upgrade now",
|
||||
"upgrade-to": "Upgrade to",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-plan": "Sign 100 docs using API. Only $0.15/doc after that.",
|
||||
"subscribe-card-teamplan": "Unlock the full power of collaboration! Create unlimited organizations, teams, and hierarchies. Share templates seamlessly across teams and assign custom user roles. Elevate your workflow today!",
|
||||
"subscribe-card-plan": "Unlock premium features starting at just {{premiumPrice}}/month. Enjoy enhanced performance and only {{addonPrice}} per additional credit after your included premium credits.",
|
||||
"user-name-limit-char": "To have a username less than 8 character please subscribe",
|
||||
"tour-content": "Don't show this again",
|
||||
"pro": "PRO",
|
||||
@@ -90,7 +94,9 @@
|
||||
"My Signature": "My Signature",
|
||||
"API Token": "API Token",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferences",
|
||||
"Teams": "Teams",
|
||||
"Team-update-successfully": "Team Updated successfully.",
|
||||
"Users": "Users"
|
||||
}
|
||||
},
|
||||
@@ -125,7 +131,11 @@
|
||||
"Share with team": "Share with team",
|
||||
"Share": "Share",
|
||||
"View": "View",
|
||||
"option": "Option"
|
||||
"option": "Option",
|
||||
"Embed": "Embed",
|
||||
"Copy TemplateId": "Copy TemplateId",
|
||||
"Copy Public URL": "Copy Public URL",
|
||||
"extend-expiry-date": "Extend expiry date"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Sr.No",
|
||||
@@ -145,7 +155,9 @@
|
||||
"Name": "Name",
|
||||
"Status": "Status",
|
||||
"created-date": "Created Date",
|
||||
"Type": "Type"
|
||||
"Type": "Type",
|
||||
"Logs": "Logs",
|
||||
"Expiry-date": "Expiry Date"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "These are documents you have started but have not finalized for sending.",
|
||||
@@ -212,8 +224,8 @@
|
||||
"initials": "Initials",
|
||||
"API": "API",
|
||||
"api-token": "API Token",
|
||||
"regenerate-token": "Regenerate Token",
|
||||
"generate-token": "Generate Token",
|
||||
"regenerate-token": "Regenerate Live Token",
|
||||
"generate-token": "Generate Live Token",
|
||||
"view-docs": "View Docs",
|
||||
"generate-token-alert": "Are you sure you want to regenerate token it will expire old token?",
|
||||
"yes": "Yes",
|
||||
@@ -256,9 +268,11 @@
|
||||
"make-template-public-alert": "Are you sure you want to make this template public?",
|
||||
"make-template-private-alert": "Are you sure you want to make this template private? This will remove it from your public profile.",
|
||||
"public-role": "Public role",
|
||||
"public-url": "Public URL",
|
||||
"public-url-copy-mssg": " Here’s your public URL: Copy it or share it with the signer, and you will be able to see all your publicly set templates.",
|
||||
"add-public-url-alert": "Please add your public URL, and you will be able to make a public template.",
|
||||
"public-url": "Public profile",
|
||||
"embed-template": "Embed Template",
|
||||
"public-url-copy": "Here’s your public URL: ",
|
||||
"public-url-copy-mssg": "Copy it or share it with the signer, and you will be able to see all your publicly set templates.",
|
||||
"add-public-url-alert": "Please complete your OpenSign public profile setup. Once done, you’ll be able to make templates public. This process only needs to be completed once.",
|
||||
"share-with-alert": "You cannot share a template if any roles already have contacts assigned. Please remove all contact assignments from the roles before sharing the template.",
|
||||
"share-with": "Share with",
|
||||
"team-not-found": "Team not found",
|
||||
@@ -281,7 +295,7 @@
|
||||
"subject": "Subject",
|
||||
"body": "Body",
|
||||
"add-contact": "Add Contact",
|
||||
"add-signer-alert": "Contact already exist!",
|
||||
"add-signer-alert": "Contact already exist! Please select it from ‘Signers’ dropdown",
|
||||
"record-delete-alert": "Record deleted successfully!",
|
||||
"record-revoke-alert": "Record revoked successfully!",
|
||||
"mail-sent-alert": "Mail sent successfully.",
|
||||
@@ -308,7 +322,7 @@
|
||||
"contact-details": "Contact Details",
|
||||
"verify-email": "Please verify your email !",
|
||||
"send-otp": " Send OTP",
|
||||
"otp-placeholder": "Enter OTP received over email",
|
||||
"otp-placeholder": "Enter verification code received over email",
|
||||
"loading-doc": "Loading Document..",
|
||||
"widgets-name": {
|
||||
"signature": "signature",
|
||||
@@ -358,7 +372,7 @@
|
||||
"initial-teb": "Initials",
|
||||
"signature-tab": "Signature",
|
||||
"your-signature": "Your signature",
|
||||
"copy-to-all-pages": "Copy to all pages",
|
||||
"copy-title": "Copy widget to",
|
||||
"contact-delete-alert": "Are you sure you want to delete this contact?",
|
||||
"reset-password-alert-1": "Reset password link has been sent to your email id",
|
||||
"reset-password-alert-2": "Please setup email adapter ",
|
||||
@@ -368,7 +382,8 @@
|
||||
"copy-type": {
|
||||
"All pages": "All pages",
|
||||
"All pages but last": "All pages but last",
|
||||
"All pages but first": "All pages but first"
|
||||
"All pages but first": "All pages but first",
|
||||
"Next to current widget": "Next to current widget"
|
||||
},
|
||||
"options": "Options",
|
||||
"minimun-check": "Minimun check",
|
||||
@@ -434,7 +449,8 @@
|
||||
"cutomize-email": "Cutomize Email",
|
||||
"upgrade-to-customize-email": "Upgrade to customize Email",
|
||||
"sign-url": "Sign url",
|
||||
"Mails Sent": "Mails Sent",
|
||||
"mails-sent": "Mails Sent",
|
||||
"mail-not-delivered": "Mail not delivered",
|
||||
"document-alert": "Document Alert",
|
||||
"owner-subscription-expired": "Owner's subscription has expired.",
|
||||
"subscription-expired": "Subscription Expired",
|
||||
@@ -445,7 +461,7 @@
|
||||
"decline-alert-3": "You can not sign this document as it has been declined/revoked.",
|
||||
"sign here": "Sign here",
|
||||
"guest-email-alert": "verification code is sent to your email",
|
||||
"get-otp-alert": "you will get a OTP via Email",
|
||||
"get-otp-alert": "you will get a verification code via Email",
|
||||
"user-already-exist": "User already exist.",
|
||||
"server-error": "Internal server error !",
|
||||
"fill-required-details!": "Please fill required details!",
|
||||
@@ -468,7 +484,7 @@
|
||||
"role-not-found": "Role not found.",
|
||||
"do-not-access": "You don't have access, please contact the admin.",
|
||||
"add-admin": "Add admin",
|
||||
"opensign-setup": "Opensign Setup",
|
||||
"opensign-setup": "OpenSign Setup",
|
||||
"join-discord": "Join our discord server",
|
||||
"admin-already-exist": "Admin already exist.",
|
||||
"password-update-alert-1": "Password updated successfully.",
|
||||
@@ -499,7 +515,7 @@
|
||||
"error": "Error",
|
||||
"invalid-document": "Error: invalid document!",
|
||||
"document-not-found": "Document not Found!",
|
||||
"document-signed-alert-1": "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers.",
|
||||
"document-signed-alert-1": "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document & completion certificate will be sent to the owner & all signers over email once it is signed by all signers.",
|
||||
"encrypted-pdf-not-support": "Currently encrypted pdf files are not supported.",
|
||||
"document-signed-alert-4": " Congratulations! 🎉 This document has been successfully signed by all participants!",
|
||||
"document-signed-alert-5": "This document has been signed by all Signers.",
|
||||
@@ -524,7 +540,7 @@
|
||||
"filed-required-correctly": "Please fill required details correctly.",
|
||||
"admin-created": "Admin created",
|
||||
"invalid-masterkey": "Invalid masterkey",
|
||||
"opensign-Setup": "Opensign Setup",
|
||||
"opensign-Setup": "OpenSign Setup",
|
||||
"master-key": " Master key",
|
||||
"profile-update-alert": "Profile updated successfully.",
|
||||
"date": "Date",
|
||||
@@ -550,7 +566,7 @@
|
||||
"home-layout-1": "You have logged in successfully! Let's take a look.",
|
||||
"home-layout-2": "To upload documents for self-signing or to request others’ signatures, simply select the respective buttons.",
|
||||
"home-layout-3": "You are ready to start using OpenSign! If you need support feel free to contact us.",
|
||||
"generate-token": "Upgrade now to generate API token",
|
||||
"generate-token": "Upgrade now to generate production API token.",
|
||||
"opensign-drive-1": "Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.",
|
||||
"opensign-drive-2": "Click the add button to create a new folder or document.",
|
||||
"opensign-drive-3": "Sort your documents by Date or Name using this menu.",
|
||||
@@ -565,9 +581,10 @@
|
||||
"pdf-request-file-5": "You can click 'Auto Sign All' to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .",
|
||||
"placeholder-sign-1": "Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.",
|
||||
"placeholder-sign-2": "Clicking 'Add recipients' button will allow you to add more signers.",
|
||||
"placeholder-sign-3": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-4": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-5": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"placeholder-sign-3": "Click this button to add more recipients/signers to the document.",
|
||||
"placeholder-sign-4": "Drag or click on a field to add it to the document.",
|
||||
"placeholder-sign-5": "The PDF content area already displays the template's existing placeholders. For your convenience, these placeholders will match the color of the recipient's name, making them easily identifiable.",
|
||||
"placeholder-sign-6": "Clicking 'Send' will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.",
|
||||
"report-1": "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. ",
|
||||
"redirect": "Click the 'Use' button to create a new document from an existing template.",
|
||||
"bulksend": "To quickly create and send multiple documents using an existing template, click the 'Quick Send' button.",
|
||||
@@ -584,7 +601,8 @@
|
||||
"Out for signatures": "Clicking on this card will take you to a list of documents awaiting signature.",
|
||||
"Recent signature requests": "This is a list of documents that are waiting for your signature.",
|
||||
"Recently sent for signatures": "This is a list of documents you've sent to other parties for signature.",
|
||||
"Drafts": "This are documents you have started but have not finalized for sending."
|
||||
"Drafts": "This are documents you have started but have not finalized for sending.",
|
||||
"public-template": "This video demonstrates how to set up your personalized public profile, such as ‘https://opensign.me/your-username’. You’ll also learn how to customize your tagline and make your templates available for public signing."
|
||||
},
|
||||
"enter-email-plaholder": "Add an email address and hit enter",
|
||||
"success-email-alert": "Email sent successfully!",
|
||||
@@ -592,5 +610,104 @@
|
||||
"expired-on-mssg": "This document expired on {{expiredDate}} and is no longer available to sign.",
|
||||
"signature-validate-alert": "Please confirm that you have selected at least {{minRequiredCount}} checkboxes.",
|
||||
"signature-validate-alert-2": "Ensure this field is accurately filled and meets all requirements.",
|
||||
"user-name-exist": "user name already exist"
|
||||
"remaing-users": "Remaining users",
|
||||
"Role": "Role",
|
||||
"additional-users": "Please purchase add-on users.",
|
||||
"Quantity-of-users": "Quantity of users",
|
||||
"Price": "Price",
|
||||
"Total-price-for-next-time": "Total price for next time",
|
||||
"Proceed": "Proceed",
|
||||
"Select": "Select",
|
||||
"Add-seats": "Add Seats",
|
||||
"user-name-exist": "user name already exist",
|
||||
"format": "format",
|
||||
"select-date-format": "Select a date format",
|
||||
"quantity-of-credits": "Quantity of premium credits",
|
||||
"remaining-credits": "Premium credits available:",
|
||||
"remaining-credits-help": "Use premium credits for API document signing, bulk sending, or embedding OpenSign integration on your website. You have {{allowedcredits}} included credits and {{addoncredits}} additional purchased credits remaining.",
|
||||
"additional-credits": "Please purchase premium credits",
|
||||
"quota-err-quicksend": "Quota Reached, You don't have enough credits.",
|
||||
"buy-credits": "Buy Premium Credits",
|
||||
"rotate-right": "Rotate right",
|
||||
"rotate-left": "Rotate left",
|
||||
"rotate-alert-mssg": "All widgets on this page will be lost. Are you sure you want to proceed?",
|
||||
"templateid": "Template-Id",
|
||||
"bulk-send-subcription-alert": "Please upgrade to Professional or Team plan to use bulk send.",
|
||||
"generate-test-token": "Generate Test Token",
|
||||
"regenerate-test-token": "Regenerate Test Token",
|
||||
"help-test-token": "This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once you’ve completed your testing, you can upgrade to one of our paid plans to generate a production token.",
|
||||
"help-api-token": "This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.",
|
||||
"reason": "Reason",
|
||||
"decline-by": "Declined/revoked by",
|
||||
"document-declined": "Document declined",
|
||||
"public-template-mssg-1": "To integrate OpenSign into your React or Next.js project, simply run the following command:",
|
||||
"public-template-mssg-2": "Ensure you have npm or yarn set up in your project. If you’re using Yarn, you can replace npm install with yarn add @opensign/react.",
|
||||
"public-template-mssg-3": "Need more details or examples?",
|
||||
"public-template-mssg-4": "Visit the",
|
||||
"public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.",
|
||||
"public-template-mssg-6": "Before you can use this code snippet, you must make this template public.",
|
||||
"copy-code": "COPY",
|
||||
"copied-code": "COPIED",
|
||||
"Installation": "Installation",
|
||||
"Usage": "Usage",
|
||||
"insufficient-credits": "Insufficient Signing Credits",
|
||||
"insufficient-credits-mssg": "The owner of this document currently lacks the necessary OpenSign credits for you to sign. Please reach out to the owner if you require further details.",
|
||||
"angular-npm-mssg-1": "To integrate OpenSign into your Angular project, simply run the following command:",
|
||||
"quota-mail-info-head": "Monthly request signatures email limit",
|
||||
"quota-mail-info": "You can send upto 15 signature request emails every month. Upgrade now to send unlimited signing requests directly.",
|
||||
"quota-mail-reset": "Your signature request email credits will be reset on",
|
||||
"quota-mail": "You've reached your limit of 15 signature request emails for this month. Upgrade now to continue sending emails directly.",
|
||||
"quota-mail-tip": "Tip: You can still sign <1>unlimited documents</1> by manually sharing the signing request links.",
|
||||
"quota-mail-head": "Quota Reached",
|
||||
"unauthorized-modal": "You don't have permission to perform this action, please contact {{adminEmail}}.",
|
||||
"sent-this-month": "Sent this month",
|
||||
"available-seats": "Available seats",
|
||||
"buy-users": "Buy more users",
|
||||
"isenable-otp": "Enable OTP verification",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Would you like to enable the verification process using a one-time password (OTP)?",
|
||||
"p2": "Selecting this option will enable OTP verification. Users will receive a verification code via email, which they must enter to sign the document.",
|
||||
"p3": "Selecting this option will disable OTP verification, allowing users to sign the document directly without additional steps.",
|
||||
"p4": "Please choose the option that best suits your document signing requirements."
|
||||
},
|
||||
"advanced-options": "Advanced options",
|
||||
"hide-advanced-options": "Hide Advanced options",
|
||||
"download-files": "Download files",
|
||||
"download-pdf": "Download Pdf",
|
||||
"pdf-certificate": "Download Pdf + Certificate",
|
||||
"document-logs": "Document logs",
|
||||
"server-down": "Unable to connect to the OpenSign server. If you are self-hosting OpenSign, please ensure that all the steps in the documentation have been followed correctly. If you're running OpenSign locally, you might be accessing it through an incorrect port number.",
|
||||
"admin-exists": "Admin already exists. Please login to the application using admin credentials in order to manage users.",
|
||||
"enable-tour": "Enable Tour",
|
||||
"istourenabled-help": {
|
||||
"p1": "This will enable a guided tour for signers, providing instructions during the signing process.",
|
||||
"p2": "This will disable the guided tour, ensuring a faster, uninterrupted signing experience.",
|
||||
"p3": "Tip: If your signers are familiar with OpenSign and frequently use it, you may want to disable the guided tour for a more seamless experience. Choose the option that best fits your document signing needs."
|
||||
},
|
||||
"form-title-1": "Document Flow Setting",
|
||||
"form-title-2": "Security Settings",
|
||||
"public-tour-message": "The template needs to be public before you can generate a shareable link.",
|
||||
"add-user-template": "You need to add a role before you can add fields for it.",
|
||||
"pdf-uncompatible": "This pdf is not compatible with opensign, please contact <support@opensignlabs.com>",
|
||||
"text-field-tour": "Fields of type 'Text' must be filled in advance before the document is sent. If you need the signers to provide input, use the 'Text Input' field instead.",
|
||||
"attach-signer-tour": "You need to attach a Signer to every role. You can do that by clicking this icon. Once you select a Signer it will be attached to all the fields associated with that role which appear in the same colour.",
|
||||
"allowed-signature-types": "Allowed signature types",
|
||||
"at-least-one-signature-type": "At least one signature type should be enabled.",
|
||||
"expect-default-one-signature-type": "Please enable one additional signature type apart from the default.",
|
||||
"add-signer-note": "Note - This contact will be added to your contact list",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "This signature preference controls which signing options are available to your signers. Only the signature types you select will appear in the signature widget when a document is signed. For example, if you choose only the 'Draw' option in preferences, your signer will see only the 'Draw' option in the signature widget, while the other three options will not be available.",
|
||||
"l1": "Create a handwritten signature directly on the screen using your finger or a stylus.",
|
||||
"l2": "Enter your name and choose from various font styles to create a signature.",
|
||||
"l3": "Upload an existing digital version of your signature.",
|
||||
"l4": "Use a pre-set signature style saved under 'My Signature' in your settings."
|
||||
},
|
||||
"notify-on-signatures": "Notify on signatures",
|
||||
"notify-on-signatures-help": {
|
||||
"p1": "The document creator will receive an email notification whenever a signer signs the document.",
|
||||
"note": "Note: Notification email will not be sent for the last signer, as the document creator will receive a completion email instead."
|
||||
},
|
||||
"expiry-date":"Expiry date",
|
||||
"expiry-date-updated": "The expiry date has been successfully extended to {{newexpirydate}}",
|
||||
"expiry-date-error": "Please provide a new expiry date that is later than the current one"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,713 @@
|
||||
{
|
||||
"header-news": "Nueva característica: los usuarios del plan Teams ahora pueden integrar sus propios depósitos de AWS S3 para el almacenamiento de archivos",
|
||||
"header-news-btn": "Configurar ahora",
|
||||
"create-account": "Crear cuenta",
|
||||
"login": "Iniciar sesión",
|
||||
"language": "Idioma",
|
||||
"name": "Nombre",
|
||||
"phone": "Teléfono",
|
||||
"phone-optional": "opcional",
|
||||
"email": "Correo",
|
||||
"company": "Compañía",
|
||||
"job-title": "Título del puesto",
|
||||
"profile": "Perfil",
|
||||
"log-out": "Cerrar sesión",
|
||||
"is-email-verified": "El correo está verificado",
|
||||
"public-profile": "Perfil público",
|
||||
"tagline": "Eslogan ",
|
||||
"has-requested-you-to-sign": "ha solicitado que firmes",
|
||||
"has-requested-you-to-review-and-sign": " ha solicitado que revises y firmes",
|
||||
"input-required": "Por favor, rellena este campo.",
|
||||
"sort-order": {
|
||||
"Ascending": "Ascendente",
|
||||
"Descending": "Descendente",
|
||||
"Name": "Nombre",
|
||||
"Date": "Fecha"
|
||||
},
|
||||
"context-menu": {
|
||||
"Download": "Descargar",
|
||||
"Rename": "Renombrar",
|
||||
"Move": "Mover",
|
||||
"Delete": "Eliminar"
|
||||
},
|
||||
"action": "Acción",
|
||||
"add": "Agregar",
|
||||
"add-new-folder": "Agregar nueva carpeta",
|
||||
"disable-documentId": "Desactivar ID de documento",
|
||||
"edit": "Editar",
|
||||
"change-password": "Cambiar contraseña",
|
||||
"save": "Guardar",
|
||||
"cancel": "Cancelar",
|
||||
"upgrade-now": "Mejorar ahora",
|
||||
"upgrade-to": "Mejorar a",
|
||||
"plan": "Plan",
|
||||
"subscribe-card-teamplan": "¡Libera todo el poder de la colaboración! Crea organizaciones, equipos y jerarquías ilimitadas. Comparte plantillas sin problemas entre equipos y asigna funciones de usuario personalizadas. ¡Mejora tu flujo de trabajo hoy mismo!",
|
||||
"subscribe-card-plan": "Desbloquea funciones premium desde solo {{premiumPrice}}/mes. Disfruta de un rendimiento mejorado y solo {{addonPrice}} por crédito adicional después de tus créditos premium incluidos.",
|
||||
"user-name-limit-char": "Para tener un nombre de usuario menor a 8 caracteres por favor suscríbete",
|
||||
"tour-content": "No mostrar esto otra vez",
|
||||
"pro": "PRO",
|
||||
"team": "EQUIPO",
|
||||
"user": "Usuario",
|
||||
"docs": "Documentación",
|
||||
"session-expired": "Tu sesión ha expirado.",
|
||||
"public-profile-help": "Esta es tu URL pública. Cópiala o compártela con el firmante, y podrás ver todas tus plantillas públicas.",
|
||||
"tagline-help": "Este eslogan aparecerá en tu perfil público de OpenSign, por ejem. https://opensign.me/alex",
|
||||
"welcome": "¡Bienvenido de nuevo!",
|
||||
"Login-to-your-account": "Ingresa a tu cuenta",
|
||||
"password": "Contraseña",
|
||||
"forgot-password": "¿Olvidaste la contraseña?",
|
||||
"loading": "Cargando...",
|
||||
"of": "de",
|
||||
"sign-SSO": "Ingresar con SSO",
|
||||
"login-page": "Página de inicio de sesión",
|
||||
"or": "O",
|
||||
"signup-page": "Página de registro",
|
||||
"password-length": "La contraseña debe tener 8 caracteres",
|
||||
"password-case": "La contraseña debe incluir mayúsculas, minúsculas y números",
|
||||
"password-special-char": " La contraseña debe incluir caracteres especiales",
|
||||
"agreee": "Estoy de acuerdo con los",
|
||||
"term": "Términos de Servicio",
|
||||
"subscribe-to-opensign": "Suscribirse al boletín de OpenSign",
|
||||
"register": "Registrarse",
|
||||
"sidebar": {
|
||||
"Dashboard": "Panel",
|
||||
"Sign yourself": "Firma tu mismo",
|
||||
"Request signatures": "Solicitar firmas",
|
||||
"Templates": "Plantillas",
|
||||
"Templates-Children": {
|
||||
"Create template": "Crear plantilla",
|
||||
"Manage templates": "Administrar plantillas"
|
||||
},
|
||||
"OpenSign™ Drive": "OpenSign™ Drive",
|
||||
"Reports": "Reportes",
|
||||
"Reports-Children": {
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In Progress": "En progreso",
|
||||
"Completed": "Completado",
|
||||
"Drafts": "Borradores",
|
||||
"Declined": "Rechazados",
|
||||
"Expired": "Expirados",
|
||||
"Contactbook": "Agenda de contactos"
|
||||
},
|
||||
"Settings": "Ajustes",
|
||||
"Settings-Children": {
|
||||
"My Signature": "Mi firma",
|
||||
"API Token": "Token API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Preferencias",
|
||||
"Teams": "Equipos",
|
||||
"Team-update-successfully": "Equipo actualizado correctamente.",
|
||||
"Users": "Usuarios"
|
||||
}
|
||||
},
|
||||
"dashboard-card": {
|
||||
"Need your Signature": "Necesitan tu firma",
|
||||
"Out for signatures": "En espera de ser firmados"
|
||||
},
|
||||
"report-name": {
|
||||
"Draft Documents": "Documentos borradores",
|
||||
"Need your sign": "Necesitan tu firma",
|
||||
"In-progress documents": "Documentos en progreso",
|
||||
"Completed Documents": "Documentos completados",
|
||||
"Declined Documents": "Documentos rechazados",
|
||||
"Expired Documents": "Documentos expirados",
|
||||
"Recently sent for signatures": "Recientemente enviados para ser firmados",
|
||||
"Recent signature requests": "Solicitudes de firma recientes",
|
||||
"Drafts": "Borradores",
|
||||
"Contactbook": "Agenda de contactos",
|
||||
"Templates": "Plantillas",
|
||||
"Teams": "Equipos",
|
||||
"Users": "Usuarios"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "firmar",
|
||||
"Sign": "FIRMAR",
|
||||
"Resend": "Reenviar",
|
||||
"Revoke": "Revocar",
|
||||
"Delete": "Eliminar",
|
||||
"Use": "Usar",
|
||||
"Quick send": "Envío rápido",
|
||||
"Edit": "Editar",
|
||||
"Share with team": "Compartir con el equipo",
|
||||
"Share": "Compartir",
|
||||
"View": "Ver",
|
||||
"option": "Opción",
|
||||
"Embed": "Incrustar",
|
||||
"Copy TemplateId": "Copiar ID de plantilla",
|
||||
"Copy Public URL": "Copiar URL pública",
|
||||
"Expiry-date": "Date d'expiration"
|
||||
},
|
||||
"report-heading": {
|
||||
"Sr.No": "Nº",
|
||||
"Title": "Título",
|
||||
"Email": "Correo",
|
||||
"Phone": "Teléfono",
|
||||
"File": "Archivo",
|
||||
"Owner": "Propietario",
|
||||
"Signers": "Firmantes",
|
||||
"Note": "Nota",
|
||||
"Folder": "Carpeta",
|
||||
"Reason": "Razón",
|
||||
"Parent Team": "Equipo padre",
|
||||
"Active": "Activo",
|
||||
"Role": "Rol",
|
||||
"Team": "Equipo",
|
||||
"Name": "Nombre",
|
||||
"Status": "Estado",
|
||||
"created-date": "Fecha de creación",
|
||||
"Type": "Tipo",
|
||||
"Logs": "Registros",
|
||||
"Expiry-date": "Date d'expiration"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Estos son documentos que has iniciado pero no has finalizado para su envío.",
|
||||
"Need your sign": "Esta es una lista de documentos que esperan tu firma.",
|
||||
"In-progress documents": "Esta es una lista de documentos que has enviado a otras partes para su firma.",
|
||||
"Completed Documents": "Esta es una lista de documentos en los que todas las partes han firmado.",
|
||||
"Declined Documents": "Esta es una lista de documentos que han sido rechazados por uno de los firmantes.",
|
||||
"Expired Documents": "Esta es una lista de documentos que han alcanzado su fecha de caducidad.",
|
||||
"Contactbook": "Esta es una lista de contactos/firmantes añadidos por ti. Aparecerán como sugerencias cuando intentes añadir firmantes a un nuevo documento.",
|
||||
"Templates": "Esta es una lista de plantillas que están a tu disposición para crear documentos. Puedes hacer clic en el botón «usar» para crear un nuevo documento utilizando una plantilla, modifica el documento y añade firmantes en el siguiente paso."
|
||||
},
|
||||
"form-name": {
|
||||
"Sign Yourself": "Firmar",
|
||||
"Request Signatures": "Solicitar firmas",
|
||||
"New Template": "Nueva plantilla"
|
||||
},
|
||||
"file-type": "pdf, png, jpg, jpeg",
|
||||
"docx": "docx",
|
||||
"file-selected": "archivo seleccionado",
|
||||
"template-title": "Título de la plantilla",
|
||||
"document-title": "Título del documento",
|
||||
"description": "Descripción",
|
||||
"time-to-complete": "Plazo de finalización (días)",
|
||||
"send-in-order": "Enviar en orden",
|
||||
"send-in-order-help": {
|
||||
"p1": "Elige cómo quieres que se envíen las solicitudes de firma a los firmantes del documento:",
|
||||
"p2": "Seleccionando esta opción se enviará inicialmente la solicitud de firma al primer firmante. Una vez que el primer firmante complete su parte, el siguiente firmante en la secuencia recibirá la solicitud. Este proceso continúa hasta que todos los firmantes hayan firmado el documento. Este método garantiza que el documento se firme en un orden específico.",
|
||||
"p3": "Seleccionando esta opción se enviarán los enlaces para la firma a todos los firmantes simultáneamente. Cada firmante puede firmar el documento cuando le convenga, independientemente de si otros firmantes han completado sus firmas. Este método es más rápido, pero no impone ningún orden de firma entre los participantes.",
|
||||
"p4": "Selecciona la opción que mejor se adapte a las necesidades de tu tramitación de documentos."
|
||||
},
|
||||
"no": "No",
|
||||
"auto-reminder": "Recordatorio automático",
|
||||
"remind-once": " Recordar una vez cada (Días)",
|
||||
"next": "Siguiente",
|
||||
"select-folder": "Seleccionar carpeta",
|
||||
"OpenSign-drive": "OpenSign™ Drive",
|
||||
"drive-document-status": {
|
||||
"Completed": "Completado",
|
||||
"Declined": "Rechazado",
|
||||
"Draft": "Borrador",
|
||||
"Expired": "Expirado",
|
||||
"In Progress": "En progreso"
|
||||
},
|
||||
"select-folder-help": "Si no seleccionas una carpeta, tu documento firmado se guardará en la carpeta principal de OpenSign Drive.",
|
||||
"no-data": "No se encontraron datos",
|
||||
"save-here": "Guardar aquí",
|
||||
"back": "Atrás",
|
||||
"add-folder": "Añadir carpeta",
|
||||
"create-folder": "Crear carpeta",
|
||||
"parent-folder": "Carpeta superior",
|
||||
"create": "Crear",
|
||||
"signers": "Firmantes",
|
||||
"signers-help": "Empieza a escribir el nombre de un contacto para ver los firmantes sugeridos entre tus contactos guardados o añade otros nuevos. Organiza el orden de firma añadiendo firmantes en la secuencia deseada. Utiliza el botón «+» para añadir nuevos firmantes y la «X» para quitar todos. Cada firmante recibirá una solicitud por correo electrónico para que firme el documento según el orden indicado.",
|
||||
"add-signer": "Añadir firmante",
|
||||
"contact-not-found": "Contacto no encontrado",
|
||||
"add-yourself": "Añadirte a ti mismo",
|
||||
"submit": "Enviar",
|
||||
"reset": "Volver a empezar",
|
||||
"my-signature": "Mi firma",
|
||||
"signature": "Firma",
|
||||
"upload-image": " Subir imagen",
|
||||
"clear": "Limpiar",
|
||||
"upload-signature/Image": "Por favor, sube la firma/imagen",
|
||||
"initials": "Iniciales",
|
||||
"API": "API",
|
||||
"api-token": "Token API",
|
||||
"regenerate-token": "Regenerar token activo",
|
||||
"generate-token": "Generar token activo",
|
||||
"view-docs": "Ver documentación",
|
||||
"generate-token-alert": "¿En definitiva quieres regenerar el token? Esto expirará el token antiguo.",
|
||||
"yes": "Sí",
|
||||
"copied": "Copiado",
|
||||
"something-went-wrong-mssg": "Algo salió mal, por favor, intenta de nuevo más tarde.",
|
||||
"token-generated": "Token generado exitosamente.",
|
||||
"webhook": "Webhook",
|
||||
"update-webhook": "Actualizar webhook",
|
||||
"add-webhook": "Agregar webhook",
|
||||
"webhook-added": "Webhook agregado exitosamente.",
|
||||
"team-disabled": "Equipo deshabilitado",
|
||||
"Team enabled": "Equipo habilitado",
|
||||
"are-you-sure": "¿En definitiva quieres ",
|
||||
"disable": "deshabilitar",
|
||||
"enable": "habilitar",
|
||||
"this-team": " este equipo?",
|
||||
"edit-team": "Editar equipo",
|
||||
"name-of-team": "Nombre del equipo",
|
||||
"prev": "Anterior",
|
||||
"no-data-avaliable": "No hay datos disponibles",
|
||||
"add-team": "Agregar equipo",
|
||||
"page-not-found": "Página no encontrada",
|
||||
"users-from-teams": "usuarios de equipos",
|
||||
"user-status": "Estado del usuario",
|
||||
"activate": "activar",
|
||||
"deactivate": "desactivar",
|
||||
"this-user": " este usuario",
|
||||
"delete-user": "Eliminar usuario",
|
||||
"delete": "eliminar",
|
||||
"add-user": "Agregar usuario",
|
||||
"password-generateed": "La contraseña solo será generada una vez; asegúrate de copiarla.",
|
||||
"Team status": "Estado del equipo",
|
||||
"user-deactivated": "Usuario desactivado.",
|
||||
"user-activated": "Usuario activado.",
|
||||
"public": "Público",
|
||||
"download": "Descargar",
|
||||
"view": "Ver",
|
||||
"make-template-public": "Convertir la plantilla en pública",
|
||||
"make-template-private": "Convertir la plantilla en privada",
|
||||
"make-template-public-alert": "¿En definitiva quieres convertir esta plantilla en pública?",
|
||||
"make-template-private-alert": "¿En definitiva quieres convertir esta plantilla en privada? Esto lo removerá de tu perfil público.",
|
||||
"public-role": "Rol público",
|
||||
"public-url": "Perfil publico",
|
||||
"embed-template": "Insertar plantilla",
|
||||
"public-url-copy": "Aquí está tu URL pública: ",
|
||||
"public-url-copy-mssg": "Cópiala o compártela con el firmante y podrás ver todas tus plantillas públicas.",
|
||||
"add-public-url-alert": "Por favor, completa la configuración de tu perfil público OpenSign. Una vez terminado, podrás crear plantillas públicas. Este proceso solo necesita ser completado una vez.",
|
||||
"share-with-alert": "No puedes compartir una plantilla si alguno de los roles ya tiene contactos asignados. Elimina todas las asignaciones de contactos de los roles antes de compartir la plantilla.",
|
||||
"share-with": "Compartir con",
|
||||
"team-not-found": "Equipo no encontrado",
|
||||
"roles": "Roles",
|
||||
"delete-document": "Eliminar documento",
|
||||
"delete-document-alert": "¿En definitiva quieres eliminar este documento?",
|
||||
"quick-send": "Envío rápido",
|
||||
"add-new": "Agregar nuevo",
|
||||
"send": "Enviar",
|
||||
"quick-send-alert-1": "Todos los roles de este documento están actualmente vinculados a contactos. Para enviar rápidamente copias de esta plantilla a varios firmantes, por favor, asegúrate de que al menos un rol no esté vinculado a ningún contacto.",
|
||||
"quick-send-alert-2": "Por favor, asegúrate de que hay al menos un widget de firma añadido para cada destinatario.",
|
||||
"quick-send-alert-3": "Por favor, añade al menos un rol a esta plantilla para poder hacer un «envío rápido» de copias a varios firmantes.",
|
||||
"quick-send-alert-4": "El envío rápido ha alcanzado el límite.",
|
||||
"copy-link": "Copiar enlace",
|
||||
"copy": "Copiar",
|
||||
"revoke-document": "Revocar documento",
|
||||
"revoke-document-alert": "¿En definitiva quieres revocar este documento?",
|
||||
"resend-mail": "Reenviar correo",
|
||||
"resend-mail-help": "Puedes usar las siguientes variables que serán reemplazadas por sus valores reales:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"subject": "Asunto",
|
||||
"body": "Cuerpo",
|
||||
"add-contact": "Agregar contacto",
|
||||
"add-signer-alert": "¡El contacto ya existe! Selecciónelo en el menú desplegable 'Firmantes'",
|
||||
"record-delete-alert": "¡Entrada eliminada exitosamente!",
|
||||
"record-revoke-alert": "¡Entrada revocada exitosamente!",
|
||||
"mail-sent-alert": "Correo enviado exitosamente.",
|
||||
"document-sent-alert": "¡Documento enviado exitosamente!",
|
||||
"template-share-alert": "Plantilla compartida exitosamente.",
|
||||
"template-public-alert-1": "Has convertido exitosamente la plantilla en pública.",
|
||||
"template-public-alert-2": "Has convertido exitosamente la plantilla en privada.",
|
||||
"template-public-alert-3": "Tienes que seleccionar un rol para los firmantes públicos.",
|
||||
"template-public-alert-4": "Por favor, asegúrate de que has añadido al menos un widget de firma para cada firmante.",
|
||||
"template-public-alert-5": "Dado que esta plantilla tiene «enviar en orden» habilitado, el rol público debe estar posicionado arriba al inicio.",
|
||||
"template-public-alert-6": "Por favor, enlaza firmantes a todos los roles excepto al usuario de rol público.",
|
||||
"template-public-alert-7": "Por favor, asigna al menos un rol público para convertir está plantilla en pública.",
|
||||
"loader": " Esto podría llevar algún tiempo",
|
||||
"pages": "Páginas",
|
||||
"document-signed": "Documento firmado",
|
||||
"close": "Cerrar",
|
||||
"validation-alert": "Alerta de validación",
|
||||
"validate-alert-mssg": " Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"otp-verification": "Verificación OTP",
|
||||
"enter-otp": "Ingresar OTP",
|
||||
"verify": "Verificar",
|
||||
"verify-email-1": "Verificar correo",
|
||||
"resend": "Reenviar",
|
||||
"contact-details": "Detalles del contacto",
|
||||
"verify-email": "¡Por favor, verifica tu correo!",
|
||||
"send-otp": " Enviar OTP",
|
||||
"otp-placeholder": "Ingresa el código de verificación enviado por correo",
|
||||
"loading-doc": "Cargando documento...",
|
||||
"widgets-name": {
|
||||
"signature": "firma",
|
||||
"stamp": "sello",
|
||||
"initials": "iniciales",
|
||||
"name": "nombre",
|
||||
"job title": "título del puesto",
|
||||
"company": "compañía",
|
||||
"date": "fecha",
|
||||
"text": "texto",
|
||||
"text input": "entrada de texto",
|
||||
"checkbox": "casilla",
|
||||
"dropdown": "desplegable",
|
||||
"radio button": "botón de radio",
|
||||
"image": "imagen",
|
||||
"email": "correo"
|
||||
},
|
||||
"fields": "Campos",
|
||||
"recipients": "Destinatarios",
|
||||
"please-add": "Por favor, agrega un",
|
||||
"signed-by": "Firmado por",
|
||||
"more": "más",
|
||||
"sent": "enviado",
|
||||
"print": "Imprimir",
|
||||
"certificate": "Certificado",
|
||||
"decline": "Rechazar",
|
||||
"finish": "Finalizar",
|
||||
"mail": "Correo",
|
||||
"sign-now": "Firmar ahora",
|
||||
"successfully-signed": "¡Firmado exitosamente!",
|
||||
"email-mssg": " Los destinatarios agregados aquí recibirán una copia del documento firmado.",
|
||||
"email-error-1": " por favor, proporciona una dirección de correo correcta",
|
||||
"email-error-2": "Solo puedes enviar hasta a 10 destinatarios a la vez.",
|
||||
"Email-verified-alert-1": "El correo está verificado.",
|
||||
"Email-verified-alert-2": "El correo ya ha sido verificado.",
|
||||
"upload-stamp-image": "Subir imagen del sello",
|
||||
"draw": "Dibujar",
|
||||
"type": "Escribir",
|
||||
"color-type": {
|
||||
"red": "rojo",
|
||||
"blue": "azul",
|
||||
"black": "negro",
|
||||
"yellow": "amarillo"
|
||||
},
|
||||
"my-initials": "Mis iniciales",
|
||||
"upload": "Subir",
|
||||
"initial-teb": "Iniciales",
|
||||
"signature-tab": "Firma",
|
||||
"your-signature": "Tu firma",
|
||||
"copy-title": "Copiar widget a",
|
||||
"contact-delete-alert": "¿En definitiva quieres eliminar este contacto?",
|
||||
"reset-password-alert-1": "El enlace para restablecer la contraseña ha sido enviado a tu correo electrónico",
|
||||
"reset-password-alert-2": "Por favor, configura un adaptador de correo ",
|
||||
"reset-password-alert-3": "Restablece tu contraseña",
|
||||
"faild-animation": "Error al cargar la animación",
|
||||
"apply": "Aplicar",
|
||||
"copy-type": {
|
||||
"All pages": "Todas las páginas",
|
||||
"All pages but last": "Todas las páginas menos la última",
|
||||
"All pages but first": "Todas las páginas menos la primera",
|
||||
"Next to current widget": "Junto al widget actual"
|
||||
},
|
||||
"options": "Opciones",
|
||||
"minimun-check": "Chequeo mínimo",
|
||||
"maximum-check": "Chequeo máximo",
|
||||
"default-value": "Valor por defecto",
|
||||
"select": "Seleccionar",
|
||||
"read-only": "Es de solo lectura",
|
||||
"hide-labels": "Esconder etiquetas",
|
||||
"checkbox": "Casilla",
|
||||
"alert": "Alerta",
|
||||
"zoom-in": "Acercar",
|
||||
"zoom-out": "Alejar",
|
||||
"document-signed-alert": "¡Felicitaciones! 🎉 ¡Este documento ha sido firmado por ti exitosamente!",
|
||||
"otp-sent-alert": "OTP enviado a tu correo",
|
||||
"fields-required": "Campos requeridos",
|
||||
"signature-widget-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma agregado",
|
||||
"signature-widget-alert-2": "Por favor, asegúrate de que todos los campos están correctamente rellenados y de que cumplen todos los requisitos.",
|
||||
"encrypted-pdf-alert": "Actualmente los archivos PDF encriptados no están soportados.",
|
||||
"encrypted-pdf-alert-1": "La contraseña del archivo PFX es inválida.",
|
||||
"user-not-exist": "El usuario no existe",
|
||||
"template-signature-field": "Por favor, agrega al menos un campo de firma para todos los roles.",
|
||||
"template-role-alert": "Por favor, primero agrega roles",
|
||||
"create-document": "Crear documento",
|
||||
"template-created-alert": "¿Quieres crear un documento usando la plantilla que acabas de crear?",
|
||||
"signers-alert": "alerta de firmantes",
|
||||
"template-creation-alert-1": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"ok": "OK",
|
||||
"radio-group": "Grupo de radio",
|
||||
"dropdown-options": "Opciones del desplegable",
|
||||
"add-role": "Agregar rol",
|
||||
"role-ex": " por ejem.: Cliente, RRHH, Director, Administrador, Estudiante, etc.",
|
||||
"add/choose-signer": "Agregar/Elegir firmante",
|
||||
"choose-from-contacts": "Elegir desde los contactos",
|
||||
"select-signer": " Por favor, selecciona el firmante",
|
||||
"edit-template": "Editar plantilla",
|
||||
"widget-info": "Información del widget",
|
||||
"validation": "Validación",
|
||||
"invalid-default-value": "valor por defecto inválido",
|
||||
"hint": "Pista",
|
||||
"color": "color",
|
||||
"role-help": {
|
||||
"p1": "¿Qué son los roles de una plantilla?",
|
||||
"p2": "Empieza por especificar cada uno de los roles necesarios para completar el documento. Piensa en las partes implicadas en el proceso de firma y cuáles son sus responsabilidades. Los roles más habituales son RRHH para los documentos internos, Cliente para los contratos o Proveedor para los convenios comerciales.",
|
||||
"p3": "¿Por qué preasignar usuarios a algunos roles?",
|
||||
"p4": "Para los roles que consistentemente involucran al mismo individuo (por ejem., la firma del Gerente en las ofertas de empleo), puedes preasignar un usuario a un rol dentro de una plantilla. Este paso es opcional pero recomendado para la eficiencia y consistencia entre documentos",
|
||||
"p5": "¿Cuando especifico el usuario asignado a cada rol?",
|
||||
"p6": "Cuando crees un documento usando tu plantilla, se te pedirá que asignes usuarios a cada rol definido. Si un rol ya tiene un usuario asignado, este será rellenado con anticipación, pero puedes modificarlo según lo necesites antes de enviar el documento."
|
||||
},
|
||||
"add-recipients": "Agregar destinatarios",
|
||||
"loading-mssg": "Esto podría llevar algún tiempo",
|
||||
"send-mail": "Enviar correo",
|
||||
"placeholder-alert-1": "Por favor, asegúrate de que hay al menos un widget de firma para cada destinatario.",
|
||||
"placeholder-alert-2": "Por favor, confirma que hayas rellenado el campo de texto.",
|
||||
"placeholder-alert-3": " ¿En definitiva quieres enviar este documento para ser firmado?",
|
||||
"placeholder-alert-4": "¡Has enviado exitosamente correos a todos los destinatarios!",
|
||||
"placeholder-alert-5": "¿Quieres firmar documentos ahora mismo?",
|
||||
"placeholder-alert-6": "¡Por favor, configura el adaptador de correo para enviar correos!",
|
||||
"placeholder-alert-7": "¡Por favor, selecciona un firmante para agregar un marcador de posición!",
|
||||
"email-subject": "asunto del correo",
|
||||
"email-body": "cuerpo del correo",
|
||||
"email-placeholder": "agregar cuerpo del correo",
|
||||
"reset-to-default": "Restablecer a por defecto",
|
||||
"cutomize-email": "Personalizar correo",
|
||||
"upgrade-to-customize-email": "Mejora para personalizar el correo",
|
||||
"sign-url": "URL de firma",
|
||||
"mails-sent": "Correos enviados",
|
||||
"mail-not-delivered": "correo no entregado",
|
||||
"document-alert": "Alerta de documento",
|
||||
"owner-subscription-expired": "La suscripción del propietario ha expirado.",
|
||||
"subscription-expired": "Suscripción expirada",
|
||||
"alert-message": "Mensaje de alerta",
|
||||
"document-decline": "Rechazar documento",
|
||||
"decline-alert-1": "¿En definitiva quieres rechazar este documento?",
|
||||
"decline-alert-2": "¡Has rechazado este documento!",
|
||||
"decline-alert-3": "No puedes firmar este documento ya que ha sido rechazado/revocado.",
|
||||
"sign here": "Firma aquí",
|
||||
"guest-email-alert": "el código de verificación ha sido enviado a tu correo",
|
||||
"get-otp-alert": "recibirás un código de verificación por correo",
|
||||
"user-already-exist": "El usuario ya existe.",
|
||||
"server-error": "¡Error interno del servidor!",
|
||||
"fill-required-details!": "¡Por favor, rellena los detalles requeridos!",
|
||||
"sign-up-form": "Formulario de registro",
|
||||
"sign-up": "Registrarse",
|
||||
"folder-already-exist!": "¡la carpeta ya existe!",
|
||||
"please-select-position!": "¡por favor, selecciona una posición!",
|
||||
"default-sign-alert": "¿En definitiva quieres firmar automáticamente en todos los lugares solicitados?",
|
||||
"Your-Signature": "Tu firma",
|
||||
"auto-sign-all": "Firmar todo automáticamente",
|
||||
"generating-certificate": "Generando certificado",
|
||||
"pdf-download": "Descargar PDF",
|
||||
"generate-certificate-alert": " Tu certificado de finalización se está generando. Por favor, espera un momento. Si la descarga no inicia pronto, presiona el botón otra vez.",
|
||||
"folder-already-exist": "¡La carpeta ya existe!",
|
||||
"folder-created-successfully": "¡Carpeta creada exitosamente!",
|
||||
"fill-folder-name": "Por favor, rellena el nombre de la carpeta",
|
||||
"already-exists-this-username": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"verification-code-sent": "¡El correo de verificación ha sido enviado a tu correo!",
|
||||
"registered-user-successfully": "Usuario registrado exitosamente.",
|
||||
"role-not-found": "Rol no encontrado.",
|
||||
"do-not-access": "No tienes acceso, por favor, contacta al administrador.",
|
||||
"add-admin": "Agregar administrador",
|
||||
"opensign-setup": "Instalación de OpenSign",
|
||||
"join-discord": "Únete a nuestro servidor de Discord",
|
||||
"admin-already-exist": "El administrador ya existe.",
|
||||
"password-update-alert-1": "Contraseña actualizada exitosamente.",
|
||||
"password-update-alert-2": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-3": "Tu contraseña actual es incorrecta o falta.",
|
||||
"password-update-alert-4": "Tu contraseña y su confirmación no son iguales.",
|
||||
"current-password": "Contraseña actual",
|
||||
"new-password": "Contraseña nueva",
|
||||
"confirm-password": "Confirmar contraseña",
|
||||
"file-alert-1": "El tamaño del archivo seleccionado es muy grande. Por favor, selecciona un archivo con un tamaño menor a",
|
||||
"file-alert-2": "Por favor, selecciona un archivo.",
|
||||
"file-alert-3": "Por favor, espera mientras se sube el documento.",
|
||||
"enter-pdf-password": "Ingresa la contraseña del PDF",
|
||||
"correct-password": "Por favor, proporciona la contraseña correcta",
|
||||
"decrypting-pdf": " Desencriptando PDF, por favor, espera...",
|
||||
"invalid-otp": "OTP inválido",
|
||||
"user-not-found": "¡Usuario no encontrado!",
|
||||
"enter-otp-alert": "¡Por favor, ingresa el OTP!",
|
||||
"get-verification-code": "Obtener código de verificación",
|
||||
"get-verification-code-2": "Obtendrás un código de verificación por correo",
|
||||
"enter-verification-code": "Ingresar código de verificación",
|
||||
"provide-your-details": "Proporciona tus detalles",
|
||||
"provide-email": "Por favor, proporciona un correo.",
|
||||
"additional-info": "Información adicional",
|
||||
"signature-saved-alert": "Firma guardada exitosamente.",
|
||||
"fill-field": "Por favor, rellena este campo",
|
||||
"error-template": "Error: ¡Plantilla no encontrada!",
|
||||
"error": "Error",
|
||||
"invalid-document": "Error: ¡Documento inválido!",
|
||||
"document-not-found": "¡Documento no encontrado!",
|
||||
"document-signed-alert-1": "Ha firmado exitosamente el documento. Puede descargar o imprimir una copia del documento parcialmente firmado. Se enviará una copia del documento firmado digitalmente y del certificado de finalización al propietario y a todos los firmantes por correo electrónico una vez que todos los firmantes lo hayan firmado.",
|
||||
"encrypted-pdf-not-support": "Los archivos PDF encriptados no están soportados actualmente.",
|
||||
"document-signed-alert-4": " ¡Felicitaciones! 🎉 ¡Este documento ha sido firmado exitosamente por todos los participantes!",
|
||||
"document-signed-alert-5": "Este documento ha sido firmado por todos los firmantes.",
|
||||
"document-signed-alert-6": "Este documento ha sido rechazado por uno o más destinatarios.",
|
||||
"document-signed-alert-7": "Este documento ya no está disponible.",
|
||||
"document-signed-alert-8": "Este documento ya ha sido enviado para ser firmado.",
|
||||
"yet-to-sign": "Todavía por firmar",
|
||||
"validation-alert-1": "Los datos ingresados no cumplen con los criterios establecidos por la expresión regular.",
|
||||
"account-already-exists": "¡La cuenta ya existe!",
|
||||
"choose-password": "Elegir contraseña",
|
||||
"enter-password": "Ingresar contraseña",
|
||||
"sure": "claro",
|
||||
"subscribed-alert": "¡Ya te has suscrito al plan!",
|
||||
"monthly": "Mensual",
|
||||
"yearly-upto": "Anual (hasta 66% de descuento)",
|
||||
"billed-yearly": "Facturado anualmente",
|
||||
"billed-monthly": "Facturado mensualmente",
|
||||
"plansubscription-1": "Alójalo por tu cuenta gratuitamente",
|
||||
"visit-github": "Visitar GitHub",
|
||||
"user-already-exist-name": "¡Un usuario con este nombre de usuario ya existe!",
|
||||
"do-not-access-contact-admin": "No tienes acceso, por favor, contacta a un administrador.",
|
||||
"filed-required-correctly": "Por favor, rellena correctamente los detalles requeridos.",
|
||||
"admin-created": "Administrador creado",
|
||||
"invalid-masterkey": "Llave maestra inválida",
|
||||
"opensign-Setup": "Instalación de OpenSign",
|
||||
"master-key": " Llave maestra",
|
||||
"profile-update-alert": "Perfil actualizado exitosamente.",
|
||||
"date": "Fecha",
|
||||
"report-not-found": "Reporte no encontrado",
|
||||
"please-wait": "por favor, espera",
|
||||
"select-foler": "Seleccionar carpeta",
|
||||
"generated-on": "Generado el",
|
||||
"text-field": "Campo de texto",
|
||||
"font-size": "Tamaño de la fuente",
|
||||
"widget-status": {
|
||||
"Required": "Requerido",
|
||||
"Optional": "Opcional"
|
||||
},
|
||||
"all-right": "Todos los derechos reservados",
|
||||
"version": "versión",
|
||||
"social-media": {
|
||||
"github": "GitHub",
|
||||
"linked-in": "LinkedIn",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord"
|
||||
},
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "¡Has iniciado sesión exitosamente! Echemos un vistazo.",
|
||||
"home-layout-2": "Para subir documentos para firmar tú mismo o para solicitar la firma de otros, simplemente selecciona los botones correspondientes.",
|
||||
"home-layout-3": "¡Ya puedes empezar a utilizar OpenSign! Si necesitas soporte siéntete libre de contactarnos.",
|
||||
"generate-token": "Mejora ahora para generar un token API de producción.",
|
||||
"opensign-drive-1": "Haz clic en los enlaces para navegar fácilmente por la estructura jerárquica de carpetas y ver los documentos de cada carpeta.",
|
||||
"opensign-drive-2": "Haz clic en el botón «Agregar» para crear una nueva carpeta o documento.",
|
||||
"opensign-drive-3": "Ordena tus documentos por «Fecha» o «Nombre» usando este menú.",
|
||||
"opensign-drive-4": "Haz clic en este menú para mostrar los documentos en vista de lista.",
|
||||
"opensign-drive-5": "La lista de documentos es mostrada de acuerdo a la opción de ordenamiento seleccionada. Los íconos situados junto a cada documento indican su estado actual.",
|
||||
"opensign-drive-6": "Haz clic con el botón derecho en un documento para ver opciones como «Descargar», «Renombrar», «Mover» y «Eliminar». Haz clic en el documento para abrirlo.",
|
||||
"opensign-drive-7": "Haz clic con el botón derecho en cualquier carpeta para ver las opciones. Elige «Renombrar» para cambiar el nombre de la carpeta o haz clic en ella para navegar por su contenido.",
|
||||
"pdf-request-file-1": "Lista de firmantes que todavía tienen que firmar este documento.",
|
||||
"pdf-request-file-2": "Haz clic en cualquiera de los marcadores de posición que aparecen en el documento para firmar. A continuación, verás opciones para dibujar tu firma, escribirla o subir una imagen .",
|
||||
"pdf-request-file-3": "Haz clic en los botones «Atrás», «Rechazar» o «Finalizar» para navegar por el documento. Utiliza el menú de tres puntos para ver otras opciones, como el botón «Descargar».",
|
||||
"pdf-request-file-4": "Lista de firmantes que ya han firmado el documento.",
|
||||
"pdf-request-file-5": "Puedes hacer clic en «Firmar todo automáticamente» para firmar de manera automática en todos los lugares que deban ser firmados por ti. Asegúrate de revisar correctamente el documento antes de hacer clic en ese botón.",
|
||||
"placeholder-sign-1": "Selecciona un destinatario de esta lista para agregar un marcador de posición dónde este destinatario tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del destinatario una vez lo sueltes en el documento.",
|
||||
"placeholder-sign-2": "Hacer clic en el botón «Agregar destinatarios» te permitirá añadir más firmantes.",
|
||||
"placeholder-sign-3": "Haz clic en este botón para agregar más destinatarios/firmantes al documento.",
|
||||
"placeholder-sign-4": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"placeholder-sign-5": "El área de contenido del PDF ya muestra los marcadores de posición existentes en la plantilla. Para tu comodidad, estos marcadores de posición coincidirán con el color del nombre del destinatario, haciéndolos fácilmente identificables.",
|
||||
"placeholder-sign-6": "Hacer clic en «Enviar» guardará el documento. En el siguiente paso podrás personalizar los correos que serán enviados a los destinatarios o copiar los enlaces para firmar y compartirlos con los destinatarios por tu cuenta.",
|
||||
"report-1": "Haz clic en el botón «Agregar» para crear una nueva plantilla. Las plantillas son documentos reusables diseñados para generar rápidamente nuevos documentos con la misma estructura pero diferentes firmantes. Por ejemplo, una plantilla de RR.HH. para el proceso de incorporación podría tener roles predefinidos como «Director de RR.HH.» y «Nuevo empleado». Cada vez que utilices la plantilla, puedes asignar el rol de «Nuevo empleado» a los distintos miembros del personal que se incorporen, mientras que el rol de « Director de RR.HH.» permanece constante, lo que facilita un proceso de incorporación fluido para cada persona contratada.",
|
||||
"redirect": "Haz clic en el botón «Usar» para crear un nuevo documento desde una plantilla existente.",
|
||||
"bulksend": "Para crear y enviar rápidamente múltiples documentos usando una plantilla existente, haz clic en el botón «Envío rápido».",
|
||||
"option": "Este menú revela más opciones como «Editar» y «Eliminar». Usa el botón «Editar» para agregar roles, modificar campos y actualizar tu plantilla. Los cambios aplicarán para todos los documentos futuros que sean creados usando esta plantilla pero no afectarán a los documentos ya existentes. Con el botón «Eliminar» puedes eliminar la plantilla.",
|
||||
"signyour-self-1": "Para personalizar el documento antes de que sea firmado, selecciona y arrastra al documento los widgets que prefieras. Escoge el espacio perfecto para cada modificación para adaptar el documento a tus necesidades.",
|
||||
"signyour-self-2": "Arrastra y suelta en cualquier lugar de esta área. Después podrás cambiar el tamaño y moverlo.",
|
||||
"template-placeholder-1": "Hacer clic en el botón «Agregar rol» te permitirá agregar varios roles de firmantes. Puedes asociar usuarios a cada rol en los siguientes pasos.",
|
||||
"template-placeholder-2": "Una vez que los roles hayan sido agregados, selecciona un rol de la lista para agregar un marcador de posición donde éste tendrá que firmar. El marcador de posición aparecerá con el mismo color que el nombre del rol una vez lo sueltes en el documento.",
|
||||
"template-placeholder-3": "Arrastra o haz clic en un capo para agregarlo al documento.",
|
||||
"template-placeholder-4": "Arrastra el marcador de posición para un rol a cualquier lugar del documento. Recuerda, aparecerá con el mismo color que el nombre del rol para poder referenciarlo fácilmente.",
|
||||
"template-placeholder-5": "Hacer clic en «Siguiente» guardará la plantilla actual. Luego de guardar, se te consultará si deseas crear un nuevo documento usando esta plantilla.",
|
||||
"webhook-1": "Mejora ahora para configurar un webhook",
|
||||
"Need your Signature": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan por tu revisión.",
|
||||
"Out for signatures": "Hacer clic en esta tarjeta te llevará a la lista de documentos que esperan tu firma.",
|
||||
"Recent signature requests": "Esta es una lista de documentos que esperan por tu firma.",
|
||||
"Recently sent for signatures": "Esta es una lista de documentos que has enviado a otras partes para que sea firmado.",
|
||||
"Drafts": "Estos son documentos que has empezado pero que todavía no finalizas para su envío.",
|
||||
"public-template": "Este video muestra cómo puedes configurar tu perfil público personalizado, como «https://opensign.me/tu-nombre-de-usuario». También aprenderás cómo personalizar tu eslogan y cómo hacer tus plantillas disponibles para su firmado público."
|
||||
},
|
||||
"enter-email-plaholder": "Agrega una dirección de correo y presiona «Enter»",
|
||||
"success-email-alert": "¡Correo enviado exitosamente!",
|
||||
"expired-doc-title": "Documento expirado",
|
||||
"expired-on-mssg": "Este documento expiro el {{expiredDate}} y ya no está disponible para ser firmado.",
|
||||
"signature-validate-alert": "Por favor, confirma que has seleccionado al menos {{minRequiredCount}} casillas.",
|
||||
"signature-validate-alert-2": "Asegúrate de que este campo esté correctamente rellenado y que cumpla con todos los requisitos.",
|
||||
"remaing-users": "Usuarios restantes",
|
||||
"Role": "Rol",
|
||||
"additional-users": "Por favor, compra el complemento Usuarios.",
|
||||
"Quantity-of-users": "Cantidad de usuarios",
|
||||
"Price": "Precio",
|
||||
"Total-price-for-next-time": "Precio total para la próxima vez",
|
||||
"Proceed": "Proceder",
|
||||
"Select": "Seleccionar",
|
||||
"Add-seats": "Agregar plazas",
|
||||
"user-name-exist": "el nombre de usuario ya existe",
|
||||
"format": "formato",
|
||||
"select-date-format": "Selecciona un formato de fecha",
|
||||
"quantity-of-credits": "Cantidad de créditos premium",
|
||||
"remaining-credits": "Créditos premium disponibles:",
|
||||
"remaining-credits-help": "Usa créditos premium para la API para la firma de documentos, envío en masa, o para incorporar la integración de OpenSign en tu página web. Te quedan {{allowedcredits}} créditos incluidos y {{addoncredits}} créditos comprados adicionalmente.",
|
||||
"additional-credits": "Por favor, compra créditos premium",
|
||||
"quota-err-quicksend": "Cuota alcanzada, no tienes créditos suficientes.",
|
||||
"buy-credits": "Comprar créditos premium",
|
||||
"rotate-right": "Rotar a la derecha",
|
||||
"rotate-left": "Rotar a la izquierda",
|
||||
"rotate-alert-mssg": "Todos los widgets de esta página se perderán. ¿En definitiva quieres proceder?",
|
||||
"templateid": "ID de plantilla",
|
||||
"bulk-send-subcription-alert": "Por favor, mejora al plan a Professional o Team para usar envío en masa.",
|
||||
"generate-test-token": "Generar token de prueba",
|
||||
"regenerate-test-token": "Regenerar token de prueba",
|
||||
"help-test-token": "Este token puede ser usado para probar las API en el punto de acceso «https://sandbox.opensignlabs.com/api/v1», permitiéndote realizar firmas de documentos ilimitadas. Por favor, ten en cuenta que el API de pruebas firmará tus documentos con certificados autofirmados, que podrían no ser reconocidos por Adobe como válidos. Una vez hayas completado tus pruebas, puedes mejorar a uno de nuestros planes de pago para generar un token de producción.",
|
||||
"help-api-token": "Este token puede ser usado para acceder a las API de producción en el punto de acceso «{{origin}}/api/v1». Solo puede ser generado con uno de nuestros planes de pago.",
|
||||
"reason": "Razón",
|
||||
"decline-by": "Rechazado/revocado por",
|
||||
"document-declined": "Documento rechazado",
|
||||
"public-template-mssg-1": "Para integrar OpenSign a tu proyecto React o Next.js, simplemente ejecuta los siguientes comandos:",
|
||||
"public-template-mssg-2": "Asegúrate de tener «npm» o «yarn» configurado en tu proyecto. Si estás usando «yarn», puedes reemplazar «npm install» con «yarn add @opensign/react».",
|
||||
"public-template-mssg-3": "¿Necesitas más detalles o ejemplos?",
|
||||
"public-template-mssg-4": "Visita la",
|
||||
"public-template-mssg-5": " «npm» para las últimas actualizaciones, documentación detallada e historial de versiones.",
|
||||
"public-template-mssg-6": "Antes de que puedas usar este fragmento de código, debes convertir esta plantilla en pública.",
|
||||
"copy-code": "COPIAR",
|
||||
"copied-code": "COPIADO",
|
||||
"Installation": "Instalación",
|
||||
"Usage": "Uso",
|
||||
"insufficient-credits": "Créditos de firma insuficientes",
|
||||
"insufficient-credits-mssg": "El propietario de este documento actualmente carece de los créditos OpenSign necesarios para que puedas firmarlo. Por favor, contacta al propietario si necesitas más detalles.",
|
||||
"angular-npm-mssg-1": "Para integrar OpenSign a tu proyecto Angular, simplemente ejecuta los siguientes comandos:",
|
||||
"quota-mail-info-head": "Límite mensual de solicitudes de firma por correo",
|
||||
"quota-mail-info": "Puedes enviar hasta 15 solicitudes de firma por correo cada mes. Mejora ahora para enviar solicitudes de firma sin límite.",
|
||||
"quota-mail-reset": "Los créditos de tu correo electrónico de solicitud de firma se restablecerán el",
|
||||
"quota-mail": "Has alcanzado tu límite de 15 solicitudes de firma por correo para este mes. Mejora ahora para continuar enviando correos.",
|
||||
"quota-mail-tip": "Consejo: Todavía puedes solciitar la firma de <1>documentos ilimitados</1> compartiendo manualmente los enlaces de solicitud de firma.",
|
||||
"quota-mail-head": "Cuota alcanzada",
|
||||
"unauthorized-modal": "No tienes permisos para realizar esta acción, por favor, contacta a {{adminEmail}}.",
|
||||
"sent-this-month": "Enviados este mes",
|
||||
"available-seats": "Plazas disponibles",
|
||||
"buy-users": "Comprar más usuarios",
|
||||
"isenable-otp": "Habilitar verificación con OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "¿Te gustaría habilitar el proceso de verificación usando una contraseña de un solo uso (OTP)?",
|
||||
"p2": "Seleccionar esta opción habilitará la verificación con OTP. Los usuarios recibirán un código de verificación por correo, la cual tendrán que ingresar para poder firmar el documento.",
|
||||
"p3": "Seleccionar esta opción deshabilitará la verificación con OTP, lo cual permitirá a los usuarios firmar el documento directamente sin pasos adicionales.",
|
||||
"p4": "Por favor, selecciona la opción que mejor se adapte a tus requisitos para la firma de documentos."
|
||||
},
|
||||
"advanced-options": "Opciones avanzadas",
|
||||
"hide-advanced-options": "Ocultar las opciones avanzadas",
|
||||
"download-files": "Descargar archivos",
|
||||
"download-pdf": "Descargar PDF",
|
||||
"pdf-certificate": "Descargar PDF + Certificado",
|
||||
"document-logs": "Registros de documentos",
|
||||
"server-down": "No se pudo conectar al servidor de OpenSign. Si estás alojando OpenSign por tu cuenta, por favor, asegúrate de que todos los pasos de la documentación han sido seguidos correctamente. Si estás ejecutando OpenSign localmente, puedes estar accediendo a OpenSign por un número de puerto incorrecto.",
|
||||
"admin-exists": "El administrador ya existe. Por favor, inicia sesión en la aplicación usando las credenciales de administrador para poder administrar los usuarios.",
|
||||
"public-tour-message": "Por favor, convierte la plantilla en pública para copiar la URL pública",
|
||||
"add-user-template": "Es necesario agregar un rol antes de poder agregarle campos.",
|
||||
"pdf-uncompatible": "Este pdf no es compatible con opensign, comuníquese con <support@opensignlabs.com>",
|
||||
"enable-tour": "Habilitar recorrido",
|
||||
"istourenabled-help": {
|
||||
"p1": "Esto permitirá realizar una visita guiada para los firmantes, brindando instrucciones durante el proceso de firma.",
|
||||
"p2": "Esto desactivará la visita guiada, lo que garantizará una experiencia de firma más rápida e ininterrumpida.",
|
||||
"p3": "Consejo: si sus firmantes están familiarizados con OpenSign y lo utilizan con frecuencia, es posible que desee desactivar la visita guiada para disfrutar de una experiencia más fluida. Elija la opción que mejor se adapte a sus necesidades de firma de documentos"
|
||||
},
|
||||
"form-title-1": "Configuración del flujo de documentos",
|
||||
"form-title-2": "Configuración de seguridad",
|
||||
"text-field-tour": "Los campos de tipo 'Texto' deben completarse con anticipación antes de enviar el documento. Si necesita que los firmantes proporcionen información, utilice el campo 'Entrada de texto'",
|
||||
"attach-signer-tour": "Debe adjuntar un firmante a cada función. Puede hacerlo haciendo clic en este icono. Una vez que seleccione un Firmante, se adjuntará a todos los campos asociados con ese rol que aparecen en el mismo color.",
|
||||
"allowed-signature-types": "Tipos de firma permitidos",
|
||||
"at-least-one-signature-type": "Se debe habilitar al menos un tipo de firma.",
|
||||
"expect-default-one-signature-type": "Habilite un tipo de firma adicional aparte del predeterminado.",
|
||||
"add-signer-note": "Nota - Este contacto se agregará a su lista de contactos",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Esta preferencia de firma controla qué opciones de firma están disponibles para sus firmantes. Solo los tipos de firma que seleccione aparecerán en el widget de firma cuando se firme un documento. Por ejemplo, si elige sólo la opción 'Draw' en las preferencias, su firmante verá sólo la opción 'Draw' en el widget de firma, mientras que las otras tres opciones no estarán disponibles.",
|
||||
"l1": "Cree una firma manuscrita directamente en la pantalla usando su dedo o un lápiz.",
|
||||
"l2": "Ingrese su nombre y elija entre varios estilos de fuente para crear una firma.",
|
||||
"l3": "Cargue una versión digital existente de su firma.",
|
||||
"l4": "Utilice un estilo de firma preestablecido guardado en 'Mi firma' en su configuración."
|
||||
},
|
||||
"notify-on-signatures": "Notificar sobre firmas",
|
||||
"notify-on-signatures-help": {
|
||||
"p1": "El creador del documento recibirá una notificación por correo electrónico cada vez que un firmante firme el documento.",
|
||||
"note": "Nota: No se enviará un correo electrónico de notificación al último firmante, ya que el creador del documento recibirá un correo electrónico de finalización."
|
||||
},
|
||||
"expiry-date": "Date d'expiration",
|
||||
"expiry-date-updated": "La fecha de vencimiento se ha extendido con éxito hasta el {{newexpirydate}}",
|
||||
"expiry-date-error": "Proporcione una nueva fecha de vencimiento que sea posterior a la actual"
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"header-news": "Nouvelle fonctionnalité : les utilisateurs du forfait Teams peuvent désormais intégrer leurs propres compartiments AWS S3 pour le stockage de fichiers",
|
||||
"header-news-btn": "Configurer maintenant",
|
||||
"create-account": "Créer un compte",
|
||||
"login": "Se Connecter",
|
||||
"language": "Langue",
|
||||
"name": "Nom et Prénom",
|
||||
"phone": "Téléphone",
|
||||
"phone-optional": "(facultatif)",
|
||||
"email": "E-mail",
|
||||
"company": "Organisation",
|
||||
"job-title": "Votre Fonction",
|
||||
@@ -39,10 +42,11 @@
|
||||
"upgrade-to": "Mettre à niveau vers",
|
||||
"pro": "PRO",
|
||||
"plan": "Offre",
|
||||
"subscribe-card-plan": "Signez 100 documents à l'aide de l'API. Seulement 0,15 $/doc par la suite.",
|
||||
"subscribe-card-teamplan": "Libérez toute la puissance de la collaboration ! Créez un nombre illimité d'organisations, d'équipes et de hiérarchies. Partagez des modèles de manière transparente entre les équipes et attribuez des rôles d'utilisateur personnalisés. Améliorez votre flux de travail dès aujourd'hui !",
|
||||
"subscribe-card-plan": "Débloquez des fonctionnalités premium à partir de seulement {{premiumPrice}}/mois. Bénéficiez de performances améliorées et de seulement {{addonPrice}} par crédit supplémentaire après vos crédits premium inclus.",
|
||||
"user-name-limit-char": "Pour avoir un nom d'utilisateur de moins de 8 caractères s'il vous plaît s'abonner",
|
||||
"tour-content": "Ne plus afficher",
|
||||
"team": "ÉQUIPE",
|
||||
"team": "Équipe",
|
||||
"docs": "Documents",
|
||||
"session-expired": "Votre session a expiré.",
|
||||
"user": "Utilisateur",
|
||||
@@ -90,7 +94,9 @@
|
||||
"My Signature": "Ma signature",
|
||||
"API Token": "Jeton API",
|
||||
"Webhook": "Webhook",
|
||||
"Preferences": "Préférences",
|
||||
"Teams": "Équipe",
|
||||
"Team-update-successfully": "Équipe mise à jour avec succès.",
|
||||
"Users": "Utilisateurs"
|
||||
}
|
||||
},
|
||||
@@ -131,7 +137,9 @@
|
||||
"Name": "Nom",
|
||||
"Status": "Statut",
|
||||
"created-date": "Date de création",
|
||||
"Type": "Saisir"
|
||||
"Type": "Saisir",
|
||||
"Logs": "Journaux",
|
||||
"Expiry-date": "Fecha de caducidad"
|
||||
},
|
||||
"btnLabel": {
|
||||
"sign": "Signer",
|
||||
@@ -144,7 +152,11 @@
|
||||
"Share with team": "Partager avec l'équipe",
|
||||
"Share": "Partager",
|
||||
"View": "Voir",
|
||||
"option": "Option"
|
||||
"option": "Option",
|
||||
"Embed": "Intégrer",
|
||||
"Copy TemplateId": "Copier l'ID du modèle",
|
||||
"Copy Public URL": "Copier l'URL publique",
|
||||
"extend-expiry-date": "Prolonger la date d'expiration"
|
||||
},
|
||||
"report-help": {
|
||||
"Draft Documents": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
@@ -197,7 +209,7 @@
|
||||
"parent-folder": "Dossier Parent",
|
||||
"create": "Créer",
|
||||
"signers": "Signataires",
|
||||
"signers-help": "Commencez à saisir le nom d'un contact pour voir les signataires suggérés par vos contacts enregistrés ou en ajouter de nouveaux. Organisez l'ordre de signature en ajoutant des signataires dans l'ordre souhaité. Utilisez le bouton « + » pour inclure les signataires et le « x » pour les supprimer. Chaque signataire recevra un e-mail invité à signer le document dans l'ordre indiqué.",
|
||||
"signers-help": "Commencez à saisir le nom d'un contact pour voir les signataires suggérés par vos contacts enregistrés ou en ajouter de nouveaux. Organisez l'ordre de signature en ajoutant des signataires dans l'ordre souhaité. Utilisez le bouton « + » pour inclure les signataires et le « x » pour les supprimer. Chaque signataire recevra un e-mail invité à signer le document dans l'ordre indiqué.",
|
||||
"add-signer": "Ajouter un signataire",
|
||||
"contact-not-found": "Contact introuvable",
|
||||
"add-yourself": "Ajoutez-vous",
|
||||
@@ -211,8 +223,8 @@
|
||||
"initials": "Initiales",
|
||||
"API": "API",
|
||||
"api-token": "Jeton API",
|
||||
"regenerate-token": "Régénérer le jeton",
|
||||
"generate-token": "Générer un jeton",
|
||||
"regenerate-token": "Régénérer en direct jeton",
|
||||
"generate-token": "Générer en direct jeton",
|
||||
"view-docs": "Afficher les documents",
|
||||
"generate-token-alert": "Êtes-vous sûr de vouloir régénérer le jeton, votre ancien jeton sera supprimer?",
|
||||
"yes": "Oui",
|
||||
@@ -252,12 +264,14 @@
|
||||
"view": "voir",
|
||||
"make-template-public": "Rendre le modèle public",
|
||||
"make-template-private": "Rendre le modèle privé",
|
||||
"make-template-public-alert": "Êtes-vous sûr de vouloir rendre ce modèle public ?",
|
||||
"make-template-public-alert": "Êtes-vous sûr de vouloir rendre ce modèle public ?",
|
||||
"make-template-private-alert": "Êtes-vous sûr de vouloir rendre ce modèle privé ? Cela le supprimera de votre profil public.",
|
||||
"public-role": "Rôle public",
|
||||
"public-url": "URL publique",
|
||||
"public-url-copy-mssg": "Voici votre URL publique : copiez-la ou partagez-le avec le signataire et vous pouvoir voir tous vos paramètres publics modèles.",
|
||||
"add-public-url-alert": "Veuillez ajouter votre URL publique et vous pourra rendre public modèle.",
|
||||
"public-url": "Profil public",
|
||||
"embed-template": "Intégrer le modèle",
|
||||
"public-url-copy": "Voici votre URL publique : ",
|
||||
"public-url-copy-mssg": "copiez-la ou partagez-le avec le signataire et vous pouvoir voir tous vos paramètres publics modèles.",
|
||||
"add-public-url-alert": "Veuillez terminer la configuration de votre profil public OpenSign. Une fois cela fait, vous pourrez rendre les modèles publics. Ce processus ne doit être effectué qu’une seule fois.",
|
||||
"share-with-alert": "Vous ne pouvez pas partager un modèle si des contacts sont déjà attribués à des rôles. Veuillez supprimer toutes les attributions de contacts des rôles avant de partager le modèle.",
|
||||
"share-with": "Partager avec",
|
||||
"team-not-found": "Équipe introuvable",
|
||||
@@ -274,14 +288,14 @@
|
||||
"copy-link": "Copier le lien",
|
||||
"copy": "Copier",
|
||||
"revoke-document": "Révoquer le document",
|
||||
"revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?",
|
||||
"revoke-document-alert": "Êtes-vous sûr de vouloir révoquer ce document ?",
|
||||
"resend-mail": "Renvoyer le courrier",
|
||||
"resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"resend-mail-help": "Vous pouvez utiliser les variables suivantes qui seront remplacées par leurs valeurs réelles : - {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email} }, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}.",
|
||||
"subject": "Sujet",
|
||||
"body": "Corps",
|
||||
"add-contact": "Ajouter le contact",
|
||||
"add-signer-alert": "Le contact existe déjà !",
|
||||
"record-delete-alert": "Enregistrement supprimé avec succès !",
|
||||
"add-signer-alert": "Le contact existe déjà ! Veuillez le sélectionner dans la liste déroulante 'Signataires'",
|
||||
"record-delete-alert": "Enregistrement supprimé avec succès !",
|
||||
"record-revoke-alert": " Enregistrement révoqué avec succès ! ",
|
||||
"mail-sent-alert": "Courriel envoyé avec succès.",
|
||||
"document-sent-alert": "Document envoyé avec succès !",
|
||||
@@ -307,7 +321,7 @@
|
||||
"contact-details": "Coordonnées",
|
||||
"verify-email": "Veuillez vérifier votre email!",
|
||||
"send-otp": "envoyer un code à usage unique",
|
||||
"otp-placeholder": "Saisissez l'OTP reçu par e-mail",
|
||||
"otp-placeholder": "Entrez le code de vérification reçu par e-mail",
|
||||
"loading-doc": "Chargement du document..",
|
||||
"widgets-name": {
|
||||
"signature": "signature",
|
||||
@@ -337,7 +351,7 @@
|
||||
"finish": "terminé",
|
||||
"mail": "Mail",
|
||||
"sign-now": "Signez maintenant",
|
||||
"successfully-signed": "Signé avec succès !",
|
||||
"successfully-signed": "Signé avec succès !",
|
||||
"email-mssg": "Les destinataires ajoutés ici recevront une copie du document signé.",
|
||||
"email-error-1": "veuillez fournir une adresse e-mail correcte",
|
||||
"email-error-2": "Vous ne pouvez envoyer qu'à dix destinataires à la fois.",
|
||||
@@ -357,8 +371,8 @@
|
||||
"initial-teb": "Initiales",
|
||||
"signature-tab": "Signature",
|
||||
"your-signature": "Votre signature",
|
||||
"copy-to-all-pages": "Copier sur toutes les pages",
|
||||
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
||||
"copy-title": "Copier le widget vers",
|
||||
"contact-delete-alert": "Êtes-vous sûr de vouloir supprimer ce contact ?",
|
||||
"reset-password": "Le lien de réinitialisation du mot de passe a été envoyé à votre identifiant de messagerie",
|
||||
"reset-password-alert-2": "Veuillez configurer l'adaptateur de messagerie",
|
||||
"reset-password-alert-3": "Réinitialisez votre mot de passe",
|
||||
@@ -367,7 +381,8 @@
|
||||
"copy-type": {
|
||||
"All pages": "Toutes les pages",
|
||||
"All pages but last": "Toutes les pages sauf la dernière",
|
||||
"All pages but first": "Toutes les pages sauf la première"
|
||||
"All pages but first": "Toutes les pages sauf la première",
|
||||
"Next to current widget": "À côté du widget actuel"
|
||||
},
|
||||
"options": "Possibilités",
|
||||
"minimun-check": "Vérification minimale",
|
||||
@@ -393,7 +408,7 @@
|
||||
"create-document": "Créer un document",
|
||||
"template-created-alert": "Voulez-vous créer un document en utilisant le modèle que vous vient de créer ?",
|
||||
"signers-alert": "alerte des signataires",
|
||||
"template-creation-alert-1": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
|
||||
"template-creation-alert-1": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
|
||||
"ok": "D'accord",
|
||||
"radio-group": "Groupe de radio",
|
||||
"dropdown-options": "Options déroulantes",
|
||||
@@ -409,9 +424,9 @@
|
||||
"hint": "Indice",
|
||||
"color": "couleur",
|
||||
"role-help": {
|
||||
"p1": "Que sont les rôles modèles ?",
|
||||
"p1": "Que sont les rôles modèles ?",
|
||||
"p2": "Commencez par spécifier chaque rôle nécessaire à la finalisation du document. Pensez aux parties impliquées dans le processus de signature et à leurs responsabilités. Les rôles courants incluent les RH pour les documents internes, le Client pour les accords ou le Fournisseur pour les accords commerciaux.",
|
||||
"p3": "Pourquoi pré-attacher les utilisateurs à certains rôles ?",
|
||||
"p3": "Pourquoi pré-attacher les utilisateurs à certains rôles ?",
|
||||
"p4": "Pour les rôles qui impliquent systématiquement la même personne (par exemple, la signature du PDG sur les lettres d'offre des employés), vous pouvez pré-attacher un utilisateur à un rôle dans le modèle. Cette étape est facultative mais recommandée pour des raisons d'efficacité et de cohérence entre les documents.",
|
||||
"p5": "Quand dois-je spécifier l'utilisateur attaché à chaque rôle ?",
|
||||
"p6": "Lorsque vous créez un document à partir de votre modèle, vous serez invité à attacher des utilisateurs à chaque rôle défini. Si un rôle est déjà associé à un utilisateur, celui-ci sera pré-rempli, mais vous pourrez le modifier si nécessaire avant d'envoyer le document."
|
||||
@@ -424,8 +439,8 @@
|
||||
"placeholder-alert-3": "Etes-vous sûr de vouloir envoyer ce document pour signature ? ",
|
||||
"placeholder-alert-4": "Vous avez envoyé avec succès des mails à tous les",
|
||||
"placeholder-alert-5": "Voulez-vous signer des documents maintenant ?",
|
||||
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !",
|
||||
"placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
|
||||
"placeholder-alert-6": "Veuillez configurer l'adaptateur de messagerie pour envoyer du courrier !",
|
||||
"placeholder-alert-7": "Veuillez sélectionner le signataire pour ajouter un espace réservé !",
|
||||
"email-subject": "Sujet du courriel",
|
||||
"email-body": "corps de l'e-mail",
|
||||
"email-placeholder": "Ajouter le corps de l'e-mail",
|
||||
@@ -433,7 +448,8 @@
|
||||
"cutomize-email": "Personnaliser l'e-mail",
|
||||
"upgrade-to-customize-email": "Mise à niveau pour personnaliser l'e-mail",
|
||||
"sign-url": "Signer l'URL",
|
||||
"Mails Sent": "Mails envoyés avec succès !",
|
||||
"mails-sent": "Mails envoyés avec succès !",
|
||||
"mail-not-delivered": "Courrier non distribué",
|
||||
"document-alert": "Alerte document",
|
||||
"owner-subscription-expired": "L'abonnement du propriétaire a expiré.",
|
||||
"subscription-expired": "Abonnement expiré",
|
||||
@@ -444,14 +460,14 @@
|
||||
"decline-alert-3": "Vous ne pouvez pas signer ce document car il a été refusé/révoqué.",
|
||||
"sign here": "Signer ici",
|
||||
"guest-email-alert": "le code de vérification a été envoyé sur votre e-mail",
|
||||
"get-otp-alert": "vous recevrez un mot de passe à usage unique par e-mail",
|
||||
"get-otp-alert": "vous recevrez un code de vérification par e-mail",
|
||||
"user-already-exist": "L'utilisateur existe déjà.",
|
||||
"server-error": "Erreur interne du serveur !",
|
||||
"fill-required-details!": "Veuillez remplir les informations requises !",
|
||||
"fill-required-details!": "Veuillez remplir les informations requises !",
|
||||
"sign-up-form": "Formulaire d'inscription",
|
||||
"sign-up": "S'inscrire",
|
||||
"folder-already-exist!": "le dossier existe déjà !",
|
||||
"please-select-position!": "Veuillez sélectionner un poste !",
|
||||
"please-select-position!": "Veuillez sélectionner un poste !",
|
||||
"default-sign-alert": "Êtes-vous sûr de vouloir vous connecter automatiquement à tous les emplacements demandés ? ",
|
||||
"Your-Signature": "Votre signature",
|
||||
"auto-sign-all": "Tout signer automatiquement",
|
||||
@@ -459,15 +475,15 @@
|
||||
"pdf-download": "Téléchargement PDF",
|
||||
"generate-certificate-alert": " Votre certificat d'achèvement est en cours de génération. Veuillez patienter quelques instants. Si le téléchargement ne démarre pas sous peu, cliquez à nouveau sur le bouton. ",
|
||||
"folder-already-exist": "Le dossier existe déjà !",
|
||||
"folder-created-successfully": "Dossier créé avec succès !",
|
||||
"folder-created-successfully": "Dossier créé avec succès !",
|
||||
"fill-folder-name": "Veuillez indiquer le nom du dossier",
|
||||
"already-exists-this-username": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
|
||||
"verification-code-sent": "Un e-mail de vérification a été envoyé à votre adresse e-mail !",
|
||||
"already-exists-this-username": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
|
||||
"verification-code-sent": "Un e-mail de vérification a été envoyé à votre adresse e-mail !",
|
||||
"registered-user-successfully": "Utilisateur enregistré avec succès.",
|
||||
"role-not-found": "Rôle introuvable.",
|
||||
"do-not-access": "Vous n'y avez pas accès, veuillez contacter l'administrateur.",
|
||||
"add-admin": "Ajouter un administrateur",
|
||||
"opensign-setup": "Configuration d'Opensign",
|
||||
"opensign-setup": "Configuration d'OpenSign",
|
||||
"join-discord": "Rejoignez notre serveur Discord",
|
||||
"admin-already-exist": "L'administrateur existe déjà.",
|
||||
"password-update-alert-1": "Mot de passe mis à jour avec succès.",
|
||||
@@ -498,7 +514,7 @@
|
||||
"error": "Erreur",
|
||||
"invalid-document": "Erreur : document invalide !",
|
||||
"document-not-found": "Document introuvable !",
|
||||
"document-signed-alert-1": "Vous avez signé le document avec succès. Vous pouvez télécharger ou imprimer une copie du document partiellement signé. Une copie du document signé numériquement sera envoyée au propriétaire par e-mail une fois qu'il sera signé par tous les signataires.",
|
||||
"document-signed-alert-1": "Vous avez signé le document avec succès. Vous pouvez télécharger ou imprimer une copie du document partiellement signé. Une copie du document signé numériquement et du certificat d'achèvement sera envoyée au propriétaire et à tous les signataires par e-mail une fois qu'il aura été signé par tous les signataires.",
|
||||
"encrypted-pdf-not-support": "Les fichiers PDF actuellement cryptés ne sont pas pris en charge. ",
|
||||
"document-signed-alert-4": "Toutes nos félicitations! 🎉 Ce document a été signé avec succès par tous les participants !",
|
||||
"document-signed-alert-5": "Ce document a été signé par tous les signataires.",
|
||||
@@ -511,19 +527,19 @@
|
||||
"choose-password": "Choisissez un mot de passe",
|
||||
"enter-password": "Entrer le mot de passe",
|
||||
"sure": "bien sûr",
|
||||
"subscribed-alert": "Vous êtes déjà abonné au forfait !",
|
||||
"subscribed-alert": "Vous êtes déjà abonné au forfait !",
|
||||
"monthly": "Mensuel",
|
||||
"yearly-upto": "Annuel (jusqu'à 66 % de réduction)",
|
||||
"yearly-upto": "Annuel (jusqu'à 66 % de réduction)",
|
||||
"billed-yearly": "Facturé annuellement",
|
||||
"billed-monthly": "Facturé mensuellement",
|
||||
"plansubscription-1": "Hébergez-le vous-même gratuitement",
|
||||
"visit-github": "Visitez Github",
|
||||
"user-already-exist-name": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
|
||||
"user-already-exist-name": "Un utilisateur existe déjà avec ce nom d'utilisateur !",
|
||||
"do-not-access-contact-admin": "Vous n'y avez pas accès, veuillez contacter l'administrateur.",
|
||||
"filed-required-correctly": "Veuillez remplir correctement les informations requises.",
|
||||
"admin-created": "Administrateur créé",
|
||||
"invalid-masterkey": "Clé principale invalide",
|
||||
"opensign-Setup": "Configuration d'Opensign",
|
||||
"opensign-Setup": "Configuration d'OpenSign",
|
||||
"master-key": "La clef maitresse",
|
||||
"profile-update-alert": "Mise à jour du profil réussie.",
|
||||
"date": "Date",
|
||||
@@ -546,10 +562,10 @@
|
||||
"discord": "Discorde"
|
||||
},
|
||||
"tour-mssg": {
|
||||
"home-layout-1": "Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.",
|
||||
"home-layout-1": "Vous vous êtes connecté avec succès ! Jetons un coup d'oeil.",
|
||||
"home-layout-2": "Pour télécharger des documents à auto-signature ou pour demander la signature d'autres personnes, sélectionnez simplement les boutons correspondants.",
|
||||
"home-layout-3": "Vous êtes prêt à commencer à utiliser OpenSign ! Si vous avez besoin d'aide, n'hésitez pas à nous contacter.",
|
||||
"generate-token": "Mettez à niveau maintenant pour générer un jeton API",
|
||||
"home-layout-3": "Vous êtes prêt à commencer à utiliser OpenSign ! Si vous avez besoin d'aide, n'hésitez pas à nous contacter.",
|
||||
"generate-token": "Effectuez la mise à niveau maintenant pour générer un jeton API de production.",
|
||||
"opensign-drive-1": "Cliquez sur les liens du fil d'Ariane pour naviguer facilement dans la hiérarchie des dossiers et afficher les documents dans chaque dossier.",
|
||||
"opensign-drive-2": "Cliquez sur le bouton Ajouter pour créer un nouveau dossier ou document.",
|
||||
"opensign-drive-3": "Triez vos documents par date ou par nom à l'aide de ce menu.",
|
||||
@@ -564,9 +580,10 @@
|
||||
"pdf-request-file-5": "Vous pouvez cliquer sur 'Signer automatiquement tout' pour signer automatiquement à tous les emplacements que vous êtes censé signer. Assurez-vous de lire correctement le document avant de cliquer sur ce bouton.",
|
||||
"placeholder-sign-1": "Sélectionnez un destinataire dans cette liste pour ajouter un espace réservé à l'endroit où il est censé signer. L'espace réservé apparaîtra de la même couleur que le nom du destinataire une fois que vous le déposerez sur le document.",
|
||||
"placeholder-sign-2": "Cliquer sur le bouton 'Ajouter des destinataires' vous permettra d'ajouter plus de signataires.",
|
||||
"placeholder-sign-3": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-4": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-5": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"placeholder-sign-3": "Cliquez sur ce bouton pour ajouter plus de destinataires/signataires au document",
|
||||
"placeholder-sign-4": "Faites glisser ou cliquez sur un champ pour l'ajouter au document.",
|
||||
"placeholder-sign-5": "La zone de contenu PDF affiche déjà les espaces réservés existants du modèle. Pour votre commodité, ces espaces réservés correspondront à la couleur du nom du destinataire, ce qui les rendra facilement identifiables.",
|
||||
"placeholder-sign-6": "Cliquer sur 'Envoyer' enregistrera le document. À l'étape suivante, vous pourrez personnaliser les e-mails à envoyer aux destinataires ou copier les liens de signature et les partager vous-même avec les destinataires.",
|
||||
"report-1": "Cliquez sur le bouton 'Ajouter' pour créer un nouveau modèle. Les modèles sont des documents réutilisables conçus pour générer rapidement de nouveaux documents avec la même structure et différents signataires. Par exemple, un modèle RH pour l'intégration peut avoir des rôles prédéfinis tels que 'Responsable RH' et ' Nouvel employé'. Chaque fois que vous utilisez le modèle, vous pouvez attribuer le rôle 'Nouvel employé' à différents membres du personnel entrants, tandis que le rôle 'Responsable RH' reste constant, facilitant ainsi un processus d'intégration fluide pour chaque recrue",
|
||||
"redirect": "Cliquez sur le bouton 'Utiliser' pour créer un nouveau document à partir d'un modèle existant.",
|
||||
"bulksend": "Pour créer et envoyer rapidement plusieurs documents à l'aide d'un modèle existant, cliquez sur le bouton 'Envoi rapide'.",
|
||||
@@ -583,7 +600,8 @@
|
||||
"Out for signatures": "En cliquant sur cette carte, vous accéderez à une liste de documents en attente de signature.",
|
||||
"Recent signature requests": "Voici une liste de documents qui attendent votre signature.",
|
||||
"Recently sent for signatures": "Il s'agit d'une liste de documents que vous avez envoyés à d'autres parties pour signature.",
|
||||
"Drafts": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi."
|
||||
"Drafts": "Il s'agit de documents que vous avez commencés mais que vous n'avez pas finalisés pour envoi.",
|
||||
"public-template": "Cette vidéo montre comment configurer votre profil public personnalisé, tel que 'https://opensign.me/your-username'. Vous apprendrez également comment personnaliser votre slogan et rendre vos modèles disponibles pour la signature publique."
|
||||
},
|
||||
"enter-email-plaholder": "Ajoutez une adresse email et appuyez sur Entrée",
|
||||
"success-email-alert": "E-mail envoyé avec succès!",
|
||||
@@ -591,5 +609,104 @@
|
||||
"expired-on-mssg": "Ce document a expiré le {{expiredDate}} et n'est plus disponible pour signature.",
|
||||
"signature-validate-alert": "Veuillez confirmer que vous avez coché au moins {{minRequiredCount}} cases.",
|
||||
"signature-validate-alert-2": "Assurez-vous que ce champ est rempli avec précision et répond à toutes les exigences.",
|
||||
"user-name-exist": "le nom d'utilisateur existe déjà"
|
||||
"user-name-exist": "le nom d'utilisateur existe déjà",
|
||||
"remaing-users": "Utilisateurs restants",
|
||||
"Role": "Rôle",
|
||||
"additional-users": "Veuillez acheter des utilisateurs supplémentaires.",
|
||||
"Quantity-of-users": "Quantité d'utilisateurs",
|
||||
"Price": "Prix",
|
||||
"Total-price-for-next-time": "Prix total pour la prochaine fois",
|
||||
"Proceed": "Procéder",
|
||||
"Select": "sélectionner",
|
||||
"Add-seats": "Ajouter des sièges",
|
||||
"format": "format",
|
||||
"select-date-format": "Sélectionnez un format de date",
|
||||
"quantity-of-credits": "Quantité de crédits de prime",
|
||||
"remaining-credits": "Crédits premium disponibles :",
|
||||
"additional-credits": "Veuillez acheter des crédits premium",
|
||||
"remaining-credits-help": "Utilisez des crédits premium pour la signature de documents API, l'envoi groupé ou l'intégration d'OpenSign sur votre site Web. Il vous reste {{allowedcredits}} crédits inclus et {{addoncredits}} crédits achetés supplémentaires.",
|
||||
"quota-err-quicksend": "Quota atteint, vous n'avez pas assez de crédits.",
|
||||
"buy-credits": "Acheter des crédits premium",
|
||||
"rotate-right": "Faire pivoter à droite",
|
||||
"rotate-left": "Faire pivoter à gauche",
|
||||
"rotate-alert-mssg": "Tous les widgets de cette page seront perdus. Êtes-vous sûr de vouloir continuer ?",
|
||||
"templateid": "ID de modèle",
|
||||
"bulk-send-subcription-alert": "Veuillez passer au forfait Professionnel ou Équipe pour utiliser Quicksend.",
|
||||
"generate-test-token": "Générer jeton de test",
|
||||
"regenerate-test-token": "Régénérer le jeton de test",
|
||||
"help-test-token": "Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à l’un de nos forfaits payants pour générer un jeton de production.",
|
||||
"help-api-token": "Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.",
|
||||
"reason": "Raison",
|
||||
"decline-by": "Refusé/révoqué par",
|
||||
"document-declined": "document refusé",
|
||||
"public-template-mssg-1": "Pour intégrer OpenSign dans votre projet React ou Next.js, exécutez simplement la commande suivante :",
|
||||
"public-template-mssg-2": "Assurez-vous que npm ou Yarn est configuré dans votre projet. Si vous utilisez Yarn, vous pouvez remplacer npm install par Yarn Add @opensign/react.",
|
||||
"public-template-mssg-3": "Besoin de plus de détails ou d'exemples ?",
|
||||
"public-template-mssg-4": "Visitez le",
|
||||
"public-template-mssg-5": "npm pour les dernières mises à jour, une documentation détaillée et l'historique des versions.",
|
||||
"public-template-mssg-6": "Avant de pouvoir utiliser cet extrait de code, vous devez rendre ce modèle public.",
|
||||
"copy-code": "COPIE",
|
||||
"copied-code": "COPIÉ",
|
||||
"Installation": "Installation",
|
||||
"Usage": "Usage",
|
||||
"insufficient-credits": "Crédits de signature insuffisants",
|
||||
"insufficient-credits-mssg": "Le propriétaire de ce document ne dispose actuellement pas des crédits OpenSign nécessaires pour que vous puissiez le signer. Veuillez contacter le propriétaire si vous avez besoin de plus de détails.",
|
||||
"angular-npm-mssg-1": "Pour intégrer OpenSign dans votre projet Angular, exécutez simplement la commande suivante :",
|
||||
"quota-mail-info-head": "Limite mensuelle d'e-mails de signatures de demandes",
|
||||
"quota-mail-info": "Vous pouvez envoyer jusqu'à 15 e-mails de demande de signature chaque mois. Mettez à niveau maintenant pour envoyer directement des demandes de signature illimitées.",
|
||||
"quota-mail-reset": "Les crédits de votre email de demande de signature seront réinitialisés le",
|
||||
"quota-mail": "Vous avez atteint votre limite de 15 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.",
|
||||
"quota-mail-tip-tip": "Astuce : Vous pouvez toujours signer un nombre <1>illimité de documents</1> en partageant manuellement les liens de demande de signature.",
|
||||
"quota-mail-head": "Quota atteint",
|
||||
"unauthorized-modal": "Vous n'êtes pas autorisé à effectuer cette action, veuillez contacter {{adminEmail}}.",
|
||||
"sent-this-month": "envoyé ce mois-ci",
|
||||
"available-seats": "Disponible sièges",
|
||||
"buy-users": "Acheter plus d'utilisateurs",
|
||||
"isenable-otp": "Activer la vérification OTP",
|
||||
"isenable-otp-help": {
|
||||
"p1": "Souhaitez-vous activer le processus de vérification à l'aide d'un mot de passe à usage unique (OTP)?",
|
||||
"p2": "La sélection de cette option activera la vérification OTP. Les utilisateurs recevront un code de vérification par e-mail, qu'ils devront saisir pour signer le document.",
|
||||
"p3": "La sélection de cette option désactivera la vérification OTP, permettant aux utilisateurs de signer le document directement sans étapes supplémentaires.",
|
||||
"p4": "Veuillez choisir l'option qui correspond le mieux à vos exigences en matière de signature de documents."
|
||||
},
|
||||
"advanced-options": "Options avancées",
|
||||
"hide-advanced-options": "Masquer les options avancées",
|
||||
"download-files": "Télécharger des fichiers",
|
||||
"download-pdf": "Télécharger le PDF",
|
||||
"pdf-certificate": "Télécharger Pdf + Certificat",
|
||||
"document-logs": "Journaux de documents",
|
||||
"server-down": "Impossible de se connecter au serveur OpenSign. Si vous hébergez vous-même OpenSign, veuillez vous assurer que toutes les étapes de la documentation ont été correctement suivies. Si vous exécutez OpenSign localement, vous y accédez peut-être via un numéro de port incorrect.",
|
||||
"admin-exists": "L'administrateur existe déjà. Veuillez vous connecter à l'application en utilisant les informations d'identification de l'administrateur afin de gérer les utilisateurs.",
|
||||
"enable-tour": "Activer la visite",
|
||||
"istourenabled-otp-help": {
|
||||
"p1": "Cela permettra une visite guidée pour les signataires, fournissant des instructions pendant le processus de signature.",
|
||||
"p2": "Cela désactivera la visite guidée, garantissant ainsi une expérience de signature plus rapide et ininterrompue.",
|
||||
"p3": "Conseil : si vos signataires connaissent OpenSign et l'utilisent fréquemment, vous souhaiterez peut-être désactiver la visite guidée pour une expérience plus fluide. Choisissez l'option qui correspond le mieux à vos besoins en matière de signature de documents."
|
||||
},
|
||||
"form-title-1": "Paramètre du flux de documents",
|
||||
"form-title-2": "Paramètre de sécurité",
|
||||
"public-tour-message": "Le modèle doit être public avant que vous puissiez générer un lien partageable.",
|
||||
"add-user-template": "Vous devez ajouter un rôle avant de pouvoir lui ajouter des champs. ",
|
||||
"pdf-uncompatible": "Ce pdf n'est pas compatible avec opensign, veuillez contacter <support@opensignlabs.com>",
|
||||
"text-field-tour": "Les champs de type 'Texte' doivent être remplis à l'avance avant l'envoi du document. Si vous avez besoin que les signataires fournissent des informations, utilisez plutôt le champ 'Saisie de texte'.",
|
||||
"attach-signer-tour": "Vous devez associer un signataire à chaque rôle. Vous pouvez le faire en cliquant sur cette icône. Une fois que vous avez sélectionné un signataire, il sera attaché à tous les champs associés à ce rôle qui apparaissent dans la même couleur.",
|
||||
"allowed-signature-types": "Types de signature autorisés",
|
||||
"at-least-one-signature-type": "Au moins un type de signature doit être activé.",
|
||||
"expect-default-one-signature-type": "Veuillez activer un type de signature supplémentaire en plus de celui par défaut.",
|
||||
"add-signer-note": "Remarque - Ce contact sera ajouté à votre liste de contacts",
|
||||
"allowed-signature-types-help": {
|
||||
"p1": "Cette préférence de signature contrôle les options de signature disponibles pour vos signataires. Seuls les types de signature que vous sélectionnez apparaîtront dans le widget de signature lorsqu'un document est signé. Par exemple, si vous choisissez uniquement l'option 'Draw' dans les préférences, votre signataire ne verra que l'option 'Draw' dans le widget de signature, tandis que les trois autres options ne seront pas disponibles.",
|
||||
"l1": "Créez une signature manuscrite directement sur l'écran à l'aide de votre doigt ou d'un stylet.",
|
||||
"l2": "Entrez votre nom et choisissez parmi différents styles de police pour créer une signature.",
|
||||
"l3": "Téléchargez une version numérique existante de votre signature.",
|
||||
"l4": "Utilisez un style de signature prédéfini enregistré sous 'Ma signature' dans vos paramètres."
|
||||
},
|
||||
"notify-on-signatures": "Notifier les signatures",
|
||||
"notify-on-signatures-help": {
|
||||
"p1": "Le créateur du document recevra une notification par e-mail chaque fois qu'un signataire signera le document.",
|
||||
"note": "Remarque : L'e-mail de notification ne sera pas envoyé pour le dernier signataire, car le créateur du document recevra à la place un e-mail de fin."
|
||||
},
|
||||
"expiry-date": "Fecha de caducidad",
|
||||
"expiry-date-updated": "La date d'expiration a été prolongée avec succès jusqu'au {{newexpirydate}}",
|
||||
"expiry-date-error": "Veuillez fournir une nouvelle date d'expiration postérieure à la date actuelle"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
border: 1px solid black;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
animation: inAnimation 2s ease-in-out;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
+104
-39
@@ -2,6 +2,7 @@ import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
export const cloudServerUrl = 'http://localhost:8080/app';
|
||||
export const appName = process.env.APP_NAME || 'OpenSign™';
|
||||
export function customAPIurl() {
|
||||
const url = new URL(cloudServerUrl);
|
||||
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
|
||||
@@ -42,36 +43,38 @@ export function replaceMailVaribles(subject, body, variables) {
|
||||
export const saveFileUsage = async (size, fileUrl, userId) => {
|
||||
//checking server url and save file's size
|
||||
try {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
if (userId) {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = { __type: 'Pointer', className: 'partners_Tenant', objectId: tenant.id };
|
||||
try {
|
||||
const tenantCredits = new Parse.Query('partners_TenantCredits');
|
||||
tenantCredits.equalTo('PartnersTenant', tenantPtr);
|
||||
const res = await tenantCredits.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const response = JSON.parse(JSON.stringify(res));
|
||||
const usedStorage = response?.usedStorage ? response.usedStorage + size : size;
|
||||
const updateCredit = new Parse.Object('partners_TenantCredits');
|
||||
updateCredit.id = res.id;
|
||||
updateCredit.set('usedStorage', usedStorage);
|
||||
await updateCredit.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
const newCredit = new Parse.Object('partners_TenantCredits');
|
||||
newCredit.set('usedStorage', size);
|
||||
newCredit.set('PartnersTenant', tenantPtr);
|
||||
await newCredit.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
saveDataFile(size, fileUrl, tenantPtr);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in fetch tenant Id', err);
|
||||
@@ -91,7 +94,7 @@ const saveDataFile = async (size, fileUrl, tenantPtr) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateMailCount = async extUserId => {
|
||||
export const updateMailCount = async (extUserId, plan, monthchange) => {
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', extUserId);
|
||||
@@ -100,13 +103,23 @@ export const updateMailCount = async extUserId => {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('EmailCount', 1);
|
||||
if (plan === 'freeplan') {
|
||||
if (monthchange) {
|
||||
contractUser.set('LastEmailCountReset', new Date());
|
||||
contractUser.set('MonthlyFreeEmails', 1);
|
||||
} else {
|
||||
if (contractUser?.get('MonthlyFreeEmails')) {
|
||||
contractUser.increment('MonthlyFreeEmails', 1);
|
||||
if (contractUser?.get('LastEmailCountReset')) {
|
||||
contractUser.set('LastEmailCountReset', new Date());
|
||||
}
|
||||
} else {
|
||||
contractUser.set('MonthlyFreeEmails', 1);
|
||||
contractUser.set('LastEmailCountReset', new Date());
|
||||
}
|
||||
}
|
||||
}
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_Users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('EmailCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating EmailCount in contracts_Users: ' + error.message);
|
||||
@@ -114,9 +127,15 @@ export const updateMailCount = async extUserId => {
|
||||
};
|
||||
|
||||
export function formatWidgetOptions(type, options) {
|
||||
const colorsArr = ['red', 'black', 'blue', 'yellow'];
|
||||
const fontSizes = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28];
|
||||
const status = options?.required === true ? 'required' : 'optional' || 'required';
|
||||
const defaultValue = options?.default || '';
|
||||
const values = options?.values || [];
|
||||
const color = options?.color ? options.color : 'black';
|
||||
const fontColor = colorsArr.includes(color) ? color : 'black';
|
||||
const size = options?.fontsize ? parseInt(options.fontsize) : 12;
|
||||
const fontSize = fontSizes.includes(size) ? size : 12;
|
||||
switch (type) {
|
||||
case 'signature':
|
||||
return { name: 'signature', status: 'required' };
|
||||
@@ -127,13 +146,34 @@ export function formatWidgetOptions(type, options) {
|
||||
case 'image':
|
||||
return { status: status, name: options.name || 'image' };
|
||||
case 'email':
|
||||
return { status: status, name: options.name || 'email', validation: { type: 'email' } };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'email',
|
||||
validation: { type: 'email' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'name':
|
||||
return { status: status, name: options.name || 'name' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'name',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'job title':
|
||||
return { status: status, name: options.name || 'job title' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'job title',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'company':
|
||||
return { status: status, name: options.name || 'company' };
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'company',
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'date': {
|
||||
let today = new Date();
|
||||
let dd = String(today.getDate()).padStart(2, '0');
|
||||
@@ -147,6 +187,8 @@ export function formatWidgetOptions(type, options) {
|
||||
name: options.name || 'date',
|
||||
response: defaultValue || today,
|
||||
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'textbox':
|
||||
@@ -156,6 +198,8 @@ export function formatWidgetOptions(type, options) {
|
||||
defaultValue: defaultValue,
|
||||
hint: options.hint,
|
||||
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
case 'checkbox': {
|
||||
const arr = options?.values;
|
||||
@@ -175,6 +219,8 @@ export function formatWidgetOptions(type, options) {
|
||||
maxRequiredCount: options?.validation?.maxselections || 0,
|
||||
},
|
||||
defaultValue: selectedvalues || [],
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'radio button': {
|
||||
@@ -185,6 +231,8 @@ export function formatWidgetOptions(type, options) {
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
}
|
||||
case 'dropdown':
|
||||
@@ -193,6 +241,8 @@ export function formatWidgetOptions(type, options) {
|
||||
name: options.name || 'dropdown',
|
||||
values: values,
|
||||
defaultValue: defaultValue,
|
||||
fontColor: fontColor,
|
||||
fontSize: fontSize,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
@@ -210,3 +260,18 @@ export const useLocal = process.env.USE_LOCAL ? process.env.USE_LOCAL.toLowerCas
|
||||
export const smtpsecure = process.env.SMTP_PORT && process.env.SMTP_PORT !== '465' ? false : true;
|
||||
export const smtpenable =
|
||||
process.env.SMTP_ENABLE && process.env.SMTP_ENABLE.toLowerCase() === 'true' ? true : false;
|
||||
|
||||
export const planCredits = {
|
||||
'pro-weekly': 100,
|
||||
'pro-yearly': 240,
|
||||
'professional-monthly': 100,
|
||||
'professional-yearly': 240,
|
||||
'team-weekly': 100,
|
||||
'team-yearly': 500,
|
||||
'teams-monthly': 100,
|
||||
'teams-yearly': 500,
|
||||
};
|
||||
|
||||
export function parseJwt(token) {
|
||||
return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
The OpenSign Enterprise license (the “Enterprise License”)
|
||||
Copyright (c) 2020-2024 Qik Innovations Private Limited.
|
||||
|
||||
With regard to the OpenSign Software:
|
||||
|
||||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the OpenSign Subscription Terms of Service(the “Enterprise Terms”), or other
|
||||
agreement governing the use of the Software, as agreed by you and OpenSign,
|
||||
and otherwise have a valid OpenSign Enterprise license for the
|
||||
correct number of user seats. Subject to the foregoing sentence, you are free to
|
||||
modify this Software and publish patches to the Software. You agree that OpenSign
|
||||
and/or its licensors (as applicable) retain all right, title and interest in and
|
||||
to all such modifications and/or patches, and all such modifications and/or
|
||||
patches may only be used, copied, modified, displayed, distributed, or otherwise
|
||||
exploited with a valid OpenSign Enterprise license for the correct
|
||||
number of user seats. Notwithstanding the foregoing, you may copy and modify
|
||||
the Software for development and testing purposes, without requiring a
|
||||
subscription. You agree that OpenSign and/or its licensors (as applicable) retain
|
||||
all right, title and interest in and to all such modifications. You are not
|
||||
granted any other rights beyond what is expressly stated herein. Subject to the
|
||||
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
|
||||
and/or sell the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
For all third party components incorporated into the OpenSign Software, those
|
||||
components are licensed under the original license provided by the owner of the
|
||||
applicable component.
|
||||
@@ -53,7 +53,7 @@ async function sendMail(doc, signer) {
|
||||
receiver_phone: signer?.Phone || '',
|
||||
expiry_date: localExpireDate,
|
||||
company_name: doc?.ExtUserPtr?.Company || '',
|
||||
signing_url: `<a href=${signPdf}>Sign here</a>`,
|
||||
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
|
||||
};
|
||||
const mail = replaceMailVaribles(subject, body, variables);
|
||||
|
||||
@@ -88,7 +88,7 @@ export default async function autoReminder(request, response) {
|
||||
docQuery.exists('Signers');
|
||||
docQuery.exists('SignedUrl');
|
||||
docQuery.descending('createdAt');
|
||||
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr');
|
||||
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr,ExtUserPtr.TenantId');
|
||||
docQuery.notEqualTo('IsCompleted', true);
|
||||
docQuery.notEqualTo('IsDeclined', true);
|
||||
docQuery.notEqualTo('IsArchive', true);
|
||||
|
||||
@@ -8,6 +8,7 @@ import saveInvoice from './saveInvoice.js';
|
||||
import savePayments from './savePayments.js';
|
||||
import gooogleauth from './googleauth.js';
|
||||
import autoReminder from './autoReminder.js';
|
||||
import validateSmtp from './validateSmtp.js';
|
||||
export const app = express();
|
||||
|
||||
dotenv.config();
|
||||
@@ -22,3 +23,4 @@ app.post('/saveinvoice', saveInvoice);
|
||||
app.post('/savepayment', savePayments);
|
||||
app.post('/googleauth', gooogleauth);
|
||||
app.post('/sendreminder', autoReminder);
|
||||
app.post('/validatesmtp', validateSmtp);
|
||||
|
||||
@@ -1,10 +1,36 @@
|
||||
import { planCredits } from '../../Utils.js';
|
||||
|
||||
export default async function saveSubscription(request, response) {
|
||||
const SubscriptionId = request.body?.data?.subscription?.subscription_id;
|
||||
const body = request.body;
|
||||
const Email = request.body.data?.subscription?.customer?.email;
|
||||
const Next_billing_date = request.body?.data?.subscription?.next_billing_at;
|
||||
const planCode = request.body?.data?.subscription?.plan?.plan_code;
|
||||
|
||||
const addons = request.body?.data?.subscription?.addons || [];
|
||||
const event = request.body?.data?.event_type || '';
|
||||
const credits = planCredits?.[planCode] || 0;
|
||||
const isTeamPlan = planCode?.includes('team');
|
||||
let newAddons = 0;
|
||||
if (addons?.length > 0) {
|
||||
let allowedUsersMonthly = 0;
|
||||
let allowedUsersYearly = 0;
|
||||
addons?.forEach(item => {
|
||||
if (item.addon_code === 'extra-teams-users-monthly') {
|
||||
allowedUsersMonthly += item.quantity;
|
||||
} else if (item.addon_code === 'extra-teams-users-yearly') {
|
||||
allowedUsersYearly += item.quantity;
|
||||
} else if (item.addon_code === 'extra-users') {
|
||||
allowedUsersMonthly += item.quantity;
|
||||
}
|
||||
});
|
||||
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
|
||||
newAddons = allowedUsersMonthly + allowedUsersYearly + 1;
|
||||
}
|
||||
} else {
|
||||
if (planCode === 'teams-yearly' || planCode === 'teams-monthly' || planCode === 'team-weekly') {
|
||||
newAddons = 1;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('Email', Email);
|
||||
@@ -18,6 +44,7 @@ export default async function saveSubscription(request, response) {
|
||||
});
|
||||
const subscription = await subcriptionCls.first({ useMasterKey: true });
|
||||
if (subscription) {
|
||||
const _resSub = JSON.parse(JSON.stringify(subscription));
|
||||
const updateSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
updateSubscription.id = subscription.id;
|
||||
updateSubscription.set('SubscriptionId', SubscriptionId);
|
||||
@@ -28,6 +55,81 @@ export default async function saveSubscription(request, response) {
|
||||
updateSubscription.unset('Next_billing_date');
|
||||
}
|
||||
updateSubscription.set('PlanCode', planCode);
|
||||
if (newAddons > 0) {
|
||||
updateSubscription.set('AllowedUsers', parseInt(newAddons));
|
||||
}
|
||||
let existAddon = 0;
|
||||
let allowedUsersMonthly = 0;
|
||||
let allowedUsersYearly = 0;
|
||||
_resSub.SubscriptionDetails?.data?.subscription?.addons?.forEach(item => {
|
||||
if (item.addon_code === 'extra-teams-users-monthly') {
|
||||
allowedUsersMonthly += item.quantity;
|
||||
} else if (item.addon_code === 'extra-teams-users-yearly') {
|
||||
allowedUsersYearly += item.quantity;
|
||||
} else if (item.addon_code === 'extra-users') {
|
||||
allowedUsersMonthly += item.quantity;
|
||||
}
|
||||
});
|
||||
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
|
||||
existAddon = allowedUsersMonthly + allowedUsersYearly + 1; // + 1 is Admin user
|
||||
} else {
|
||||
if (
|
||||
planCode === 'teams-yearly' ||
|
||||
planCode === 'teams-monthly' ||
|
||||
planCode === 'team-weekly'
|
||||
) {
|
||||
existAddon = 1; // 1 is Admin user
|
||||
}
|
||||
}
|
||||
const isSameAsPrevPlan = subscription?.get('PlanCode') === planCode;
|
||||
if (isSameAsPrevPlan) {
|
||||
const planCredits = subscription?.get('PlanCredits');
|
||||
const existAllowedCredits = subscription?.get('AllowedCredits') || 0;
|
||||
if (planCredits) {
|
||||
const oldAddons = existAddon;
|
||||
const substractedAddon = newAddons - oldAddons;
|
||||
if (isTeamPlan && substractedAddon > 0) {
|
||||
const newCredits = existAllowedCredits + substractedAddon * planCredits;
|
||||
updateSubscription.set('AllowedCredits', newCredits);
|
||||
if (event === 'subscription_created') {
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
} else if (isTeamPlan) {
|
||||
const existCredits = existAddon * planCredits;
|
||||
updateSubscription.set('AllowedCredits', existCredits);
|
||||
if (event === 'subscription_created') {
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
} else {
|
||||
updateSubscription.set('AllowedCredits', planCredits);
|
||||
if (event === 'subscription_created') {
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (credits > 0) {
|
||||
if (isTeamPlan) {
|
||||
const newCredits = newAddons * credits;
|
||||
updateSubscription.set('AllowedCredits', newCredits);
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
} else {
|
||||
updateSubscription.set('AllowedCredits', credits);
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (credits > 0) {
|
||||
if (isTeamPlan) {
|
||||
const newCredits = newAddons * credits;
|
||||
updateSubscription.set('AllowedCredits', newCredits);
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
} else {
|
||||
updateSubscription.set('AllowedCredits', credits);
|
||||
updateSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
}
|
||||
}
|
||||
await updateSubscription.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'update subscription!' });
|
||||
} else {
|
||||
@@ -55,6 +157,19 @@ export default async function saveSubscription(request, response) {
|
||||
createSubscription.unset('Next_billing_date');
|
||||
}
|
||||
createSubscription.set('PlanCode', planCode);
|
||||
if (newAddons > 0) {
|
||||
createSubscription.set('AllowedUsers', parseInt(newAddons));
|
||||
}
|
||||
if (credits > 0) {
|
||||
if (isTeamPlan) {
|
||||
const totalCredits = parseInt(newAddons) * credits;
|
||||
createSubscription.set('AllowedCredits', totalCredits);
|
||||
createSubscription.set('PlanCredits', credits);
|
||||
} else {
|
||||
createSubscription.set('AllowedCredits', credits);
|
||||
createSubscription.set('PlanCredits', credits);
|
||||
}
|
||||
}
|
||||
await createSubscription.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'create subscription!' });
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user