Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12a6a84450 | ||
|
|
1f350fc459 | ||
|
|
d9fd2a5531 | ||
|
|
cd4fd6e3de | ||
|
|
49cd30de96 | ||
|
|
a24ebe25a8 | ||
|
|
3e664bf677 | ||
|
|
6161e975fb | ||
|
|
c2a45c230b | ||
|
|
a629aa0bff | ||
|
|
899678d98a | ||
|
|
076a0f54c5 | ||
|
|
a94e67cd7c | ||
|
|
e78a3991f0 | ||
|
|
593a8499f4 | ||
|
|
70d643a5e2 | ||
|
|
f2b1a54695 | ||
|
|
4353a4f732 | ||
|
|
34c99edf1a | ||
|
|
91f3bdb299 | ||
|
|
276fc974fa | ||
|
|
081e11f9af | ||
|
|
4f056cc9d3 | ||
|
|
d9da25e908 | ||
|
|
99b3436c8c | ||
|
|
452d4d7a1d | ||
|
|
ed613c3424 | ||
|
|
bf70fa0b11 | ||
|
|
8684197d49 | ||
|
|
648d1224b3 | ||
|
|
126b6ad355 | ||
|
|
0512f27e2e | ||
|
|
2c61630017 | ||
|
|
72c2b2dde6 | ||
|
|
29eb78c960 | ||
|
|
aa9ba05dde | ||
|
|
365b074f62 | ||
|
|
af6cb4c17d | ||
|
|
6bcad91b0f | ||
|
|
1aa2b003b2 | ||
|
|
1a27e9addf | ||
|
|
c2891a0858 | ||
|
|
c1b226db96 | ||
|
|
62e1a1bd9b | ||
|
|
58eb71c52d | ||
|
|
c1e6f2b5ab | ||
|
|
ba0c18b591 | ||
|
|
c3a902dcc7 | ||
|
|
5ece3c0694 | ||
|
|
1053540410 | ||
|
|
24773759c2 | ||
|
|
7a6de70169 | ||
|
|
04b2c7c4da | ||
|
|
d11171e91c | ||
|
|
a4a78cb96c | ||
|
|
dfdcde8fcc | ||
|
|
5e1b84d2e6 | ||
|
|
e643d96797 | ||
|
|
1ff4160ca4 | ||
|
|
62f66bf524 | ||
|
|
de3bc37fa5 | ||
|
|
ea660384c3 | ||
|
|
210cb5e754 | ||
|
|
ed45bfef84 | ||
|
|
a8e77e93f7 | ||
|
|
969071bb28 | ||
|
|
76491a77f1 | ||
|
|
9a51eed2bc | ||
|
|
a675a3c8fd | ||
|
|
fea30930eb | ||
|
|
7373b086e4 | ||
|
|
2fc1eda761 | ||
|
|
2c100ad399 | ||
|
|
1cefe01aee | ||
|
|
f1cf395761 | ||
|
|
4af3d8f78b | ||
|
|
cf21ac7405 | ||
|
|
5f11e92c0c | ||
|
|
c3ffda4dd2 | ||
|
|
c929b6b7a9 | ||
|
|
eb2abd3e6d | ||
|
|
1c4c2e87b4 | ||
|
|
4fda5b4531 | ||
|
|
df61e143af | ||
|
|
3a55c35445 | ||
|
|
08476ba3d6 | ||
|
|
db9ae71d67 | ||
|
|
39806e3ad6 | ||
|
|
7882c61b81 | ||
|
|
b8f886902f | ||
|
|
17660dae21 | ||
|
|
7584667643 | ||
|
|
b47d1cff07 | ||
|
|
b99d8e6d6e | ||
|
|
8b15bceac8 | ||
|
|
86b0e90948 | ||
|
|
3bc57a999c | ||
|
|
27730c89e8 | ||
|
|
82eeffa6e4 | ||
|
|
53147caebc | ||
|
|
6004522d5c | ||
|
|
266c2463c9 | ||
|
|
1b215f53e7 | ||
|
|
73b2ba6abd | ||
|
|
c04fd80a38 | ||
|
|
61b123cb61 | ||
|
|
08e805569f | ||
|
|
375aa0b7e7 | ||
|
|
b94e0fa052 | ||
|
|
b6752549ed | ||
|
|
51505af6d8 | ||
|
|
0e5cffa1fa | ||
|
|
885b5287b4 | ||
|
|
5348c6620a | ||
|
|
73c219fa4d | ||
|
|
f4090c0c61 | ||
|
|
cd2be94d29 | ||
|
|
878dca3cd6 | ||
|
|
90cba26904 | ||
|
|
c5c001f030 | ||
|
|
a3ba4e26c3 | ||
|
|
dd95862d5a | ||
|
|
5d3de41e63 | ||
|
|
5cad0a96cd | ||
|
|
e74fd79efc | ||
|
|
6f49fc9126 | ||
|
|
bbf3cc2f44 | ||
|
|
7bf196ef75 | ||
|
|
4b5b3aff49 | ||
|
|
15b0cb132c | ||
|
|
e67f91ea90 | ||
|
|
59350f02fb | ||
|
|
32cde66d64 | ||
|
|
7ba111a275 | ||
|
|
8d3fa6cef8 | ||
|
|
ef7aede146 | ||
|
|
11ae58aad3 | ||
|
|
05e30a5d19 | ||
|
|
64773cafec | ||
|
|
c6be39ffc3 | ||
|
|
376d4293c2 | ||
|
|
67b3752297 | ||
|
|
f463ffc8ed | ||
|
|
6e2aac51b4 | ||
|
|
a9a3c25dfd | ||
|
|
645898ab14 | ||
|
|
3ad1c46455 | ||
|
|
d3d33c85d9 | ||
|
|
c2796f66fb | ||
|
|
f57fcd25c9 | ||
|
|
9bda7dee7f | ||
|
|
7e1f21b02b | ||
|
|
a83acabf1a | ||
|
|
fb99c76b0c | ||
|
|
cc0ff1f5e7 | ||
|
|
afbfd9ed43 | ||
|
|
fcad04461f | ||
|
|
ca98b5a210 | ||
|
|
bd72ef84ed | ||
|
|
cb3c62fb6c | ||
|
|
2976220e4d | ||
|
|
192371c07b | ||
|
|
708a3f4ad9 | ||
|
|
fbcdfa97cf | ||
|
|
4e39762b46 | ||
|
|
4239e8d5fb | ||
|
|
c8ee455865 | ||
|
|
d5be7b8c57 | ||
|
|
bc2f4f1ef3 | ||
|
|
e72baf3c1f | ||
|
|
b2b52b6af7 | ||
|
|
4f417ccf0f | ||
|
|
780c3f5f6d | ||
|
|
f9eff12f94 | ||
|
|
e912a8d4e0 | ||
|
|
224af292d6 | ||
|
|
e07a4457ec | ||
|
|
58cd0495e6 | ||
|
|
6b9b2afbef | ||
|
|
c55a8d09a2 | ||
|
|
71b06f92ec | ||
|
|
692ec8ecbf | ||
|
|
523ea7f314 | ||
|
|
f48444f21d | ||
|
|
b4c5b3cbe6 | ||
|
|
ed88ae013d | ||
|
|
0fe7937808 | ||
|
|
564f57b879 | ||
|
|
a47dc8e767 | ||
|
|
41e192acb0 | ||
|
|
ff4fb191b8 | ||
|
|
2feee80168 | ||
|
|
59fed8a9cc | ||
|
|
7cfca0a650 | ||
|
|
6f1a5fd3a9 | ||
|
|
cd6869778a | ||
|
|
2e8d84430e | ||
|
|
b8f32a6d8d | ||
|
|
11ab37baf1 | ||
|
|
9dbde43a44 | ||
|
|
2a24d71ee3 | ||
|
|
134444c20a | ||
|
|
44b87f178e | ||
|
|
666636d554 | ||
|
|
5ec9d59382 | ||
|
|
addc7622ab | ||
|
|
1c7c3a4777 | ||
|
|
2a46b02487 | ||
|
|
5974515362 | ||
|
|
3fab1d9c6f | ||
|
|
a283f2ee5d | ||
|
|
934823fad4 | ||
|
|
351ed795ca | ||
|
|
a335d02156 | ||
|
|
3e0747164a | ||
|
|
8ad396d3ba | ||
|
|
26b54f0c00 | ||
|
|
de5b58fff6 | ||
|
|
b9b329dde4 | ||
|
|
2581500307 | ||
|
|
ef5c20a9f2 | ||
|
|
01aee7c028 | ||
|
|
569c7f4b48 | ||
|
|
bb849f175c | ||
|
|
ede0eea944 | ||
|
|
47150cca41 | ||
|
|
86df98a6a4 | ||
|
|
8dd5e90f8b | ||
|
|
f31023e2ed | ||
|
|
740d2e5a6c | ||
|
|
20d37e1ac9 | ||
|
|
f3f79ed499 | ||
|
|
c581222022 | ||
|
|
a3fd738b46 | ||
|
|
3f16e973eb | ||
|
|
cb1013762f | ||
|
|
74e275c5d0 | ||
|
|
dbeb513f65 | ||
|
|
6f4561a38f | ||
|
|
12507190d8 | ||
|
|
85dac65030 | ||
|
|
4f1708619e | ||
|
|
84b545c37d | ||
|
|
b24086e679 | ||
|
|
8308376f53 | ||
|
|
137f371528 | ||
|
|
5707039002 | ||
|
|
d4fabf0fe9 | ||
|
|
a4a0630102 | ||
|
|
439ac7eac6 | ||
|
|
ef1b82f426 | ||
|
|
e7b9f3f121 | ||
|
|
14b4d10be3 | ||
|
|
03750e3145 | ||
|
|
ff4879a984 | ||
|
|
7a6a543df5 | ||
|
|
edda20940b | ||
|
|
987a4bd04c | ||
|
|
35d20bd42c | ||
|
|
97983c423f | ||
|
|
68050e3a35 | ||
|
|
4f2801d901 | ||
|
|
aaa3dfc304 | ||
|
|
7a9bebe035 | ||
|
|
ce86510d8e | ||
|
|
9d15b035c5 | ||
|
|
156a05f6ed | ||
|
|
22de053cd3 | ||
|
|
13845adbd5 | ||
|
|
ef68f2da16 | ||
|
|
9114908521 | ||
|
|
f565edc655 | ||
|
|
b0e26b8ae2 | ||
|
|
b35d8406a3 | ||
|
|
3910486b17 | ||
|
|
60ba77a06f | ||
|
|
f86ea04a02 | ||
|
|
9e185fa33c | ||
|
|
5142a8c62d | ||
|
|
db4b5f0452 | ||
|
|
042ad8465e | ||
|
|
ebfd9d3af7 | ||
|
|
c0ae0c7ffa | ||
|
|
861a065849 | ||
|
|
3884909965 | ||
|
|
6a0788c2a1 | ||
|
|
bd60f91734 | ||
|
|
b58c3fd467 | ||
|
|
9d431af0b9 | ||
|
|
cb1ebc6a72 | ||
|
|
a198bce3e2 | ||
|
|
e5c50a0dbb | ||
|
|
caa1dfaf34 | ||
|
|
eb01a0b17d | ||
|
|
1add575d26 | ||
|
|
4a52d327ce | ||
|
|
ef68fe1c85 | ||
|
|
83fa9c9ebd | ||
|
|
1a774bfcfa | ||
|
|
db9ec6bfd5 | ||
|
|
5736306f8c | ||
|
|
67cb508944 | ||
|
|
cbcfb8c063 | ||
|
|
da846dcbbb | ||
|
|
e7280bf017 | ||
|
|
9692f57663 | ||
|
|
f0655893eb | ||
|
|
f9e59905b7 | ||
|
|
c115c7c757 | ||
|
|
4edcf807ad | ||
|
|
416d8c823c | ||
|
|
bea158b8f2 | ||
|
|
39c6560a2d | ||
|
|
509b004014 | ||
|
|
f1b7e1164b | ||
|
|
3a2f626cb9 | ||
|
|
7cde6fd73c | ||
|
|
06d6eecaf0 | ||
|
|
c9ed29261d | ||
|
|
87fe5cdd6f | ||
|
|
f74819f50a | ||
|
|
98c8af5b9c | ||
|
|
ca17f1cfc9 | ||
|
|
74e2582ff9 | ||
|
|
8bbbf9b3d8 | ||
|
|
048629dd32 | ||
|
|
fe03da1d97 | ||
|
|
0a6622159f | ||
|
|
7a5cdefcfa | ||
|
|
0a3d89d0e2 | ||
|
|
882b97da5e | ||
|
|
fa405752da | ||
|
|
2d7571af5a | ||
|
|
07a57969ac | ||
|
|
01b7b1cab8 | ||
|
|
fee3d8f672 | ||
|
|
f1628e46fa | ||
|
|
17cb4479cb | ||
|
|
b52511113b | ||
|
|
95cc15ec4c | ||
|
|
1a4b4655c8 | ||
|
|
9fc69ba7e6 | ||
|
|
8ce31d3d80 | ||
|
|
d097f9a741 | ||
|
|
8686a35405 | ||
|
|
a92d90db4a | ||
|
|
bfa0e0e144 | ||
|
|
d3b2e1f24b | ||
|
|
bc90da4cf2 | ||
|
|
61afc35ac8 | ||
|
|
00e9396590 | ||
|
|
bbedb13f3e | ||
|
|
55cfbae8a7 | ||
|
|
bfa19eec0c | ||
|
|
07851ccde2 | ||
|
|
ad5776cfba | ||
|
|
a13478fa64 | ||
|
|
85a1c10d26 | ||
|
|
6918282e31 | ||
|
|
fd778c25e6 | ||
|
|
af344c8ae2 | ||
|
|
c505f8cf86 | ||
|
|
52320e0cd9 | ||
|
|
214db474c2 | ||
|
|
1bffabc94d | ||
|
|
7c8a5e02f2 | ||
|
|
a49c76c188 | ||
|
|
1f16123865 | ||
|
|
e85bbb7bd9 | ||
|
|
34fc3afb1e | ||
|
|
67479a9c1e | ||
|
|
2a8cedca58 | ||
|
|
57fc4aa06b | ||
|
|
39f3f73896 | ||
|
|
f4e907de36 | ||
|
|
72f7e1fb27 | ||
|
|
3dc109c3dd | ||
|
|
83df89c115 | ||
|
|
4070409a59 | ||
|
|
bb732d7606 | ||
|
|
70963c06b8 | ||
|
|
a1a84b6928 | ||
|
|
eefaf5fe50 | ||
|
|
9500b2d678 | ||
|
|
221a49bb4a | ||
|
|
babe17838b | ||
|
|
f5d11f9ba6 | ||
|
|
e52c7c97cf | ||
|
|
ebdc5ef566 | ||
|
|
939d917b7b | ||
|
|
2afb868edc | ||
|
|
a5fe4a345b | ||
|
|
629bf7f51f | ||
|
|
350854ab6b | ||
|
|
cc980758fd | ||
|
|
0f1ce43cc5 | ||
|
|
5e1a9cdc20 | ||
|
|
550ce10c92 | ||
|
|
6080c59bd7 | ||
|
|
273a35d42f | ||
|
|
2f195fe02e | ||
|
|
0715ca9d85 | ||
|
|
a8d564d74d | ||
|
|
89ba3e0fa4 | ||
|
|
eebcfe94d9 | ||
|
|
ea9b0370c4 | ||
|
|
e264511a6e | ||
|
|
1ec10fbcfc | ||
|
|
274620d17b | ||
|
|
06db7d2360 | ||
|
|
2b3549e4df | ||
|
|
d16478792c | ||
|
|
7ba2ef6420 | ||
|
|
b5d98796b7 | ||
|
|
97c17cfe84 | ||
|
|
4b4e200090 | ||
|
|
232d38440d | ||
|
|
341bd9bef0 | ||
|
|
2f4d04c722 | ||
|
|
148ae40e1a | ||
|
|
9cb0ced4f6 | ||
|
|
9a3b70de43 | ||
|
|
742b8f7eae | ||
|
|
abc58941e3 | ||
|
|
aac200bfd3 | ||
|
|
27d3297d61 | ||
|
|
72431b2c82 | ||
|
|
3ca48e5848 | ||
|
|
14784ec80f | ||
|
|
127741eb27 | ||
|
|
2ed987fe10 | ||
|
|
7aaa1876c8 | ||
|
|
390ffb83a2 | ||
|
|
d63e51b8cb | ||
|
|
7bd8d64649 | ||
|
|
f9e6f49ede | ||
|
|
b701d2863f | ||
|
|
cf47093227 | ||
|
|
81d0aea66c | ||
|
|
506ca35be8 | ||
|
|
1cd6718489 | ||
|
|
153c5db4b5 | ||
|
|
ac9367a715 | ||
|
|
081dae0b90 | ||
|
|
e286390aec | ||
|
|
cf88eb2124 | ||
|
|
3226e4fd85 | ||
|
|
cab94eb63d | ||
|
|
bbd5d6805e | ||
|
|
2904ba3085 | ||
|
|
ee609061ed | ||
|
|
6c98a4e899 | ||
|
|
eef9ee7f3c | ||
|
|
2cb4575db1 | ||
|
|
e7bcad80cc | ||
|
|
353839a1dc | ||
|
|
e6d30744ba | ||
|
|
b67932f448 | ||
|
|
c185c42b80 | ||
|
|
4648ace84e | ||
|
|
ccdf50dbe0 | ||
|
|
852a3dc8bb | ||
|
|
d49b7bd201 | ||
|
|
07f2ceed6e | ||
|
|
751c7b5787 | ||
|
|
e82384dff8 | ||
|
|
92d3a85147 | ||
|
|
c5762a0baf | ||
|
|
dc40b04a18 | ||
|
|
1cca5a3085 | ||
|
|
4fe4ca638c | ||
|
|
b8c7110c63 | ||
|
|
288365b74b | ||
|
|
b57e17c01c | ||
|
|
711ab6a556 | ||
|
|
7a12f54587 | ||
|
|
056f4b3e80 | ||
|
|
730a7a4e15 | ||
|
|
a4b4dc885c | ||
|
|
8562a4bb46 | ||
|
|
d3b56432e0 | ||
|
|
527c5be42c | ||
|
|
6c1d0c712a | ||
|
|
fd0ec5fd31 | ||
|
|
f5f19c9b52 | ||
|
|
642ef4f5b7 | ||
|
|
66b65e600b | ||
|
|
01a130ef8c | ||
|
|
c3bccb5412 | ||
|
|
b32e6e71f8 | ||
|
|
3b5875e283 | ||
|
|
da42fe97ae | ||
|
|
56bb7d36eb | ||
|
|
d86b7345d7 | ||
|
|
c1c17160ff | ||
|
|
d400c843e4 | ||
|
|
4e67ab50fe | ||
|
|
74c9e21d55 | ||
|
|
fefcdf6ffc | ||
|
|
0c14d71fa8 | ||
|
|
5170e4eee2 | ||
|
|
8ebfd781c3 | ||
|
|
4e8988bf57 | ||
|
|
c26734751f | ||
|
|
ca6b4e7d82 | ||
|
|
cb4a94d036 | ||
|
|
300ddb810f | ||
|
|
0b687310fd | ||
|
|
3e741ac2f9 | ||
|
|
f65b23ca26 | ||
|
|
92527e5847 | ||
|
|
db9b8d2ba1 | ||
|
|
4866447f33 | ||
|
|
2b345ff840 | ||
|
|
b47bf437a9 | ||
|
|
c44149050c | ||
|
|
0576123a77 |
@@ -1,6 +1,5 @@
|
||||
# Frontend config ************************************************************************************************************************************************************************************************
|
||||
# Set CI to false while running the app locally
|
||||
CI=false
|
||||
|
||||
# Set it to the URL form where the app home page will be accessed
|
||||
PUBLIC_URL=https://app.opensignlabs.com/
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
@@ -8,13 +7,13 @@ GENERATE_SOURCEMAP=false
|
||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
REACT_APP_SERVERURL=https://app.opensignlabs.com/api/app
|
||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||
REACT_APP_APPID=aDsADsqweqwe
|
||||
REACT_APP_APPID=opensign
|
||||
# Google tag manager container id for configuring GTM on all pages of the app
|
||||
REACT_APP_GTM=GTM-123N7LD
|
||||
|
||||
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
||||
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
||||
APP_ID=aDsADsqweqwe
|
||||
APP_ID=opensign
|
||||
# Name of the app. It will be visible in the verification emails sent out.
|
||||
appName=open_sign_server
|
||||
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
# When used as it is, this env config will connect to our hosted UAT backend
|
||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||
|
||||
# Set CI to false while running the app locally
|
||||
CI=false
|
||||
# Set it to the URL form where the app home page will be accessed
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
@@ -11,4 +9,4 @@ GENERATE_SOURCEMAP=false
|
||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
|
||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||
REACT_APP_APPID=opensignstgn
|
||||
REACT_APP_APPID=opensign
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
# When used as it is, this env config will connect to our hosted UAT backend
|
||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||
|
||||
# Set CI to false while running the app locally
|
||||
CI=false
|
||||
# Set it to the URL form where the app home page will be accessed
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
@@ -11,18 +9,18 @@ GENERATE_SOURCEMAP=false
|
||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
REACT_APP_SERVERURL=http://localhost:8080/app
|
||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||
REACT_APP_APPID=opensignstgn
|
||||
REACT_APP_APPID=opensign
|
||||
|
||||
|
||||
# Backend ExpressJS config ****************************************************************************************************************************************************************************************
|
||||
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
|
||||
APP_ID=opensignstgn
|
||||
APP_ID=opensign
|
||||
# Name of the app. It will be visible in the verification emails sent out.
|
||||
appName=open_sign_server
|
||||
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
||||
MASTER_KEY=XnAadwKxxByMr
|
||||
# Mongodb URI to connect to
|
||||
MONGODB_URI=mongodb://host.docker.internal:27017/OpenSignDB
|
||||
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
|
||||
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
|
||||
PARSE_MOUNT=/app
|
||||
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
@@ -45,14 +43,14 @@ DO_REGION=us-west
|
||||
USE_LOCAL=TRUE
|
||||
|
||||
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||
MAILGUN_API_KEY=XXXXX
|
||||
MAILGUN_API_KEY=
|
||||
MAILGUN_DOMAIN=mail.yourdomain.com
|
||||
MAILGUN_SENDER=postmaster@mail.yourdomain.com
|
||||
SMTP_ENABLE=
|
||||
SMTP_HOST=
|
||||
SMTP_PORT=
|
||||
SMTP_USER_EMAIL=
|
||||
SMTP_PASS=
|
||||
SMTP_HOST=smtp.yourhost.com
|
||||
SMTP_PORT=443
|
||||
SMTP_USER_EMAIL=mailer@yourdomain.com
|
||||
SMTP_PASS=password # if your password includes spaces then write password in single quotes ('asdf pasd asdf bgds').
|
||||
|
||||
|
||||
# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
|
||||
|
||||
@@ -2,7 +2,7 @@ name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
@@ -11,9 +11,9 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- image: amolshejole/OpenSign
|
||||
dockerfile: apps/OpenSign/Dockerfile
|
||||
dockerfile: apps/OpenSign/Dockerhubfile
|
||||
- image: amolshejole/OpenSignServer
|
||||
dockerfile: apps/OpenSignServer/Dockerfile
|
||||
dockerfile: apps/OpenSignServer/Dockerhubfile
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
@@ -31,8 +31,11 @@ jobs:
|
||||
with:
|
||||
images: ${{ matrix.image }}
|
||||
-
|
||||
name: Debug - List files
|
||||
name: Debug - List files opensign
|
||||
run: ls -R apps/OpenSign/ # Adjust the path as needed
|
||||
-
|
||||
name: Debug - List files opensignserver
|
||||
run: ls -R apps/OpenSignServer/ # Adjust the path as needed
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Deploy to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- feat-docs
|
||||
# Review gh actions docs if you want to further define triggers, paths, etc
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy to GitHub Pages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: npm
|
||||
- name: Install dependencies
|
||||
working-directory: ./docs
|
||||
run: npm ci
|
||||
- name: Clean api-docs
|
||||
working-directory: ./docs
|
||||
run: npm run docusaurus clean-api-docs all
|
||||
- name: Build api-docs
|
||||
working-directory: ./docs
|
||||
run: npm run docusaurus gen-api-docs all
|
||||
- name: Build website
|
||||
working-directory: ./docs
|
||||
run: npm run build
|
||||
|
||||
# Popular action to deploy to GitHub Pages:
|
||||
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Build output to publish to the `gh-pages` branch:
|
||||
publish_dir: ./docs/build
|
||||
# The following lines assign commit authorship to the official
|
||||
# GH-Actions bot for deploys to `gh-pages` branch:
|
||||
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
|
||||
# The GH actions bot is used by default if you didn't specify the two fields.
|
||||
# You can swap them out with your own user credentials.
|
||||
user_name: github-actions[bot]
|
||||
user_email: 41898282+github-actions[bot]@users.noreply.github.com
|
||||
@@ -1,149 +1,29 @@
|
||||
|
||||
# INSTALLATON INSTRUCTIONS
|
||||
# INSTALLATON INSTRUCTIONS HAS A NEW HOME
|
||||
|
||||
You can use our app as a cloud version from [OpenSignLabs](https://www.opensignlabs.com)
|
||||
We've updated our installation instructions and moved them to our dedicated documentation portal to provide you with a better experience and updated guidance. Whether you're looking to self-host OpenSign™ or contribute to its development, you can find detailed instructions tailored to your needs at [docs.opensignlabs.com](https://docs.opensignlabs.com).
|
||||
|
||||
or follow below instructions to install it on your own infrastructure.
|
||||
## Self Hosting
|
||||
|
||||
- Localhost(Frontend only)
|
||||
- Digital Ocean
|
||||
- Localhost(Docker)
|
||||
If you're interested in self-hosting OpenSign™, our new documentation portal provides comprehensive, step-by-step instructions to help you set up OpenSign™ in your own environment. This section is designed for system administrators and those looking to deploy OpenSign™ within their organization or for personal use.
|
||||
|
||||
## Localhost(Frontend only)
|
||||
- **Access Self Hosting Instructions:** [Self Hosting Guide](https://docs.opensignlabs.com/docs/category/docker)
|
||||
|
||||
This is the easiest way to run the frontend application for development or testing purpose without the hassle of installing backend & DB. All the features including document upload, signing, email notifications will work seamlessly from staging backend.
|
||||
## Contributors
|
||||
|
||||
**Warning:** All data, including user accounts and documents, is stored in the staging backend. This data will be erased every time there is a merge to the main branch. As a result, you may need to recreate user accounts repeatedly. If you find that login attempts are failing, it is likely because the data has been cleared due to a recent merge.
|
||||
For developers and contributors who are looking to build upon or contribute to OpenSign™, we've prepared a separate section that covers the setup, development environment configuration, and guidelines for contributing to the OpenSign™ project.
|
||||
|
||||
Below are the steps to follow -
|
||||
- [Clone the repository](https://help.github.com/articles/cloning-a-repository/) to your local machine using below command -
|
||||
```
|
||||
git clone https://github.com/OpenSignLabs/OpenSign.git
|
||||
```
|
||||
- Copy the .env.frontend_dev file to apps/OpenSign/.env using below command(on mac & linux). For windows use COPY command instead.
|
||||
```
|
||||
cp .env.frontend_dev apps/OpenSign/.env
|
||||
```
|
||||
- CD to /apps/OpenSign directory
|
||||
- Install NPM packages using
|
||||
```
|
||||
npm install
|
||||
```
|
||||
- Run the project locally using
|
||||
```
|
||||
npm run start
|
||||
```
|
||||
You should be able to access the application from http://localhost:3000 after this.
|
||||
Create an account by signing-up and start contributing.
|
||||
- **Access Contributor Instructions:** [Contributors Guide](https://docs.opensignlabs.com/docs/contribute/INSTALLATION)
|
||||
|
||||
## Additional Resources
|
||||
|
||||
## Digital Ocean
|
||||
Should you need further assistance or have any questions, please feel free to reach out to us through our support channels:
|
||||
|
||||
OpenSign application consistes of 3 components -
|
||||
- ReactJS frontend
|
||||
- NodeJS API
|
||||
- MongoDB database
|
||||
- **Twitter:** [OpenSignHQ on Twitter](https://twitter.com/OpenSignHQ)
|
||||
- **Facebook:** [OpenSign on Facebook](https://www.facebook.com/profile.php?id=61551030403669)
|
||||
- **LinkedIn:** [OpenSign™ on LinkedIn](https://www.linkedin.com/company/opensign%E2%84%A2/)
|
||||
- **Discord:** [Join our Discord Community](https://discord.com/invite/opensign)
|
||||
- **YouTube:** [OpenSignHQ on YouTube](https://www.youtube.com/@opensignhq)
|
||||
|
||||
You can install all 3 components on digital ocean using the button below -
|
||||
We are committed to providing you with the support you need to successfully install and use OpenSign™. Visit our website at [www.opensignlabs.com](https://www.opensignlabs.com) for more information about our project and its features.
|
||||
|
||||
[](https://cloud.digitalocean.com/apps/new?repo=https://github.com/OpenSignLabs/Deploy-OpenSign-to-Digital-Ocean/tree/main&refcode=30db1c901ab0)
|
||||
|
||||
## You will need to create an AWS S3 bucket or digital ocean space in order to store your uploaded documents
|
||||
|
||||
### AWS S3 -
|
||||
- Step 1 : Create a S3 bucket
|
||||
- Login to AWS console
|
||||
- Navigate to S3 under services
|
||||
- Hit "Create Bucket" button on upper right corner
|
||||
- Remove the check from "block all public access" checkbox(we need this in order to provide access to not-logged in users after OTP verification)
|
||||
- Set bucket versioning and tags as per your requirements
|
||||
- Hit "Create bucket" button
|
||||
- Step 2 : Create IAM user and provide access to AWS bucket
|
||||
- Search for "IAM" on the search bar in AWS console
|
||||
- On IAM dashboard, click the number of users(count) under IAM resources table
|
||||
- Hit "create user" button on the upper right corner of the page
|
||||
- Enter the user name & click next
|
||||
- Click create policy, search for S3 and provide the Read, Write & list permissions
|
||||
- Click next and click "Create user"
|
||||
- Step 3 : Generate Credentials
|
||||
- Go to IAM/Users in AWS console
|
||||
- Hit the hyperlink for the user created in the previous step
|
||||
- Click the "Security credentials" tab
|
||||
- Scroll down to "Access keys" and hit "Create access key"
|
||||
- In the next step select "Application running outside AWS"
|
||||
- Add a description tag if needed & hit "Create access key"
|
||||
- In the next step you will see "Access key" and "Secret Access key". Copy both the values.
|
||||
- Set the value of "Access key" to "DO_ACCESS_KEY_ID" environment variable
|
||||
- Set the value of "Secret Access key" to "DO_SECRET_ACCESS_KEY" environment variable
|
||||
- Step 3 : Copy bucket credentials
|
||||
- Visit "Amazon S3 -> Buckets" in aws console
|
||||
- Click the bucket created in previous steps & visit the properties tab
|
||||
- Under "Bucket overview" you will find the value of AWS region(for ex. ap-south-1). Set that value to env variable "DO_REGION"
|
||||
- You can create the value for "DO_ENDPOINT" env variable by appending the region value to amazonaws.com (for ex. s3.ap-south-1.amazonaws.com)
|
||||
- You can create the value for "DO_BASEURL" by adding the bucketname in front of the endpoint value(for ex. https://bucketname.s3.ap-south-1.amazonaws.com)
|
||||
|
||||
Visit below link if you face any issues while following the above instructions -
|
||||
- https://repost.aws/knowledge-center/create-access-key
|
||||
|
||||
|
||||
## Localhost(Docker)
|
||||
|
||||
For local Setup we need to need following prerequisite:
|
||||
|
||||
Environment Varaibles:
|
||||
|
||||
| Environment Varibale | Value | Description |
|
||||
| ------------- | ------------- | ------------- |
|
||||
| CI | false | Set CI to false while running the app locally |
|
||||
| PUBLIC_URL | http://localhost:3000 | Set it to the URL form where the app home page will be accessed |
|
||||
| GENERATE_SOURCEMAP | false | Set it to true if you want to generate the Sourcemap for debugging |
|
||||
| REACT_APP_SERVERURL | http://localhost:8080/app | Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead) |
|
||||
| REACT_APP_APPID | opensignstgn | A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API. |
|
||||
| APP_ID | opensignstgn | A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App. |
|
||||
| appName | open_sign_server | Name of the app. It will be visible in the verification emails sent out. |
|
||||
| MASTER_KEY | XnAadwKxxByMr | A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database. |
|
||||
| MONGODB_URI | mongodb://host.docker.internal:27017/OpenSignDB | Mongodb URI to connect to |
|
||||
| PARSE_MOUNT |/app | Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions. |
|
||||
| SERVER_URL | http://127.0.0.1:8080/app | Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead) |
|
||||
| DO_SPACE | DOSPACENAME | Digital ocean space name or AWS S3 bucket name for uploading documents |
|
||||
| DO_ENDPOINT | ams3.digitaloceanspaces.com | Digital ocean spaces endpoint or AWS S3 endpoint for uploading documents |
|
||||
| DO_BASEURL |https://DOSPACENAME.ams3.digitaloceanspaces.com | Digital ocean baseurl or AWS S3 base URL |
|
||||
| DO_ACCESS_KEY_ID | YOUR_S3_ACCESS_ID | Digital ocean spaces access key ID or AWS s3 Access key ID for uploading the docs |
|
||||
| DO_SECRET_ACCESS_KEY | YOUR_S3_ACCESS_KEY | Digital ocean spaces secret access key or AWS s3 secret access key for uploading the docs |
|
||||
| DO_REGION | YOUR_S3_REGION | Digital ocean spaces region or AWS s3 region |
|
||||
| USE_LOCAL | FALSE | To use local file storage to save file |
|
||||
| MAILGUN_API_KEY | YOUR_MAILGUNAPI_KEY | Mailgun API Key |
|
||||
| MAILGUN_DOMAIN | YOUR_MAILGUNAPI_DOMAIN | Mailgun API Domain |
|
||||
| MAILGUN_SENDER | - | Mailgun Sender Mail ID |
|
||||
| PFX_BASE64 | - | Base64 encoded PFX or p12 document signing certificate file. You can generate base64 encoded self sign certificate using the passphrase `emudhra` |
|
||||
|
||||
# Steps to Generate Self Sign Certificate
|
||||
```
|
||||
# execute below command and use passphrase emudhra
|
||||
openssl genrsa -des3 -out ./cert/local_dev.key 2048
|
||||
openssl req -key ./cert/local_dev.key -new -x509 -days 365 -out ./cert/local_dev.crt
|
||||
openssl pkcs12 -inkey ./cert/local_dev.key -in ./cert/local_dev.crt -export -out ./cert/local_dev.pfx
|
||||
openssl base64 -in ./cert/local_dev.pfx -out ./cert/base64_pfx
|
||||
```
|
||||
|
||||
# CORS Configuration
|
||||
|
||||
As document storage is delegated to S3-compatible services that reside in a different host than the OpenSign one, document operations (loading, storing, deleting) are subject to [Cross-Origin Resource Sharing](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) restriction policies; as a consequence, OpenSign app may fail with (browser console) errors like the following:
|
||||
```
|
||||
Access to fetch at 'https://foo.nyc3.digitaloceanspaces.com/exported_file_4627_0000-00-00T00%3A45%3A43.344Z.pdf'
|
||||
from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header
|
||||
is present on the requested resource. If an opaque response serves your needs, set the request's mode to
|
||||
'no-cors' to fetch the resource with CORS disabled.
|
||||
```
|
||||
|
||||
In order to address this, your document storage system must be instructed to accept requests from other hosts; below the relevant documentation links:
|
||||
- [How to Configure CORS on DigitalOcean Spaces](https://docs.digitalocean.com/products/spaces/how-to/configure-cors/)
|
||||
- [Configuring cross-origin resource sharing on AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html)
|
||||
|
||||
# Build Local Environment
|
||||
|
||||
Command to build project -
|
||||
- Execute `make build`
|
||||
|
||||
Command to run project -
|
||||
- Execute `make run`
|
||||
@@ -1,7 +1,6 @@
|
||||
build:
|
||||
cp .env.local_dev .env
|
||||
rm -rf apps/OpenSign/public/mfbuild
|
||||
cd microfrontends/SignDocuments && npm install && npm run build
|
||||
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
|
||||
docker compose up --build --force-recreate
|
||||
|
||||
run:
|
||||
|
||||
@@ -38,10 +38,11 @@
|
||||
7. [Acknowledgments](#acknowledgments)
|
||||
|
||||
---
|
||||
Please star ⭐ the repo to support us! 😀
|
||||
|
||||
### Introduction
|
||||
|
||||
Welcome to OpenSign, an open-source document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
||||
Welcome to OpenSign, the premier open-source docusign alternative - document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:16
|
||||
FROM node:18
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /usr/src/app
|
||||
@@ -21,5 +21,5 @@ EXPOSE 3000
|
||||
# ENV NODE_ENV production
|
||||
|
||||
# Run the application
|
||||
ENTRYPOINT npm run start-dev
|
||||
ENTRYPOINT npm run start
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Use an official Node runtime as the base image
|
||||
FROM node:18
|
||||
|
||||
# Set the working directory inside the container
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package.json and package-lock.json first to leverage Docker cache
|
||||
COPY apps/OpenSign/package*.json ./
|
||||
|
||||
# Install application dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy the current directory contents into the container
|
||||
COPY apps/OpenSign/ .
|
||||
COPY apps/OpenSign/.husky .
|
||||
|
||||
# build
|
||||
RUN npm run build
|
||||
|
||||
# Make port 3000 available to the world outside this container
|
||||
EXPOSE 3000
|
||||
|
||||
# Define environment variables if needed
|
||||
# ENV NODE_ENV production
|
||||
|
||||
# Run the application
|
||||
CMD ["npm", "start"]
|
||||
@@ -1,29 +0,0 @@
|
||||
const { dependencies } = require("./package.json");
|
||||
|
||||
module.exports = {
|
||||
name: "shell_app", // change me
|
||||
filename: "remoteEntry.js",
|
||||
exposes: {},
|
||||
remotes: {},
|
||||
shared: {
|
||||
...dependencies,
|
||||
react: {
|
||||
singleton: true,
|
||||
import: "react", // fallback is also react
|
||||
shareScope: "default",
|
||||
requiredVersion: dependencies.react
|
||||
},
|
||||
"react-dom": {
|
||||
singleton: true,
|
||||
requiredVersion: dependencies["react-dom"]
|
||||
},
|
||||
"react-router-dom": {
|
||||
singleton: true,
|
||||
requiredVersion: dependencies["react-router-dom"]
|
||||
},
|
||||
"rjsf-conditionals": {
|
||||
singleton: true,
|
||||
requiredVersion: dependencies["rjsf-conditionals"]
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3,48 +3,54 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.7",
|
||||
"@rjsf/core": "^5.13.0",
|
||||
"@rjsf/utils": "^5.13.0",
|
||||
"@rjsf/validator-ajv8": "^5.13.0",
|
||||
"axios": "^1.5.1",
|
||||
"daisyui": "^3.9.2",
|
||||
"@formkit/auto-animate": "^0.8.1",
|
||||
"@radix-ui/themes": "^2.0.3",
|
||||
"@react-pdf/renderer": "^3.4.0",
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"file-saver": "^2.0.5",
|
||||
"html-react-parser": "^4.2.2",
|
||||
"json-rules-engine-simplified": "^0.1.17",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"mf-cra": "^0.0.5",
|
||||
"moment": "^2.29.4",
|
||||
"parse": "^4.2.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"parse": "^4.3.1",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"print-js": "^1.6.0",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.9.0",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-datepicker": "^6.4.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
"react-dnd-touch-backend": "^16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-drag-listview": "^2.0.0",
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-select": "^5.7.7",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.3",
|
||||
"reactour": "^1.19.2",
|
||||
"redux": "^4.2.1",
|
||||
"redux-thunk": "^2.4.2",
|
||||
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
"serve": "^14.2.1",
|
||||
"styled-components": "^4.4.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "serve -s build",
|
||||
"start-dev": "mf-cra start",
|
||||
"start-dev": "react-scripts start",
|
||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||
"build": "npm run version && rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build",
|
||||
"micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build",
|
||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||
"build": "npm run version && react-scripts build",
|
||||
"build-win": "npm run version-win && react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"release": "standard-version",
|
||||
@@ -74,16 +80,16 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs2": "^7.23.2",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"@babel/runtime-corejs2": "^7.24.1",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"commitizen": "^4.3.0",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"lint-staged": "^15.0.1",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^2.8.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"tailwindcss": "^3.3.3"
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.37",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.3.1",
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,54 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, lazy } from "react";
|
||||
import { Routes, Route, BrowserRouter } from "react-router-dom";
|
||||
import Pgsignup from "./routes/Pgsignup";
|
||||
import Login from "./routes/Login";
|
||||
import Microapp from "./routes/RemoteApp";
|
||||
import Signup from "./routes/Signup";
|
||||
import Form from "./routes/Form";
|
||||
import Report from "./routes/Report";
|
||||
import Dashboard from "./routes/Dashboard";
|
||||
import PlanSubscriptions from "./routes/PlanSubscriptions";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import Login from "./pages/Login";
|
||||
import Form from "./pages/Form";
|
||||
import Report from "./pages/Report";
|
||||
import Dashboard from "./pages/Dashboard";
|
||||
import HomeLayout from "./layout/HomeLayout";
|
||||
import UserProfile from "./routes/UserProfile";
|
||||
import PageNotFound from "./routes/PageNotFound";
|
||||
import ForgetPassword from "./routes/ForgetPassword";
|
||||
import ChangePassword from "./routes/ChangePassword";
|
||||
import ReportMicroapp from "./components/ReportMicroapp";
|
||||
import LoadMf from "./routes/LoadMf";
|
||||
import PageNotFound from "./pages/PageNotFound";
|
||||
import ValidateRoute from "./primitives/ValidateRoute";
|
||||
import Validate from "./primitives/Validate";
|
||||
import TemplatePlaceholder from "./pages/TemplatePlaceholder";
|
||||
import SignYourSelf from "./pages/SignyourselfPdf";
|
||||
import DraftDocument from "./components/pdf/DraftDocument";
|
||||
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
||||
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
||||
import LazyPage from "./primitives/LazyPage";
|
||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||
const Pgsignup = lazy(() => import("./pages/Pgsignup"));
|
||||
const Subscriptions = lazy(() => import("./pages/PlanSubscriptions"));
|
||||
const ChangePassword = lazy(() => import("./pages/ChangePassword"));
|
||||
const UserProfile = lazy(() => import("./pages/UserProfile"));
|
||||
const Signup = lazy(() => import("./pages/Signup"));
|
||||
const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
||||
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
||||
|
||||
const Loader = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
function App() {
|
||||
const [isloading, setIsLoading] = useState(true);
|
||||
useEffect(() => {
|
||||
@@ -23,12 +56,16 @@ function App() {
|
||||
}, []);
|
||||
|
||||
const handleCredentials = () => {
|
||||
const appId = process.env.REACT_APP_APPID;
|
||||
const baseurl = process.env.REACT_APP_SERVERURL;
|
||||
const appId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const baseurl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
const appName = "contracts";
|
||||
try {
|
||||
localStorage.setItem("BaseUrl12", `${baseurl}/`);
|
||||
localStorage.setItem("AppID12", appId);
|
||||
localStorage.setItem("baseUrl", `${baseurl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
localStorage.setItem("domain", appName);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
@@ -39,107 +76,107 @@ function App() {
|
||||
return (
|
||||
<div className="bg-[#eef1f5]">
|
||||
{isloading ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
<Loader />
|
||||
) : (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route exact path="/" element={<Login />} />
|
||||
<Route exact path="/signup" element={<Signup />} />
|
||||
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
|
||||
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
|
||||
<Route element={<ValidateRoute />}>
|
||||
<Route exact path="/" element={<Login />} />
|
||||
<Route path="/signup" element={<LazyPage Page={Signup} />} />
|
||||
</Route>
|
||||
<Route element={<Validate />}>
|
||||
<Route
|
||||
path="/load/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/placeholdersign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/load/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="/loadmf/signmicroapp/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||
element={<LazyPage Page={GuestLogin} />}
|
||||
/>
|
||||
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||
<Route
|
||||
path="/forgetpassword"
|
||||
element={<LazyPage Page={ForgetPassword} />}
|
||||
/>
|
||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
||||
<>
|
||||
<Route exact path="/pgsignup" element={<Pgsignup />} />
|
||||
<Route
|
||||
exact
|
||||
path="/pgsignup"
|
||||
element={<LazyPage Page={Pgsignup} />}
|
||||
/>
|
||||
<Route
|
||||
path="/subscription"
|
||||
element={<PlanSubscriptions />}
|
||||
element={<LazyPage Page={Subscriptions} />}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Route
|
||||
exact
|
||||
path="/changepassword"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<ChangePassword />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/mf/:remoteApp/*"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<Microapp />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/asmf/:remoteApp/*"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<Microapp />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/rpmf/:remoteApp/*"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<ReportMicroapp />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/form/:id"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<Form />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/report/:id"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<Report />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/dashboard/:id"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<Dashboard />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/profile"
|
||||
element={
|
||||
<HomeLayout>
|
||||
<UserProfile />
|
||||
</HomeLayout>
|
||||
}
|
||||
/>
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route
|
||||
path="/changepassword"
|
||||
element={<LazyPage Page={ChangePassword} />}
|
||||
/>
|
||||
<Route path="/form/:id" element={<Form />} />
|
||||
<Route path="/report/:id" element={<Report />} />
|
||||
<Route path="/dashboard/:id" element={<Dashboard />} />
|
||||
<Route
|
||||
path="/profile"
|
||||
element={<LazyPage Page={UserProfile} />}
|
||||
/>
|
||||
<Route
|
||||
path="/opensigndrive"
|
||||
element={<LazyPage Page={Opensigndrive} />}
|
||||
/>
|
||||
<Route
|
||||
path="/managesign"
|
||||
element={<LazyPage Page={ManageSign} />}
|
||||
/>
|
||||
<Route
|
||||
path="/generatetoken"
|
||||
element={<LazyPage Page={GenerateToken} />}
|
||||
/>
|
||||
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
|
||||
<Route
|
||||
path="/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
/>
|
||||
{/* signyouself route with no rowlevel data using docId from url */}
|
||||
<Route path="/signaturePdf/:docId" element={<SignYourSelf />} />
|
||||
{/* draft document route to handle and navigate route page accordiing to document status */}
|
||||
<Route path="/draftDocument" element={<DraftDocument />} />
|
||||
{/* recipient placeholder set route with no rowlevel data using docId from url*/}
|
||||
<Route
|
||||
path="/placeHolderSign/:docId"
|
||||
element={<PlaceHolderSign />}
|
||||
/>
|
||||
{/* for user signature (need your sign route) with row level data */}
|
||||
<Route path="/pdfRequestFiles" element={<PdfRequestFiles />} />
|
||||
{/* for user signature (need your sign route) with no row level data */}
|
||||
<Route
|
||||
path="/pdfRequestFiles/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
{/* <Route exact path="/ForgotPassword" element={<ForgotPassword />} /> */}
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
)}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { TouchBackend } from "react-dnd-touch-backend";
|
||||
import {
|
||||
DndProvider,
|
||||
TouchTransition,
|
||||
MouseTransition,
|
||||
Preview
|
||||
} from "react-dnd-multi-backend";
|
||||
import DragElement from "./microappComponent/DragElement";
|
||||
import TagManager from "react-gtm-module";
|
||||
|
||||
const HTML5toTouch = {
|
||||
backends: [
|
||||
{
|
||||
id: "html5",
|
||||
backend: HTML5Backend,
|
||||
transition: MouseTransition
|
||||
},
|
||||
{
|
||||
id: "touch",
|
||||
backend: TouchBackend,
|
||||
options: { enableMouseEvents: true },
|
||||
preview: true,
|
||||
transition: TouchTransition
|
||||
}
|
||||
]
|
||||
};
|
||||
const generatePreview = (props) => {
|
||||
const { item, style } = props;
|
||||
const newStyle = {
|
||||
...style
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={newStyle}>
|
||||
<DragElement {...item} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.REACT_APP_GTM) {
|
||||
const tagManagerArgs = {
|
||||
gtmId: process.env.REACT_APP_GTM
|
||||
};
|
||||
TagManager.initialize(tagManagerArgs);
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
||||
|
||||
const AppendFormInForm = (props) => {
|
||||
const AddSigner = (props) => {
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -46,9 +47,8 @@ const AppendFormInForm = (props) => {
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||
contactQuery.set("Name", name);
|
||||
@@ -105,13 +105,18 @@ const AppendFormInForm = (props) => {
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
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
|
||||
@@ -158,13 +163,18 @@ const AppendFormInForm = (props) => {
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
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);
|
||||
@@ -282,14 +292,16 @@ const AppendFormInForm = (props) => {
|
||||
<div className="mt-4 flex justify-start">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-[#1ab6ce] text-sm text-white px-4 py-2 rounded shadow focus:outline-none"
|
||||
style={{ backgroundColor: modalSubmitBtnColor }}
|
||||
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none"
|
||||
style={{ backgroundColor: modalCancelBtnColor }}
|
||||
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
@@ -300,4 +312,4 @@ const AppendFormInForm = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AppendFormInForm;
|
||||
export default AddSigner;
|
||||
@@ -0,0 +1,281 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import Title from "./Title";
|
||||
import Alert from "../primitives/Alert";
|
||||
|
||||
const AddUser = (props) => {
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
|
||||
const checkUserExist = async () => {
|
||||
try {
|
||||
const res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: email,
|
||||
userId: Parse.User.current().id
|
||||
});
|
||||
if (res) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err", err);
|
||||
}
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
setIsUserExist(true);
|
||||
setIsLoader(false);
|
||||
setTimeout(() => {
|
||||
setIsUserExist(false);
|
||||
}, 1000);
|
||||
} else {
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Users");
|
||||
contactQuery.set("Name", name);
|
||||
contactQuery.set("Phone", phone);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_User");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
});
|
||||
}
|
||||
|
||||
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("phone", phone);
|
||||
_user.set("password", phone);
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_User",
|
||||
userId: user.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_User",
|
||||
userId: userRes.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
});
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log("err", err);
|
||||
setIsLoader(false);
|
||||
alert("something went wrong!");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<Title title={"Add User"} />
|
||||
{isUserExist && <Alert type="danger">User already exists!</Alert>}
|
||||
{isLoader && (
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">Add User</h1>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Email
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Phone
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUser;
|
||||
@@ -1,49 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
|
||||
export class ErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return {
|
||||
hasError: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
console.log(error);
|
||||
console.log(info);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return (
|
||||
<div className="p-2">
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
|
||||
flexFlow: "column"
|
||||
}}
|
||||
>
|
||||
<h1
|
||||
style={{
|
||||
fontSize: "2em",
|
||||
fontFamily: "sans-serif",
|
||||
textShadow: "0 0 20px black"
|
||||
}}
|
||||
>
|
||||
Something went wrong.
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Package from "../../package.json";
|
||||
import axios from "axios";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
const Footer = () => {
|
||||
const [showButton, setShowButton] = useState(false);
|
||||
const [version, setVersion] = useState("");
|
||||
@@ -37,12 +38,18 @@ const Footer = () => {
|
||||
}, []);
|
||||
|
||||
const appName = "OpenSign™";
|
||||
|
||||
const openUrl = () => {
|
||||
openInNewTab(
|
||||
"https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version
|
||||
);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
|
||||
All Rights Reserved © {new Date().getFullYear()}
|
||||
{appName} ( version: {version ? version : `${Package.version} `})
|
||||
<span onClick={openUrl} className="hover:underline cursor-pointer">
|
||||
{appName} ( version: {version ? version : `${Package.version} `})
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
className={`${
|
||||
|
||||
@@ -4,14 +4,13 @@ import FullScreenButton from "./FullScreenButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
const Header = ({ showSidebar }) => {
|
||||
const navigation = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
let applogo = localStorage.getItem("appLogo") || "";
|
||||
let username = localStorage.getItem("username");
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
@@ -21,8 +20,6 @@ const Header = ({ showSidebar }) => {
|
||||
|
||||
const closeDropdown = () => {
|
||||
setIsOpen(false);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
Parse.User.logOut();
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
@@ -31,8 +28,8 @@ const Header = ({ showSidebar }) => {
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
@@ -43,8 +40,8 @@ const Header = ({ showSidebar }) => {
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
navigation("/");
|
||||
};
|
||||
@@ -118,6 +115,12 @@ const Header = ({ showSidebar }) => {
|
||||
<ul>
|
||||
<li
|
||||
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
|
||||
>
|
||||
<i className="fa-solid fa-book"></i> Docs
|
||||
</li>
|
||||
<li
|
||||
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigation("/profile");
|
||||
|
||||
@@ -86,7 +86,7 @@ const LoginFacebook = ({
|
||||
email: userDetails.Email,
|
||||
// "passsword":userDetails.Phone,
|
||||
phone: userDetails.Phone,
|
||||
role: "contracts_Admin",
|
||||
role: "contracts_User",
|
||||
company: userDetails.Company
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useState, useRef } from "react";
|
||||
import Parse from "parse";
|
||||
import jwtDecode from "jwt-decode";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { useScript } from "../hook/useScript";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
||||
|
||||
/*
|
||||
* `GoogleSignInBtn`as it's name indicates it render google sign in button
|
||||
@@ -46,8 +48,8 @@ const GoogleSignInBtn = ({
|
||||
if (Parse.User.current()) {
|
||||
await Parse.User.logOut();
|
||||
}
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let appversion = localStorage.getItem("appVersion");
|
||||
@@ -61,8 +63,8 @@ const GoogleSignInBtn = ({
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("appversion", appversion);
|
||||
@@ -148,7 +150,7 @@ const GoogleSignInBtn = ({
|
||||
email: userDetails.Email,
|
||||
// "passsword":userDetails.Phone,
|
||||
phone: userDetails.Phone,
|
||||
role: "contracts_Admin",
|
||||
role: "contracts_User",
|
||||
company: userDetails.Company,
|
||||
jobTitle: userDetails.Destination
|
||||
}
|
||||
@@ -189,8 +191,8 @@ const GoogleSignInBtn = ({
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
@@ -201,8 +203,8 @@ const GoogleSignInBtn = ({
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
};
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
@@ -231,118 +233,94 @@ const GoogleSignInBtn = ({
|
||||
</div>
|
||||
)}
|
||||
<div ref={googleBtn} className="text-sm"></div>
|
||||
|
||||
{isModal && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="exampleModal"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block", zIndex: 1 }}
|
||||
>
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title">Sign up form</h5>
|
||||
<span>
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form>
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="Phone"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Phone{" "}
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
className="form-control"
|
||||
id="Phone"
|
||||
value={userDetails.Phone}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Phone: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="Company"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Company{" "}
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="Company"
|
||||
value={userDetails.Company}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Company: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="JobTitle"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Job Title{" "}
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="JobTitle"
|
||||
value={userDetails.Destination}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Destination: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#17a2b8] p-2 text-white rounded"
|
||||
onClick={() => handleSubmitbtn()}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
Sign up
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#6c757d] p-2 text-white rounded"
|
||||
onClick={handleCloseModal}
|
||||
style={{ width: 90 }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
|
||||
<form className="px-4 py-3">
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="Phone"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
id="Phone"
|
||||
value={userDetails.Phone}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Phone: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="Company"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
id="Company"
|
||||
value={userDetails.Company}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Company: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="JobTitle"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
id="JobTitle"
|
||||
value={userDetails.Destination}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Destination: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||
onClick={() => handleSubmitbtn()}
|
||||
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
|
||||
>
|
||||
Sign up
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
|
||||
onClick={handleCloseModal}
|
||||
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { Stage, Layer, Rect } from "react-konva";
|
||||
const RenderDebugPdf = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
padding: 10,
|
||||
zIndex: 1,
|
||||
backgroundColor: "white",
|
||||
border: "1px solid grey",
|
||||
margin: "5px 0"
|
||||
}}
|
||||
>
|
||||
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
position: "relative",
|
||||
border: "1px solid grey",
|
||||
overflow: "auto"
|
||||
}}
|
||||
onMouseMove={props.handleMouseMoveDiv}
|
||||
>
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
props.setPdfLoadFail(load);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={props.pageDetails}
|
||||
ref={props.pdfRef}
|
||||
file={props.pdfUrl}
|
||||
>
|
||||
<Page
|
||||
onLoadSuccess={props.handlePageLoadSuccess}
|
||||
pageNumber={props.pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
</Document>
|
||||
|
||||
<Stage
|
||||
onMouseMove={props.handleMouseMove}
|
||||
onMouseDown={props.handleMouseDown}
|
||||
onMouseUp={props.handleMouseUp}
|
||||
width={props.pdfDimension.width}
|
||||
height={props.pdfDimension.height}
|
||||
style={{ position: "absolute", top: 0, left: 0 }}
|
||||
>
|
||||
<Layer>
|
||||
{props.annotations
|
||||
.filter((value) => value.page === props.pageNumber)
|
||||
.map((value) => {
|
||||
return (
|
||||
<Rect
|
||||
key={value.key}
|
||||
x={value.x}
|
||||
y={value.y}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
fill="transparent"
|
||||
stroke="black"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Layer>
|
||||
</Stage>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RenderDebugPdf;
|
||||
@@ -1,66 +0,0 @@
|
||||
import useFederatedComponent from "mf-cra";
|
||||
import React, { Suspense, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const MicroappModal = () => {
|
||||
const { remoteApp } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
return () => localStorage.removeItem("rowlevel");
|
||||
}, []);
|
||||
let paramString = remoteApp;
|
||||
let queryString = new URLSearchParams(paramString);
|
||||
let obj = {};
|
||||
for (const [key, value] of queryString.entries()) {
|
||||
// console.log("value ", key + ":" + value);
|
||||
|
||||
if (key === "remoteUrl") {
|
||||
const url = window.location.origin + "/mfbuild/remoteEntry.js";
|
||||
// console.log("atob(value) ", atob(value))
|
||||
obj = { ...obj, [key]: url };
|
||||
} else {
|
||||
if (key === "moduleToLoad") {
|
||||
obj = { ...obj, [key]: "./" + value };
|
||||
} else {
|
||||
obj = { ...obj, [key]: value };
|
||||
}
|
||||
}
|
||||
}
|
||||
// console.log("app ", remoteApp);
|
||||
const { Component: RemoteComponent } = useFederatedComponent(obj);
|
||||
// console.log("RemoteComponent ", RemoteComponent);
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center ">
|
||||
<div className="w-full">
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#ffffff",
|
||||
width: "100%",
|
||||
overflow: "hidden",
|
||||
minHeight: "50vh"
|
||||
}}
|
||||
>
|
||||
{RemoteComponent && <RemoteComponent />}
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MicroappModal;
|
||||
@@ -1,21 +1,11 @@
|
||||
import React from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
function Title(props) {
|
||||
function Title({ title, drive }) {
|
||||
return (
|
||||
<Helmet>
|
||||
<title>{`${props.title} - OpenSign™`}</title>
|
||||
{/* <title>
|
||||
{`${localStorage.getItem("appTitle") ? localStorage.getItem("appTitle") : ""} - ${props.title}`}
|
||||
</title> */}
|
||||
<meta
|
||||
name="description"
|
||||
content={`${props.title} - OpenSign™`}
|
||||
/>
|
||||
{/* <meta
|
||||
name="description"
|
||||
content={`${localStorage.getItem("appTitle")} - ${props.title}`}
|
||||
/> */}
|
||||
<title>{drive ? title : `${title} - OpenSign™`}</title>
|
||||
<meta name="description" content={`${title} - OpenSign™`} />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
|
||||
@@ -1,391 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Form from "@rjsf/core";
|
||||
import validator from "@rjsf/validator-ajv8";
|
||||
import Parse from "parse";
|
||||
import "../styles/loader.css";
|
||||
import LayoutField from "./fields/Rjsf-layout";
|
||||
import TimeWidget from "./fields/TimeWidget";
|
||||
import Level1Dropdown from "./fields/Level1Dropdown";
|
||||
import HiddenField from "./fields/HiddenField";
|
||||
import MultiSelectField from "./fields/MultiSelectField";
|
||||
import TreeWidget from "../components/TreeWidget";
|
||||
import { formJson } from "../json/FormJson";
|
||||
const TreeEditForm = (props) => {
|
||||
const widget = {
|
||||
TimeWidget: TimeWidget
|
||||
};
|
||||
const fields = {
|
||||
layout: LayoutField,
|
||||
Level1Dropdown: Level1Dropdown,
|
||||
HiddenField: HiddenField,
|
||||
MultiSelectField: MultiSelectField,
|
||||
FolderComponent: TreeWidget
|
||||
};
|
||||
|
||||
const [schema, setschema] = useState({});
|
||||
const [ui_schema, setui_schema] = useState({});
|
||||
const [title, settitle] = useState("");
|
||||
const [schemaState, setschemaState] = useState({});
|
||||
const [formData, setformData] = useState({});
|
||||
const [active, setactive] = useState(true);
|
||||
const [loading, setloading] = useState(false);
|
||||
const [_validate, set_validate] = useState(null);
|
||||
const [noValidate, setnoValidate] = useState(false);
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
|
||||
const getForm = async (id) => {
|
||||
setloading(true);
|
||||
try {
|
||||
//get json form data (schema ,uischema)
|
||||
const results = formJson(id);
|
||||
try {
|
||||
let _record = Parse.Object.extend(results.class);
|
||||
var query1 = new Parse.Query(_record);
|
||||
await query1.get(props.objectId).then(
|
||||
(x) => {
|
||||
try {
|
||||
if (x) {
|
||||
let result = x.toJSON();
|
||||
let new_result = result;
|
||||
for (let [key, value] of Object.entries(result)) {
|
||||
if (value["__type"] === "Date") {
|
||||
let todayTime1 = new Date(value.iso);
|
||||
var month = String(todayTime1.getMonth() + 1);
|
||||
var day = String(todayTime1.getDate());
|
||||
var year = String(todayTime1.getFullYear());
|
||||
if (month.length < 2) month = "0" + month;
|
||||
if (day.length < 2) day = "0" + day;
|
||||
let date1 = year + "-" + month + "-" + day;
|
||||
let bindVar = date1;
|
||||
if (!results.jsonSchema.properties[key].format) {
|
||||
bindVar = todayTime1.toISOString();
|
||||
}
|
||||
new_result[key] = bindVar;
|
||||
}
|
||||
}
|
||||
setschemaState(results.jsonSchema);
|
||||
setformData(new_result);
|
||||
}
|
||||
} catch (error) {
|
||||
//alert(error.message);
|
||||
|
||||
setloading(false);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log("err", error);
|
||||
setloading(false);
|
||||
}
|
||||
);
|
||||
setschema(results.jsonSchema);
|
||||
setui_schema(results.uiSchema);
|
||||
set_validate(results.validFunction);
|
||||
setnoValidate(results.noValidate);
|
||||
settitle(results.class);
|
||||
setloading(false);
|
||||
setactive(true);
|
||||
} catch (error) {
|
||||
setloading(false);
|
||||
}
|
||||
} catch (e) {
|
||||
setloading(false);
|
||||
console.error("Problem", e);
|
||||
}
|
||||
};
|
||||
|
||||
const wrap = (s) => "{ return " + s + " };";
|
||||
|
||||
const dynamicValidate = (formData, errors) => {
|
||||
try {
|
||||
let body = atob(_validate);
|
||||
let res = new Function(wrap(body))
|
||||
.call(null)
|
||||
.call(null, formData, errors);
|
||||
return res;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async ({ formData }) => {
|
||||
setactive(false);
|
||||
setloading(true);
|
||||
let RowData = formData;
|
||||
RowData &&
|
||||
Object.entries(RowData).forEach(([key, value]) => {
|
||||
if (typeof value === "string") {
|
||||
RowData[key] = value.trim();
|
||||
}
|
||||
});
|
||||
let _scanData = schemaState;
|
||||
if (_scanData.dependencies) {
|
||||
Object.keys(_scanData.dependencies).forEach((key) => {
|
||||
if (_scanData.dependencies[key].oneOf) {
|
||||
_scanData.dependencies[key].oneOf.forEach((val) => {
|
||||
Object.keys(val.properties).forEach((k) => {
|
||||
if (typeof val.properties[k] === "object") {
|
||||
if (val.properties[k].format === "date") {
|
||||
if (RowData[k]) {
|
||||
let newdate = new Date(RowData[k]);
|
||||
RowData[k] = newdate;
|
||||
}
|
||||
}
|
||||
if (val.properties[k].component === "HtmlEditor") {
|
||||
if (RowData[k]) {
|
||||
let newHtml = RowData[k]
|
||||
.replace(/<p[^>]*>/g, "")
|
||||
.replace(/<\/p>/g, " ");
|
||||
RowData[k] = newHtml;
|
||||
}
|
||||
}
|
||||
if (val.properties[k].component === "DateTime") {
|
||||
if (RowData[k]) {
|
||||
let newDate11 = new Date(RowData[k]);
|
||||
RowData[k] = newDate11;
|
||||
}
|
||||
}
|
||||
if (val.properties[k].component === "CurrencyInput") {
|
||||
if (val.properties[k].currencyColumn) {
|
||||
RowData[`${val.properties[k].currencyColumn}`] =
|
||||
val.properties[k].defaultcurrency;
|
||||
}
|
||||
}
|
||||
if (val.properties[k].type === "string") {
|
||||
if (typeof RowData[k] === "string")
|
||||
RowData[k] = RowData[k].trim();
|
||||
}
|
||||
if (val.properties[k].data !== undefined) {
|
||||
if (val.properties[k].data.isPointer) {
|
||||
let pointer = undefined;
|
||||
if (val.properties[k].data.class) {
|
||||
if (RowData[k]) {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: val.properties[k].data.class,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (RowData[k]) {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: localStorage.getItem("extended_class"),
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
}
|
||||
RowData[k] = pointer;
|
||||
}
|
||||
if (val.properties[k].data.FolderTypeValue) {
|
||||
if (RowData[k]) {
|
||||
let obj = {
|
||||
__type: "Pointer",
|
||||
className: val.properties[k].data.ClassName,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
RowData[k] = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
let temp = [];
|
||||
|
||||
let _dd = _scanData.properties;
|
||||
let allowed = [];
|
||||
if (_dd) {
|
||||
Object.keys(_dd).forEach(function (k) {
|
||||
allowed.push(k);
|
||||
if (RowData[k]) {
|
||||
let pointer = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].class,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
RowData[k] = pointer;
|
||||
}
|
||||
if (_dd[k].format === "date") {
|
||||
if (RowData[k]) {
|
||||
let newdate = new Date(RowData[k]);
|
||||
if (!isNaN(newdate.getTime())) {
|
||||
RowData[k] = newdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_dd[k].component === "HtmlEditor") {
|
||||
if (RowData[k]) {
|
||||
let newHtml = RowData[k]
|
||||
.replace(/<p[^>]*>/g, "")
|
||||
.replace(/<\/p>/g, " ");
|
||||
RowData[k] = newHtml;
|
||||
}
|
||||
}
|
||||
if (_dd[k].component === "DateTime") {
|
||||
if (RowData[k]) {
|
||||
let newdate = new Date(RowData[k]);
|
||||
if (!isNaN(newdate.getTime())) {
|
||||
RowData[k] = newdate;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_dd[k].format === "date-time") {
|
||||
let newdate = new Date(RowData[k]);
|
||||
RowData[k] = newdate;
|
||||
}
|
||||
if (_dd[k].component === "CurrencyInput") {
|
||||
if (_dd[k].currencyColumn) {
|
||||
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
|
||||
}
|
||||
}
|
||||
if (_dd[k].data !== undefined) {
|
||||
if (_dd[k].data.isPointer) {
|
||||
let pointer = undefined;
|
||||
if (typeof RowData[k] === "object") {
|
||||
if (RowData[k]) {
|
||||
if (RowData[k].objectId !== "Select") {
|
||||
let obj = {
|
||||
__type: "Pointer",
|
||||
className: RowData[k].className,
|
||||
objectId: RowData[k].objectId
|
||||
};
|
||||
pointer = obj;
|
||||
}
|
||||
}
|
||||
} else if (_dd[k].data.class) {
|
||||
if (RowData[k]) {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].data.class,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (RowData[k]) {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: localStorage.getItem("extended_class"),
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RowData[k] = pointer;
|
||||
}
|
||||
if (_dd[k].data.FolderTypeValue) {
|
||||
if (RowData[k]) {
|
||||
let obj = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].data.ClassName,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
RowData[k] = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_dd[k].type === "string") {
|
||||
if (typeof RowData[k] === "string") RowData[k] = RowData[k].trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
const filtered = Object.keys(RowData)
|
||||
.filter((key) => allowed.includes(key))
|
||||
.reduce((obj, key) => {
|
||||
obj[key] = RowData[key];
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
try {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
if (temp.length !== 0) {
|
||||
let userPointer = {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: currentUser.id
|
||||
};
|
||||
filtered[[temp[0]]] = userPointer;
|
||||
}
|
||||
var data = Parse.Object.extend(title);
|
||||
var query = new Parse.Query(data);
|
||||
query.get(props.objectId).then(async (object) => {
|
||||
object.save(filtered).then(
|
||||
() => {
|
||||
try {
|
||||
alert("Record updated successfully");
|
||||
props.HideView(false);
|
||||
setloading(false);
|
||||
setactive(true);
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log("err", error);
|
||||
setloading(false);
|
||||
setactive(true);
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
|
||||
alert(error.message);
|
||||
setloading(false);
|
||||
setactive(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props.FormId) {
|
||||
getForm(props.FormId);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.FormId]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div
|
||||
className="loader-01"
|
||||
style={{
|
||||
marginTop: "50px",
|
||||
marginLeft: "50%",
|
||||
color: "rgb(0,28,28)",
|
||||
fontSize: "35px"
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Form
|
||||
schema={schema}
|
||||
uiSchema={ui_schema}
|
||||
formData={formData}
|
||||
showErrorList={false}
|
||||
widgets={widget}
|
||||
fields={fields}
|
||||
validate={noValidate && dynamicValidate}
|
||||
onSubmit={handleSubmit}
|
||||
validator={validator}
|
||||
>
|
||||
<div>
|
||||
{active ? (
|
||||
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit">
|
||||
Update
|
||||
</button>
|
||||
) : (
|
||||
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit" disabled>
|
||||
Update
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default TreeEditForm;
|
||||
@@ -1,237 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Form from "@rjsf/core";
|
||||
import validator from "@rjsf/validator-ajv8";
|
||||
import LayoutField from "./fields/Rjsf-layout";
|
||||
import Level1Dropdown from "./fields/Level1Dropdown";
|
||||
import HiddenField from "./fields/HiddenField";
|
||||
import MultiSelectField from "./fields/MultiSelectField";
|
||||
import "../styles/loader.css";
|
||||
import "../styles/form.css";
|
||||
import FileUpload from "./fields/FileUpload";
|
||||
import parse from "html-react-parser";
|
||||
import TimeWidget from "./fields/TimeWidget";
|
||||
import { formJson } from "../json/FormJson";
|
||||
function TreeFormComponent(props) {
|
||||
const widget = {
|
||||
TimeWidget: TimeWidget
|
||||
};
|
||||
|
||||
const fields = {
|
||||
layout: LayoutField,
|
||||
FileUpload: FileUpload,
|
||||
Level1Dropdown: Level1Dropdown,
|
||||
HiddenField: HiddenField,
|
||||
MultiSelectField: MultiSelectField
|
||||
};
|
||||
|
||||
const [link, setLink] = useState("");
|
||||
const [help, setHelp] = useState("");
|
||||
const toastDescription = "";
|
||||
const toastColor = "#5cb85c";
|
||||
const [loading, setLoding] = useState(true);
|
||||
const [schemaState, setSchemaState] = useState({});
|
||||
const [formData, setFormData] = useState({});
|
||||
const [ui_schema, setUi_schema] = useState({});
|
||||
const active = true;
|
||||
|
||||
console.log("new form", props);
|
||||
|
||||
const getForm = async (id) => {
|
||||
setLoding(true);
|
||||
try {
|
||||
// get json from data(jsonschema, uischema)
|
||||
const results = formJson(id);
|
||||
// console.log("result", results);
|
||||
if (results) {
|
||||
let parentObject = {},
|
||||
childObject = {};
|
||||
const resultjson = results;
|
||||
for (let [key, value] of Object.entries(
|
||||
resultjson.jsonSchema.properties
|
||||
)) {
|
||||
if (key === props.ParentField) {
|
||||
if (props.ParentValue[key]) {
|
||||
parentObject = { [key]: props.ParentValue[key] };
|
||||
}
|
||||
} else {
|
||||
if (props.ParentValue && typeof props.ParentValue === "object") {
|
||||
Object.entries(props.ParentValue).forEach(([k, value]) => {
|
||||
if (k === key) {
|
||||
let newO = { [k]: value };
|
||||
parentObject = { ...parentObject, ...newO };
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (props.ChildField) {
|
||||
Object.entries(props.ChildField).forEach(([k, value]) => {
|
||||
if (k === key) {
|
||||
let newO = { [k]: value };
|
||||
childObject = { ...childObject, ...newO };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
for (let [k, v] of Object.entries(value)) {
|
||||
if (k === "format" && v === "date") {
|
||||
let today = new Date();
|
||||
let date =
|
||||
today.getFullYear() +
|
||||
"-" +
|
||||
("0" + (today.getMonth() + 1)).slice(-2) +
|
||||
"-" +
|
||||
("0" + today.getDate()).slice(-2);
|
||||
value.default = date;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (props.ParentField) {
|
||||
setFormData({ ...parentObject, ...childObject });
|
||||
}
|
||||
|
||||
let txt, link;
|
||||
if (resultjson.help) {
|
||||
if (resultjson.help.htmlbody) {
|
||||
txt = resultjson.help.htmlbody;
|
||||
}
|
||||
if (resultjson.help.link) {
|
||||
link = resultjson.help.link;
|
||||
}
|
||||
}
|
||||
setHelp(txt);
|
||||
setLink(link);
|
||||
|
||||
props.ClassName(resultjson.class);
|
||||
props.SchemaState(resultjson.jsonSchema);
|
||||
setSchemaState(resultjson.jsonSchema);
|
||||
setUi_schema(resultjson.uiSchema);
|
||||
setLoding(false);
|
||||
} else {
|
||||
alert("form not found");
|
||||
setLoding(false);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
setLoding(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.IsEdit) {
|
||||
getForm(props.Id);
|
||||
}
|
||||
// console.log("props tree", props);
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
let formView = (
|
||||
<React.Fragment>
|
||||
<Form
|
||||
schema={schemaState}
|
||||
uiSchema={ui_schema}
|
||||
showErrorList={false}
|
||||
widgets={widget}
|
||||
fields={fields}
|
||||
formData={formData}
|
||||
onSubmit={props.handleSubmit}
|
||||
validator={validator}
|
||||
>
|
||||
{active ? (
|
||||
<button className="btn btn-info submiBtn" type="submit">
|
||||
{props.IsEdit ? "update folder" : "create folder"}
|
||||
</button>
|
||||
) : (
|
||||
<button className="btn btn-info submiBtn " type="submit" disabled>
|
||||
submitting...
|
||||
</button>
|
||||
)}
|
||||
</Form>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
formView = (
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="card">
|
||||
<div className="card-body no-padding height-9">
|
||||
{help ? (
|
||||
<div className="dropdown" style={{ marginTop: "-30px" }}>
|
||||
<i
|
||||
className="far fa-question-circle dropdown-toggle hovereffect"
|
||||
aria-hidden="true"
|
||||
id="dropdownMenuButton"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
color: "purple",
|
||||
cursor: "pointer !important",
|
||||
position: "relative",
|
||||
top: "40px",
|
||||
left: "98%"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
style={{
|
||||
marginleft: "-121px",
|
||||
margintop: "-14px",
|
||||
position: "absolute",
|
||||
padding: "10px",
|
||||
width: "300px",
|
||||
top: "102px!important"
|
||||
}}
|
||||
>
|
||||
{parse(`
|
||||
${help}
|
||||
`)}
|
||||
<br />
|
||||
{link ? (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
window.location.href = link;
|
||||
}}
|
||||
target="_blank"
|
||||
className="btn btn-xs btn-primary"
|
||||
>
|
||||
Read more..
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ fontSize: "13px" }}>{formView}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snackbar" style={{ backgroundColor: toastColor }}>
|
||||
{toastDescription}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default TreeFormComponent;
|
||||
@@ -1,895 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import LabelField from "./fields/LabelField";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import "../styles/spinner.css";
|
||||
import TreeFormComponent from "./TreeFormComponent";
|
||||
import TreeEditForm from "./TreeEditForm";
|
||||
import "../styles/modal.css";
|
||||
import Modal from "react-modal";
|
||||
|
||||
const TreeWidget = (props) => {
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [isAddField, setIsAddField] = useState(false);
|
||||
const [folderList, setFolderList] = useState([]);
|
||||
const [tabList, setTabList] = useState([]);
|
||||
const [appId, setAppId] = useState(undefined);
|
||||
const [selectedFolder, setSelectedFolder] = useState(undefined);
|
||||
const [loader, setLoader] = useState(false);
|
||||
const [className, setClassName] = useState("");
|
||||
const [schemaState, setSchemaState] = useState({});
|
||||
const [TabURL, setTabURL] = useState("");
|
||||
const [editable, setEditable] = useState(false);
|
||||
const [editId, setEditId] = useState("");
|
||||
const [defaultState, setDefaultState] = useState(false);
|
||||
const [isShowModal, setIsShowModal] = useState(false);
|
||||
const selectFolderHandle = async () => {
|
||||
setIsShowModal(true);
|
||||
setIsAddField(false);
|
||||
setDefaultState(false);
|
||||
setFolderList([]);
|
||||
setLoader(true);
|
||||
|
||||
if (tabList.length > 0) {
|
||||
let len = tabList.length - 1;
|
||||
selectedItemList(tabList[len]);
|
||||
} else {
|
||||
try {
|
||||
var test;
|
||||
if (props.schema.parent) {
|
||||
try {
|
||||
if (props.parent[props.schema.parent]) {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
let res;
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
res = data[0];
|
||||
} else {
|
||||
res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (res) res = res.toJSON();
|
||||
}
|
||||
if (res) {
|
||||
let json = res;
|
||||
setAppId(json.AppId);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
let reg = /(\#.*?\#)/gi;
|
||||
let str = props.schema.data.Query;
|
||||
if (str.includes("#")) {
|
||||
test = str.replace(reg, props.parent[props.schema.parent]);
|
||||
} else {
|
||||
test = str;
|
||||
}
|
||||
} else {
|
||||
if (props.child[props.schema.parent]) {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
let res;
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
res = data[0];
|
||||
} else {
|
||||
res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (res) res = res.toJSON();
|
||||
}
|
||||
|
||||
if (res) {
|
||||
let json = res;
|
||||
setAppId(json.AppId);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
let reg = /(\#.*?\#)/gi;
|
||||
let str = props.schema.data.Query;
|
||||
if (str.includes("#")) {
|
||||
test = str.replace(reg, props.child[props.schema.parent]);
|
||||
} else {
|
||||
test = str;
|
||||
}
|
||||
} else {
|
||||
alert(`Please select ${props.schema.parent}`);
|
||||
setLoader(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error ", error);
|
||||
}
|
||||
} else {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
// eslint-disable-next-line
|
||||
let reg = /(\#.*?\#)/gi;
|
||||
let str = props.schema.data.Query;
|
||||
let res;
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
res = data[0];
|
||||
} else {
|
||||
// emp = Parse.Object.extend(localStorage.getItem("extended_class"));
|
||||
// q = new Parse.Query(emp);
|
||||
// q.equalTo("UserId", currentUser);
|
||||
// res = await q.first();
|
||||
// if (res) res = res.toJSON();
|
||||
const currentUser = Parse.User.current();
|
||||
res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (res) res = res.toJSON();
|
||||
}
|
||||
|
||||
if (res) {
|
||||
let json = res;
|
||||
setAppId(json.AppId);
|
||||
}
|
||||
if (str.includes("#")) {
|
||||
if (res) {
|
||||
let json = res.toJSON();
|
||||
let output = str.match(reg);
|
||||
if (output.length === 1) {
|
||||
output = output.join();
|
||||
output = output.substring(1, output.length - 1);
|
||||
output = output.split(".");
|
||||
if (output.length === 1) {
|
||||
let out = output[0];
|
||||
if (json[out]) {
|
||||
if (typeof json[out] === "object") {
|
||||
test = str.replace(reg, JSON.stringify(json[out]));
|
||||
} else {
|
||||
test = str.replace(reg, json[out]);
|
||||
}
|
||||
} else {
|
||||
test = str.replace(reg, currentUser.id);
|
||||
}
|
||||
} else if (output.length === 2) {
|
||||
let out1 = json[output[0]][output[1]];
|
||||
if (out1) {
|
||||
test = str.replace(reg, out1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
test = str;
|
||||
}
|
||||
}
|
||||
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?${test}`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios
|
||||
.get(url, { headers: headers })
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
setFolderList(res.data.results);
|
||||
setLoader(false);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoader(false);
|
||||
console.log(err);
|
||||
});
|
||||
} catch (error) {
|
||||
setLoader(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const selectedItemList = async (folder) => {
|
||||
setLoader(true);
|
||||
setFolderList([]);
|
||||
try {
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${folder.objectId}"}}`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.get(url, { headers: headers }).then((result) => {
|
||||
if (result) {
|
||||
setFolderList(result.data.results);
|
||||
setLoader(false);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const removeTabListItem = async (e, i) => {
|
||||
e.preventDefault();
|
||||
setEditable(false);
|
||||
setLoader(true);
|
||||
setFolderList([]);
|
||||
let list = tabList.filter((itm, j) => {
|
||||
if (j <= i) {
|
||||
return itm;
|
||||
}
|
||||
});
|
||||
setTabList(list);
|
||||
setIsAddField(false);
|
||||
try {
|
||||
let _len = list.length - 1;
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${list[_len].objectId}"}}`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.get(url, { headers: headers }).then((result) => {
|
||||
if (result) {
|
||||
setFolderList(result.data.results);
|
||||
setLoader(false);
|
||||
}
|
||||
setLoader(false);
|
||||
});
|
||||
} catch (error) {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteFolder = async (e) => {
|
||||
e.preventDefault();
|
||||
if (folderList.length > 0) {
|
||||
alert(
|
||||
"Folder should not delete. To delete first delete sub items in it."
|
||||
);
|
||||
} else if (window.confirm(`Are you sure you want to delete this record`)) {
|
||||
try {
|
||||
setLoader(true);
|
||||
let len = tabList.length - 1;
|
||||
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
var deleted = Parse.Object.extend(props.schema.data.ClassName);
|
||||
var query = new Parse.Query(deleted);
|
||||
await query.get(tabList[len].objectId).then(
|
||||
(del) => {
|
||||
del.destroy().then(
|
||||
(y) => {
|
||||
if (y) {
|
||||
setLoader(false);
|
||||
let list = tabList.filter((itm, j) => {
|
||||
if (j !== len) return itm;
|
||||
});
|
||||
setTabList(list);
|
||||
if (list.length > 0) {
|
||||
let len = list.length - 1;
|
||||
selectedItemList(list[len]);
|
||||
} else {
|
||||
selectFolderHandle();
|
||||
}
|
||||
alert("Folder deleted successfully.");
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
alert(error.message);
|
||||
setLoader(false);
|
||||
}
|
||||
);
|
||||
},
|
||||
(error) => {
|
||||
console.log("error ", error);
|
||||
// The object was not retrieved successfully.
|
||||
setLoader(false);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
setLoader(false);
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmitResult = async (e) => {
|
||||
setIsShowModal(false);
|
||||
e.preventDefault();
|
||||
//console.log("tabList ", tabList);
|
||||
if (tabList.length > 0) {
|
||||
try {
|
||||
let len = tabList.length - 1;
|
||||
setSelectedFolder({
|
||||
Topic: tabList[len][`${props.schema.data.FolderNameField}`],
|
||||
description: tabList[len][`${props.schema.data.FolderDescription}`]
|
||||
});
|
||||
props.onChange(tabList[len]["objectId"]);
|
||||
let url = "Folders";
|
||||
tabList.forEach((t) => {
|
||||
url = url + " / " + t[`${props.schema.data.FolderNameField}`];
|
||||
});
|
||||
setTabURL(url);
|
||||
setIsAddField(false);
|
||||
if (!props.formData) {
|
||||
setTabList([]);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsAddField(false);
|
||||
alert(error.message);
|
||||
}
|
||||
} else {
|
||||
setIsAddField(false);
|
||||
}
|
||||
};
|
||||
|
||||
const editFolderDisplay = async (formData) => {
|
||||
try {
|
||||
let objectId = formData.objectId;
|
||||
let className = formData.className;
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const fldr = Parse.Object.extend(className);
|
||||
let query = new Parse.Query(fldr);
|
||||
query.equalTo("objectId", objectId);
|
||||
const result = await query.first();
|
||||
let resultJson = result.toJSON();
|
||||
let CustomTabList = [];
|
||||
if (resultJson) {
|
||||
CustomTabList.push(resultJson);
|
||||
if (resultJson[`${props.schema.data.ParentFolderField}`]) {
|
||||
let tabsRes = resultJson[`${props.schema.data.ParentFolderField}`];
|
||||
let looping = false;
|
||||
do {
|
||||
let fldttr = Parse.Object.extend(tabsRes.className);
|
||||
let query2 = new Parse.Query(fldttr);
|
||||
query2.equalTo("objectId", tabsRes.objectId);
|
||||
let custome_res = await query2.first();
|
||||
if (custome_res) {
|
||||
let tabsData = custome_res.toJSON();
|
||||
CustomTabList.push(tabsData);
|
||||
if (tabsData[`${props.schema.data.ParentFolderField}`]) {
|
||||
tabsRes = tabsData[`${props.schema.data.ParentFolderField}`];
|
||||
looping = true;
|
||||
} else {
|
||||
looping = false;
|
||||
}
|
||||
}
|
||||
} while (looping);
|
||||
}
|
||||
let url = "Folders";
|
||||
CustomTabList.length > 0 &&
|
||||
CustomTabList.reverse().forEach(
|
||||
(t) => (url = url + " / " + t[props.schema.data.FolderNameField])
|
||||
);
|
||||
setTabURL(url);
|
||||
setSelectedFolder({
|
||||
Topic: resultJson[`${props.schema.data.FolderNameField}`],
|
||||
description: resultJson[`${props.schema.data.FolderDescription}`]
|
||||
});
|
||||
setTabList(CustomTabList);
|
||||
props.onChange(resultJson.objectId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async ({ formData }) => {
|
||||
setLoader(true);
|
||||
|
||||
try {
|
||||
let RowData = formData;
|
||||
let _scanData = schemaState;
|
||||
Object.keys(_scanData).forEach(function (key) {
|
||||
let _dd = _scanData[key];
|
||||
Object.keys(_dd).forEach(function (k) {
|
||||
if (_dd[k].component === "AutoSuggest" && _dd[k].isPointer) {
|
||||
let pointer = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].class,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
RowData[k] = pointer;
|
||||
}
|
||||
if (_dd[k].format === "date") {
|
||||
let newdate = new Date(RowData[k]);
|
||||
RowData[k] = newdate;
|
||||
}
|
||||
if (_dd[k].component === "DateTime") {
|
||||
let newDate11;
|
||||
if (!RowData[k]) {
|
||||
newDate11 = new Date();
|
||||
} else {
|
||||
newDate11 = new Date(RowData[k]);
|
||||
}
|
||||
RowData[k] = newDate11;
|
||||
}
|
||||
if (_dd[k].component === "CurrencyInput") {
|
||||
if (_dd[k].currencyColumn) {
|
||||
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
|
||||
}
|
||||
}
|
||||
if (_dd[k].data !== undefined) {
|
||||
if (_dd[k].data.isPointer) {
|
||||
let pointer = {};
|
||||
if (RowData[k] && RowData[k] !== "Select") {
|
||||
if (_dd[k].data.class) {
|
||||
if (RowData[k]) {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].data.class,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
pointer = {
|
||||
__type: "Pointer",
|
||||
className: localStorage.getItem("extended_class"),
|
||||
objectId: RowData[k]
|
||||
};
|
||||
}
|
||||
|
||||
RowData[k] = pointer;
|
||||
}
|
||||
}
|
||||
if (_dd[k].data.FolderTypeValue) {
|
||||
let obj = {
|
||||
__type: "Pointer",
|
||||
className: _dd[k].data.ClassName,
|
||||
objectId: RowData[k]
|
||||
};
|
||||
RowData[k] = obj;
|
||||
}
|
||||
}
|
||||
if (_dd[k].type === "string") {
|
||||
let d = RowData[k];
|
||||
if (typeof d === "string") {
|
||||
RowData[k] = d.trim();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
|
||||
var forms = Parse.Object.extend(className);
|
||||
var folder = new forms();
|
||||
folder.set(
|
||||
props.schema.data.FolderNameField,
|
||||
formData[props.schema.data.FolderNameField]
|
||||
);
|
||||
if (props.schema.data.FolderDescription) {
|
||||
folder.set(
|
||||
props.schema.data.FolderDescription,
|
||||
formData[props.schema.data.FolderDescription]
|
||||
);
|
||||
}
|
||||
|
||||
folder.set("AppId", appId);
|
||||
folder.set(
|
||||
props.schema.data.FolderTypeField,
|
||||
props.schema.data.FolderTypeValue
|
||||
);
|
||||
const currentUser = Parse.User.current();
|
||||
folder.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||
if (tabList.length > 0) {
|
||||
let len = tabList.length - 1;
|
||||
folder.set(props.schema.data.ParentFolderField, {
|
||||
__type: "Pointer",
|
||||
className: props.schema.data.ClassName,
|
||||
objectId: tabList[len]["objectId"]
|
||||
});
|
||||
}
|
||||
folder.save(RowData).then(
|
||||
(result) => {
|
||||
RowData = {};
|
||||
if (result) {
|
||||
setIsAddField(false);
|
||||
setLoader(false);
|
||||
if (tabList.length > 0) {
|
||||
let len = tabList.length - 1;
|
||||
selectedItemList(tabList[len]);
|
||||
} else {
|
||||
selectFolderHandle();
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log("error ", error);
|
||||
setLoader(false);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props.formData) {
|
||||
editFolderDisplay(props.formData);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [props.formData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultState) {
|
||||
selectFolderHandle();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [defaultState]);
|
||||
const customStyles = {
|
||||
content: {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
right: "auto",
|
||||
bottom: "auto",
|
||||
marginRight: "-50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
backgroundColor: "white",
|
||||
padding: 0
|
||||
},
|
||||
overlay: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
||||
zIndex: 50
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LabelField
|
||||
Title={props.schema.title}
|
||||
Name={props.name}
|
||||
Required={props.required}
|
||||
HelpBody={props.schema.helpbody}
|
||||
HelpLink={props.schema.helplink}
|
||||
/>
|
||||
<br />
|
||||
<div className="block max-w-sm bg-white border border-gray-200 shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
color: "black",
|
||||
fontWeight: "bold",
|
||||
fontSize: "13px",
|
||||
padding: "2%",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<div style={{ width: "20%" }}>
|
||||
<div
|
||||
className="pull-left"
|
||||
title="Select Folder"
|
||||
style={{
|
||||
color: "#33bbff",
|
||||
margin: "auto",
|
||||
padding: "10px 10px",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="far fa-folder-open"
|
||||
style={{ fontSize: "40px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ margin: "0px 10px 0 20px", width: "80%" }}>
|
||||
<div>
|
||||
{selectedFolder && selectedFolder.Topic
|
||||
? selectedFolder.Topic
|
||||
: "Root"}
|
||||
<i
|
||||
className="fa fa-pencil"
|
||||
title="Select Folder"
|
||||
style={{ fontSize: 17, cursor: "pointer", marginLeft: 12 }}
|
||||
onClick={selectFolderHandle}
|
||||
></i>
|
||||
{selectedFolder && (
|
||||
<a
|
||||
className="pull-right"
|
||||
onClick={() => {
|
||||
setSelectedFolder(undefined);
|
||||
setTabURL(undefined);
|
||||
props.onChange(undefined);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-times-circle-o"
|
||||
aria-hidden="true"
|
||||
style={{ fontSize: 17 }}
|
||||
></i>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
{TabURL && (
|
||||
<div
|
||||
style={{ fontSize: "10px", color: "gray", fontWeight: "bold" }}
|
||||
>
|
||||
({TabURL})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* save document in folder and add new folder modal */}
|
||||
<Modal
|
||||
isOpen={isShowModal}
|
||||
onRequestClose={() => setIsShowModal(false)}
|
||||
shouldCloseOnOverlayClick={false}
|
||||
contentLabel="Modal"
|
||||
style={customStyles}
|
||||
>
|
||||
<div className="w-full min-w-[300px] md:min-w-[500px]">
|
||||
{/* header */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
backgroundColor: "rgba(203, 200, 200, 0.09)"
|
||||
}}
|
||||
>
|
||||
<div style={{ padding: "10px" }}>
|
||||
<h4 className=" text-black">Select Folder</h4>
|
||||
</div>
|
||||
<div style={{ padding: "10px" }}>
|
||||
<button
|
||||
className="btn btn-sm pull-right"
|
||||
data-dismiss="modal"
|
||||
title="Select Folder"
|
||||
style={{
|
||||
width: "auto",
|
||||
backgroundColor: "white",
|
||||
border: "1px solid gray",
|
||||
boxshadow: "rgba(100, 100, 111, 0.2) 0px 7px 29px 0px"
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsAddField(false);
|
||||
setFolderList([]);
|
||||
setEditable(false);
|
||||
if (!props.formData) {
|
||||
setTabList([]);
|
||||
}
|
||||
setIsShowModal(false);
|
||||
}}
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{/* body */}
|
||||
<div className="p-3 space-y-6">
|
||||
<div
|
||||
style={{
|
||||
color: "#ac4848",
|
||||
cursor: "pointer",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500"
|
||||
}}
|
||||
>
|
||||
<a
|
||||
onClick={() => {
|
||||
setTabList([]);
|
||||
setIsAddField(false);
|
||||
setDefaultState(true);
|
||||
setEditable(false);
|
||||
}}
|
||||
>
|
||||
Folders
|
||||
</a>
|
||||
{" / "}
|
||||
|
||||
{tabList &&
|
||||
tabList.map((tab, i) => (
|
||||
<React.Fragment key={`${tab.objectId}-${i}`}>
|
||||
<a
|
||||
title={tab[`${props.schema.data.FolderNameField}`]}
|
||||
onClick={(e) => removeTabListItem(e, i)}
|
||||
>
|
||||
{tab[`${props.schema.data.FolderNameField}`]}
|
||||
</a>
|
||||
{" / "}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<hr />
|
||||
</div>
|
||||
{/* {editable && (
|
||||
<TreeEditForm
|
||||
FormId={props.schema.data.FormId}
|
||||
objectId={editId}
|
||||
HideView={(cl) => {
|
||||
setEditable(cl);
|
||||
selectFolderHandle();
|
||||
}}
|
||||
/>
|
||||
)} */}
|
||||
{isAddField && !loader && !editable && (
|
||||
<TreeFormComponent
|
||||
Id={props.schema.data.FormId}
|
||||
handleSubmit={handleSubmit}
|
||||
ParentField={props.schema.parent && props.schema.parent}
|
||||
ParentValue={props.schema.parent && props.parent}
|
||||
ChildField={props.child && props.child}
|
||||
ClassName={(cls) => setClassName(cls)}
|
||||
SchemaState={(cls) => setSchemaState(cls)}
|
||||
/>
|
||||
)}
|
||||
{loader && (
|
||||
<div
|
||||
className="loader-01"
|
||||
style={{
|
||||
marginTop: "50px",
|
||||
marginLeft: "50%",
|
||||
color: "rgb(0,28,28)",
|
||||
fontSize: "35px"
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
|
||||
{!isAddField && !editable && (
|
||||
<div
|
||||
style={{
|
||||
maxHeight: "280px",
|
||||
overflowY: "auto",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
<ul>
|
||||
{folderList &&
|
||||
folderList.map(
|
||||
(fldr) =>
|
||||
fldr[props.schema.data.FolderTypeField] ===
|
||||
props.schema.data.FolderTypeValue && (
|
||||
<li
|
||||
key={`${fldr.objectId}`}
|
||||
style={{
|
||||
listStyle: "none",
|
||||
padding: "10px 20px",
|
||||
textTransform: "capitalize",
|
||||
fontWeight: 300,
|
||||
border: "1px solid gray",
|
||||
borderRadius: "1px",
|
||||
marginTop: "8px",
|
||||
transition: "all 0.75s ease",
|
||||
WebkitTransition: "all 0.5s ease",
|
||||
MozTransition: "all 0.5s ease",
|
||||
MsTransition: "all 0.5s ease",
|
||||
OTransition: "all 0.5 ease",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className={"fa fa-folder"}
|
||||
style={{
|
||||
fontSize: "1.4rem",
|
||||
color: "skyblue"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<a
|
||||
style={{
|
||||
fontWeight: "bold",
|
||||
fontSize: "12px",
|
||||
marginLeft: "4px",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => {
|
||||
setTabList((tabs) => tabs.concat(fldr));
|
||||
selectedItemList(fldr);
|
||||
}}
|
||||
>
|
||||
{fldr[`${props.schema.data.FolderNameField}`]}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* {fldr[props.schema.data.FolderTypeField] ===
|
||||
props.schema.data.FolderTypeValue && (
|
||||
<a
|
||||
className="float-right"
|
||||
onClick={() => {
|
||||
setEditable(true);
|
||||
setEditId(fldr.objectId);
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className={"fa fa-pencil"}
|
||||
title={"Edit Folder"}
|
||||
style={{
|
||||
fontSize: "0.9rem",
|
||||
color: "purple",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</a>
|
||||
)} */}
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* footer */}
|
||||
|
||||
<div className="flex items-center p-3 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600 justify-between">
|
||||
<button
|
||||
className="btn btn-sm float-left createFolder"
|
||||
title="Create New Folder"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsAddField(true);
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<i
|
||||
className="fa fa-plus"
|
||||
style={{ fontSize: "1.5rem" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
{!isAddField && !editable && (
|
||||
<div className="flex">
|
||||
{tabList.length > 0 && (
|
||||
<button
|
||||
className="btn btn-sm float-right deleteFolder"
|
||||
title="Delete Folder"
|
||||
onClick={onDeleteFolder}
|
||||
>
|
||||
{" "}
|
||||
<i
|
||||
className="fa fa-trash"
|
||||
style={{ fontSize: "1rem" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-sm float-right saveFolder"
|
||||
title="Save Here"
|
||||
onClick={onSubmitResult}
|
||||
>
|
||||
{" "}
|
||||
{/* <i
|
||||
className="fa fa-floppy-o"
|
||||
style={{ fontSize: "2rem" }}
|
||||
aria-hidden="true"
|
||||
></i> */}
|
||||
<i
|
||||
className="fas fa-save"
|
||||
aria-hidden="true"
|
||||
style={{ fontSize: "20px" }}
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
parent: state.Level1_Dropdown,
|
||||
child: state.Level2_Dropdown
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(TreeWidget);
|
||||
@@ -0,0 +1,47 @@
|
||||
import React from "react";
|
||||
import "../../styles/loader.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { openInNewTab } from "../../constant/Utils";
|
||||
|
||||
const DashboardButton = (props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
function openReport() {
|
||||
if (props.Data && props.Data.Redirect_type) {
|
||||
const Redirect_type = props.Data.Redirect_type;
|
||||
const id = props.Data.Redirect_id;
|
||||
if (Redirect_type === "Form") {
|
||||
navigate(`/form/${id}`);
|
||||
} else if (Redirect_type === "Report") {
|
||||
navigate(`/report/${id}`);
|
||||
} else if (Redirect_type === "Url") {
|
||||
openInNewTab(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClick={() => openReport()}
|
||||
className={`${
|
||||
props.Data && props.Data.Redirect_type
|
||||
? "cursor-pointer"
|
||||
: "cursor-default"
|
||||
} w-full px-3 py-2 flex text-white rounded-md shadow overflow-hidden`}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-5">
|
||||
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||
<i
|
||||
className={`${
|
||||
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||
} text-[25px] lg:text-[30px]`}
|
||||
></i>
|
||||
</span>
|
||||
<div className="">
|
||||
<div className="text-base lg:text-lg text-black"> {props.Label}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardButton;
|
||||
@@ -4,6 +4,7 @@ import Parse from "parse";
|
||||
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||
import "../../styles/loader.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
|
||||
const DashboardCard = (props) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -24,9 +25,6 @@ const DashboardCard = (props) => {
|
||||
};
|
||||
let body = {};
|
||||
let currentUser;
|
||||
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
let currentUser1 = Parse.User.current();
|
||||
currentUser = currentUser1.id;
|
||||
let res;
|
||||
@@ -82,8 +80,6 @@ const DashboardCard = (props) => {
|
||||
} else {
|
||||
setLoading(true);
|
||||
try {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
let reg1 = /(\#.*?\#)/gi; // eslint-disable-line
|
||||
let _query = props.Data.query;
|
||||
@@ -223,8 +219,6 @@ const DashboardCard = (props) => {
|
||||
|
||||
if (restr.includes("#")) {
|
||||
try {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
let res;
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
@@ -342,7 +336,7 @@ const DashboardCard = (props) => {
|
||||
props.Data && props.Data.Redirect_type
|
||||
? "cursor-pointer"
|
||||
: "cursor-default"
|
||||
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden`}
|
||||
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden relative`}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-5">
|
||||
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||
@@ -359,6 +353,9 @@ const DashboardCard = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs absolute top-1 right-1">
|
||||
<Tooltip id={props.Label} iconColor={"white"} message={props?.Data?.tourMessage} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -45,18 +45,16 @@ function DashboardReport(props) {
|
||||
setActions(json.actions);
|
||||
setReportName(json.reportName);
|
||||
setHeading(json.heading);
|
||||
Parse.serverURL = localStorage.getItem("BaseUrl12");
|
||||
Parse.initialize(localStorage.getItem("AppID12"));
|
||||
const currentUser = Parse.User.current().id;
|
||||
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("AppID12"),
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
try {
|
||||
const params = { reportId: id, skip: skipUserRecord, limit: limit };
|
||||
const url = `${localStorage.getItem("BaseUrl12")}/functions/getReport`;
|
||||
const url = `${localStorage.getItem("baseUrl")}/functions/getReport`;
|
||||
const res = await axios.post(url, params, {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
|
||||
@@ -1,21 +1,71 @@
|
||||
import React, { Suspense, lazy } from "react";
|
||||
import ErrorBoundary from "../ErrorBoundary";
|
||||
|
||||
const DashboardButton = lazy(() => import("./DashboardButton"));
|
||||
const DashboardCard = lazy(() => import("./DashboardCard"));
|
||||
|
||||
const DashboardReport = lazy(() => import("./DashboardReport"));
|
||||
|
||||
const buttonList = [
|
||||
{
|
||||
label: "Sign yourself",
|
||||
redirectId: "sHAnZphf69",
|
||||
redirectType: "Form",
|
||||
icon: "fas fa-pen-nib"
|
||||
},
|
||||
{
|
||||
label: "Request signature",
|
||||
redirectId: "8mZzFxbG1z",
|
||||
redirectType: "Form",
|
||||
icon: "fa-solid fa-paper-plane"
|
||||
}
|
||||
];
|
||||
const GetDashboard = (props) => {
|
||||
const Button = ({ label, redirectId, redirectType, icon }) => (
|
||||
<div className={"bg-white rounded-md shadow w-full"}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<DashboardButton
|
||||
Icon={icon}
|
||||
Label={label}
|
||||
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
const renderSwitchWithTour = (col) => {
|
||||
switch (col.widget.type) {
|
||||
case "Card":
|
||||
return (
|
||||
<div
|
||||
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
|
||||
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
|
||||
data-tut={col.widget.data.tourSection}
|
||||
style={{ background: col.widget.bgColor }}
|
||||
>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<DashboardCard
|
||||
Icon={col.widget.icon}
|
||||
Label={col.widget.label}
|
||||
@@ -31,7 +81,7 @@ const GetDashboard = (props) => {
|
||||
return (
|
||||
<div data-tut={col.widget.data.tourSection}>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-4 md:mb-0">
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
@@ -47,7 +97,7 @@ const GetDashboard = (props) => {
|
||||
case "Card":
|
||||
return (
|
||||
<div
|
||||
className={"bg-[#2ed8b6] rounded-md shadow mb-4 md:mb-0"}
|
||||
className={"bg-[#2ed8b6] rounded-md shadow mb-3 md:mb-0"}
|
||||
style={{ background: col.widget.bgColor }}
|
||||
>
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
@@ -65,7 +115,7 @@ const GetDashboard = (props) => {
|
||||
case "report": {
|
||||
return (
|
||||
<Suspense fallback={<div>please wait</div>}>
|
||||
<div className="mb-4 md:mb-0">
|
||||
<div className="mb-3 md:mb-0">
|
||||
<DashboardReport Record={col.widget} />
|
||||
</div>
|
||||
</Suspense>
|
||||
@@ -76,25 +126,39 @@ const GetDashboard = (props) => {
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div>
|
||||
{props.dashboard.map((val, key) => (
|
||||
<div key={"a" + key} className="row">
|
||||
{val.columns.map((col, i) =>
|
||||
col.widget.data && col.widget.data.tourSection ? (
|
||||
<div key={i} className={props.classnameArray[key][i]}>
|
||||
{renderSwitchWithTour(col)}
|
||||
</div>
|
||||
) : (
|
||||
<div key={i} className={props.classnameArray[key][i]}>
|
||||
{renderSwitch(col)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div>
|
||||
<div className="mb-3">
|
||||
<div
|
||||
data-tut={"tourbutton"}
|
||||
className="flex flex-col md:flex-row gap-6 md:gap-8"
|
||||
>
|
||||
{buttonList.map((btn) => (
|
||||
<Button
|
||||
key={btn.label}
|
||||
label={btn.label}
|
||||
redirectType={btn.redirectType}
|
||||
redirectId={btn.redirectId}
|
||||
icon={btn.icon}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
{props.dashboard.map((val, key) => (
|
||||
<div key={"a" + key} className="row">
|
||||
{val.columns.map((col, i) =>
|
||||
col.widget.data && col.widget.data.tourSection ? (
|
||||
<div key={i} className={props.classnameArray[key][i]}>
|
||||
{renderSwitchWithTour(col)}
|
||||
</div>
|
||||
) : (
|
||||
<div key={i} className={props.classnameArray[key][i]}>
|
||||
{renderSwitch(col)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,308 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { SaveFileSize } from "../../constant/saveFileSize";
|
||||
import Parse from "parse";
|
||||
import sanitizeFileName from "../../primitives/sanitizeFileName";
|
||||
import DropboxChooser from "./DropboxChoose";
|
||||
const FileUpload = (props) => {
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [_fileupload, setFileUpload] = useState("");
|
||||
const [fileload, setfileload] = useState(false);
|
||||
const [Message] = useState(false);
|
||||
const [percentage, setpercentage] = useState(0);
|
||||
|
||||
const REQUIRED_FIELD_SYMBOL = "*";
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.formData) {
|
||||
setFileUpload([]);
|
||||
props.onChange(undefined);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.formData]);
|
||||
const onChange = (e) => {
|
||||
try {
|
||||
let files = e.target.files;
|
||||
if (typeof files[0] !== "undefined") {
|
||||
if (props.schema.filetypes && props.schema.filetypes.length > 0) {
|
||||
var fileName = files[0].name;
|
||||
var fileNameExt = fileName
|
||||
.substr(fileName.lastIndexOf(".") + 1)
|
||||
.toLowerCase();
|
||||
let Extensions = props.schema.filetypes.map((x) => x.toLowerCase());
|
||||
let arr = Extensions.filter((x) => x === fileNameExt);
|
||||
if (arr.length > 0) {
|
||||
console.log("multiple type");
|
||||
} else {
|
||||
alert(
|
||||
"Only these file types are accepted : " + Extensions.join(", ")
|
||||
);
|
||||
}
|
||||
}
|
||||
if (props.schema.maxfilesizeKB && props.schema.maxfilesizeKB !== "") {
|
||||
// console.log(Math.round(files[0].size / 1024));
|
||||
if (
|
||||
Math.round(Number(files[0].size) / 1024) >=
|
||||
props.schema.maxfilesizeKB
|
||||
) {
|
||||
alert(
|
||||
`The selected file size is too large. Please select a file less than ${Math.round(
|
||||
props.schema.maxfilesizeKB / 1024
|
||||
)} MB`
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
handleFileUpload(files[0]);
|
||||
} else {
|
||||
alert("Please select file.");
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (file) => {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
setfileload(true);
|
||||
const size = file.size;
|
||||
const fileName = file.name;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const pdfFile = file;
|
||||
const parseFile = new Parse.File(name, pdfFile);
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
// console.log("percentCompleted ", percentCompleted);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setFileUpload(response.url());
|
||||
props.onChange(response.url());
|
||||
setfileload(false);
|
||||
// The response object will contain information about the uploaded file
|
||||
// console.log("File uploaded:", response);
|
||||
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
// console.log("File URL:", response.url());
|
||||
if (response.url()) {
|
||||
SaveFileSize(size, response.url());
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const dropboxSuccess = async (files) => {
|
||||
// console.log("file ", files);
|
||||
setfileload(true);
|
||||
const file = files[0];
|
||||
const url = file.link;
|
||||
const size = file.bytes;
|
||||
const mb = Math.round(file.bytes / Math.pow(1024, 2));
|
||||
|
||||
if (mb > 10) {
|
||||
setTimeout(() => {
|
||||
alert(
|
||||
`The selected file size is too large. Please select a file less than 10 MB`
|
||||
);
|
||||
}, 500);
|
||||
return;
|
||||
} else {
|
||||
const name = sanitizeFileName(file.name);
|
||||
|
||||
const parseFile = new Parse.File(name, { uri: url });
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
// console.log("percentCompleted ", percentCompleted);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
// console.log("response.url() ", response.url());
|
||||
setFileUpload(response.url());
|
||||
props.onChange(response.url());
|
||||
setfileload(false);
|
||||
|
||||
if (response.url()) {
|
||||
SaveFileSize(size, response.url());
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const dropboxCancel = async () => {
|
||||
console.log("cancel clicked ");
|
||||
};
|
||||
let fileView =
|
||||
props.formData &&
|
||||
props.schema.uploadtype === "s3viajw" ? null : props.formData &&
|
||||
props.formData ? (
|
||||
<React.Fragment>
|
||||
<a
|
||||
href={props.formData}
|
||||
title={props.formData}
|
||||
style={{ paddingBottom: "10px", color: "blue" }}
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
</React.Fragment>
|
||||
) : fileload === false ? null : (
|
||||
<React.Fragment>
|
||||
<a
|
||||
href={_fileupload}
|
||||
title={_fileupload}
|
||||
style={{ paddingBottom: "10px" }}
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
<br />
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ display: "inline-block" }}>
|
||||
<label htmlFor={props.name}>
|
||||
{props.schema.title}
|
||||
{props.required && (
|
||||
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
|
||||
)}
|
||||
{fileload ? (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
) : (
|
||||
Message && (
|
||||
<span style={{ fontSize: "9px", marginLeft: "4px" }}>
|
||||
Processing will take 5-10 mins....
|
||||
</span>
|
||||
)
|
||||
)}
|
||||
{props.schema.helpbody ? (
|
||||
<div className="dropdown pull-right">
|
||||
<i
|
||||
className="far fa-question-circle dropdown-toggle hovereffect"
|
||||
aria-hidden="true"
|
||||
id="dropdownMenuButton"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "purple",
|
||||
cursor: "pointer !important",
|
||||
position: "relative",
|
||||
bottom: "0px",
|
||||
left: "0px",
|
||||
paddingBottom: "4px",
|
||||
paddingLeft: "4px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
style={{
|
||||
marginleft: "-121px",
|
||||
margintop: "-14px",
|
||||
position: "absolute",
|
||||
padding: "10px",
|
||||
top: "102px!important"
|
||||
}}
|
||||
>
|
||||
<br />
|
||||
{props.schema.helplink ? (
|
||||
<a
|
||||
href={props.schema.helplink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="btn btn-xs btn-primary"
|
||||
>
|
||||
Read more..
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<span style={{ marginLeft: "10px" }}>{fileView}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<>
|
||||
{props.formData ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-[3px] w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {props.formData.split("/")[3]}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
console.log("clicked");
|
||||
setFileUpload([]);
|
||||
props.onChange(undefined);
|
||||
}}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<input
|
||||
type="file"
|
||||
id="hashfile"
|
||||
style={{
|
||||
border: "1px solid #ccc",
|
||||
color: "gray",
|
||||
backgroundColor: "white",
|
||||
padding: "5px 10px",
|
||||
borderRadius: "4px",
|
||||
fontSize: "13px",
|
||||
width: "100%",
|
||||
fontWeight: "bold"
|
||||
}}
|
||||
accept="application/pdf,application/vnd.ms-excel"
|
||||
onChange={onChange}
|
||||
/>
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileUpload;
|
||||
@@ -1,98 +0,0 @@
|
||||
import React, { Component } from "react";
|
||||
import Parse from "parse";
|
||||
// import axios from "axios";
|
||||
|
||||
export class HiddenField extends Component {
|
||||
state = {
|
||||
baseUrl: localStorage.getItem("baseUrl"),
|
||||
parseAppId: localStorage.getItem("parseAppId"),
|
||||
_fields: ""
|
||||
};
|
||||
loadData = async () => {
|
||||
if (this.props.schema.default) {
|
||||
this.setState({ _fields: this.props.schema.default });
|
||||
} else {
|
||||
try {
|
||||
Parse.serverURL = this.state.baseUrl;
|
||||
Parse.initialize(this.state.parseAppId);
|
||||
let currentUser = await Parse.User.current();
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
let _rdata = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
let custome_Filed = "";
|
||||
let splitArray = this.props.schema.data.valueKey.split(".");
|
||||
if (splitArray.length > 1) {
|
||||
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
|
||||
} else {
|
||||
let _Filed = _rdata[0][splitArray[0]];
|
||||
if (typeof _Filed === "object") {
|
||||
custome_Filed = {
|
||||
__type: "Pointer",
|
||||
className: _Filed["className"],
|
||||
objectId: _Filed["objectId"]
|
||||
};
|
||||
} else {
|
||||
custome_Filed = _rdata[0][splitArray[0]];
|
||||
}
|
||||
}
|
||||
this.setState({ _fields: custome_Filed });
|
||||
} else {
|
||||
if (currentUser) {
|
||||
// let url = `${this.state.baseUrl}classes/${localStorage.getItem(
|
||||
// "extended_class"
|
||||
// )}?where={"UserId":{"__type":"Pointer","className":"_User","objectId":"${
|
||||
// currentUser.id
|
||||
// }"}}&keys=${this.props.schema.data.valueKey}`;
|
||||
// const headers = {
|
||||
// "Content-Type": "application/json",
|
||||
// "X-Parse-Application-Id": this.state.parseAppId
|
||||
// };
|
||||
// await axios.get(url, { headers: headers }).then((res) => {
|
||||
// let custome_Filed = "";
|
||||
// let _rdata = res.data.results;
|
||||
// let splitArray = this.props.schema.data.valueKey.split(".");
|
||||
// if (splitArray.length > 1) {
|
||||
// custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
|
||||
// } else {
|
||||
// console.log(_rdata[0][splitArray[0]]);
|
||||
// custome_Filed = _rdata[0][splitArray[0]];
|
||||
// }
|
||||
// this.setState({ _fields: custome_Filed });
|
||||
// });
|
||||
|
||||
const res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
let custome_Filed = "";
|
||||
const result = res.toJSON();
|
||||
let _rdata = [result];
|
||||
let splitArray = this.props.schema.data.valueKey.split(".");
|
||||
if (splitArray.length > 1) {
|
||||
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
|
||||
} else {
|
||||
console.log(_rdata[0][splitArray[0]]);
|
||||
custome_Filed = _rdata[0][splitArray[0]];
|
||||
}
|
||||
this.setState({ _fields: custome_Filed });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
componentDidMount() {
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<input
|
||||
type="hidden"
|
||||
value={this.state._fields}
|
||||
onChange={this.props.onChange(this.state._fields)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default HiddenField;
|
||||
@@ -1,70 +0,0 @@
|
||||
import React from "react";
|
||||
import parse from "html-react-parser";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const LabelField = (props) => {
|
||||
const { Title, Name, Required, HelpBody, HelpLink } = props;
|
||||
const REQUIRED_FIELD_SYMBOL = "*";
|
||||
return (
|
||||
<div style={{ display: "inline-block" }}>
|
||||
<label htmlFor={Name}>
|
||||
{Title}
|
||||
{Required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
|
||||
{HelpBody ? (
|
||||
<div className="dropdown pull-right">
|
||||
<i
|
||||
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
|
||||
aria-hidden="true"
|
||||
id="dropdownMenuButton"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "purple",
|
||||
cursor: "pointer !important",
|
||||
position: "relative",
|
||||
bottom: "0px",
|
||||
left: "0px",
|
||||
paddingBottom: "4px",
|
||||
paddingLeft: "4px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
style={{
|
||||
marginleft: "-121px",
|
||||
margintop: "-14px",
|
||||
position: "absolute",
|
||||
padding: "10px",
|
||||
top: "102px!important"
|
||||
}}
|
||||
>
|
||||
{parse(`
|
||||
${HelpBody}
|
||||
`)}
|
||||
<br />
|
||||
{HelpLink ? (
|
||||
<a
|
||||
href={HelpLink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="btn btn-xs btn-primary"
|
||||
>
|
||||
Read more..
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
LabelField.propTypes = {
|
||||
Name: PropTypes.string.isRequired,
|
||||
Title: PropTypes.string.isRequired
|
||||
};
|
||||
export default LabelField;
|
||||
@@ -1,482 +0,0 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { onChangeLevel1Dropdown } from "../../redux/actions/index";
|
||||
import axios from "axios";
|
||||
import Parse from "parse";
|
||||
import parse from "html-react-parser";
|
||||
|
||||
const Level1Dropdown = (props) => {
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [Level1_DD, setLevel1_DD] = useState([]);
|
||||
const [toastColor] = useState("#d9534f");
|
||||
const [toastDescription, setToastDescription] = useState("");
|
||||
const [active, setActive] = useState(false);
|
||||
|
||||
const getParams = (offset) => {
|
||||
const params = {
|
||||
skip: offset
|
||||
};
|
||||
return params;
|
||||
};
|
||||
|
||||
const Level1DropdownData = async () => {
|
||||
try {
|
||||
if (
|
||||
props.formData &&
|
||||
props.uiSchema["ui:disabled"] &&
|
||||
props.formData !== "Select"
|
||||
) {
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData}`;
|
||||
if (typeof props.formData === "object") {
|
||||
url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData.objectId}`;
|
||||
}
|
||||
let headers1 = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
let data = [];
|
||||
let response = [];
|
||||
await axios.get(url, { headers: headers1 }).then((res) => {
|
||||
if (res) {
|
||||
data.push(res.data);
|
||||
}
|
||||
});
|
||||
|
||||
if (data.length > 0) {
|
||||
let temp = [];
|
||||
data.forEach((x) => {
|
||||
let opt = "";
|
||||
if (Array.isArray(x[props.schema.data.valueKey])) {
|
||||
x[props.schema.data.valueKey].forEach((tt) => {
|
||||
if (typeof tt === "object") {
|
||||
opt = {
|
||||
objectId: tt[props.schema.data.displayKey],
|
||||
option: tt[props.schema.data.displayKey]
|
||||
};
|
||||
} else {
|
||||
opt = {
|
||||
objectId: tt,
|
||||
option: tt
|
||||
};
|
||||
}
|
||||
temp.push(opt);
|
||||
});
|
||||
} else if (props.schema.data.isPointer) {
|
||||
if (props.schema.data.valueKey.includes(".")) {
|
||||
let newArr = props.schema.data.valueKey.split(".");
|
||||
if (newArr.length === 2) {
|
||||
opt = {
|
||||
objectId: x[newArr[0]][newArr[1]],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[newArr[0]],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[props.schema.data.valueKey],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
temp.push(opt);
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[props.schema.data.valueKey],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
temp.push(opt);
|
||||
}
|
||||
});
|
||||
response = temp;
|
||||
setLevel1_DD(response);
|
||||
setActive(false);
|
||||
if (response.length === 1) {
|
||||
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
|
||||
props.onChange(response[0].objectId);
|
||||
} else if (response.length === 0) {
|
||||
props.onChangeLevel1Dropdown("", props.name);
|
||||
props.onChange("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let response = [];
|
||||
setActive(true);
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const currentUser = Parse.User.current();
|
||||
|
||||
// eslint-disable-next-line
|
||||
var reg = /(\#.*?\#)/gi;
|
||||
let str = props.schema.data.query;
|
||||
var test;
|
||||
if (str.includes("#")) {
|
||||
let res;
|
||||
if (localStorage.getItem("Extand_Class")) {
|
||||
let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
res = data[0];
|
||||
} else {
|
||||
const currentUser = Parse.User.current();
|
||||
res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (res) res = res.toJSON();
|
||||
}
|
||||
|
||||
if (res) {
|
||||
let json = res;
|
||||
let output = str.match(reg);
|
||||
if (output.length === 1) {
|
||||
output = output.join();
|
||||
output = output.substring(1, output.length - 1);
|
||||
output = output.split(".");
|
||||
if (output.length === 1) {
|
||||
let out = output[0];
|
||||
if (json[out]) {
|
||||
if (typeof json[out] === "object") {
|
||||
test = str.replace(reg, JSON.stringify(json[out]));
|
||||
} else {
|
||||
test = str.replace(reg, json[out]);
|
||||
}
|
||||
} else {
|
||||
test = str.replace(reg, currentUser.id);
|
||||
}
|
||||
} else if (output.length === 2) {
|
||||
let out1 = json[output[0]][output[1]];
|
||||
if (out1) {
|
||||
test = str.replace(reg, out1);
|
||||
}
|
||||
}
|
||||
} else if (output.length === 2) {
|
||||
let output1 = output[0];
|
||||
output1 = output1.substring(1, output1.length - 1);
|
||||
output1 = output1.split(".");
|
||||
if (output1.length === 1) {
|
||||
let out = output1[0];
|
||||
if (json[out]) {
|
||||
if (typeof json[out] === "object") {
|
||||
str = str.replace(output[0], JSON.stringify(json[out]));
|
||||
} else {
|
||||
str = str.replace(output[0], json[out]);
|
||||
}
|
||||
} else {
|
||||
str = str.replace(output[0], currentUser.id);
|
||||
}
|
||||
} else if (output1.length === 2) {
|
||||
let out1 = json[output1[0]][output1[1]];
|
||||
if (out1) {
|
||||
str = str.replace(output[0], out1);
|
||||
}
|
||||
}
|
||||
let output2 = output[1];
|
||||
output2 = output2.substring(1, output2.length - 1);
|
||||
output2 = output2.split(".");
|
||||
if (output2.length === 1) {
|
||||
let out = output2[0];
|
||||
if (json[out]) {
|
||||
if (typeof json[out] === "object") {
|
||||
str = str.replace(output[1], JSON.stringify(json[out]));
|
||||
} else {
|
||||
str = str.replace(output[1], json[out]);
|
||||
}
|
||||
} else {
|
||||
str = str.replace(output[1], currentUser.id);
|
||||
}
|
||||
} else if (output2.length === 2) {
|
||||
let out1 = json[output2[0]][output2[1]];
|
||||
if (out1) {
|
||||
str = str.replace(output[1], out1);
|
||||
}
|
||||
}
|
||||
test = str;
|
||||
}
|
||||
} else {
|
||||
setToastDescription(
|
||||
`User not found in ${localStorage.getItem("extended_class")}`
|
||||
);
|
||||
setActive(false);
|
||||
var x = document.getElementById("Level_1");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
}, 5000);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
test = str;
|
||||
}
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${test}`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
let fetchCount = 0;
|
||||
let offset = 0;
|
||||
let data = [];
|
||||
/* eslint-disable no-constant-condition */
|
||||
while (true) {
|
||||
const params = getParams(offset);
|
||||
await axios
|
||||
.get(url, { params: params, headers: headers })
|
||||
.then((response) => {
|
||||
fetchCount = response.data.results.length;
|
||||
offset += response.data.results.length;
|
||||
|
||||
if (fetchCount > 0) {
|
||||
response.data.results.forEach((x) => {
|
||||
data.push(x);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Problem", err.response);
|
||||
setToastDescription(err.response.data.message);
|
||||
setActive(false);
|
||||
var x = document.getElementById("Level_1");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
}, 5000);
|
||||
});
|
||||
if (fetchCount === 0 || fetchCount < 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data.length > 0) {
|
||||
let temp = [];
|
||||
data.forEach((x) => {
|
||||
let opt = "";
|
||||
if (Array.isArray(x[props.schema.data.valueKey])) {
|
||||
x[props.schema.data.valueKey].forEach((tt) => {
|
||||
if (typeof tt === "object") {
|
||||
if (props.schema.data.isPointer) {
|
||||
opt = {
|
||||
objectId: tt["objectId"],
|
||||
option: tt[props.schema.data.displayKey]
|
||||
};
|
||||
} else {
|
||||
opt = {
|
||||
objectId: tt[props.schema.data.displayKey],
|
||||
option: tt[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opt = {
|
||||
objectId: tt,
|
||||
option: tt
|
||||
};
|
||||
}
|
||||
temp.push(opt);
|
||||
});
|
||||
} else if (props.schema.data.isPointer) {
|
||||
if (props.schema.data.valueKey.includes(".")) {
|
||||
let newArr = props.schema.data.valueKey.split(".");
|
||||
if (newArr.length === 2) {
|
||||
let _dis = props.schema.data.displayKey.split(".");
|
||||
if (_dis.length === 2) {
|
||||
opt = {
|
||||
objectId: x[newArr[0]][newArr[1]],
|
||||
option: x[_dis[0]][_dis[1]]
|
||||
};
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[newArr[0]][newArr[1]],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[newArr[0]],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[props.schema.data.valueKey],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
}
|
||||
temp.push(opt);
|
||||
} else {
|
||||
opt = {
|
||||
objectId: x[props.schema.data.valueKey],
|
||||
option: x[props.schema.data.displayKey]
|
||||
};
|
||||
temp.push(opt);
|
||||
}
|
||||
});
|
||||
response = temp;
|
||||
setLevel1_DD(response);
|
||||
setActive(false);
|
||||
if (!props.formData) {
|
||||
props.onChangeLevel1Dropdown("", props.name);
|
||||
}
|
||||
if (response.length === 1) {
|
||||
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
|
||||
props.onChange(response[0].objectId);
|
||||
} else if (response.length === 0) {
|
||||
props.onChangeLevel1Dropdown("", props.name);
|
||||
props.onChange("");
|
||||
}
|
||||
} else {
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
setToastDescription(e.message);
|
||||
setActive(false);
|
||||
x = document.getElementById("Level_1");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
}, 5000);
|
||||
} catch (error) {
|
||||
console.log("Err", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Level1DropdownData();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.formData && props.formData[`objectId`]) {
|
||||
props.onChangeLevel1Dropdown(props.formData.objectId, props.name);
|
||||
} else if (props.formData) {
|
||||
props.onChangeLevel1Dropdown(props.formData, props.name);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [props.formData]);
|
||||
|
||||
const selectDefault = () => {
|
||||
props.onChange(Level1_DD[0].objectId);
|
||||
props.onChangeLevel1Dropdown(Level1_DD[0].objectId, props.name);
|
||||
};
|
||||
|
||||
const REQUIRED_FIELD_SYMBOL = "*";
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div style={{ display: "inline-block" }}>
|
||||
<label htmlFor={props.name}>
|
||||
{props.schema.title}
|
||||
{props.required && (
|
||||
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
|
||||
)}
|
||||
{props.schema.data.helpbody ? (
|
||||
<div className="dropdown pull-right">
|
||||
<i
|
||||
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
|
||||
aria-hidden="true"
|
||||
id="dropdownMenuButton"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "purple",
|
||||
cursor: "pointer !important",
|
||||
position: "relative",
|
||||
bottom: "0px",
|
||||
left: "0px",
|
||||
paddingBottom: "4px",
|
||||
paddingLeft: "4px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className="dropdown-menu"
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
style={{
|
||||
marginleft: "-121px",
|
||||
margintop: "-14px",
|
||||
position: "absolute",
|
||||
padding: "10px",
|
||||
top: "102px!important"
|
||||
}}
|
||||
>
|
||||
{parse(`
|
||||
${props.schema.data.helpbody}
|
||||
`)}
|
||||
<br />
|
||||
{props.schema.data.helplink ? (
|
||||
<a
|
||||
href={props.schema.data.helplink}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="btn btn-xs btn-primary"
|
||||
>
|
||||
Read more..
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{Level1_DD && (
|
||||
<select
|
||||
className="form-control"
|
||||
name={props.name}
|
||||
value={props.value}
|
||||
required={props.required}
|
||||
disabled={props.uiSchema["ui:disabled"]}
|
||||
onChange={(e) => {
|
||||
if (!e.target.value) {
|
||||
if (props.schema.data.isPointer) {
|
||||
localStorage.setItem("Level1", undefined);
|
||||
props.onChangeLevel1Dropdown(undefined, props.name);
|
||||
return props.onChange(undefined);
|
||||
} else {
|
||||
localStorage.setItem("Level1", e.target.value);
|
||||
props.onChangeLevel1Dropdown(e.target.value, props.name);
|
||||
return props.onChange(e.target.value);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem("Level1", e.target.value);
|
||||
props.onChangeLevel1Dropdown(e.target.value, props.name);
|
||||
return props.onChange(e.target.value);
|
||||
}
|
||||
}}
|
||||
readOnly={props.uiSchema["ui:disabled"] ? true : false}
|
||||
defaultValue={Level1_DD.length === 1 && selectDefault}
|
||||
>
|
||||
<option value="">{!active ? "Select" : "loading..."}</option>
|
||||
{Level1_DD &&
|
||||
Level1_DD.map((x, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{props.formData && props.formData.objectId ? (
|
||||
props.formData.objectId === x.objectId ? (
|
||||
<option value={x.objectId} selected>
|
||||
{x.option}
|
||||
</option>
|
||||
) : (
|
||||
<option value={x.objectId}>{x.option}</option>
|
||||
)
|
||||
) : props.formData && props.formData === x.objectId ? (
|
||||
<option value={x.objectId} selected>
|
||||
{x.option}
|
||||
</option>
|
||||
) : (
|
||||
<option value={x.objectId}>{x.option}</option>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
<div id="Level_1" style={{ backgroundColor: toastColor }}>
|
||||
{toastDescription}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(null, { onChangeLevel1Dropdown })(Level1Dropdown);
|
||||
@@ -1,709 +0,0 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import Select from "react-select";
|
||||
import axios from "axios";
|
||||
import LabelField from "./LabelField";
|
||||
import ReactDragListView from "react-drag-listview";
|
||||
import AppendFormInForm from "../AppendFormInForm";
|
||||
import Modal from "react-modal";
|
||||
function arrayMove(array, from, to) {
|
||||
array = array.slice();
|
||||
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* react-sortable-hoc is depcreated not usable from react 18.x.x
|
||||
* need to replace it with @dnd-kit
|
||||
* code changes required
|
||||
*/
|
||||
|
||||
const MultiSelectField = (props) => {
|
||||
Modal.setAppElement("body");
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [state, setState] = useState(undefined);
|
||||
// const [editFormData, setEditFormData] = useState([]);
|
||||
const [selected, setSelected] = React.useState([]);
|
||||
const [isModal, setIsModel] = useState(false);
|
||||
const onChange = (selectedOptions) => setSelected(selectedOptions);
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||
|
||||
const onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
const newValue = arrayMove(selected, oldIndex, newIndex);
|
||||
setSelected(newValue);
|
||||
};
|
||||
// console.log("props",props)
|
||||
const GetSelectListData = async () => {
|
||||
try {
|
||||
let response = [];
|
||||
let str = props.schema.data.query;
|
||||
let _query;
|
||||
/* eslint-disable no-useless-escape */
|
||||
let reg = /(\#.*?\#)/gi;
|
||||
let extclass =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
let extRow = extclass.length && extclass[0];
|
||||
if (str.includes("#")) {
|
||||
let output = str.match(reg);
|
||||
output = output.join();
|
||||
output = output.substring(1, output.length - 1);
|
||||
output = output.split(".");
|
||||
|
||||
if (output.length === 2) {
|
||||
let out1 = extRow[output[0]][output[1]];
|
||||
_query = str.replace(reg, out1 && out1);
|
||||
} else {
|
||||
let out1 = extRow[output[0]];
|
||||
_query = str.replace(reg, out1 && out1);
|
||||
}
|
||||
} else {
|
||||
_query = str;
|
||||
}
|
||||
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.get(url, { headers: headers }).then((res) => {
|
||||
let temp = [];
|
||||
let formArray = [];
|
||||
let _selected = [];
|
||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
||||
if (props.formData) {
|
||||
props.formData.forEach((x) => {
|
||||
if (typeof x === "object") {
|
||||
formArray.push(x.objectId);
|
||||
} else {
|
||||
formArray.push(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
res.data.results.forEach((x) => {
|
||||
let obj = {};
|
||||
if (formArray.includes(x[props.schema.data.valueKey])) {
|
||||
obj = {
|
||||
name: x[props.schema.data.displayKey],
|
||||
value: x[props.schema.data.valueKey],
|
||||
isChecked: true
|
||||
};
|
||||
} else {
|
||||
if (props.schema.selectAll) {
|
||||
obj = {
|
||||
name: x[props.schema.data.displayKey],
|
||||
value: x[props.schema.data.valueKey],
|
||||
isChecked: true
|
||||
};
|
||||
} else {
|
||||
obj = {
|
||||
name: x[props.schema.data.displayKey],
|
||||
value: x[props.schema.data.valueKey],
|
||||
isChecked: false
|
||||
};
|
||||
}
|
||||
}
|
||||
temp.push(obj);
|
||||
});
|
||||
response = temp;
|
||||
if (props.formData) {
|
||||
let checkData = [];
|
||||
response.forEach((x) => {
|
||||
if (x.isChecked) {
|
||||
checkData.push(x.value);
|
||||
}
|
||||
});
|
||||
setSelected(checkData);
|
||||
}
|
||||
setState({ [`${props.name}_DD`]: response });
|
||||
} else {
|
||||
res.data.results.forEach((x) => {
|
||||
let obj = {};
|
||||
if (props.formData) {
|
||||
props.formData.forEach((x) => {
|
||||
if (typeof x === "object") {
|
||||
formArray.push(x.objectId);
|
||||
} else {
|
||||
formArray.push(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
obj = {
|
||||
label: x[props.schema.data.displayKey],
|
||||
value: x[props.schema.data.valueKey]
|
||||
};
|
||||
if (formArray.includes(x[props.schema.data.valueKey])) {
|
||||
_selected.push(obj);
|
||||
}
|
||||
if (props.schema.selectAll) {
|
||||
_selected.push(obj);
|
||||
}
|
||||
temp.push(obj);
|
||||
});
|
||||
response = temp;
|
||||
setState({ [`${props.name}_DD`]: response });
|
||||
setSelected(_selected);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCheckChieldElement = (event) => {
|
||||
let SelectLists = state[`${props.name}_DD`];
|
||||
let List = [];
|
||||
SelectLists.forEach((select) => {
|
||||
if (select.value === event.target.value) {
|
||||
select.isChecked = event.target.checked;
|
||||
}
|
||||
if (select.isChecked) {
|
||||
List.push(select.value);
|
||||
}
|
||||
});
|
||||
setSelected(List);
|
||||
setState({ [`${props.name}_DD`]: SelectLists });
|
||||
};
|
||||
|
||||
// const Level1CheckList = async (id) => {
|
||||
// try {
|
||||
// let response = [];
|
||||
// // eslint-disable-next-line
|
||||
// let reg = /(\#.*?\#)/gi;
|
||||
// let _query = props.schema.data.query;
|
||||
// let output = _query.match(reg);
|
||||
// if (output.length === 2) {
|
||||
// let res;
|
||||
// if (localStorage.getItem("Extand_Class")) {
|
||||
// let data = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
// res = data[0];
|
||||
// }
|
||||
// output = output.filter((x) => x !== "#queryString#");
|
||||
// if (output.length === 1) {
|
||||
// _query = _query.replace("#queryString#", id);
|
||||
// output = output.join();
|
||||
// output = output.substring(1, output.length - 1);
|
||||
// output = output.split(".");
|
||||
// if (output.length > 0) {
|
||||
// _query = _query.replace(reg, res[output[0]][output[1]]);
|
||||
// } else {
|
||||
// _query = _query.replace(reg, res[output[0]]);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// _query = props.schema.data.query.replace(reg, id);
|
||||
// }
|
||||
|
||||
// let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
|
||||
// const headers = {
|
||||
// "Content-Type": "application/json",
|
||||
// "X-Parse-Application-Id": parseAppId,
|
||||
// "X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
// };
|
||||
// await axios.get(url, { headers: headers }).then(async (res) => {
|
||||
// let temp = [];
|
||||
// let formArray = [];
|
||||
// let _selected = [];
|
||||
// if (editFormData.length > 0) {
|
||||
// editFormData.forEach((x) => {
|
||||
// if (typeof x === "object") {
|
||||
// formArray.push(x.objectId);
|
||||
// } else {
|
||||
// formArray.push(x);
|
||||
// }
|
||||
// });
|
||||
// } else if (
|
||||
// props.schema.selectedData &&
|
||||
// Object.keys(props.schema.selectedData).length !== 0 &&
|
||||
// props.schema.selectedData.constructor === Object
|
||||
// ) {
|
||||
// try {
|
||||
// let selectedDataQuery = props.schema.selectedData.query.replace(
|
||||
// reg,
|
||||
// id
|
||||
// );
|
||||
// let selectedDataUrl = `${parseBaseUrl}classes/${props.schema.selectedData.class}?${selectedDataQuery}`;
|
||||
|
||||
// await axios.get(selectedDataUrl, { headers }).then((sltres) => {
|
||||
// let sltData = sltres.data.results;
|
||||
// sltData.forEach((x) => {
|
||||
// if (props.schema.selectedData.valueKey.includes(".")) {
|
||||
// let sltArr = props.schema.selectedData.valueKey.split(".");
|
||||
|
||||
// if (Array.isArray(x[sltArr[0]])) {
|
||||
// x[sltArr[0]].forEach((l) => {
|
||||
// formArray.push(l[sltArr[1]]);
|
||||
// });
|
||||
// } else {
|
||||
// formArray.push(x[sltArr[0][sltArr[1]]]);
|
||||
// }
|
||||
// } else {
|
||||
// formArray.push(x[props.schema.selectedData.valueKey]);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// } catch (error) {}
|
||||
// }
|
||||
// res.data.results.forEach((x) => {
|
||||
// let obj = {};
|
||||
// if (props.schema.data.valueKey.includes(".")) {
|
||||
// let newArr = props.schema.data.valueKey.split(".");
|
||||
// if (Array.isArray(x[newArr[0]])) {
|
||||
// if (props.schema.data.displayKey.includes(".")) {
|
||||
// let _dis = props.schema.data.displayKey.split(".");
|
||||
// x[newArr[0]].forEach((l) => {
|
||||
// if (formArray.includes(l[newArr[1]])) {
|
||||
// obj = {
|
||||
// label: l[_dis[1]],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: l[_dis[1]],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: l[_dis[1]],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// temp.push(obj);
|
||||
// });
|
||||
// } else {
|
||||
// x[newArr[0]].forEach((l) => {
|
||||
// if (formArray.includes(l[newArr[1]])) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: l[newArr[1]],
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// temp.push(obj);
|
||||
// });
|
||||
// }
|
||||
// } else {
|
||||
// if (props.schema.data.displayKey.includes(".")) {
|
||||
// let disArr = props.schema.data.displayKey.split(".");
|
||||
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
|
||||
// obj = {
|
||||
// label: x[disArr[0]][disArr[1]],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: x[disArr[0]][disArr[1]],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: x[disArr[0]][disArr[1]],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[newArr[0]][newArr[1]],
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if (Array.isArray(x[props.schema.data.valueKey])) {
|
||||
// x[props.schema.data.valueKey].forEach((t) => {
|
||||
// if (formArray.includes(t)) {
|
||||
// obj = {
|
||||
// label: t,
|
||||
// value: t,
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: t,
|
||||
// value: t,
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: t,
|
||||
// value: t,
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// temp.push(obj);
|
||||
// });
|
||||
// } else if (formArray.includes(x[props.schema.data.valueKey])) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[props.schema.data.valueKey],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// if (props.schema.selectAll) {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[props.schema.data.valueKey],
|
||||
// isChecked: true
|
||||
// };
|
||||
// _selected.push(obj);
|
||||
// } else {
|
||||
// obj = {
|
||||
// label: x[props.schema.data.displayKey],
|
||||
// value: x[props.schema.data.valueKey],
|
||||
// isChecked: false
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// temp.push(obj);
|
||||
// }
|
||||
// });
|
||||
// response = temp;
|
||||
// if (props.schema.uiLayout === "MultiDropdownList") {
|
||||
// if (editFormData) {
|
||||
// let checkData = [];
|
||||
// response.forEach((x) => {
|
||||
// if (x.isChecked) {
|
||||
// checkData.push(x.value);
|
||||
// }
|
||||
// });
|
||||
// setSelected(checkData);
|
||||
// }
|
||||
// setState({ [`${props.name}_DD`]: response });
|
||||
// } else {
|
||||
// setState({ [`${props.name}_DD`]: response });
|
||||
// setSelected(_selected);
|
||||
// }
|
||||
// });
|
||||
// } catch (error) {}
|
||||
// };
|
||||
|
||||
const dragProps = {
|
||||
onDragEnd(fromIndex, toIndex) {
|
||||
const data = [...state[`${props.name}_DD`]];
|
||||
const item = data.splice(fromIndex, 1)[0];
|
||||
data.splice(toIndex, 0, item);
|
||||
let NewList = [];
|
||||
data.forEach((x) => {
|
||||
if (x.isChecked) {
|
||||
NewList.push(x.value);
|
||||
}
|
||||
});
|
||||
|
||||
setState({ [`${props.name}_DD`]: data });
|
||||
setSelected(NewList);
|
||||
},
|
||||
nodeSelector: "li",
|
||||
handleSelector: "a"
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.schema.parent) GetSelectListData();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
||||
props.onChange(selected);
|
||||
} else {
|
||||
if (selected && selected.length) {
|
||||
let newData = [];
|
||||
selected.forEach((x) => {
|
||||
newData.push(x.value);
|
||||
});
|
||||
props.onChange(newData);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [selected]);
|
||||
|
||||
// useState(() => {
|
||||
// if (props.formData) {
|
||||
// if (props.formData === "Select") {
|
||||
// } else {
|
||||
// setEditFormData(props.formData);
|
||||
// }
|
||||
// }
|
||||
// // eslint-disable-next-line
|
||||
// }, [props.formData]);
|
||||
|
||||
const handleModalCloseClick = () => {
|
||||
setIsModel(false);
|
||||
setModalIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setModalIsOpen(true);
|
||||
};
|
||||
|
||||
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
|
||||
const handleNewDetails = (data) => {
|
||||
setState({ [`${props.name}_DD`]: [...state[`${props.name}_DD`], data] });
|
||||
if (selected.length > 0) {
|
||||
setSelected([...selected, data]);
|
||||
} else {
|
||||
setSelected([data]);
|
||||
}
|
||||
};
|
||||
if (props.schema.uiLayout === "MultiDropdownList") {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LabelField
|
||||
Title={props.schema.title}
|
||||
Name={props.name}
|
||||
Required={props.required}
|
||||
HelpBody={props.schema.helpbody}
|
||||
HelpLink={props.schema.helplink}
|
||||
/>
|
||||
<ReactDragListView {...dragProps}>
|
||||
<ul
|
||||
style={{
|
||||
listStyleType: "square",
|
||||
listStylePosition: "outside",
|
||||
maxHeight: "250px",
|
||||
overflow: "auto",
|
||||
paddingTop: "10px",
|
||||
paddingBottom: "10px",
|
||||
border: "0px solid #b3b3b3",
|
||||
borderRadius: "5px"
|
||||
}}
|
||||
>
|
||||
{state && state[`${props.name}_DD`].length > 0 ? (
|
||||
state[`${props.name}_DD`].map((x, i) => {
|
||||
return (
|
||||
<li key={i} className="multiSelectList">
|
||||
<input
|
||||
key={x.value}
|
||||
onClick={handleCheckChieldElement}
|
||||
type="checkbox"
|
||||
checked={x.isChecked}
|
||||
style={{ marginTop: "7px" }}
|
||||
value={x.value}
|
||||
disabled={props.disabled}
|
||||
/>{" "}
|
||||
<span style={{ marginTop: "7px" }}>{x.name}</span>
|
||||
{props.schema.isSortable && !props.disabled && (
|
||||
<a
|
||||
onClick={(e) => e.preventDefault()}
|
||||
className="pull-right"
|
||||
title="Sort List"
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<i
|
||||
className="fa fa-bars"
|
||||
style={{
|
||||
fontSize: "30px",
|
||||
color: "skyblue",
|
||||
paddingTop: "11px"
|
||||
}}
|
||||
></i>
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<li className="multiSelectList">No Record Found</li>
|
||||
)}
|
||||
</ul>
|
||||
</ReactDragListView>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<LabelField
|
||||
Title={props.schema.title}
|
||||
Name={props.name}
|
||||
Required={props.required}
|
||||
HelpBody={props.schema.helpbody}
|
||||
HelpLink={props.schema.helplink}
|
||||
/>
|
||||
<div style={{ display: "flex", gap: 5 }}>
|
||||
<div style={{ flexWrap: "wrap", width: "100%" }}>
|
||||
<Select
|
||||
onSortEnd={onSortEnd}
|
||||
distance={4}
|
||||
isMulti
|
||||
options={(state && state[`${props.name}_DD`]) || []}
|
||||
value={selected}
|
||||
onChange={onChange}
|
||||
closeMenuOnSelect={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{props.schema.data.quickAddFormId && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsModel(true);
|
||||
openModal();
|
||||
}}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
borderRadius: 4,
|
||||
border: "1px solid #ccc",
|
||||
minHeight: 38,
|
||||
minWidth: 48,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
</div>
|
||||
)}
|
||||
{/* {props.schema.data.quickAddFormId && (
|
||||
<div
|
||||
className="modal fade"
|
||||
id={"multiSelectModal" + props.schema.data.quickAddFormId}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="exampleModalLabel"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="modal-dialog modal-lg" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button
|
||||
type="button"
|
||||
className="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={handleModalCloseClick}
|
||||
>
|
||||
<i className="fa fa-times-circle" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{isModal && (
|
||||
<AppendFormInForm
|
||||
id={props.schema.data.quickAddFormId}
|
||||
valueKey={props.schema.data.valueKey}
|
||||
displayKey={props.schema.data.displayKey}
|
||||
details={handleNewDetails}
|
||||
closePopup={handleModalCloseClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
<Modal
|
||||
isOpen={modalIsOpen}
|
||||
onRequestClose={handleModalCloseClick}
|
||||
shouldCloseOnOverlayClick={false}
|
||||
contentLabel="Modal"
|
||||
style={{
|
||||
content: {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
right: "auto",
|
||||
bottom: "auto",
|
||||
transform: "translate(-50%, -50%)",
|
||||
padding: 0
|
||||
},
|
||||
overlay: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
||||
zIndex: 50
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="min-w-full md:min-w-[500px]">
|
||||
<div
|
||||
type="button"
|
||||
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
|
||||
>
|
||||
<div className=" text-black text-xl font-semibold pl-3">
|
||||
Add Signer
|
||||
</div>
|
||||
<button onClick={handleModalCloseClick}>
|
||||
<i
|
||||
style={{ fontSize: 25 }}
|
||||
className="fa fa-times-circle"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isModal && (
|
||||
<AppendFormInForm
|
||||
id={props.schema.data.quickAddFormId}
|
||||
valueKey={props.schema.data.valueKey}
|
||||
displayKey={props.schema.data.displayKey}
|
||||
details={handleNewDetails}
|
||||
closePopup={handleModalCloseClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
Level1_Dropdown: state.Level1_Dropdown,
|
||||
Level2_Dropdown: state.Level2_Dropdown,
|
||||
Level3_Dropdown: state.Level3_Dropdown
|
||||
};
|
||||
};
|
||||
export default connect(mapStateToProps, null)(MultiSelectField);
|
||||
@@ -1,92 +0,0 @@
|
||||
import React from "react";
|
||||
import ObjectField from "@rjsf/core/lib/components/fields/ObjectField";
|
||||
import validator from "@rjsf/validator-ajv8";
|
||||
import { Col } from "react-bootstrap";
|
||||
import { retrieveSchema } from "@rjsf/utils";
|
||||
|
||||
class GridField extends ObjectField {
|
||||
state = {};
|
||||
render() {
|
||||
const {
|
||||
uiSchema,
|
||||
errorSchema,
|
||||
idSchema,
|
||||
|
||||
disabled,
|
||||
readonly,
|
||||
onBlur,
|
||||
formData
|
||||
} = this.props;
|
||||
const { fields, rootSchema } = this.props.registry;
|
||||
|
||||
const { SchemaField } = fields;
|
||||
const schema = retrieveSchema(validator, this.props.schema, rootSchema);
|
||||
const title = schema.title === undefined ? "" : schema.title;
|
||||
const description =
|
||||
schema && schema.description === undefined ? "" : schema.description;
|
||||
// console.log('this.props.schema ', this.props.schema)
|
||||
// console.log("schema ", schema);
|
||||
const layout = uiSchema["ui:layout"];
|
||||
|
||||
return (
|
||||
<fieldset>
|
||||
{title ? <h4>{title}</h4> : null}
|
||||
{description ? <h6>{description}</h6> : null}
|
||||
{layout.map((row, index) => {
|
||||
return (
|
||||
<div className="row" key={index}>
|
||||
{Object.keys(row).map((name, index) => {
|
||||
const { doShow, ...rowProps } = row[name];
|
||||
let style = {};
|
||||
if (doShow && !doShow({ formData })) {
|
||||
style = { display: "none" };
|
||||
}
|
||||
if (schema.properties[name]) {
|
||||
return (
|
||||
<Col {...rowProps} key={index} style={style}>
|
||||
<SchemaField
|
||||
name={name}
|
||||
required={this.isRequired(name)}
|
||||
schema={schema.properties[name]}
|
||||
uiSchema={uiSchema[name]}
|
||||
errorSchema={errorSchema[name]}
|
||||
idSchema={idSchema[name]}
|
||||
formData={formData[name]}
|
||||
onChange={this.onPropertyChange(name)}
|
||||
onBlur={onBlur}
|
||||
registry={this.props.registry}
|
||||
disabled={disabled}
|
||||
readonly={readonly}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
} else {
|
||||
const { render, ...rowProps } = row[name];
|
||||
let UIComponent = () => null;
|
||||
|
||||
if (render) {
|
||||
UIComponent = render;
|
||||
}
|
||||
|
||||
return (
|
||||
<Col {...rowProps} key={index} style={style}>
|
||||
<UIComponent
|
||||
name={name}
|
||||
formData={formData}
|
||||
errorSchema={errorSchema}
|
||||
uiSchema={uiSchema}
|
||||
schema={schema}
|
||||
registry={this.props.registry}
|
||||
/>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default GridField;
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
function TimeWidget(props) {
|
||||
const {
|
||||
onChange,
|
||||
registry: {
|
||||
widgets: { BaseInput }
|
||||
}
|
||||
} = props;
|
||||
return (
|
||||
<BaseInput
|
||||
type="time"
|
||||
{...props}
|
||||
onChange={(value) => onChange(value || undefined)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
TimeWidget.propTypes = {
|
||||
value: PropTypes.string
|
||||
};
|
||||
}
|
||||
|
||||
export default TimeWidget;
|
||||
@@ -1,34 +1,35 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import folder from "../../../assests/folder.png";
|
||||
import "../LegaDrive.css";
|
||||
import draftDoc from "../../../assests/draftDoc1.png";
|
||||
import pdfLogo from "../../../assests/pdf3.png";
|
||||
import folder from "../../assets/images/folder.png";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import pdfLogo from "../../assets/images/pdf3.png";
|
||||
import axios from "axios";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { saveAs } from "file-saver";
|
||||
import { getHostUrl } from "../../../utils/Utils";
|
||||
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 { themeColor } from "../../constant/const";
|
||||
import FolderModal from "../shared/fields/FolderModal";
|
||||
|
||||
function PdfFileComponent({
|
||||
pdfData,
|
||||
setFolderName,
|
||||
setDocId,
|
||||
setIsLoading,
|
||||
setPdfData,
|
||||
isList,
|
||||
}) {
|
||||
function DriveBody(props) {
|
||||
const [rename, setRename] = useState("");
|
||||
const [renameValue, setRenameValue] = useState("");
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const [isOpenMoveModal, setIsOpenMoveModal] = useState(false);
|
||||
const [selectDoc, setSelectDoc] = useState();
|
||||
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
|
||||
const contextMenu = [
|
||||
{ type: "Download", icon: "fa-solid fa-arrow-down" },
|
||||
{ type: "Rename", icon: "fa-solid fa-font" },
|
||||
{ type: "Move", icon: "fa-solid fa-file-export" },
|
||||
{ type: "Delete", icon: "fa-solid fa-trash" }
|
||||
];
|
||||
const navigate = useNavigate();
|
||||
|
||||
//to focus input box on press rename to change doc name
|
||||
useEffect(() => {
|
||||
if (rename && inputRef.current) {
|
||||
// console.log("ref", inputRef.current)
|
||||
setTimeout(() => {
|
||||
inputRef.current.focus();
|
||||
}, 10);
|
||||
@@ -39,30 +40,27 @@ function PdfFileComponent({
|
||||
const handleOnclikFolder = (data) => {
|
||||
const folderData = {
|
||||
name: data.Name,
|
||||
objectId: data.objectId,
|
||||
objectId: data.objectId
|
||||
};
|
||||
setFolderName((prev) => [...prev, folderData]);
|
||||
const loadObj = {
|
||||
props.setFolderName((prev) => [...prev, folderData]);
|
||||
props.setIsLoading({
|
||||
isLoad: true,
|
||||
message: "This might take some time",
|
||||
};
|
||||
|
||||
setIsLoading(loadObj);
|
||||
setDocId(data.objectId);
|
||||
message: "This might take some time"
|
||||
});
|
||||
props.setDocId(data.objectId);
|
||||
props.setPdfData([]);
|
||||
props.setSkip(0);
|
||||
};
|
||||
//function for change doc name and update doc name in _document class
|
||||
const handledRenameDoc = async (data) => {
|
||||
setRename("");
|
||||
const trimmedValue = renameValue.trim();
|
||||
|
||||
if (trimmedValue.length > 0) {
|
||||
const updateName = {
|
||||
Name: renameValue,
|
||||
Name: renameValue
|
||||
};
|
||||
const docId = data.objectId;
|
||||
|
||||
const docData = pdfData;
|
||||
|
||||
const docData = props.pdfData;
|
||||
const updatedData = docData.map((item) => {
|
||||
if (item.objectId === docId) {
|
||||
// If the item's ID matches the target ID, update the name
|
||||
@@ -71,8 +69,7 @@ function PdfFileComponent({
|
||||
// If the item's ID doesn't match, keep it unchanged
|
||||
return item;
|
||||
});
|
||||
|
||||
setPdfData(updatedData);
|
||||
props.setPdfData(updatedData);
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
@@ -83,167 +80,255 @@ function PdfFileComponent({
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken"),
|
||||
},
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
)
|
||||
.then((result) => {
|
||||
.then(() => {
|
||||
// const res = result.data;
|
||||
// console.log("res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("error updating field is decline ", err);
|
||||
console.log("Err ", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//function for navigate user to microapp-signature component
|
||||
const checkPdfStatus = async (data) => {
|
||||
|
||||
const hostUrl = getHostUrl();
|
||||
const expireDate = data.ExpiryDate.iso;
|
||||
const expireUpdateDate = new Date(expireDate).getTime();
|
||||
const currDate = new Date().getTime();
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const signUrl = data.SignedUrl && data.SignedUrl;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
//checking and navigate to signyouself page
|
||||
const checkPlaceHolder = data.Placeholders;
|
||||
//checking if document has completed
|
||||
if (data.IsCompleted && signerExist) {
|
||||
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
|
||||
|
||||
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
|
||||
} else if (data.IsCompleted && !signerExist) {
|
||||
navigate(
|
||||
`${hostUrl}signaturePdf/${data.objectId}`
|
||||
);
|
||||
// window.location.hash = `/recipientSignPdf/${data.objectId}/${data.ExtUserPtr.Phone}`;
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
|
||||
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
|
||||
} else if (currDate > expireUpdateDate && signerExist && !isPlaceholder) {
|
||||
// window.location.hash = `/placeHolderSign/${data.objectId}`;
|
||||
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
currDate > expireUpdateDate &&
|
||||
!signerExist &&
|
||||
!isPlaceholder &&
|
||||
!signUrl
|
||||
signerExist?.length > 0 &&
|
||||
isPlaceholder?.length > 0 &&
|
||||
!signedUrl
|
||||
) {
|
||||
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
|
||||
// window.location.hash = `/signaturePdf/${data.objectId}`;
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//checking if document has expired
|
||||
else if (currDate > expireUpdateDate) {
|
||||
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
|
||||
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
|
||||
} //checking if document is draft signers type and signers placeholder does not placed yet
|
||||
else if (!signUrl && signerExist) {
|
||||
// window.location.hash = `/placeHolderSign/${data.objectId}`;
|
||||
navigate(`${hostUrl}placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//checking if document is request type and signers placeholder exist and does not completed yet
|
||||
//then user can check progress of document and sign also
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
checkPlaceHolder &&
|
||||
checkPlaceHolder.length > 0 &&
|
||||
!data.IsCompleted
|
||||
(signerExist?.length > 0 &&
|
||||
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||
) {
|
||||
navigate(`${hostUrl}pdfRequestFiles/${data.objectId}`);
|
||||
// window.location.hash = `/pdfRequestFiles/${data.objectId}`;
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//checking document is draft and signyourself type then user can sign document
|
||||
else {
|
||||
navigate(`${hostUrl}signaturePdf/${data.objectId}`);
|
||||
// navigate(`/signaturePdf/${data.objectId}`);
|
||||
// window.location.hash = `/signaturePdf/${data.objectId}`;
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (selectType, data) => {
|
||||
switch (selectType) {
|
||||
case "Download": {
|
||||
const pdfName = data && data.Name;
|
||||
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
break;
|
||||
}
|
||||
case "Rename": {
|
||||
setRenameValue(data.Name);
|
||||
setRename(data.objectId);
|
||||
break;
|
||||
}
|
||||
case "Delete": {
|
||||
setIsDeleteDoc(true);
|
||||
setSelectDoc(data);
|
||||
break;
|
||||
}
|
||||
case "Move": {
|
||||
handleMoveDocument(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
null;
|
||||
}
|
||||
};
|
||||
//function for delete document
|
||||
const handleDeleteDocument = async (docData) => {
|
||||
setIsDeleteDoc(false);
|
||||
const docId = docData.objectId;
|
||||
const data = {
|
||||
IsArchive: true
|
||||
};
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${docId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
)
|
||||
.then((result) => {
|
||||
const res = result.data;
|
||||
if (res) {
|
||||
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
|
||||
props.setPdfData(updatedData);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Err ", err);
|
||||
props.setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
});
|
||||
};
|
||||
const handleMoveDocument = async (docData) => {
|
||||
setIsOpenMoveModal(true);
|
||||
setSelectDoc(docData);
|
||||
};
|
||||
//function for move document from one folder to another folder
|
||||
const handleMoveFolder = async (selectFolderData) => {
|
||||
const selecFolderId = selectDoc?.Folder?.objectId;
|
||||
const moveFolderId = selectFolderData?.ObjectId;
|
||||
let updateDocId = selectDoc?.objectId;
|
||||
let updateData;
|
||||
const checkExist = moveFolderId
|
||||
? selecFolderId === moveFolderId
|
||||
? true
|
||||
: false
|
||||
: selecFolderId
|
||||
? false
|
||||
: true;
|
||||
if (!checkExist) {
|
||||
if (moveFolderId) {
|
||||
updateData = {
|
||||
Folder: {
|
||||
__type: "Pointer",
|
||||
className: `${localStorage.getItem("_appName")}_Document`,
|
||||
objectId: moveFolderId
|
||||
}
|
||||
};
|
||||
} else {
|
||||
updateData = {
|
||||
Folder: { __op: "Delete" }
|
||||
};
|
||||
}
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${updateDocId}`,
|
||||
updateData,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
.then((Listdata) => {
|
||||
// console.log("Listdata ", Listdata);
|
||||
const res = Listdata.data;
|
||||
if (res) {
|
||||
const updatedData = props.pdfData.filter(
|
||||
(x) => x.objectId !== updateDocId
|
||||
);
|
||||
props.setPdfData(updatedData);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
});
|
||||
|
||||
setIsOpenMoveModal(false);
|
||||
} else {
|
||||
alert("folder already exist!");
|
||||
setIsOpenMoveModal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
}
|
||||
};
|
||||
|
||||
//component to handle type of document and render according to type
|
||||
const handleFolderData = (data, ind, listType) => {
|
||||
// console.log("data", data);
|
||||
let createddate,
|
||||
status,
|
||||
isDecline,
|
||||
signerExist,
|
||||
isExpire,
|
||||
isComplete,
|
||||
signUrl,
|
||||
isPlaceholder;
|
||||
let createddate, status, isDecline, signerExist, isComplete;
|
||||
if (data.Type !== "Folder") {
|
||||
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
|
||||
const createdDate = data.createdAt && data.createdAt;
|
||||
createddate = new Date(createdDate).toLocaleDateString();
|
||||
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
|
||||
isDecline = data.IsDeclined && data.IsDeclined;
|
||||
isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
signerExist = data.Signers && data.Signers;
|
||||
signUrl = data.SignedUrl && data.SignedUrl;
|
||||
const signedUrl = data.SignedUrl;
|
||||
|
||||
const expireUpdateDate = new Date(expireDate).getTime();
|
||||
const currDate = new Date().getTime();
|
||||
|
||||
let isExpire = false;
|
||||
if (currDate > expireUpdateDate) {
|
||||
isExpire = true;
|
||||
} else {
|
||||
isExpire = false;
|
||||
}
|
||||
|
||||
if (isComplete) {
|
||||
status = "Completed";
|
||||
} else if (isDecline) {
|
||||
status = "Declined";
|
||||
} else if (isExpire && !isPlaceholder && signerExist) {
|
||||
// status = "Expired";
|
||||
status = "Draft";
|
||||
} else if (isExpire && !isPlaceholder && !signerExist && !signUrl) {
|
||||
// status = "Expired";
|
||||
} else if (!signedUrl) {
|
||||
status = "Draft";
|
||||
} else if (isExpire) {
|
||||
status = "Expired";
|
||||
} else if (!signUrl) {
|
||||
status = "Draft";
|
||||
} else {
|
||||
status = "InComplete";
|
||||
status = "In Progress";
|
||||
}
|
||||
}
|
||||
|
||||
const handleDraftDoc = (data) => {
|
||||
window.location.hash = `/mf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9MZWdhR2VuaWUtTWljcm9hcHBWMi9yZW1vdGVFbnRyeS5qcw==&moduleToLoad=AppRoutes&remoteName=legageniemicroapp/legagenie?${data.objectId}`;
|
||||
const signersName = () => {
|
||||
const getSignersName = signerExist.map((data) => data.Name);
|
||||
const signerName = getSignersName.join(",");
|
||||
|
||||
return (
|
||||
<span
|
||||
className="statusSpan"
|
||||
style={{ width: "90%", wordWrap: "break-word" }}
|
||||
>
|
||||
{signerName}{" "}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (selectType, data) => {
|
||||
// console.log("data",data)
|
||||
if (selectType === "Download") {
|
||||
// console.log("download")
|
||||
const pdfName = data && data.Name;
|
||||
const pdfUrl = data && data.SignedUrl ? data.SignedUrl : data.URL;
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
} else if (selectType === "Rename") {
|
||||
// console.log("rename")
|
||||
setRenameValue(data.Name);
|
||||
setRename(data.objectId);
|
||||
}
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, '_');
|
||||
}
|
||||
|
||||
const handleEnterPress = (e, data) => {
|
||||
if (e.key === "Enter") {
|
||||
handledRenameDoc(data);
|
||||
}
|
||||
};
|
||||
|
||||
return listType === "table" ? (
|
||||
data.Type === "Folder" ? (
|
||||
<tr onClick={() => handleOnclikFolder(data)}>
|
||||
<td>
|
||||
<td
|
||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<i
|
||||
className="fa fa-folder"
|
||||
aria-hidden="true"
|
||||
@@ -259,26 +344,11 @@ function PdfFileComponent({
|
||||
<td>_</td>
|
||||
<td>_</td>
|
||||
</tr>
|
||||
) : data.Type === "AIDoc" ? (
|
||||
<tr onClick={() => handleDraftDoc(data)}>
|
||||
<td>
|
||||
<i
|
||||
className="fa fa-file-text"
|
||||
style={{ color: "#0ea3ed", marginRight: "8px", fontSize: "26px" }}
|
||||
></i>
|
||||
|
||||
<span style={{ fontSize: "12px", fontWeight: "500" }}>
|
||||
{data.Name}
|
||||
</span>
|
||||
</td>
|
||||
<td>{createddate}</td>
|
||||
<td>LegaGenie Draft</td>
|
||||
<td>_</td>
|
||||
<td>_</td>
|
||||
</tr>
|
||||
) : (
|
||||
<tr onClick={() => checkPdfStatus(data)}>
|
||||
<td>
|
||||
<td
|
||||
style={{ cursor: "pointer", display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<i
|
||||
className="fa fa-file-pdf"
|
||||
style={{ color: "#ed4d0e", marginRight: "8px", fontSize: "26px" }}
|
||||
@@ -312,7 +382,7 @@ function PdfFileComponent({
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
cursor: "pointer",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
@@ -333,7 +403,6 @@ function PdfFileComponent({
|
||||
}}
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
// onFocus={()=>console.log("focus")}
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
@@ -344,7 +413,7 @@ function PdfFileComponent({
|
||||
width: "100px",
|
||||
border: "1.5px solid black",
|
||||
borderRadius: "2px",
|
||||
fontSize: "10px",
|
||||
fontSize: "10px"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -359,99 +428,19 @@ function PdfFileComponent({
|
||||
sideOffset={5}
|
||||
align="end"
|
||||
>
|
||||
{/* <ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Download", data)}
|
||||
onSelect={(e) => console.log("event", e)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Download
|
||||
</ContextMenu.Item> */}
|
||||
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Rename
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
) : data.Type === "AIDoc" ? (
|
||||
<div key={ind} className="folderBox">
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="no img"
|
||||
onClick={() => handleDraftDoc(data)}
|
||||
src={draftDoc}
|
||||
width={100}
|
||||
height={103}
|
||||
/>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
onFocus={() => {
|
||||
inputRef.current.setSelectionRange(0, 0);
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
// onFocus={()=>console.log("focus")}
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
// value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
style={{
|
||||
width: "100px",
|
||||
border: "1.5px solid black",
|
||||
borderRadius: "2px",
|
||||
fontSize: "10px",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span className="foldName">{data.Name}</span>
|
||||
)}
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
|
||||
<ContextMenu.Portal>
|
||||
<ContextMenu.Content
|
||||
className="ContextMenuContent"
|
||||
sideOffset={5}
|
||||
align="end"
|
||||
>
|
||||
{/* <ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Download", data)}
|
||||
onSelect={(e) => console.log("event", e)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Download
|
||||
</ContextMenu.Item> */}
|
||||
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Rename
|
||||
<i className="fa-solid fa-font"></i>
|
||||
<span style={{ marginLeft: "8px" }}>Rename</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
) : (
|
||||
<HoverCard.Root>
|
||||
<HoverCard.Root openDelay={0} closeDelay={100}>
|
||||
<HoverCard.Trigger asChild>
|
||||
<div>
|
||||
<ContextMenu.Root>
|
||||
@@ -484,7 +473,7 @@ function PdfFileComponent({
|
||||
width: "100px",
|
||||
border: "1.5px solid black",
|
||||
borderRadius: "2px",
|
||||
fontSize: "10px",
|
||||
fontSize: "10px"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -508,7 +497,7 @@ function PdfFileComponent({
|
||||
<i className="fa fa-file"></i>
|
||||
</div>
|
||||
) : (
|
||||
status === "InComplete" && (
|
||||
status === "In Progress" && (
|
||||
<div className="status-badge in-progress">
|
||||
<i className="fa fa-paper-plane"></i>
|
||||
</div>
|
||||
@@ -522,20 +511,18 @@ function PdfFileComponent({
|
||||
sideOffset={5}
|
||||
align="end"
|
||||
>
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Download", data)}
|
||||
onSelect={(e) => console.log("event", e)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Download
|
||||
</ContextMenu.Item>
|
||||
|
||||
<ContextMenu.Item
|
||||
onClick={() => handleMenuItemClick("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
Rename
|
||||
</ContextMenu.Item>
|
||||
{contextMenu.map((menu, ind) => {
|
||||
return (
|
||||
<ContextMenu.Item
|
||||
key={ind}
|
||||
onClick={() => handleMenuItemClick(menu.type, data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className={menu.icon}></i>
|
||||
<span style={{ marginLeft: "8px" }}>{menu.type}</span>
|
||||
</ContextMenu.Item>
|
||||
);
|
||||
})}
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Portal>
|
||||
</ContextMenu.Root>
|
||||
@@ -558,57 +545,112 @@ function PdfFileComponent({
|
||||
{signerExist && (
|
||||
<>
|
||||
<strong style={{ fontSize: "13px" }}>Signers: </strong>
|
||||
{signerExist.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
<span className="statusSpan">{data.Name}, </span>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
||||
{signersName()}
|
||||
</>
|
||||
)}
|
||||
|
||||
<HoverCard.Arrow className="HoverCardArrow" />
|
||||
</HoverCard.Content>
|
||||
</HoverCard.Portal>
|
||||
</HoverCard.Root>
|
||||
);
|
||||
};
|
||||
//component to handle type of document and render according to type
|
||||
|
||||
return isList ? (
|
||||
<div className="container" style={{overflowX:"auto"}}>
|
||||
<Table striped bordered hover >
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created Date</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pdfData.map((data, ind) => {
|
||||
//component to handle type of document and render according to type
|
||||
return (
|
||||
<>
|
||||
{props.isList ? (
|
||||
<div className="container" style={{ overflowX: "auto" }}>
|
||||
<Table striped bordered hover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Created Date</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.pdfData.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{handleFolderData(data, ind, "table")}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pdfContainer">
|
||||
{props.pdfData.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{handleFolderData(data, ind, "table")}
|
||||
</React.Fragment>
|
||||
<div className="box" key={ind}>
|
||||
{handleFolderData(data, ind, "list")}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="pdfContainer">
|
||||
{pdfData.map((data, ind) => {
|
||||
return (
|
||||
<div className="box" key={ind}>
|
||||
{handleFolderData(data, ind, "list")}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isOpenMoveModal && (
|
||||
<FolderModal
|
||||
onSuccess={handleMoveFolder}
|
||||
isOpenModal={isOpenMoveModal}
|
||||
folderCls={"contracts_Document"}
|
||||
setIsOpenMoveModal={setIsOpenMoveModal}
|
||||
setPdfData={props.setPdfData}
|
||||
/>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isDeleteDoc}
|
||||
headerColor={themeColor}
|
||||
title={"Delete Document"}
|
||||
handleClose={() => {
|
||||
setIsDeleteDoc(false);
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<p>Are you sure you want to delete this document?</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
handleDeleteDocument(selectDoc);
|
||||
}}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsDeleteDoc(false);
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PdfFileComponent;
|
||||
export default DriveBody;
|
||||
@@ -0,0 +1,67 @@
|
||||
import React from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
|
||||
const AddRoleModal = (props) => {
|
||||
return (
|
||||
<ModalUi
|
||||
title={"Add Role"}
|
||||
isOpen={props.isModalRole}
|
||||
handleClose={props.handleCloseRoleModal}
|
||||
>
|
||||
<div className="addusercontainer">
|
||||
<form
|
||||
style={{ display: "flex", flexDirection: "column" }}
|
||||
onSubmit={props.handleAddRole}
|
||||
>
|
||||
<input
|
||||
value={props.roleName}
|
||||
onChange={(e) => props.setRoleName(e.target.value)}
|
||||
placeholder={
|
||||
props.signersdata.length > 0
|
||||
? "User " + (props.signersdata.length + 1)
|
||||
: "User 1"
|
||||
}
|
||||
className="addUserInput"
|
||||
/>
|
||||
<p
|
||||
style={{
|
||||
color: "grey",
|
||||
fontSize: 11,
|
||||
margin: "10px 0 10px 5px"
|
||||
}}
|
||||
>
|
||||
e.g: Hr, Director, Manager, New joinee, Accountant, etc...
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
type="submit"
|
||||
style={{
|
||||
background: "#00a2b7"
|
||||
}}
|
||||
className="finishBtn"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
onClick={props.handleCloseRoleModal}
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddRoleModal;
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
|
||||
function BorderResize() {
|
||||
function BorderResize({ right, top }) {
|
||||
return (
|
||||
<div
|
||||
className="borderResize"
|
||||
style={{
|
||||
right: "-1px",
|
||||
bottom: "-1px",
|
||||
right: right ? right + "px" : "-1px",
|
||||
bottom: top ? top + "px" : "-1px",
|
||||
borderRight: "3px solid #188ae2",
|
||||
borderBottom: "3px solid #188ae2"
|
||||
}}
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./certificate.css";
|
||||
import opensignLogo from "../../assests/open-sign-logo.png";
|
||||
|
||||
import opensignLogo from "../../assets/images/logo.png";
|
||||
import {
|
||||
Page,
|
||||
Text,
|
||||
@@ -18,6 +16,7 @@ function Certificate({ pdfData }) {
|
||||
|
||||
useEffect(() => {
|
||||
handleSignerData();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const handleSignerData = () => {
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
||||
const confirmToaddDefaultSign = () => {
|
||||
if (xyPostion.length > 0) {
|
||||
setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage:
|
||||
"Are you sure you want to auto sign at requested all locations?"
|
||||
});
|
||||
} else {
|
||||
setDefaultSignAlert({
|
||||
isShow: true,
|
||||
alertMessage: "please select position!"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
padding: "5px",
|
||||
fontFamily: "sans-serif",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
Signature
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
marginTop: "10px",
|
||||
fontWeight: "600"
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<p>Your Signature</p>
|
||||
<div className="defaultSignBox">
|
||||
<img
|
||||
alt="default img"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
src={defaultSignImg}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
marginTop: "20px",
|
||||
cursor: "pointer",
|
||||
marginBottom: "10px"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn finishnHover"
|
||||
onClick={() => confirmToaddDefaultSign()}
|
||||
>
|
||||
Auto Sign All
|
||||
</button>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DefaultSignature;
|
||||
@@ -0,0 +1,97 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Loader from "../../primitives/LoaderWithMsg";
|
||||
import { contractDocument } from "../../constant/Utils";
|
||||
import HandleError from "../../primitives/HandleError";
|
||||
function DraftDocument() {
|
||||
const navigate = useNavigate();
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoader: true,
|
||||
message: "This might take some time"
|
||||
});
|
||||
|
||||
const rowLevel =
|
||||
localStorage.getItem("rowlevel") &&
|
||||
JSON.parse(localStorage.getItem("rowlevel"));
|
||||
const docId =
|
||||
rowLevel && rowLevel?.id
|
||||
? rowLevel.id
|
||||
: rowLevel?.objectId && rowLevel.objectId;
|
||||
|
||||
useEffect(() => {
|
||||
getDocumentDetails();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//get document details
|
||||
const getDocumentDetails = async () => {
|
||||
//getting document details
|
||||
const documentData = await contractDocument(docId);
|
||||
if (documentData && documentData.length > 0) {
|
||||
handleDraftDoc(documentData);
|
||||
} else if (
|
||||
documentData === "Error: Something went wrong!" ||
|
||||
(documentData.result && documentData.result.error)
|
||||
) {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: "Error: Something went wrong!"
|
||||
});
|
||||
} else {
|
||||
setIsLoading({
|
||||
isLoader: false,
|
||||
message: "No data found!"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
//check document type and render on signyour self and placeholder route
|
||||
const handleDraftDoc = (documentData) => {
|
||||
const data = documentData[0];
|
||||
const signerExist = data.Signers && data.Signers;
|
||||
const isDecline = data.IsDeclined && data.IsDeclined;
|
||||
const isPlaceholder = data.Placeholders && data.Placeholders;
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
navigate(`/signaturePdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
isPlaceholder?.length > 0 &&
|
||||
!signedUrl
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
(signerExist?.length > 0 &&
|
||||
(!isPlaceholder || isPlaceholder?.length === 0)) ||
|
||||
((!signerExist || signerExist?.length === 0) && isPlaceholder?.length > 0)
|
||||
) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isLoading.isLoader ? (
|
||||
<Loader isLoading={isLoading} />
|
||||
) : (
|
||||
<HandleError handleError={isLoading.message} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DraftDocument;
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import { getWidgetType, widgets } from "../../constant/Utils";
|
||||
|
||||
function DragElement(item) {
|
||||
const getWidgets = widgets;
|
||||
const filterWidgetPreview = getWidgets.filter(
|
||||
(data) => data.type === item?.text
|
||||
);
|
||||
|
||||
return <div>{getWidgetType(filterWidgetPreview[0])}</div>;
|
||||
}
|
||||
|
||||
export default DragElement;
|
||||
@@ -0,0 +1,425 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { radioButtonWidget } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
function DropdownWidgetOption(props) {
|
||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||
"option-1",
|
||||
"option-2"
|
||||
]);
|
||||
const [minCount, setMinCount] = useState(0);
|
||||
const [maxCount, setMaxCount] = useState(0);
|
||||
const [dropdownName, setDropdownName] = useState(props.type);
|
||||
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||
const [isHideLabel, setIsHideLabel] = useState(false);
|
||||
const [status, setStatus] = useState("required");
|
||||
const [defaultValue, setDefaultValue] = useState("");
|
||||
const statusArr = ["required", "optional"];
|
||||
const [defaultCheckbox, setDefaultCheckbox] = useState([]);
|
||||
|
||||
const resetState = () => {
|
||||
setDropdownOptionList(["option-1", "option-2"]);
|
||||
setDropdownName(props.type);
|
||||
setIsReadOnly(false);
|
||||
setIsHideLabel(false);
|
||||
setMinCount(0);
|
||||
setMaxCount(0);
|
||||
setDefaultCheckbox([]);
|
||||
setDefaultValue("");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
props.currWidgetsDetails?.options?.name &&
|
||||
props.currWidgetsDetails?.options?.values
|
||||
) {
|
||||
setDropdownName(props.currWidgetsDetails?.options?.name);
|
||||
setDropdownOptionList(props.currWidgetsDetails?.options?.values);
|
||||
setMinCount(
|
||||
props.currWidgetsDetails?.options?.validation?.minRequiredCount
|
||||
);
|
||||
setMaxCount(
|
||||
props.currWidgetsDetails?.options?.validation?.maxRequiredCount
|
||||
);
|
||||
setIsReadOnly(props.currWidgetsDetails?.options?.isReadOnly);
|
||||
setIsHideLabel(props.currWidgetsDetails?.options?.isHideLabel);
|
||||
setStatus(props.currWidgetsDetails?.options?.status || "required");
|
||||
setDefaultValue(props.currWidgetsDetails?.options?.defaultValue || "");
|
||||
setDefaultCheckbox(props.currWidgetsDetails?.options?.defaultValue || []);
|
||||
} else {
|
||||
setStatus("required");
|
||||
resetState();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.currWidgetsDetails]);
|
||||
const handleInputChange = (index, value) => {
|
||||
setDropdownOptionList((prevInputs) => {
|
||||
const newInputs = [...prevInputs];
|
||||
newInputs[index] = value;
|
||||
return newInputs;
|
||||
});
|
||||
};
|
||||
|
||||
//function add add checkbox option and add width of checkbox
|
||||
const handleAddInput = () => {
|
||||
const deleteOption = false;
|
||||
const addOption = true;
|
||||
setDropdownOptionList((prevInputs) => [...prevInputs, ""]);
|
||||
props.handleSaveWidgetsOptions(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
addOption,
|
||||
deleteOption
|
||||
);
|
||||
};
|
||||
|
||||
//function add add checkbox option and delete width of checkbox
|
||||
const handleDeleteInput = (ind) => {
|
||||
const deleteOption = true;
|
||||
const addOption = false;
|
||||
const getUpdatedOptions = dropdownOptionList.filter(
|
||||
(data, index) => index !== ind
|
||||
);
|
||||
setDropdownOptionList(getUpdatedOptions);
|
||||
props.handleSaveWidgetsOptions(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
addOption,
|
||||
deleteOption
|
||||
);
|
||||
};
|
||||
|
||||
const handleSaveOption = () => {
|
||||
const defaultData =
|
||||
defaultCheckbox && defaultCheckbox.length > 0
|
||||
? defaultCheckbox
|
||||
: defaultValue;
|
||||
|
||||
props.handleSaveWidgetsOptions(
|
||||
dropdownName,
|
||||
dropdownOptionList,
|
||||
minCount,
|
||||
maxCount,
|
||||
isReadOnly,
|
||||
null,
|
||||
null,
|
||||
status,
|
||||
defaultData,
|
||||
isHideLabel
|
||||
);
|
||||
// props.setShowDropdown(false);
|
||||
setDropdownOptionList(["option-1", "option-2"]);
|
||||
setDropdownName(props.type);
|
||||
// props.setCurrWidgetsDetails({});
|
||||
setIsReadOnly(false);
|
||||
setIsHideLabel(false);
|
||||
setMinCount(0);
|
||||
setMaxCount(0);
|
||||
setDefaultCheckbox([]);
|
||||
setDefaultValue("");
|
||||
};
|
||||
|
||||
const handleSetMinMax = (e) => {
|
||||
const minValue = e.target.value;
|
||||
if (minValue > dropdownOptionList.length) {
|
||||
return "";
|
||||
} else {
|
||||
return minValue;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDefaultCheck = (index) => {
|
||||
const getDefaultCheck = defaultCheckbox.includes(index);
|
||||
if (getDefaultCheck) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
|
||||
<div className="h-full p-[15px]">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSaveOption();
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={dropdownName}
|
||||
value={dropdownName}
|
||||
onChange={(e) => setDropdownName(e.target.value)}
|
||||
className="drodown-input"
|
||||
/>
|
||||
|
||||
<label
|
||||
style={{ fontSize: "13px", fontWeight: "600", marginTop: "5px" }}
|
||||
>
|
||||
Options
|
||||
</label>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column"
|
||||
}}
|
||||
>
|
||||
{dropdownOptionList.map((option, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
marginBottom: "5px",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={handleDefaultCheck(index)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
const getDefaultCheck =
|
||||
defaultCheckbox?.includes(index);
|
||||
if (!getDefaultCheck) {
|
||||
setDefaultCheckbox((prev) => [...prev, index]);
|
||||
}
|
||||
} else {
|
||||
const removeOption = defaultCheckbox.filter(
|
||||
(data) => data !== index
|
||||
);
|
||||
setDefaultCheckbox(removeOption);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
width: "23px",
|
||||
height: "19px",
|
||||
marginRight: "5px"
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
required
|
||||
className="drodown-input"
|
||||
type="text"
|
||||
value={option}
|
||||
onChange={(e) => handleInputChange(index, e.target.value)}
|
||||
/>
|
||||
|
||||
<i
|
||||
className="fa-solid fa-rectangle-xmark"
|
||||
onClick={() => handleDeleteInput(index)}
|
||||
style={{
|
||||
color: "red",
|
||||
fontSize: "25px",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
></i>
|
||||
</div>
|
||||
))}
|
||||
<i
|
||||
onClick={handleAddInput}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: themeColor,
|
||||
fontSize: "25px",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
className="fa-solid fa-square-plus"
|
||||
></i>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Minimun check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={minCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMinCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||
<>
|
||||
<label
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
Default value
|
||||
</label>
|
||||
<select
|
||||
onChange={(e) => setDefaultValue(e.target.value)}
|
||||
className="drodown-input"
|
||||
name="defaultvalue"
|
||||
value={defaultValue}
|
||||
placeholder="select default value"
|
||||
>
|
||||
<option value="" disabled hidden style={{ fontSize: "13px" }}>
|
||||
Select...
|
||||
</option>
|
||||
{dropdownOptionList.map((data, ind) => {
|
||||
return (
|
||||
<option
|
||||
style={{ fontSize: "13px" }}
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</>
|
||||
)}
|
||||
{props.type !== "checkbox" && props.type !== radioButtonWidget && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginTop: "0.5rem"
|
||||
}}
|
||||
>
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 5,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<input
|
||||
style={{ accentColor: "red" }}
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() => setStatus(data.toLowerCase())}
|
||||
checked={status.toLowerCase() === data.toLowerCase()}
|
||||
/>
|
||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
||||
{data}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{["checkbox", radioButtonWidget].includes(props.type) && (
|
||||
<div className="flex flex-row gap-5 mt-2 items-center text-center">
|
||||
{!props.isSignYourself && (
|
||||
<div>
|
||||
<input
|
||||
id="isreadonly"
|
||||
type="checkbox"
|
||||
checked={isReadOnly}
|
||||
onChange={(e) => {
|
||||
setIsReadOnly(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
<label className="ml-1" htmlFor="isreadonly">
|
||||
Is read only
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<input
|
||||
id="ishidelabel"
|
||||
type="checkbox"
|
||||
checked={isHideLabel}
|
||||
onChange={(e) => {
|
||||
setIsHideLabel(e.target.checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<label className="ml-1" htmlFor="ishidelabel">
|
||||
Hide labels
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
props.type === "checkbox" && !props.isSignYourself
|
||||
? "mb-[15px]"
|
||||
: "my-[15px]"
|
||||
} w-full h-[1px] bg-[#9f9f9f]`}
|
||||
></div>
|
||||
|
||||
<button
|
||||
disabled={dropdownOptionList.length === 0 && true}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="submit"
|
||||
className={"finishBtn"}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
{props.currWidgetsDetails?.options?.values?.length > 0 && (
|
||||
<button
|
||||
type="submit"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
props.handleClose && props.handleClose();
|
||||
resetState();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default DropdownWidgetOption;
|
||||
@@ -0,0 +1,142 @@
|
||||
import React, { useState } from "react";
|
||||
import "../../styles/AddUser.css";
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
// const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
Name: template?.Name || "",
|
||||
Note: template?.Note || "",
|
||||
Description: template?.Description || "",
|
||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
|
||||
});
|
||||
|
||||
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 data = { ...formData, SendinOrder: isChecked };
|
||||
onSuccess(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addusercontainer">
|
||||
<div className="form-wrapper">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
File
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
padding: "0.5rem 0.75rem",
|
||||
border: "1px solid #d1d5db",
|
||||
borderRadius: "0.375rem",
|
||||
fontSize: "0.75rem",
|
||||
fontWeight: "700"
|
||||
}}
|
||||
>
|
||||
{template.URL?.split("/")?.pop()?.split("_")[1]}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Name"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="Note" style={{ fontSize: 13 }}>
|
||||
Note
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Note"
|
||||
id="Note"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="Description" style={{ fontSize: 13 }}>
|
||||
Description
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="Description"
|
||||
id="Description"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
className="addUserInput"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label style={{ fontSize: 13 }}>Send In Order</label>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginLeft: 8,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div style={{ fontSize: 12 }}>Yes</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginLeft: 8,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div style={{ fontSize: 12 }}>No</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
|
||||
<div className="buttoncontainer">
|
||||
<button type="submit" className="submitbutton">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditTemplate;
|
||||
@@ -0,0 +1,429 @@
|
||||
import React, { useState } from "react";
|
||||
import { saveAs } from "file-saver";
|
||||
import celebration from "../../assets/images/newCeleb.gif";
|
||||
import axios from "axios";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import printModule from "print-js";
|
||||
import loader from "../../assets/images/loader2.gif";
|
||||
|
||||
function EmailComponent({
|
||||
isEmail,
|
||||
pdfUrl,
|
||||
isCeleb,
|
||||
setIsEmail,
|
||||
setSuccessEmail,
|
||||
pdfName,
|
||||
sender,
|
||||
setIsAlert
|
||||
}) {
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
//function for send email
|
||||
const sendEmail = async () => {
|
||||
setIsLoading(true);
|
||||
let sendMail;
|
||||
for (let i = 0; i < emailList.length; i++) {
|
||||
try {
|
||||
const imgPng =
|
||||
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
|
||||
|
||||
let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||
const themeBGcolor = themeColor;
|
||||
let params = {
|
||||
pdfName: pdfName,
|
||||
url: pdfUrl,
|
||||
recipient: emailList[i],
|
||||
subject: `${sender.name} has signed the doc - ${pdfName}`,
|
||||
from: sender.email,
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
|
||||
imgPng +
|
||||
" height='50' style='padding:20px,width:170px,height:40px'/> </div><div style='padding:2px;font-family:system-ui; background-color:" +
|
||||
themeBGcolor +
|
||||
";'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>" +
|
||||
pdfName +
|
||||
" </strong>is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||
sender.email +
|
||||
" directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
|
||||
openSignUrl +
|
||||
" target=_blank>here</a> </p></div></div></body></html>"
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (sendMail && sendMail.data.result.status === "success") {
|
||||
setSuccessEmail(true);
|
||||
setTimeout(() => {
|
||||
setSuccessEmail(false);
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}, 1500);
|
||||
|
||||
setIsLoading(false);
|
||||
} else if (sendMail && sendMail.data.result.status === "error") {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
setIsEmail(false);
|
||||
setIsAlert({
|
||||
isShow: true,
|
||||
alertMessage: "something went wrong"
|
||||
});
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}
|
||||
};
|
||||
|
||||
//function for remove email
|
||||
const removeChip = (index) => {
|
||||
const updateEmailCount = emailList.filter((data, key) => key !== index);
|
||||
setEmailList(updateEmailCount);
|
||||
};
|
||||
//function for get email value
|
||||
const handleEmailValue = (e) => {
|
||||
const value = e.target.value;
|
||||
setEmailValue(value);
|
||||
};
|
||||
|
||||
//function for save email in array after press enter
|
||||
const handleEnterPress = (e) => {
|
||||
if (e.key === "Enter" && emailValue) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
} else if (e === "add" && emailValue) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
}
|
||||
};
|
||||
|
||||
// function for print signed pdf
|
||||
const handleToPrint = async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const pdf = await getBase64FromUrl(pdfUrl);
|
||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||
const isAppleDevice =
|
||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||
!window.MSStream;
|
||||
if (isAndroidDevice || isAppleDevice) {
|
||||
const byteArray = Uint8Array.from(
|
||||
atob(pdf)
|
||||
.split("")
|
||||
.map((char) => char.charCodeAt(0))
|
||||
);
|
||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
window.open(blobUrl, "_blank");
|
||||
} else {
|
||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||
}
|
||||
};
|
||||
|
||||
//handle download signed pdf
|
||||
const handleDownloadPdf = () => {
|
||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||
};
|
||||
|
||||
const sanitizeFileName = (pdfName) => {
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
|
||||
const isAndroid = /Android/i.test(navigator.userAgent);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
{isEmail && (
|
||||
<div className="modaloverlay">
|
||||
<div className="modalcontainer">
|
||||
{isLoading && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
zIndex: "20",
|
||||
backgroundColor: "#e6f2f2",
|
||||
opacity: 0.8
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="no img"
|
||||
src={loader}
|
||||
style={{ width: "70px", height: "70px" }}
|
||||
/>
|
||||
<span style={{ fontSize: "12px", fontWeight: "bold" }}>
|
||||
This might take some time
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ background: "#32a3ac" }} className="modalheader">
|
||||
<span style={{ color: "white" }}>Successfully signed!</span>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div></div>
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="btn btn-primary btn-sm"
|
||||
>
|
||||
<i
|
||||
className="fa fa-print"
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
marginRight: "3px",
|
||||
color: "white"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
className="btn btn-danger btn-sm"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{isCeleb && (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
marginLeft: "50px"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="print img"
|
||||
width={300}
|
||||
height={250}
|
||||
// style={styles.gifCeleb}
|
||||
src={celebration}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "system-ui",
|
||||
verticalAlign: "baseline",
|
||||
fontWeight: "500",
|
||||
color: "#403f3e",
|
||||
fontSize: "15px",
|
||||
marginBottom: "5px"
|
||||
}}
|
||||
>
|
||||
Recipients added here will get a copy of the signed document.
|
||||
</p>
|
||||
{emailList.length > 0 ? (
|
||||
<>
|
||||
<div className="addEmail">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
|
||||
flexWrap: "wrap"
|
||||
}}
|
||||
>
|
||||
{emailList.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
className="emailChip"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
key={ind}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "13px"
|
||||
}}
|
||||
>
|
||||
{data}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
marginLeft: 7,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => removeChip(ind)}
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{emailList.length <= 9 && (
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="addEmailInput"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="emailInput"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
placeholder="Add the email addresses"
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<span
|
||||
style={{
|
||||
cursor: emailValue && "pointer"
|
||||
}}
|
||||
onClick={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
style={{
|
||||
backgroundColor: themeColor,
|
||||
padding: "5px 7px",
|
||||
marginTop: "10px",
|
||||
color: "white",
|
||||
borderRadius: "2px"
|
||||
}}
|
||||
className="fa fa-plus"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: "#e3e2e1",
|
||||
marginTop: "10px",
|
||||
padding: "5px",
|
||||
borderRadius: "3px"
|
||||
}}
|
||||
>
|
||||
<span style={{ fontWeight: "700" }}>Note:</span>
|
||||
<span style={{ fontSize: "15px" }}>
|
||||
{" "}
|
||||
You can only send to ten recipients at a time.
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
disabled={emailList.length === 0 && true}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
type="button"
|
||||
className={emailList.length === 0 ? "defaultBtn" : "finishBtn"}
|
||||
onClick={() => sendEmail()}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default EmailComponent;
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from "react";
|
||||
import PrevNext from "./prevNext";
|
||||
import PrevNext from "./PrevNext";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
import Certificate from "../Certificate/Certificate";
|
||||
import Certificate from "./Certificate";
|
||||
import printModule from "print-js";
|
||||
import { getBase64FromUrl } from "../../utils/Utils";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { saveAs } from "file-saver";
|
||||
import "../../css/signature.css";
|
||||
import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../../utils/ThemeColor/backColor";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function Header({
|
||||
isPdfRequestFiles,
|
||||
@@ -21,7 +21,7 @@ function Header({
|
||||
changePage,
|
||||
pdfUrl,
|
||||
documentStatus,
|
||||
embedImages,
|
||||
embedWidgetsData,
|
||||
pdfDetails,
|
||||
signerPos,
|
||||
signersdata,
|
||||
@@ -34,8 +34,12 @@ function Header({
|
||||
dataTut4,
|
||||
alreadySign,
|
||||
isSignYourself,
|
||||
setIsEmail
|
||||
setIsEmail,
|
||||
completeBtnTitle,
|
||||
setIsEditTemplate
|
||||
}) {
|
||||
const filterPrefill =
|
||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const navigate = useNavigate();
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
@@ -117,7 +121,7 @@ function Header({
|
||||
style={{ textDecoration: "none", zIndex: "35" }}
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
>
|
||||
{({ blob, url, loading, error }) => (
|
||||
{({ blob, loading }) => (
|
||||
<>
|
||||
{loading ? (
|
||||
<div
|
||||
@@ -127,20 +131,15 @@ function Header({
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
marginRight: "2px"
|
||||
}}
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
style={{ border: "none", backgroundColor: "#fff" }}
|
||||
onClick={() =>
|
||||
handleDownload(
|
||||
blob,
|
||||
@@ -151,10 +150,8 @@ function Header({
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
marginRight: "2px"
|
||||
}}
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
@@ -174,59 +171,34 @@ function Header({
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`}
|
||||
>
|
||||
{({ blob, url, loading, error }) =>
|
||||
loading ? (
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn certificateBtn"
|
||||
{({ loading }) => (
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn certificateBtn"
|
||||
disabled={loading}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn certificateBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-certificate"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</button>
|
||||
)
|
||||
}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</button>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{ padding: !isGuestSigner && "5px 0px 5px 0px" }}
|
||||
className="mobileHead"
|
||||
>
|
||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||
{isMobile && isShowHeader ? (
|
||||
<div
|
||||
id="navbar"
|
||||
@@ -246,7 +218,7 @@ function Header({
|
||||
<i
|
||||
className="fa fa-arrow-left"
|
||||
aria-hidden="true"
|
||||
style={{ color: themeColor(), cursor: "pointer" }}
|
||||
style={{ color: themeColor, cursor: "pointer" }}
|
||||
></i>
|
||||
</div>
|
||||
<div>
|
||||
@@ -284,7 +256,7 @@ function Header({
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div
|
||||
style={{
|
||||
color: themeColor(),
|
||||
color: themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px",
|
||||
@@ -345,7 +317,7 @@ function Header({
|
||||
}}
|
||||
>
|
||||
<i
|
||||
class="fa fa-envelope"
|
||||
className="fa fa-envelope"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
@@ -408,27 +380,27 @@ function Header({
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: isMailSend ? "gray" : themeColor(),
|
||||
color: isMailSend ? "gray" : themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px"
|
||||
}}
|
||||
data-tut={dataTut4}
|
||||
>
|
||||
Send
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedImages();
|
||||
embedWidgetsData();
|
||||
} else if (isPdfRequestFiles) {
|
||||
embedImages();
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color: themeColor(),
|
||||
color: themeColor,
|
||||
border: "none",
|
||||
fontWeight: "650",
|
||||
fontSize: "16px"
|
||||
@@ -454,7 +426,7 @@ function Header({
|
||||
{recipient ? (
|
||||
pdfUrl || isAlreadySign.mssg ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{pdfDetails[0] && pdfDetails.length > 0 && (
|
||||
{pdfDetails && pdfDetails.length > 0 && (
|
||||
<CertificateComponent />
|
||||
)}
|
||||
<button
|
||||
@@ -522,7 +494,7 @@ function Header({
|
||||
className="finishBtn sendHover"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedImages();
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -533,17 +505,17 @@ function Header({
|
||||
) : isPlaceholder ? (
|
||||
<>
|
||||
{!isMailSend &&
|
||||
signersdata.Signers &&
|
||||
signersdata.Signers.length !== signerPos.length && (
|
||||
signersdata.length > 0 &&
|
||||
signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{signerPos.length === 0 ? (
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add all {signersdata.Signers.length - signerPos.length}{" "}
|
||||
Add {signersdata.length - filterPrefill.length}{" "}
|
||||
recipients signature
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.Signers.length - signerPos.length} more
|
||||
Add {signersdata.length - filterPrefill.length} more
|
||||
recipients signature
|
||||
</span>
|
||||
)}
|
||||
@@ -551,6 +523,18 @@ function Header({
|
||||
)}
|
||||
|
||||
<div>
|
||||
{setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => setIsEditTemplate(true)}
|
||||
style={{
|
||||
border: "none",
|
||||
outline: "none",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
@@ -573,7 +557,7 @@ function Header({
|
||||
}}
|
||||
className={isMailSend ? "sendMail" : "sendMail sendHover"}
|
||||
>
|
||||
Send
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
@@ -656,7 +640,7 @@ function Header({
|
||||
type="button"
|
||||
className="finishBtn sendHover"
|
||||
onClick={() => {
|
||||
embedImages();
|
||||
embedWidgetsData();
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
@@ -723,7 +707,7 @@ function Header({
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<i
|
||||
class="fa fa-envelope"
|
||||
className="fa fa-envelope"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
@@ -754,7 +738,7 @@ function Header({
|
||||
className="finishBtn sendHover"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedImages();
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -0,0 +1,750 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import BorderResize from "./BorderResize";
|
||||
import PlaceholderBorder from "./PlaceholderBorder";
|
||||
import { Rnd } from "react-rnd";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
handleCopyNextToWidget,
|
||||
isMobile,
|
||||
onChangeInput,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import PlaceholderType from "./PlaceholderType";
|
||||
import moment from "moment";
|
||||
import "../../styles/opensigndrive.css";
|
||||
|
||||
const selectFormat = (data) => {
|
||||
switch (data) {
|
||||
case "L":
|
||||
return "MM/dd/yyyy";
|
||||
case "DD-MM-YYYY":
|
||||
return "dd-MM-yyyy";
|
||||
case "DD/MM/YYYY":
|
||||
return "dd/MM/yyyy";
|
||||
case "LL":
|
||||
return "MMMM dd, yyyy";
|
||||
case "DD MMM, YYYY":
|
||||
return "dd MMM, yyyy";
|
||||
case "YYYY-MM-DD":
|
||||
return "yyyy-MM-dd";
|
||||
case "MM-DD-YYYY":
|
||||
return "MM-dd-yyyy";
|
||||
case "MM.DD.YYYY":
|
||||
return "MM.dd.yyyy";
|
||||
case "MMM DD, YYYY":
|
||||
return "MMM dd, yyyy";
|
||||
case "DD MMMM, YYYY":
|
||||
return "dd MMMM, yyyy";
|
||||
default:
|
||||
return "MM/dd/yyyy";
|
||||
}
|
||||
};
|
||||
|
||||
const changeDateToMomentFormat = (format) => {
|
||||
switch (format) {
|
||||
case "MM/dd/yyyy":
|
||||
return "L";
|
||||
case "dd-MM-yyyy":
|
||||
return "DD-MM-YYYY";
|
||||
case "dd/MM/yyyy":
|
||||
return "DD/MM/YYYY";
|
||||
case "MMMM dd, yyyy":
|
||||
return "LL";
|
||||
case "dd MMM, yyyy":
|
||||
return "DD MMM, YYYY";
|
||||
case "yyyy-MM-dd":
|
||||
return "YYYY-MM-DD";
|
||||
case "MM-dd-yyyy":
|
||||
return "MM-DD-YYYY";
|
||||
case "MM.dd.yyyy":
|
||||
return "MM.DD.YYYY";
|
||||
case "MMM dd, yyyy":
|
||||
return "MMM DD, YYYY";
|
||||
case "dd MMMM, yyyy":
|
||||
return "DD MMMM, YYYY";
|
||||
default:
|
||||
return "L";
|
||||
}
|
||||
};
|
||||
|
||||
//function to get default date
|
||||
const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
|
||||
let date;
|
||||
if (format && format === "dd-MM-yyyy") {
|
||||
const newdate = selectedDate
|
||||
? selectedDate
|
||||
: moment(new Date()).format(changeDateToMomentFormat(format));
|
||||
const [day, month, year] = newdate.split("-");
|
||||
date = new Date(`${year}-${month}-${day}`);
|
||||
} else {
|
||||
date = new Date(selectedDate);
|
||||
}
|
||||
const value = date;
|
||||
return value;
|
||||
};
|
||||
//function to get default format
|
||||
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||
|
||||
function Placeholder(props) {
|
||||
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
||||
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
|
||||
const [selectDate, setSelectDate] = useState({
|
||||
date:
|
||||
props.pos.type === "date"
|
||||
? moment(
|
||||
getDefaultdate(
|
||||
props?.pos?.options?.response,
|
||||
props.pos?.options?.validation?.format
|
||||
).getTime()
|
||||
).format(
|
||||
changeDateToMomentFormat(props.pos?.options?.validation?.format)
|
||||
)
|
||||
: "",
|
||||
format:
|
||||
props.pos.type === "date"
|
||||
? getDefaultFormat(props.pos?.options?.validation?.format)
|
||||
: ""
|
||||
});
|
||||
const [dateFormat, setDateFormat] = useState([]);
|
||||
const [startDate, setStartDate] = useState(
|
||||
props.pos.type === "date" &&
|
||||
getDefaultdate(
|
||||
props?.pos?.options?.response,
|
||||
props.pos?.options?.validation?.format
|
||||
)
|
||||
);
|
||||
const [getCheckboxRenderWidth, setGetCheckboxRenderWidth] = useState({
|
||||
width: null,
|
||||
height: null
|
||||
});
|
||||
const dateFormatArr = [
|
||||
"L",
|
||||
"DD-MM-YYYY",
|
||||
"YYYY-MM-DD",
|
||||
"MM.DD.YYYY",
|
||||
"MM-DD-YYYY",
|
||||
"MMM DD, YYYY",
|
||||
"LL",
|
||||
"DD MMM, YYYY",
|
||||
"DD MMMM, YYYY"
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
const updateWidth = () => {
|
||||
const rndElement = document.getElementById(props.pos.key);
|
||||
if (rndElement) {
|
||||
const { width, height } = rndElement.getBoundingClientRect();
|
||||
setGetCheckboxRenderWidth({ width: width, height: height });
|
||||
}
|
||||
};
|
||||
|
||||
// Delay to ensure rendering is complete
|
||||
const timer = setTimeout(updateWidth, 0);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [props.pos]);
|
||||
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (!isDraggingEnabled && !e.target.closest("#changeIsDragging")) {
|
||||
setDraggingEnabled(true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isDraggingEnabled]);
|
||||
//function change format array list with selected date and format
|
||||
const changeDateFormat = () => {
|
||||
const updateDate = [];
|
||||
dateFormatArr.map((data) => {
|
||||
let date;
|
||||
if (selectDate && selectDate.format === "dd-MM-yyyy") {
|
||||
const [day, month, year] = selectDate.date.split("-");
|
||||
date = new Date(`${year}-${month}-${day}`);
|
||||
} else {
|
||||
date = new Date(selectDate?.date);
|
||||
}
|
||||
const milliseconds = date.getTime();
|
||||
const newDate = moment(milliseconds).format(data);
|
||||
const dateObj = {
|
||||
date: newDate,
|
||||
format: selectFormat(data)
|
||||
};
|
||||
updateDate.push(dateObj);
|
||||
});
|
||||
setDateFormat(updateDate);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (props.isPlaceholder || props.isSignYourself) {
|
||||
selectDate && changeDateFormat();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectDate]);
|
||||
|
||||
//it detect outside click of date dropdown menu
|
||||
useEffect(() => {
|
||||
const closeMenuOnOutsideClick = (e) => {
|
||||
if (isShowDateFormat && !e.target.closest("#menu-container")) {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
}
|
||||
};
|
||||
document.addEventListener("click", closeMenuOnOutsideClick);
|
||||
return () => {
|
||||
// Cleanup the event listener when the component unmounts
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isShowDateFormat]);
|
||||
|
||||
//`handleWidgetIdandPopup` is used to set current widget id and open relative popup
|
||||
const handleWidgetIdandPopup = () => {
|
||||
if (props.setSelectWidgetId) {
|
||||
props.setSelectWidgetId(props.pos.key);
|
||||
}
|
||||
|
||||
const widgetTypeExist = [
|
||||
textInputWidget,
|
||||
"checkbox",
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"date",
|
||||
"email",
|
||||
textWidget
|
||||
].includes(props.pos.type);
|
||||
|
||||
if (widgetTypeExist) {
|
||||
setDraggingEnabled(false);
|
||||
}
|
||||
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
|
||||
if (props.pos.type) {
|
||||
if (
|
||||
props.pos.type === "signature" ||
|
||||
props.pos.type === "stamp" ||
|
||||
props.pos.type === "image"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.pos.type === "initials") {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
props.setIsInitial(true);
|
||||
}
|
||||
} else {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
|
||||
}
|
||||
} else if (
|
||||
props.isPlaceholder &&
|
||||
!props.isDragging &&
|
||||
props.pos.type !== textWidget
|
||||
) {
|
||||
if (props.pos.key === props.selectWidgetId) {
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
} else if (!props.pos.type) {
|
||||
if (
|
||||
!props.pos.type &&
|
||||
props.isNeedSign &&
|
||||
props.data.signerObjId === props.signerObjId
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (
|
||||
(props.isNeedSign && props.pos.type === "signature") ||
|
||||
props.pos.type === "stamp"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.isNeedSign && props.pos.type === "dropdown") {
|
||||
props.setSignKey(props.pos.key);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleOnClickPlaceholder = () => {
|
||||
if (!props.isNeedSign) {
|
||||
props.setWidgetType(props.pos.type);
|
||||
}
|
||||
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
|
||||
handleWidgetIdandPopup();
|
||||
} else if (props.isPlaceholder || props.isSignYourself) {
|
||||
handleWidgetIdandPopup();
|
||||
}
|
||||
};
|
||||
//`handleOnClickSettingIcon` is used set current widget details and open setting of it
|
||||
const handleOnClickSettingIcon = () => {
|
||||
if (props.pos.type === radioButtonWidget) {
|
||||
props.setIsRadio(true);
|
||||
} else if (props.pos.type === "dropdown") {
|
||||
props?.setShowDropdown(true);
|
||||
} else if (props.pos.type === "checkbox") {
|
||||
props?.setIsCheckbox(true);
|
||||
} else {
|
||||
props?.handleNameModal(true);
|
||||
}
|
||||
|
||||
if (props.isPlaceholder && props.type !== textWidget) {
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setWidgetType(props.pos.type);
|
||||
props.setCurrWidgetsDetails(props.pos);
|
||||
};
|
||||
//function to set required state value onclick on widget's copy icon
|
||||
const handleCopyPlaceholder = (e) => {
|
||||
e.stopPropagation();
|
||||
//condition to handle text widget signer obj id and unique id
|
||||
//when user click on copy icon of text widget in that condition text widget does not have any signerObjId
|
||||
//in that case i have to save in tempSignerId as a unique id of previous select signer's unique id
|
||||
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
|
||||
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
} else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
}
|
||||
|
||||
//checking widget's type and open widget copy modal for required widgets
|
||||
if (
|
||||
["signature", textWidget, "stamp", "initial"].includes(props.pos.type)
|
||||
) {
|
||||
props.setIsPageCopy(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
} else {
|
||||
//function to create new widget next to just widget
|
||||
handleCopyNextToWidget(
|
||||
props.pos,
|
||||
props.pos.type,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//function to save date and format on local array onchange date and onclick format
|
||||
const handleSaveDate = (data, isDateChange) => {
|
||||
let updateDate = data.date;
|
||||
//check if date change by user
|
||||
if (isDateChange) {
|
||||
//`changeDateToMomentFormat` is used to convert date as per required to moment package
|
||||
updateDate = moment(data.date).format(
|
||||
changeDateToMomentFormat(data.format)
|
||||
);
|
||||
}
|
||||
//using moment package is used to change date as per the format provided in selectDate obj e.g. - MM/dd/yyyy -> 03/12/2024
|
||||
//`getDefaultdate` is used to convert update date in new Date() format
|
||||
const date = moment(
|
||||
getDefaultdate(updateDate, data?.format).getTime()
|
||||
).format(changeDateToMomentFormat(data?.format));
|
||||
|
||||
//`onChangeInput` is used to save data related to date in a placeholder field
|
||||
onChangeInput(
|
||||
date,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
data?.format
|
||||
);
|
||||
setSelectDate({ date: date, format: data?.format });
|
||||
};
|
||||
const PlaceholderIcon = () => {
|
||||
return (
|
||||
props.isShowBorder && (
|
||||
<>
|
||||
{(props.isPlaceholder || props.isSignYourself) && (
|
||||
<>
|
||||
{props.pos.type === "checkbox" && props.isSignYourself ? (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: "29px",
|
||||
top: "-19px"
|
||||
}}
|
||||
></i>
|
||||
) : (
|
||||
((!props?.pos?.type && props.pos.isStamp) ||
|
||||
(props?.pos?.type &&
|
||||
!["date", textWidget, "signature"].includes(
|
||||
props.pos.type
|
||||
) &&
|
||||
!props.isSignYourself)) && (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{ color: "#188ae2", right: "47px", top: "-19px" }}
|
||||
></i>
|
||||
)
|
||||
)}
|
||||
|
||||
{props.pos.type !== textWidget && !props.isSignYourself && (
|
||||
<i
|
||||
data-tut="reactourLinkUser"
|
||||
className="fa-regular fa-user signUserIcon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}}
|
||||
style={{ color: "#188ae2", right: "32px", top: "-18px" }}
|
||||
></i>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{props.pos.type === "date" && selectDate && (
|
||||
<div
|
||||
id="menu-container"
|
||||
style={{
|
||||
zIndex: "99",
|
||||
top: "-19px",
|
||||
right: props.isPlaceholder ? "60px" : "44px"
|
||||
}}
|
||||
className={
|
||||
isShowDateFormat
|
||||
? "dropdown signUserIcon show"
|
||||
: "dropdown signUserIcon"
|
||||
}
|
||||
onClick={(e) => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
e.stopPropagation();
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
fontSize: "14px"
|
||||
}}
|
||||
></i>
|
||||
<div
|
||||
className={
|
||||
isShowDateFormat ? "dropdown-menu show" : "dropdown-menu"
|
||||
}
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isShowDateFormat ? "true" : "false"}
|
||||
>
|
||||
{dateFormat.map((data, ind) => {
|
||||
return (
|
||||
<span
|
||||
key={ind}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
handleSaveDate(data);
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
setSelectDate(data);
|
||||
handleSaveDate(data);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<i
|
||||
className="fa-regular fa-copy signCopy"
|
||||
onClick={(e) => handleCopyPlaceholder(e)}
|
||||
onTouchEnd={(e) => handleCopyPlaceholder(e)}
|
||||
style={{ color: "#188ae2", right: "12px", top: "-18px" }}
|
||||
></i>
|
||||
<i
|
||||
className="fa-regular fa-circle-xmark signCloseBtn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data.Id);
|
||||
} else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data?.Id);
|
||||
} else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
}}
|
||||
style={{ color: "#188ae2", right: "-8px", top: "-18px" }}
|
||||
></i>
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Rnd
|
||||
id={props.pos.key}
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
props.pos.type !== textWidget &&
|
||||
(props.pos.Width
|
||||
? props.pos.Width / props.pos.Height
|
||||
: defaultWidthHeight(props.pos.type).width /
|
||||
defaultWidthHeight(props.pos.type).height)
|
||||
}
|
||||
enableResizing={{
|
||||
top: false,
|
||||
right: false,
|
||||
bottom: false,
|
||||
left: false,
|
||||
topRight: false,
|
||||
bottomRight:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.type !== radioButtonWidget
|
||||
? true
|
||||
: false
|
||||
: props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId &&
|
||||
true,
|
||||
bottomLeft: false,
|
||||
topLeft: false
|
||||
}}
|
||||
bounds="parent"
|
||||
className="signYourselfBlock"
|
||||
style={{
|
||||
border: "1px solid #007bff",
|
||||
borderRadius: "2px",
|
||||
textAlign:
|
||||
props.pos.type !== "name" &&
|
||||
props.pos.type !== "company" &&
|
||||
props.pos.type !== "job title" &&
|
||||
"center",
|
||||
cursor:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId
|
||||
? "pointer"
|
||||
: "not-allowed"
|
||||
: "all-scroll",
|
||||
zIndex:
|
||||
props.pos.type === "date"
|
||||
? props.pos.key === props.selectWidgetId
|
||||
? 99 + 1
|
||||
: 99
|
||||
: props?.pos?.zIndex
|
||||
? props.pos.zIndex
|
||||
: "5",
|
||||
background: props.data ? props.data.blockColor : "rgb(203 233 237)"
|
||||
}}
|
||||
onDrag={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.handleTabDrag && props.handleTabDrag(props.pos.key);
|
||||
}}
|
||||
size={{
|
||||
width:
|
||||
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget || props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResizeStart={() => {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={() => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
}}
|
||||
disableDragging={
|
||||
props.isNeedSign
|
||||
? true
|
||||
: props.isPlaceholder && ![textWidget].includes(props.pos.type)
|
||||
? false
|
||||
: !isDraggingEnabled
|
||||
}
|
||||
onDragStop={(event, dragElement) =>
|
||||
props.handleStop &&
|
||||
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||
}
|
||||
default={{
|
||||
x: props.xPos(props.pos, props.isSignYourself),
|
||||
y: props.yPos(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResize={(e, direction, ref) => {
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
props.data && props.data.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.isShowBorder &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId ? (
|
||||
<BorderResize right={-12} top={-11} />
|
||||
) : props.data && props.isNeedSign && props.pos.type !== "checkbox" ? (
|
||||
props.data?.signerObjId === props.signerObjId &&
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" ? (
|
||||
<BorderResize />
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
) : (
|
||||
props.pos.type !== radioButtonWidget &&
|
||||
props.pos.type !== "checkbox" &&
|
||||
props.pos.key === props.selectWidgetId && <BorderResize />
|
||||
)}
|
||||
|
||||
{props.isShowBorder && props.pos.key === props.selectWidgetId && (
|
||||
<PlaceholderBorder
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pos={props.pos}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||
/>
|
||||
)}
|
||||
{isMobile ? (
|
||||
<div
|
||||
style={{
|
||||
left: props.xPos(props.pos, props.isSignYourself),
|
||||
top: props.yPos(props.pos, props.isSignYourself),
|
||||
width:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
// "auto", //props.posWidth(props.pos, props.isSignYourself),
|
||||
// height: props.posHeight(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posHeight(props.pos, props.isSignYourself),
|
||||
zIndex: "10"
|
||||
}}
|
||||
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
<PlaceholderType
|
||||
pos={props.pos}
|
||||
xyPostion={props.xyPostion}
|
||||
index={props.index}
|
||||
setXyPostion={props.setXyPostion}
|
||||
data={props.data}
|
||||
setSignKey={props.setSignKey}
|
||||
isShowDropdown={props?.isShowDropdown}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
isSignYourself={props.isSignYourself}
|
||||
signerObjId={props.signerObjId}
|
||||
handleUserName={props.handleUserName}
|
||||
setDraggingEnabled={setDraggingEnabled}
|
||||
pdfDetails={props?.pdfDetails && props?.pdfDetails[0]}
|
||||
isNeedSign={props.isNeedSign}
|
||||
setSelectDate={setSelectDate}
|
||||
selectDate={selectDate}
|
||||
setValidateAlert={props.setValidateAlert}
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Rnd>
|
||||
);
|
||||
}
|
||||
|
||||
export default Placeholder;
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
isMobile,
|
||||
radioButtonWidget,
|
||||
resizeBorderExtraWidth,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
function PlaceholderBorder(props) {
|
||||
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
|
||||
const defaultWidth = defaultWidthHeight(props.pos.type).width;
|
||||
const defaultHeight = defaultWidthHeight(props.pos.type).height;
|
||||
|
||||
const handleMinWidth = () => {
|
||||
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
|
||||
} else {
|
||||
return props.pos.Width
|
||||
? props.pos.Width + getResizeBorderExtraWidth
|
||||
: defaultWidth + getResizeBorderExtraWidth;
|
||||
}
|
||||
};
|
||||
const handleMinHeight = () => {
|
||||
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
|
||||
} else {
|
||||
return props.pos.Height
|
||||
? props.pos.Height + getResizeBorderExtraWidth
|
||||
: defaultHeight + getResizeBorderExtraWidth;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => !isMobile && props?.setDraggingEnabled(true)}
|
||||
onTouchEnd={() =>
|
||||
props.pos.type === textWidget && props?.setDraggingEnabled(true)
|
||||
}
|
||||
className="borderResize"
|
||||
style={{
|
||||
borderColor: themeColor,
|
||||
borderStyle: "dashed",
|
||||
minWidth: handleMinWidth(),
|
||||
minHeight: handleMinHeight(),
|
||||
borderWidth: "0.2px",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlaceholderBorder;
|
||||
@@ -0,0 +1,252 @@
|
||||
import React, { useState } from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { randomId, textWidget } from "../../constant/Utils";
|
||||
|
||||
function PlaceholderCopy(props) {
|
||||
const copyType = ["All pages", "All pages but last", "All pages but first"];
|
||||
const [selectCopyType, setSelectCopyType] = useState("");
|
||||
|
||||
//function for get copy placeholder position
|
||||
const getCopyPlaceholderPosition = (
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
) => {
|
||||
const filterPosition =
|
||||
existPlaceholderPosition &&
|
||||
existPlaceholderPosition.filter(
|
||||
(data) => data.xPosition !== currentPlaceholder.xPosition
|
||||
);
|
||||
//copy all page placeholder at requested position except first page
|
||||
if (newPageNumber === 1 && type === "first") {
|
||||
if (filterPosition && filterPosition.length > 0) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position except last page
|
||||
else if (newPageNumber === props.allPages && type === "last") {
|
||||
if (existPlaceholderPosition) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition]
|
||||
};
|
||||
}
|
||||
}
|
||||
//copy all page placeholder at requested position with existing placeholder position
|
||||
else if (existPlaceholderPosition) {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [...filterPosition, currentPlaceholder]
|
||||
};
|
||||
}
|
||||
//copy all page placeholder at requested position
|
||||
else {
|
||||
return {
|
||||
pageNumber: newPageNumber,
|
||||
pos: [currentPlaceholder]
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
//function for copy placeholder as per select copy type
|
||||
const copyPlaceholder = (type) => {
|
||||
let newPlaceholderPosition = [];
|
||||
let newPageNumber = 1;
|
||||
const signerPosition = props.xyPostion;
|
||||
const signerId = props.signerObjId ? props.signerObjId : props.Id;
|
||||
|
||||
//handle placeholder array and copy for multiple signers placeholder at requested location
|
||||
if (signerId) {
|
||||
//get current signers data
|
||||
let filterSignerPosition;
|
||||
if (props?.signerObjId) {
|
||||
filterSignerPosition = signerPosition.filter(
|
||||
(data) => data.signerObjId === signerId
|
||||
);
|
||||
} else {
|
||||
filterSignerPosition = signerPosition.filter(
|
||||
(item) => item.Id === signerId
|
||||
);
|
||||
}
|
||||
//get current pagenumber's all placeholder position data
|
||||
const placeholderPosition = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||
(position) => position.key === props.signKey
|
||||
);
|
||||
|
||||
for (let i = 0; i < props.allPages; i++) {
|
||||
const newId = randomId();
|
||||
currentPlaceholder.key = newId;
|
||||
//get exist placeholder position for particular page
|
||||
const existPlaceholder = filterSignerPosition[0].placeHolder.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
newPageNumber++;
|
||||
}
|
||||
let updatedSignerPlaceholder;
|
||||
if (props?.signerObjId) {
|
||||
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||
if (signersData.signerObjId === props.signerObjId) {
|
||||
return {
|
||||
...signersData,
|
||||
placeHolder: newPlaceholderPosition
|
||||
};
|
||||
}
|
||||
return signersData;
|
||||
});
|
||||
} else {
|
||||
updatedSignerPlaceholder = signerPosition.map((signersData) => {
|
||||
if (signersData.Id === props.Id) {
|
||||
return {
|
||||
...signersData,
|
||||
placeHolder: newPlaceholderPosition
|
||||
};
|
||||
}
|
||||
return signersData;
|
||||
});
|
||||
}
|
||||
|
||||
const signersData = signerPosition;
|
||||
signersData.splice(0, signerPosition.length, ...updatedSignerPlaceholder);
|
||||
props.setXyPostion(signersData);
|
||||
}
|
||||
//handle signyourself array and copy for single signers placeholder at requested location
|
||||
else {
|
||||
const xyPostion = props.xyPostion;
|
||||
|
||||
const placeholderPosition = xyPostion.filter(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
//get current placeholder position data which user want to copy
|
||||
const currentPlaceholder = placeholderPosition[0].pos.find(
|
||||
(pos) => pos.key === props.signKey
|
||||
);
|
||||
|
||||
for (let i = 0; i < props.allPages; i++) {
|
||||
//get exist placeholder position for particular page
|
||||
const existPlaceholder = xyPostion.filter(
|
||||
(data) => data.pageNumber === newPageNumber
|
||||
);
|
||||
const existPlaceholderPosition =
|
||||
existPlaceholder[0] && existPlaceholder[0].pos;
|
||||
|
||||
const newId = randomId();
|
||||
currentPlaceholder.key = newId;
|
||||
//function for get copy to requested location of placeholder position
|
||||
const getPlaceholderObj = getCopyPlaceholderPosition(
|
||||
type,
|
||||
currentPlaceholder,
|
||||
newPageNumber,
|
||||
existPlaceholderPosition
|
||||
);
|
||||
if (getPlaceholderObj) {
|
||||
newPlaceholderPosition.push(getPlaceholderObj);
|
||||
}
|
||||
|
||||
newPageNumber++;
|
||||
}
|
||||
props.setXyPostion(newPlaceholderPosition);
|
||||
}
|
||||
};
|
||||
|
||||
//function for getting selected type placeholder copy
|
||||
const handleApplyCopy = () => {
|
||||
if (selectCopyType === "All pages") {
|
||||
copyPlaceholder("all");
|
||||
} else if (selectCopyType === "All pages but last") {
|
||||
copyPlaceholder("last");
|
||||
} else if (selectCopyType === "All pages but first") {
|
||||
copyPlaceholder("first");
|
||||
}
|
||||
};
|
||||
const handleUniqueId = () => {
|
||||
if (props.widgetType === textWidget) {
|
||||
props.setUniqueId(props?.tempSignerId);
|
||||
props.setTempSignerId("");
|
||||
}
|
||||
props.setIsPageCopy(false);
|
||||
};
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isPageCopy}
|
||||
title={"Copy to all pages"}
|
||||
handleClose={() => {
|
||||
props.setIsPageCopy(false);
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{copyType.map((data, key) => {
|
||||
return (
|
||||
<div key={key} style={{ display: "flex", flexDirection: "column" }}>
|
||||
<label key={key} style={{ fontSize: "16px", fontWeight: "500" }}>
|
||||
<input
|
||||
style={{ accentColor: "red", marginRight: "10px" }}
|
||||
type="radio"
|
||||
value={data}
|
||||
onChange={() => setSelectCopyType(data)}
|
||||
checked={selectCopyType === data}
|
||||
/>
|
||||
|
||||
{data}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<div
|
||||
style={{
|
||||
height: "1px",
|
||||
backgroundColor: "#9f9f9f",
|
||||
width: "100%",
|
||||
marginTop: "15px",
|
||||
marginBottom: "15px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleApplyCopy();
|
||||
handleUniqueId();
|
||||
}}
|
||||
style={{ background: themeColor }}
|
||||
type="button"
|
||||
disabled={!selectCopyType}
|
||||
className="finishBtn"
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="finishBtn cancelBtn"
|
||||
onClick={() => {
|
||||
handleUniqueId();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlaceholderCopy;
|
||||
@@ -0,0 +1,814 @@
|
||||
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||
import {
|
||||
getMonth,
|
||||
getYear,
|
||||
isMobile,
|
||||
onChangeHeightOfTextArea,
|
||||
onChangeInput,
|
||||
radioButtonWidget,
|
||||
range,
|
||||
textInputWidget,
|
||||
textWidget
|
||||
} from "../../constant/Utils";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import "../../styles/signature.css";
|
||||
import RegexParser from "regex-parser";
|
||||
|
||||
function PlaceholderType(props) {
|
||||
const type = props?.pos?.type;
|
||||
const [selectOption, setSelectOption] = useState("");
|
||||
const [validatePlaceholder, setValidatePlaceholder] = useState("");
|
||||
const inputRef = useRef(null);
|
||||
const [textValue, setTextValue] = useState();
|
||||
const [selectedCheckbox, setSelectedCheckbox] = useState([]);
|
||||
const years = range(1990, getYear(new Date()) + 16, 1);
|
||||
const months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December"
|
||||
];
|
||||
const validateExpression = (regexValidation) => {
|
||||
let regexObject = regexValidation;
|
||||
if (props.pos?.options.validation.type === "regex") {
|
||||
regexObject = RegexParser(regexValidation);
|
||||
}
|
||||
// new RegExp(regexValidation);
|
||||
let isValidate = regexObject.test(textValue);
|
||||
if (!isValidate) {
|
||||
props?.setValidateAlert(true);
|
||||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputBlur = () => {
|
||||
props.setDraggingEnabled(true);
|
||||
const validateType = props.pos?.options?.validation?.type;
|
||||
let regexValidation;
|
||||
if (validateType) {
|
||||
switch (validateType) {
|
||||
case "email":
|
||||
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "number":
|
||||
regexValidation = /^\d+$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "text":
|
||||
regexValidation = /^[a-zA-Z\s]+$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
default:
|
||||
regexValidation = props.pos?.options.validation.pattern;
|
||||
validateExpression(regexValidation);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTextValid = (e) => {
|
||||
const textInput = e.target.value;
|
||||
setTextValue(textInput);
|
||||
};
|
||||
function checkRegularExpress(validateType) {
|
||||
switch (validateType) {
|
||||
case "email":
|
||||
setValidatePlaceholder("demo@gmail.com");
|
||||
break;
|
||||
case "number":
|
||||
setValidatePlaceholder("12345");
|
||||
break;
|
||||
case textInputWidget:
|
||||
setValidatePlaceholder("enter text");
|
||||
break;
|
||||
default:
|
||||
setValidatePlaceholder("enter text");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (type && type === "checkbox" && props.isNeedSign) {
|
||||
const isDefaultValue = props.pos.options?.defaultValue;
|
||||
if (isDefaultValue) {
|
||||
setSelectedCheckbox(isDefaultValue);
|
||||
}
|
||||
} else if (props.pos?.options?.hint) {
|
||||
setValidatePlaceholder(props.pos?.options.hint);
|
||||
} else if (props.pos?.options?.validation?.type) {
|
||||
checkRegularExpress(props.pos?.options?.validation?.type);
|
||||
}
|
||||
setTextValue(
|
||||
props.pos?.options?.response
|
||||
? props.pos?.options?.response
|
||||
: props.pos?.options?.defaultValue
|
||||
? props.pos?.options?.defaultValue
|
||||
: ""
|
||||
);
|
||||
setSelectOption(
|
||||
props.pos?.options?.response
|
||||
? props.pos?.options?.response
|
||||
: props.pos?.options?.defaultValue
|
||||
? props.pos?.options?.defaultValue
|
||||
: ""
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
["name", "email", "job title", "company"].includes(props.pos?.type) &&
|
||||
props.isNeedSign &&
|
||||
props.data?.signerObjId === props?.signerObjId
|
||||
) {
|
||||
const defaultData = props.pos?.options?.defaultValue;
|
||||
if (defaultData) {
|
||||
setTextValue(defaultData);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos]);
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
["name", "email", "job title", "company"].includes(type) &&
|
||||
props.isNeedSign &&
|
||||
props.data?.signerObjId === props.signerObjId
|
||||
) {
|
||||
const isDefault = true;
|
||||
const senderUser = localStorage.getItem(`Extand_Class`);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
onChangeInput(
|
||||
jsonSender && jsonSender[0],
|
||||
null,
|
||||
props.xyPostion,
|
||||
null,
|
||||
props.setXyPostion,
|
||||
props.data.Id,
|
||||
isDefault
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [type]);
|
||||
|
||||
const calculateFontSize = () => {
|
||||
const fontSize = 10 + Math.min(props.pos.Width, props.pos.Height) * 0.1;
|
||||
const size = fontSize ? fontSize : 12;
|
||||
return size + "px";
|
||||
};
|
||||
//function for show checked checkbox
|
||||
const selectCheckbox = (ind) => {
|
||||
const res = props.pos.options?.response;
|
||||
const defaultCheck = props.pos.options?.defaultValue;
|
||||
if (res && res?.length > 0) {
|
||||
const isSelectIndex = res.indexOf(ind);
|
||||
if (isSelectIndex > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// }
|
||||
} else if (defaultCheck) {
|
||||
const isSelectIndex = defaultCheck.indexOf(ind);
|
||||
if (isSelectIndex > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRadioCheck = (data) => {
|
||||
const defaultData = props.pos.options?.defaultValue;
|
||||
if (textValue === data) {
|
||||
return true;
|
||||
} else if (defaultData === data) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//function for set checked and unchecked value of checkbox
|
||||
const handleCheckboxValue = (isChecked, ind) => {
|
||||
let updateSelectedCheckbox = [],
|
||||
checkedList;
|
||||
let isDefaultValue, isDefaultEmpty;
|
||||
if (type === "checkbox") {
|
||||
updateSelectedCheckbox = selectedCheckbox ? selectedCheckbox : [];
|
||||
|
||||
if (isChecked) {
|
||||
updateSelectedCheckbox.push(ind);
|
||||
setSelectedCheckbox(updateSelectedCheckbox);
|
||||
} else {
|
||||
checkedList = selectedCheckbox.filter((data) => data !== ind);
|
||||
setSelectedCheckbox(checkedList);
|
||||
}
|
||||
if (props.isNeedSign) {
|
||||
isDefaultValue = props.pos.options?.defaultValue;
|
||||
}
|
||||
if (isDefaultValue && isDefaultValue.length > 0) {
|
||||
isDefaultEmpty = true;
|
||||
}
|
||||
onChangeInput(
|
||||
checkedList ? checkedList : updateSelectedCheckbox,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
null,
|
||||
isDefaultEmpty
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//function to handle select radio widget and set value seletced by user
|
||||
const handleCheckRadio = (isChecked, data) => {
|
||||
let isDefaultValue,
|
||||
isDefaultEmpty,
|
||||
isRadio = true;
|
||||
if (props.isNeedSign) {
|
||||
isDefaultValue = props.pos.options?.defaultValue;
|
||||
}
|
||||
if (isDefaultValue) {
|
||||
isDefaultEmpty = true;
|
||||
}
|
||||
if (isChecked) {
|
||||
setTextValue(data);
|
||||
} else {
|
||||
setTextValue("");
|
||||
}
|
||||
onChangeInput(
|
||||
data,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data.Id,
|
||||
false,
|
||||
null,
|
||||
isDefaultEmpty,
|
||||
isRadio
|
||||
);
|
||||
};
|
||||
//function to set onchange date
|
||||
const handleOnDateChange = (date) => {
|
||||
props.setStartDate(date);
|
||||
const isDateChange = true;
|
||||
const dateObj = {
|
||||
date: date,
|
||||
format: props.selectDate.format
|
||||
};
|
||||
props.handleSaveDate(dateObj, isDateChange);
|
||||
};
|
||||
//handle height on enter press in text area
|
||||
const handleEnterPress = (e) => {
|
||||
const height = 15;
|
||||
if (e.key === "Enter") {
|
||||
//function to save height of text area
|
||||
onChangeHeightOfTextArea(
|
||||
height,
|
||||
props.pos.type,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id
|
||||
);
|
||||
}
|
||||
};
|
||||
switch (type) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "stamp":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "checkbox":
|
||||
return (
|
||||
<div style={{ zIndex: props.isSignYourself && "99" }}>
|
||||
{props.pos.options?.values?.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||
<input
|
||||
style={{
|
||||
// width: props.pos.Width,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: ind === 0 ? 0 : "5px"
|
||||
}}
|
||||
onBlur={handleInputBlur}
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
type="checkbox"
|
||||
checked={selectCheckbox(ind)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
if (!props.isPlaceholder) {
|
||||
const maxRequired =
|
||||
props.pos.options?.validation?.maxRequiredCount;
|
||||
const maxCountInt =
|
||||
maxRequired && parseInt(maxRequired);
|
||||
|
||||
if (maxCountInt > 0) {
|
||||
if (
|
||||
selectedCheckbox &&
|
||||
selectedCheckbox?.length <= maxCountInt - 1
|
||||
) {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
} else {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleCheckboxValue(e.target.checked, ind);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<label className="text-xs mb-0 text-center">{data}</label>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case textInputWidget:
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
ref={inputRef}
|
||||
placeholder={validatePlaceholder}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
type="text"
|
||||
tabIndex="0"
|
||||
disabled={
|
||||
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
|
||||
? true
|
||||
: props.isPlaceholder
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
setTextValue(e.target.value);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case "dropdown":
|
||||
return props.data?.signerObjId === props.signerObjId ? (
|
||||
<select
|
||||
className="inputPlaceholder"
|
||||
id="myDropdown"
|
||||
style={{ fontSize: "12px" }}
|
||||
value={selectOption}
|
||||
onChange={(e) => {
|
||||
setSelectOption(e.target.value);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
>
|
||||
{/* Default/Title option */}
|
||||
<option value="" disabled hidden>
|
||||
{props?.pos?.options?.name}
|
||||
</option>
|
||||
|
||||
{props.pos?.options?.values.map((data, ind) => {
|
||||
return (
|
||||
<option key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
) : (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
>
|
||||
{props.pos?.options?.name ? props.pos.options.name : type}
|
||||
</div>
|
||||
);
|
||||
case "initials":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "name":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
tabIndex="0"
|
||||
ref={inputRef}
|
||||
placeholder={"name"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
const isDefault = false;
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
isDefault
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case "company":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"company"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case "job title":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"job title"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
<div>
|
||||
<DatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
<div className="flex justify-start ml-2 ">
|
||||
<select
|
||||
className="bg-transparent outline-none"
|
||||
value={months[getMonth(date)]}
|
||||
onChange={({ target: { value } }) =>
|
||||
changeMonth(months.indexOf(value))
|
||||
}
|
||||
>
|
||||
{months.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
className="bg-transparent outline-none"
|
||||
value={getYear(date)}
|
||||
onChange={({ target: { value } }) => changeYear(value)}
|
||||
>
|
||||
{years.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
disabled={
|
||||
props.isPlaceholder ||
|
||||
(props.isNeedSign &&
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
closeOnScroll={true}
|
||||
className="inputPlaceholder"
|
||||
style={{ outlineColor: "#007bff" }}
|
||||
selected={
|
||||
props?.startDate
|
||||
? props?.startDate
|
||||
: props.pos.options?.response &&
|
||||
new Date(props.pos.options.response)
|
||||
}
|
||||
onChange={(date) => handleOnDateChange(date)}
|
||||
popperPlacement="top-end"
|
||||
customInput={<ExampleCustomInput />}
|
||||
dateFormat={
|
||||
props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/yyyy"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
case "image":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
props?.data?.Role,
|
||||
props?.pos?.type
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
case "email":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
placeholder={"email"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case radioButtonWidget:
|
||||
return (
|
||||
<div>
|
||||
{props.pos.options?.values.map((data, ind) => {
|
||||
return (
|
||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||
<input
|
||||
style={{
|
||||
width: props.pos.Width,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
marginTop: ind === 0 ? 0 : "5px"
|
||||
}}
|
||||
type="radio"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
(props.pos.options?.isReadOnly ||
|
||||
props.data?.signerObjId !== props.signerObjId)
|
||||
}
|
||||
checked={handleRadioCheck(data)}
|
||||
onChange={(e) => {
|
||||
if (!props.isPlaceholder) {
|
||||
handleCheckRadio(e.target.checked, data);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{!props.pos.options?.isHideLabel && (
|
||||
<label className="text-xs mb-0">{data}</label>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
case textWidget:
|
||||
return (
|
||||
<textarea
|
||||
placeholder="Enter label"
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
setTextValue(e.target.value);
|
||||
onChangeInput(
|
||||
e.target.value,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.index,
|
||||
props.setXyPostion,
|
||||
props.data && props.data?.Id,
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return props.pos.SignUrl ? (
|
||||
<div style={{ pointerEvents: "none" }}>
|
||||
<img
|
||||
alt="signimg"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "black",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PlaceholderType;
|
||||
@@ -21,7 +21,7 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
fontWeight: "600",
|
||||
|
||||
border: "0px",
|
||||
marginRight: "5px",
|
||||
marginRight: "5px"
|
||||
}}
|
||||
disabled={pageNumber <= 1}
|
||||
onClick={previousPage}
|
||||
@@ -31,7 +31,7 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
fontWeight: "600"
|
||||
}}
|
||||
>
|
||||
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
||||
@@ -43,7 +43,7 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
background: "#d3edeb",
|
||||
fontWeight: "600",
|
||||
marginLeft: "5px",
|
||||
border: "0px",
|
||||
border: "0px"
|
||||
}}
|
||||
disabled={pageNumber >= allPages}
|
||||
onClick={nextPage}
|
||||
@@ -0,0 +1,285 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import {
|
||||
color,
|
||||
darkenColor,
|
||||
getFirstLetter,
|
||||
isMobile,
|
||||
nameColor
|
||||
} from "../../constant/Utils";
|
||||
|
||||
const RecipientList = (props) => {
|
||||
const [animationParent] = useAutoAnimate();
|
||||
const [isHover, setIsHover] = useState();
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
//function for onhover signer name change background color
|
||||
const inputRef = useRef(null);
|
||||
const onHoverStyle = (ind, blockColor) => {
|
||||
const style = {
|
||||
background: blockColor ? blockColor : color[ind % color.length],
|
||||
padding: "10px",
|
||||
marginTop: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
borderBottom: "1px solid #e3e1e1",
|
||||
alignItems: "center"
|
||||
};
|
||||
return style;
|
||||
};
|
||||
//function for onhover signer name remove background color
|
||||
const nonHoverStyle = () => {
|
||||
const style = {
|
||||
padding: "10px",
|
||||
marginTop: "2px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
borderBottom: "1px solid #e3e1e1",
|
||||
alignItems: "center"
|
||||
};
|
||||
return style;
|
||||
};
|
||||
const isWidgetExist = (Id) => {
|
||||
return props.signerPos.some((x) => x.Id === Id);
|
||||
};
|
||||
|
||||
//handle drag start
|
||||
const handleDragStart = (e, id) => {
|
||||
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
|
||||
// The first argument specifies the type of data being set, and the second argument is the actual data you want to transfer.
|
||||
e.dataTransfer.setData("text/plain", id);
|
||||
};
|
||||
|
||||
//handleDragOver prevents the default behavior of the dragover event, which is necessary for the drop event to be triggered.
|
||||
const handleDragOver = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
//handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list
|
||||
const handleChangeSequence = (e, ind, isUp, isDown) => {
|
||||
e.preventDefault();
|
||||
let draggedItemId;
|
||||
let index = ind;
|
||||
|
||||
//if isUp true then set item's id and index in mobile view
|
||||
if (isUp) {
|
||||
draggedItemId = index;
|
||||
index = index - 1;
|
||||
}
|
||||
//if isDown true then set item's id and index in mobile view
|
||||
else if (isDown) {
|
||||
draggedItemId = index;
|
||||
index = index + 1;
|
||||
} //else set id on drag element in desktop viiew
|
||||
else {
|
||||
//`e.dataTransfer.getData('text/plain')` is used to get data that you have saved.
|
||||
draggedItemId = e.dataTransfer.getData("text/plain");
|
||||
}
|
||||
|
||||
//convert string to number
|
||||
const intDragId = parseInt(draggedItemId);
|
||||
//get that item to change position
|
||||
const draggedItem = props.signersdata.filter((_, ind) => ind === intDragId);
|
||||
const remainingItems = props.signersdata.filter(
|
||||
(_, ind) => ind !== intDragId
|
||||
);
|
||||
//splice method is used to replace or add new value in array at specific index
|
||||
remainingItems.splice(index, 0, ...draggedItem);
|
||||
|
||||
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
|
||||
props.setSignersData(remainingItems);
|
||||
props.setSignerObjId(remainingItems[index]?.objectId || "");
|
||||
props.setIsSelectId(index);
|
||||
props.setContractName(remainingItems[index]?.className || "");
|
||||
props.setUniqueId(remainingItems[index]?.Id);
|
||||
props.setRoleName(remainingItems[index]?.Role);
|
||||
props.setBlockColor(remainingItems[index]?.blockColor);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.signersdata.length > 0 &&
|
||||
props.signersdata.map((obj, ind) => {
|
||||
return (
|
||||
<div
|
||||
ref={animationParent}
|
||||
key={ind}
|
||||
draggable={props.sendInOrder && !isMobile ? true : false}
|
||||
onDragStart={(e) =>
|
||||
props.sendInOrder && !isMobile && handleDragStart(e, ind)
|
||||
}
|
||||
onDragOver={(e) =>
|
||||
props.sendInOrder && !isMobile && handleDragOver(e)
|
||||
}
|
||||
onDrop={(e) =>
|
||||
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
|
||||
}
|
||||
data-tut="reactourFirst"
|
||||
onMouseEnter={() => setIsHover(ind)}
|
||||
onMouseLeave={() => setIsHover(null)}
|
||||
className={props.sendInOrder && "dragCursor"}
|
||||
style={
|
||||
(!isMobile && isHover === ind) || props.isSelectListId === ind
|
||||
? onHoverStyle(ind, obj?.blockColor)
|
||||
: nonHoverStyle(ind)
|
||||
}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
props.setSignerObjId(obj?.objectId || "");
|
||||
props.setIsSelectId(ind);
|
||||
props.setContractName(obj?.className || "");
|
||||
props.setUniqueId(obj.Id);
|
||||
props.setRoleName(obj.Role);
|
||||
props.setBlockColor(obj?.blockColor);
|
||||
if (props.handleModal) {
|
||||
props.handleModal();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
width: "100%"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: obj?.blockColor
|
||||
? darkenColor(obj?.blockColor, 0.4)
|
||||
: nameColor[ind % nameColor.length],
|
||||
width: 30,
|
||||
height: 30,
|
||||
display: "flex",
|
||||
borderRadius: 30 / 2,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: "12px"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={props.sendInOrder && "dragCursor"}
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold",
|
||||
color: "white",
|
||||
textTransform: "uppercase"
|
||||
}}
|
||||
>
|
||||
{isWidgetExist(obj.Id) ? (
|
||||
<i className="fa-solid fa-check"></i>
|
||||
) : (
|
||||
<>
|
||||
{obj.Name
|
||||
? getFirstLetter(obj.Name)
|
||||
: getFirstLetter(obj.Role)}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: obj.Name ? "column" : "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{obj.Name ? (
|
||||
<span className={"userName"}>{obj.Name}</span>
|
||||
) : (
|
||||
<>
|
||||
<span
|
||||
className="userName"
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
setIsEdit({ [obj.Id]: true });
|
||||
props.setRoleName(obj.Role);
|
||||
}}
|
||||
>
|
||||
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
width: "inherit",
|
||||
padding: "3px"
|
||||
}}
|
||||
value={obj.Role}
|
||||
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||
onBlur={() => {
|
||||
setIsEdit({});
|
||||
props.handleOnBlur(obj.Role, obj.Id);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
inputRef.current.blur();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span style={{ padding: "3px" }}>{obj.Role}</span>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{obj.Name && <span className={"useEmail"}>{obj.Role}</span>}
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && props.sendInOrder && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 5
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={(e) => {
|
||||
if (ind !== 0) {
|
||||
e.stopPropagation();
|
||||
handleChangeSequence(e, ind, "up");
|
||||
}
|
||||
}}
|
||||
style={{ color: ind === 0 ? "gray" : "black" }}
|
||||
>
|
||||
▲
|
||||
</div>
|
||||
<div
|
||||
onClick={(e) => {
|
||||
if (ind !== props.signersdata.length - 1) {
|
||||
e.stopPropagation();
|
||||
handleChangeSequence(e, ind, null, "down");
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
ind === props.signersdata.length - 1 ? "gray" : "black"
|
||||
}}
|
||||
>
|
||||
▼
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.handleDeleteUser && (
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleDeleteUser(obj.Id);
|
||||
}}
|
||||
style={{ cursor: "pointer", marginLeft: "5px" }}
|
||||
>
|
||||
<i className="fa-regular fa-trash-can"></i>
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecipientList;
|
||||
@@ -0,0 +1,101 @@
|
||||
import React from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function RenderAllPdfPage({
|
||||
pdfUrl,
|
||||
signPdfUrl,
|
||||
allPages,
|
||||
setAllPages,
|
||||
setPageNumber,
|
||||
setSignBtnPosition,
|
||||
pageNumber
|
||||
}) {
|
||||
//set all number of pages after load pdf
|
||||
function onDocumentLoad({ numPages }) {
|
||||
setAllPages(numPages);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="showPages">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
backgroundColor: "white",
|
||||
height: "100%"
|
||||
}}
|
||||
>
|
||||
<div style={{ width: "140px" }}>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor
|
||||
// color:"white"
|
||||
}}
|
||||
className="signedStyle"
|
||||
>
|
||||
Pages
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<RSC
|
||||
id="RSC-Example"
|
||||
style={{
|
||||
width: "135px",
|
||||
height: window.innerHeight - 130 + "px"
|
||||
}}
|
||||
>
|
||||
<Document
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={onDocumentLoad}
|
||||
file={pdfUrl ? pdfUrl : signPdfUrl}
|
||||
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
|
||||
>
|
||||
{Array.from(new Array(allPages), (el, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
width: "100px",
|
||||
border:
|
||||
pageNumber - 1 === index
|
||||
? "2px solid red"
|
||||
: "2px solid #878787",
|
||||
// padding: "5px",
|
||||
margin: "10px",
|
||||
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
contain: "content"
|
||||
}}
|
||||
onClick={() => {
|
||||
setPageNumber(index + 1);
|
||||
if (setSignBtnPosition) {
|
||||
setSignBtnPosition([]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Page
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={100}
|
||||
height={100}
|
||||
scale={1}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Document>
|
||||
</RSC>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RenderAllPdfPage;
|
||||
@@ -0,0 +1,693 @@
|
||||
import React from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
handleImageResize,
|
||||
handleSignYourselfImageResize
|
||||
} from "../../constant/Utils";
|
||||
import Placeholder from "./Placeholder";
|
||||
import Alert from "../../primitives/Alert";
|
||||
|
||||
function RenderPdf({
|
||||
pageNumber,
|
||||
pdfOriginalWidth,
|
||||
pdfNewWidth,
|
||||
drop,
|
||||
signerPos,
|
||||
successEmail,
|
||||
handleTabDrag,
|
||||
handleStop,
|
||||
isDragging,
|
||||
setIsSignPad,
|
||||
setIsStamp,
|
||||
handleDeleteSign,
|
||||
setSignKey,
|
||||
pdfDetails,
|
||||
xyPostion,
|
||||
pdfUrl,
|
||||
numPages,
|
||||
pageDetails,
|
||||
pdfRequest,
|
||||
setCurrentSigner,
|
||||
signerObjectId,
|
||||
signedSigners,
|
||||
setPdfLoadFail,
|
||||
placeholder,
|
||||
pdfLoadFail,
|
||||
setSignerPos,
|
||||
setXyPostion,
|
||||
index,
|
||||
containerWH,
|
||||
setIsResize,
|
||||
handleLinkUser,
|
||||
setUniqueId,
|
||||
signersdata,
|
||||
setIsPageCopy,
|
||||
setSignerObjId,
|
||||
setShowDropdown,
|
||||
setIsInitial,
|
||||
setIsValidate,
|
||||
setWidgetType,
|
||||
setValidateAlert,
|
||||
setIsRadio,
|
||||
setCurrWidgetsDetails,
|
||||
setSelectWidgetId,
|
||||
selectWidgetId,
|
||||
unSignedWidgetId,
|
||||
setIsCheckbox,
|
||||
handleNameModal,
|
||||
setTempSignerId,
|
||||
uniqueId
|
||||
}) {
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const newWidth = containerWH.width;
|
||||
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
|
||||
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
// handle signature block width and height according to screen
|
||||
const posWidth = (pos, signYourself) => {
|
||||
const defaultWidth = defaultWidthHeight(pos.type).width;
|
||||
const posWidth = pos.Width ? pos.Width : defaultWidth;
|
||||
let width;
|
||||
if (signYourself) {
|
||||
width = posWidth;
|
||||
return width;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) / scale;
|
||||
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
width = posWidth;
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) * pos.scale;
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const posHeight = (pos, signYourself) => {
|
||||
let height;
|
||||
const posHeight = pos.Height;
|
||||
const defaultHeight = defaultWidthHeight(pos.type).height;
|
||||
|
||||
if (signYourself) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
|
||||
return height;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) / scale;
|
||||
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) * pos.scale;
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const xPos = (pos, signYourself) => {
|
||||
const resizePos = pos.xPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const yPos = (pos, signYourself) => {
|
||||
const resizePos = pos.yPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//function for render placeholder block over pdf document
|
||||
const checkSignedSignes = (data) => {
|
||||
const checkSign = signedSigners.filter(
|
||||
(sign) => sign.objectId === data.signerObjId
|
||||
);
|
||||
if (data.signerObjId === signerObjectId) {
|
||||
setCurrentSigner(true);
|
||||
}
|
||||
const handleAllUserName = (Id, Role, type) => {
|
||||
return pdfDetails[0].Signers.map((signerData, key) => {
|
||||
return (
|
||||
signerData.objectId === data.signerObjId && (
|
||||
<React.Fragment key={key}>
|
||||
{signerData?.Name && (
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{signerData.Name}
|
||||
</div>
|
||||
)}
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
{Role && (
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
checkSign.length === 0 &&
|
||||
data.placeHolder.map((placeData, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setSignKey={setSignKey}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setIsStamp={setIsStamp}
|
||||
handleSignYourselfImageResize={handleImageResize}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
isShowBorder={false}
|
||||
signerObjId={signerObjectId}
|
||||
isShowDropdown={true}
|
||||
isNeedSign={true}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
handleUserName={handleAllUserName}
|
||||
isDragging={false}
|
||||
pdfDetails={pdfDetails}
|
||||
setIsInitial={setIsInitial}
|
||||
setValidateAlert={setValidateAlert}
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleUserName = (Id, Role, type) => {
|
||||
if (Id) {
|
||||
const checkSign = signersdata.find((sign) => sign.Id === Id);
|
||||
if (checkSign?.Name) {
|
||||
return (
|
||||
<>
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{checkSign?.Name}
|
||||
</div>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{Role}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{Role}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
||||
{isMobile && scale ? (
|
||||
<div
|
||||
style={{
|
||||
border: "0.1px solid #ebe8e8",
|
||||
marginTop: isGuestSigner && "30px"
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
{pdfLoadFail.status &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder // placeholder mobile
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
setPdfLoadFail(true);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
// ref={pdfRef}'
|
||||
onClick={() => {
|
||||
if (setSelectWidgetId) {
|
||||
setSelectWidgetId("");
|
||||
}
|
||||
}}
|
||||
file={
|
||||
pdfUrl
|
||||
? pdfUrl
|
||||
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||
? pdfDetails[0].SignedUrl
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
width={containerWH.width}
|
||||
height={containerWH.height}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<RSC
|
||||
style={{
|
||||
position: "relative",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
width:
|
||||
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
|
||||
height: window.innerHeight - 110 + "px"
|
||||
}}
|
||||
noScrollY={false}
|
||||
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
border: "0.1px solid #ebe8e8",
|
||||
width: pdfOriginalWidth
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
{pdfLoadFail.status &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.placeHolder.map((placeData, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
{placeData.pageNumber === pageNumber &&
|
||||
placeData.pos.map((pos) => {
|
||||
return (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
handleSignYourselfImageResize={
|
||||
handleImageResize
|
||||
}
|
||||
index={pageNumber}
|
||||
xyPostion={signerPos}
|
||||
setXyPostion={setSignerPos}
|
||||
setSignerObjId={setSignerObjId}
|
||||
data={data}
|
||||
setIsResize={setIsResize}
|
||||
setShowDropdown={setShowDropdown}
|
||||
isShowBorder={true}
|
||||
isPlaceholder={true}
|
||||
setUniqueId={setUniqueId}
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
setIsValidate={setIsValidate}
|
||||
setWidgetType={setWidgetType}
|
||||
setIsRadio={setIsRadio}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={(event, dragElement) =>
|
||||
handleStop(event, dragElement, pos.type)
|
||||
}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
);
|
||||
}))}
|
||||
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
setPdfLoadFail(load);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
onClick={() => {
|
||||
if (setSelectWidgetId) {
|
||||
setSelectWidgetId("");
|
||||
}
|
||||
}}
|
||||
// ref={pdfRef}
|
||||
file={
|
||||
pdfUrl
|
||||
? pdfUrl
|
||||
: pdfDetails[0] && pdfDetails[0].SignedUrl
|
||||
? pdfDetails[0].SignedUrl
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
</RSC>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default RenderPdf;
|
||||
@@ -0,0 +1,695 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function SignPad({
|
||||
isSignPad,
|
||||
isStamp,
|
||||
setIsImageSelect,
|
||||
onSaveSign,
|
||||
setIsSignPad,
|
||||
setImage,
|
||||
isImageSelect,
|
||||
imageRef,
|
||||
onImageChange,
|
||||
onSaveImage,
|
||||
image,
|
||||
defaultSign,
|
||||
setSignature,
|
||||
myInitial,
|
||||
isInitial,
|
||||
setIsInitial,
|
||||
setIsStamp,
|
||||
widgetType
|
||||
}) {
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const allColor = ["blue", "red", "black"];
|
||||
const canvasRef = useRef(null);
|
||||
const [isDefaultSign, setIsDefaultSign] = useState(false);
|
||||
const [isTab, setIsTab] = useState("draw");
|
||||
const [isSignImg, setIsSignImg] = useState("");
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
{ value: "Dancing Script" },
|
||||
{ value: "Cedarville Cursive" },
|
||||
{ value: "Delicious Handrawn" }
|
||||
// Add more font options as needed
|
||||
];
|
||||
const [fontSelect, setFontSelect] = useState(fontOptions[0].value);
|
||||
|
||||
const senderUser =
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
) &&
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
|
||||
useEffect(() => {
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setFontSelect("Fasthand");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
if (isTab === "draw") {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.clear();
|
||||
} else if (isStamp) {
|
||||
setImage("");
|
||||
}
|
||||
|
||||
setIsSignImg("");
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
}
|
||||
setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = () => {
|
||||
setSignature(canvasRef.current.toDataURL());
|
||||
setIsSignImg(canvasRef.current.toDataURL());
|
||||
};
|
||||
|
||||
//save button component
|
||||
const SaveBtn = () => {
|
||||
return (
|
||||
<div style={{ marginTop: "2px" }}>
|
||||
{(isTab === "draw" || isTab === "uploadImage") && (
|
||||
<button
|
||||
style={{
|
||||
color: "black",
|
||||
border: "1px solid #ccc"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn saveBtn"
|
||||
onClick={() => handleClear()}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!image) {
|
||||
if (isTab === "mysignature") {
|
||||
setIsSignImg("");
|
||||
if (isInitial) {
|
||||
onSaveSign("initials");
|
||||
} else {
|
||||
onSaveSign("default");
|
||||
}
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(false, textWidth, textHeight);
|
||||
} else {
|
||||
onSaveSign();
|
||||
}
|
||||
}
|
||||
|
||||
setPenColor("blue");
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveImage();
|
||||
}
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
disabled={
|
||||
isSignImg || image || isDefaultSign || textWidth ? false : true
|
||||
}
|
||||
type="button"
|
||||
className={
|
||||
isSignImg || image || isDefaultSign || textWidth
|
||||
? "finishBtn saveBtn"
|
||||
: "disabledFinish saveBtn"
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (canvasRef.current && isSignImg) {
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setFontSelect("Fasthand");
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isSignPad]);
|
||||
useEffect(() => {
|
||||
const loadFont = async () => {
|
||||
try {
|
||||
await document.fonts.load(`20px ${fontSelect}`);
|
||||
const selectFontSTyle = fontOptions.find(
|
||||
(font) => font.value === fontSelect
|
||||
);
|
||||
setFontSelect(selectFontSTyle?.value || fontOptions[0].value);
|
||||
} catch (error) {
|
||||
console.error("Error loading font:", error);
|
||||
}
|
||||
};
|
||||
|
||||
loadFont();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fontSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
// Load the default signature after the component mounts
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
}
|
||||
if (isTab === "type") {
|
||||
const trimmedName = signValue ? signValue.trim() : currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : signValue;
|
||||
setSignValue(userName);
|
||||
convertToImg(fontSelect, userName);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
|
||||
//function for convert input text value in image
|
||||
const convertToImg = async (fontStyle, text, color) => {
|
||||
//get text content to convert in image
|
||||
const textContent = text;
|
||||
const fontfamily = fontStyle
|
||||
? fontStyle
|
||||
: fontSelect
|
||||
? fontSelect
|
||||
: "Fasthand";
|
||||
|
||||
//creating span for getting text content width
|
||||
const span = document.createElement("span");
|
||||
span.textContent = textContent;
|
||||
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
||||
span.style.color = color ? color : penColor;
|
||||
span.style.display = "hidden";
|
||||
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
||||
|
||||
//create canvas to render text in canvas and convert in image
|
||||
const canvasElement = document.createElement("canvas");
|
||||
// Draw the text content on the canvas
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
const width = span.offsetWidth;
|
||||
const height = span.offsetHeight;
|
||||
setTextWidth(width);
|
||||
setTextHeight(height);
|
||||
const font = span.style["font"];
|
||||
// Set the canvas dimensions to match the span
|
||||
canvasElement.width = width * pixelRatio;
|
||||
canvasElement.height = height * pixelRatio;
|
||||
|
||||
// You can customize text styles if needed
|
||||
ctx.font = font;
|
||||
ctx.fillStyle = color ? color : penColor; // Set the text color
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.scale(pixelRatio, pixelRatio);
|
||||
// Draw the content of the span onto the canvas
|
||||
ctx.fillText(span.textContent, width / 2, height / 2); // Adjust the x,y-coordinate as needed
|
||||
//remove span tag
|
||||
document.body.removeChild(span);
|
||||
// Convert the canvas to image data
|
||||
const dataUrl = canvasElement.toDataURL("image/png");
|
||||
setSignature(dataUrl);
|
||||
};
|
||||
|
||||
const PenColorComponent = (props) => {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{allColor.map((data, key) => {
|
||||
return (
|
||||
<i
|
||||
style={{
|
||||
margin: "5px",
|
||||
color: data,
|
||||
borderBottom:
|
||||
key === 0 && penColor === "blue"
|
||||
? "2px solid blue"
|
||||
: key === 1 && penColor === "red"
|
||||
? "2px solid red"
|
||||
: key === 2 && penColor === "black"
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
onClick={() => {
|
||||
props?.convertToImg &&
|
||||
props?.convertToImg(fontSelect, signValue, data);
|
||||
if (key === 0) {
|
||||
setPenColor("blue");
|
||||
} else if (key === 1) {
|
||||
setPenColor("red");
|
||||
} else if (key === 2) {
|
||||
setPenColor("black");
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa solid fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isSignPad && (
|
||||
<div className="modaloverlay">
|
||||
<div className="modalcontainer">
|
||||
<div
|
||||
className="modalheader"
|
||||
style={{ padding: "0 13px", marginTop: "5px" }}
|
||||
>
|
||||
<div className="modaltitle">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
background: "white",
|
||||
marginTop: "3px"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-around",
|
||||
alignItems: "end",
|
||||
gap: 10
|
||||
}}
|
||||
>
|
||||
{isStamp ? (
|
||||
<span style={{ color: themeColor }} className="signTab">
|
||||
{widgetType === "image"
|
||||
? "Upload image"
|
||||
: "Upload stamp image"}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color: isTab === "draw" ? themeColor : "#515252",
|
||||
marginLeft: "2px"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
Draw
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "draw"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
isTab === "uploadImage" ? themeColor : "#515252"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
Upload Image
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "uploadImage"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color: isTab === "type" ? themeColor : "#515252",
|
||||
marginLeft: "2px"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
Type
|
||||
</span>
|
||||
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "type"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
{!isInitial && defaultSign ? (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
isTab === "mysignature"
|
||||
? themeColor
|
||||
: "#515252"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
My Signature
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "mysignature"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
isInitial &&
|
||||
myInitial && (
|
||||
<div>
|
||||
<span
|
||||
onClick={() => {
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
isTab === "mysignature"
|
||||
? themeColor
|
||||
: "#515252"
|
||||
}}
|
||||
className="signTab"
|
||||
>
|
||||
My Initials
|
||||
</span>
|
||||
<div
|
||||
style={{
|
||||
border:
|
||||
isTab === "mysignature"
|
||||
? "1.5px solid #108783"
|
||||
: "1.5px solid #ffffff"
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="closebtn"
|
||||
style={{ color: "black" }}
|
||||
onClick={() => {
|
||||
setPenColor("blue");
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
setIsImageSelect(false);
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
{isDefaultSign ? (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "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"
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "rgb(255, 255, 255)",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
draggable="false"
|
||||
src={isInitial ? myInitial : defaultSign}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
) : isImageSelect || isStamp ? (
|
||||
!image ? (
|
||||
<div style={{ display: "flex", justifyContent: "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/*"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="uploadImg">Upload</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "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"
|
||||
style={{
|
||||
objectFit: "contain",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{ display: "flex", justifyContent: "flex-end" }}
|
||||
>
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<span className="signatureText">
|
||||
{isInitial ? "Initials" : "Signature"}:
|
||||
</span>
|
||||
|
||||
<input
|
||||
maxLength={isInitial ? 3 : 30}
|
||||
style={{ fontFamily: fontSelect, color: penColor }}
|
||||
type="text"
|
||||
className="signatureInput"
|
||||
placeholder="Your signature"
|
||||
value={signValue}
|
||||
onChange={(e) => {
|
||||
setSignValue(e.target.value);
|
||||
convertToImg(fontSelect, e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="fontOptionContainer">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
fontFamily: font.value,
|
||||
backgroundColor:
|
||||
fontSelect === font.value && "rgb(206 225 247)"
|
||||
}}
|
||||
onClick={() => {
|
||||
setFontSelect(font.value);
|
||||
convertToImg(font.value, signValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: "5px 10px 5px 10px",
|
||||
fontSize: "20px",
|
||||
color: penColor
|
||||
}}
|
||||
>
|
||||
{signValue ? signValue : "Your signature"}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "10px"
|
||||
}}
|
||||
>
|
||||
<PenColorComponent convertToImg={convertToImg} />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
className: isInitial
|
||||
? "intialSignatureCanvas"
|
||||
: "signatureCanvas",
|
||||
style: {
|
||||
border: "1.6px solid #007bff",
|
||||
borderRadius: "2px"
|
||||
}
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "10px"
|
||||
}}
|
||||
>
|
||||
<PenColorComponent />
|
||||
<SaveBtn />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignPad;
|
||||
@@ -0,0 +1,64 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import "../../styles/signature.css";
|
||||
function Signedby({ pdfDetails }) {
|
||||
const getFirstLetter = (name) => {
|
||||
const firstLetter = name.charAt(0);
|
||||
return firstLetter;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="signerComponent">
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="signedStyle"
|
||||
>
|
||||
Signed By
|
||||
</div>
|
||||
<div style={{ marginTop: "2px", background: "white" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
padding: "10px",
|
||||
background: "#93a3db"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="signerStyle"
|
||||
style={{
|
||||
background: "#abd1d0",
|
||||
width: 20,
|
||||
height: 20,
|
||||
display: "flex",
|
||||
borderRadius: 30 / 2,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: "20px",
|
||||
marginTop: "5px"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "8px",
|
||||
textAlign: "center",
|
||||
fontWeight: "bold"
|
||||
}}
|
||||
>
|
||||
{getFirstLetter(pdfDetails.ExtUserPtr.Name)}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
|
||||
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Signedby;
|
||||
@@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import RecipientList from "./RecipientList";
|
||||
|
||||
function SignerListPlace(props) {
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
padding: "5px"
|
||||
}}
|
||||
>
|
||||
<span className="signedStyle">
|
||||
{props.title ? props.title : "Recipients"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="signerList">
|
||||
<RecipientList {...props} />
|
||||
</div>
|
||||
{props.handleAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
||||
onClick={() => props.handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignerListPlace;
|
||||
@@ -0,0 +1,431 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import RecipientList from "./RecipientList";
|
||||
import { useDrag } from "react-dnd";
|
||||
import WidgetList from "./WidgetList";
|
||||
import {
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
widgets
|
||||
} from "../../constant/Utils";
|
||||
import { themeColor } from "../../constant/const";
|
||||
function WidgetComponent({
|
||||
dragSignature,
|
||||
signRef,
|
||||
handleDivClick,
|
||||
handleMouseLeave,
|
||||
dragStamp,
|
||||
isSignYourself,
|
||||
addPositionOfSignature,
|
||||
signersdata,
|
||||
isSelectListId,
|
||||
setSignerObjId,
|
||||
setIsSelectId,
|
||||
setContractName,
|
||||
isSigners,
|
||||
dataTut,
|
||||
isMailSend,
|
||||
handleAddSigner,
|
||||
setUniqueId,
|
||||
setRoleName,
|
||||
handleDeleteUser,
|
||||
signerPos,
|
||||
handleRoleChange,
|
||||
handleOnBlur,
|
||||
title,
|
||||
setSignersData,
|
||||
sendInOrder,
|
||||
isTemplateFlow,
|
||||
setBlockColor,
|
||||
blockColor
|
||||
}) {
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 5,
|
||||
text: "dropdown"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDropdown: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, checkbox] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 6,
|
||||
text: "checkbox"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCheck: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, textInput] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 7,
|
||||
text: textInputWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragTextInput: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, initials] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 8,
|
||||
text: "initials"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragInitial: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, name] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 9,
|
||||
text: "name"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragName: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, company] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 10,
|
||||
text: "company"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragCompany: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, jobTitle] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 11,
|
||||
text: "job title"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragJobtitle: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, date] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 12,
|
||||
text: "date"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragDate: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, image] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 13,
|
||||
text: "image"
|
||||
},
|
||||
|
||||
collect: (monitor) => ({
|
||||
isDragImage: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, email] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 14,
|
||||
text: "email"
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragEmail: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, radioButton] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 15,
|
||||
text: radioButtonWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragRadiotton: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const [, text] = useDrag({
|
||||
type: "BOX",
|
||||
|
||||
item: {
|
||||
type: "BOX",
|
||||
id: 16,
|
||||
text: textWidget
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragText: !!monitor.isDragging()
|
||||
})
|
||||
});
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [widget, setWidget] = useState([]);
|
||||
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
"#c0e3bc",
|
||||
"#bce3db",
|
||||
"#b8ccdb",
|
||||
"#ceb8db",
|
||||
"#ffccff",
|
||||
"#99ffcc",
|
||||
"#cc99ff",
|
||||
"#ffcc99",
|
||||
"#66ccff",
|
||||
"#ffffcc"
|
||||
];
|
||||
const handleModal = () => {
|
||||
setIsSignersModal(!isSignersModal);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const widgetRef = [
|
||||
dragSignature,
|
||||
dragStamp,
|
||||
initials,
|
||||
name,
|
||||
jobTitle,
|
||||
company,
|
||||
date,
|
||||
text,
|
||||
textInput,
|
||||
checkbox,
|
||||
dropdown,
|
||||
radioButton,
|
||||
image,
|
||||
email
|
||||
];
|
||||
const getWidgetArray = widgets;
|
||||
const newUpdateSigner = getWidgetArray.map((obj, ind) => {
|
||||
return { ...obj, ref: widgetRef[ind] };
|
||||
});
|
||||
|
||||
setWidget(newUpdateSigner);
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const filterWidgets = widget.filter(
|
||||
(data) =>
|
||||
data.type !== "dropdown" &&
|
||||
data.type !== radioButtonWidget &&
|
||||
data.type !== textInputWidget
|
||||
);
|
||||
const textWidgetData = widget.filter((data) => data.type !== textWidget);
|
||||
const updateWidgets = isSignYourself
|
||||
? filterWidgets
|
||||
: isTemplateFlow
|
||||
? textWidgetData
|
||||
: widget;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
!isMailSend && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
id="navbar"
|
||||
className="stickyfooter"
|
||||
style={{ width: window.innerWidth + "px" }}
|
||||
>
|
||||
{isSigners && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
style={{
|
||||
background: blockColor
|
||||
? blockColor
|
||||
: isSelectListId
|
||||
? color[isSelectListId % color.length]
|
||||
: color[0],
|
||||
padding: "10px 20px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
onClick={() => handleModal()}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "700",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
{title ? title : "Recipient"}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "700",
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
{signersdata[isSelectListId]?.Role && (
|
||||
<div>
|
||||
{signersdata[isSelectListId]?.Name ? (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Name?.length > 12
|
||||
? `${signersdata[isSelectListId].Name.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Name}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Role?.length > 12
|
||||
? `${signersdata[isSelectListId].Role.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Role}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginLeft: 6, fontSize: 16 }}>
|
||||
<i className="fa-solid fa-angle-down"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{handleAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
style={{
|
||||
margin: "5px 0 5px 0",
|
||||
backgroundColor: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
||||
onClick={() => handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
style={{
|
||||
background: "white",
|
||||
borderTop: `2px solid ${themeColor}`
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
overflowX: "scroll",
|
||||
whiteSpace: "nowrap",
|
||||
padding: "10px 5px 10px 1px"
|
||||
}}
|
||||
>
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
signRef={signRef}
|
||||
marginLeft={5}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div data-tut={dataTut} className="signerComponent">
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
padding: "5px"
|
||||
}}
|
||||
>
|
||||
<span className="signedStyle">Fields</span>
|
||||
</div>
|
||||
|
||||
<div className="signLayoutContainer1">
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
signRef={signRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isSignersModal && (
|
||||
<ModalUi
|
||||
title={"Roles"}
|
||||
isOpen={isSignersModal}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
{signersdata.length > 0 ? (
|
||||
<div style={{ maxHeight: "600px", overflow: "auto" }}>
|
||||
<RecipientList
|
||||
signerPos={signerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
setSignerObjId={setSignerObjId}
|
||||
setIsSelectId={setIsSelectId}
|
||||
setContractName={setContractName}
|
||||
setUniqueId={setUniqueId}
|
||||
setRoleName={setRoleName}
|
||||
handleDeleteUser={handleDeleteUser}
|
||||
handleRoleChange={handleRoleChange}
|
||||
handleOnBlur={handleOnBlur}
|
||||
handleModal={handleModal}
|
||||
sendInOrder={sendInOrder}
|
||||
setSignersData={setSignersData}
|
||||
setBlockColor={setBlockColor}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
padding: 20,
|
||||
fontSize: 15,
|
||||
fontWeight: "500",
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
Please add a role
|
||||
</div>
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default WidgetComponent;
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import { getWidgetType } from "../../constant/Utils";
|
||||
|
||||
function WidgetList(props) {
|
||||
return props.updateWidgets.map((item, ind) => {
|
||||
return (
|
||||
<div key={ind} style={{ marginBottom: "5px" }}>
|
||||
<div
|
||||
className="widgets"
|
||||
onClick={() => {
|
||||
props.addPositionOfSignature &&
|
||||
props.addPositionOfSignature("onclick", item);
|
||||
}}
|
||||
ref={(element) => {
|
||||
if (!props.isMobile) {
|
||||
item.ref(element);
|
||||
if (element) {
|
||||
if (props?.signRef) {
|
||||
props.signRef.current = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
onMouseMove={props?.handleDivClick}
|
||||
onMouseDown={() => {
|
||||
props?.handleMouseLeave();
|
||||
}}
|
||||
>
|
||||
{item.ref && getWidgetType(item, props?.marginLeft)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default WidgetList;
|
||||
@@ -0,0 +1,287 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
import { textInputWidget } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
status: "required",
|
||||
hint: "",
|
||||
textvalidate: "text"
|
||||
});
|
||||
const [isValid, setIsValid] = useState(true);
|
||||
const statusArr = ["Required", "Optional"];
|
||||
const inputOpt = ["text", "email", "number"];
|
||||
|
||||
useEffect(() => {
|
||||
if (props.defaultdata) {
|
||||
setFormdata({
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || "",
|
||||
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||
status: props.defaultdata?.options?.status || "required",
|
||||
hint: props.defaultdata?.options?.hint || "",
|
||||
textvalidate:
|
||||
props.defaultdata?.options?.validation?.type === "regex"
|
||||
? props.defaultdata?.options?.validation?.pattern
|
||||
: props.defaultdata?.options?.validation?.type || ""
|
||||
});
|
||||
} else {
|
||||
setFormdata({
|
||||
...formdata,
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || ""
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.defaultdata, props.widgetName]);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
props.handleData(formdata);
|
||||
setFormdata({
|
||||
name: "",
|
||||
defaultValue: "",
|
||||
status: "required",
|
||||
hint: "",
|
||||
textvalidate: "text"
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
const handledefaultChange = (e) => {
|
||||
if (formdata.textvalidate) {
|
||||
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
|
||||
const isValidate = regexObject?.test(e.target.value);
|
||||
setIsValid(isValidate);
|
||||
} else {
|
||||
setIsValid(true);
|
||||
}
|
||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
function handleValidation(type) {
|
||||
switch (type) {
|
||||
case "email":
|
||||
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
|
||||
case "number":
|
||||
return "/^\\d+$/";
|
||||
case "text":
|
||||
return "/^[a-zA-Zs]+$/";
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
function handleBlurRegex() {
|
||||
if (!formdata.textvalidate) {
|
||||
setFormdata({ ...formdata, textvalidate: "text" });
|
||||
} else {
|
||||
if (formdata.defaultValue) {
|
||||
const regexObject = RegexParser(
|
||||
handleValidation(formdata.textvalidate)
|
||||
);
|
||||
const isValidate = regexObject?.test(formdata.defaultValue);
|
||||
if (isValidate === false) {
|
||||
setFormdata({ ...formdata, defaultValue: "" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ModalUi
|
||||
isOpen={props.isOpen}
|
||||
handleClose={props.handleClose && props.handleClose}
|
||||
title={"Widget info"}
|
||||
>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget) && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget
|
||||
? "pt-0"
|
||||
: ""
|
||||
} p-[20px]`}
|
||||
>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Name
|
||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget) && (
|
||||
<>
|
||||
<div className="form-section">
|
||||
<label htmlFor="textvalidate" style={{ fontSize: 13 }}>
|
||||
Validation
|
||||
</label>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginBottom: "0.5rem"
|
||||
}}
|
||||
>
|
||||
<div style={{ width: "100%", position: "relative" }}>
|
||||
<input
|
||||
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
|
||||
name="textvalidate"
|
||||
placeholder="Enter custom expression"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
// onBlur={() => handleBlurRegex()}
|
||||
/>
|
||||
<select
|
||||
className="validationlist addUserInput"
|
||||
name="textvalidate"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onBlur={() => handleBlurRegex()}
|
||||
>
|
||||
<option disabled style={{ fontSize: "13px" }}>
|
||||
Select...
|
||||
</option>
|
||||
{inputOpt.map((data, ind) => {
|
||||
return (
|
||||
<option
|
||||
style={{ fontSize: "13px" }}
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Default value
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="defaultValue"
|
||||
value={formdata.defaultValue}
|
||||
onChange={(e) => handledefaultChange(e)}
|
||||
autoComplete="off"
|
||||
onBlur={() => {
|
||||
if (isValid === false) {
|
||||
setFormdata({ ...formdata, defaultValue: "" });
|
||||
setIsValid(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isValid === false && (
|
||||
<div className="warning defaultvalueWarning" style={{ fontSize: 12 }}>
|
||||
<i
|
||||
className="fas fa-exclamation-circle"
|
||||
style={{ color: "#fab005", fontSize: 15 }}
|
||||
></i>{" "}
|
||||
invalid default value
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="form-section">
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 10,
|
||||
marginBottom: "0.5rem"
|
||||
}}
|
||||
>
|
||||
{statusArr.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: 5,
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<input
|
||||
style={{ accentColor: "red", marginRight: "10px" }}
|
||||
type="radio"
|
||||
name="status"
|
||||
onChange={() =>
|
||||
setFormdata({ ...formdata, status: data.toLowerCase() })
|
||||
}
|
||||
checked={
|
||||
formdata.status.toLowerCase() === data.toLowerCase()
|
||||
}
|
||||
/>
|
||||
<div style={{ fontSize: "13px", fontWeight: "500" }}>
|
||||
{data}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props?.widgetName === textInputWidget) && (
|
||||
<div className="form-section">
|
||||
<label htmlFor="hint" style={{ fontSize: 13 }}>
|
||||
Hint
|
||||
</label>
|
||||
<input
|
||||
className="addUserInput"
|
||||
name="hint"
|
||||
value={formdata.hint}
|
||||
onChange={(e) => handleChange(e)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
height: 1,
|
||||
backgroundColor: "#b7b3b3",
|
||||
width: "100%",
|
||||
marginBottom: "16px"
|
||||
}}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
color: "white",
|
||||
padding: "5px 20px",
|
||||
backgroundColor: "#32a3ac"
|
||||
}}
|
||||
type="submit"
|
||||
className="finishBtn"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
);
|
||||
};
|
||||
|
||||
export default WidgetNameModal;
|
||||
@@ -0,0 +1,163 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Alert from "../../../primitives/Alert";
|
||||
|
||||
const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: parentFolderId
|
||||
};
|
||||
const [name, setName] = useState("");
|
||||
const [folderList, setFolderList] = useState([]);
|
||||
const [isAlert, setIsAlert] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [selectedParent, setSelectedParent] = useState();
|
||||
const [alert, setAlert] = useState({ type: "info", message: "" });
|
||||
useEffect(() => {
|
||||
fetchFolder();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const fetchFolder = async () => {
|
||||
try {
|
||||
const FolderQuery = new Parse.Query(folderCls);
|
||||
if (parentFolderId) {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
if (res) {
|
||||
const result = JSON.parse(JSON.stringify(res));
|
||||
if (result) {
|
||||
setFolderList(result);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Err ", error);
|
||||
}
|
||||
};
|
||||
const handleCreateFolder = async (event) => {
|
||||
event.preventDefault();
|
||||
handleLoader(true);
|
||||
if (name) {
|
||||
const currentUser = Parse.User.current();
|
||||
const exsitQuery = new Parse.Query(folderCls);
|
||||
exsitQuery.equalTo("Name", name);
|
||||
exsitQuery.equalTo("Type", "Folder");
|
||||
if (parentFolderId) {
|
||||
exsitQuery.equalTo("Folder", folderPtr);
|
||||
}
|
||||
const templExist = await exsitQuery.first();
|
||||
if (templExist) {
|
||||
setAlert({ type: "danger", message: "Folder already exist!" });
|
||||
setIsAlert(true);
|
||||
setTimeout(() => {
|
||||
setIsAlert(false);
|
||||
}, 1000);
|
||||
} else {
|
||||
const template = new Parse.Object(folderCls);
|
||||
template.set("Name", name);
|
||||
template.set("Type", "Folder");
|
||||
|
||||
if (selectedParent) {
|
||||
template.set("Folder", {
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: selectedParent
|
||||
});
|
||||
} else if (parentFolderId) {
|
||||
template.set("Folder", folderPtr);
|
||||
}
|
||||
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||
const res = await template.save();
|
||||
if (res) {
|
||||
handleLoader(false);
|
||||
setAlert({
|
||||
type: "success",
|
||||
message: "Folder created successfully!"
|
||||
});
|
||||
setIsAlert(true);
|
||||
setTimeout(() => {
|
||||
setIsAlert(false);
|
||||
}, 1000);
|
||||
if (onSuccess) {
|
||||
onSuccess(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handleLoader(false);
|
||||
setAlert({ type: "info", message: "Please fill folder name" });
|
||||
setIsAlert(true);
|
||||
setTimeout(() => {
|
||||
setIsAlert(false);
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
const handleOptions = (e) => {
|
||||
setSelectedParent(e.target.value);
|
||||
};
|
||||
const handleLoader = (status) => {
|
||||
setIsLoader(status);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{isAlert && <Alert type={alert.type}>{alert.message}</Alert>}
|
||||
<div id="createFolder" className="relative">
|
||||
{isLoader && (
|
||||
<div className="absolute h-full w-full flex justify-center items-center ">
|
||||
<div
|
||||
style={{ fontSize: "45px", color: "#3dd3e0" }}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-base font-semibold">Create Folder</h1>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Name<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Parent Folder</label>
|
||||
<select
|
||||
value={selectedParent}
|
||||
onChange={handleOptions}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
>
|
||||
<option>select</option>
|
||||
{folderList.length > 0 &&
|
||||
folderList.map((x) => (
|
||||
<option key={x.objectId} value={x.objectId}>
|
||||
{x.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
onClick={handleCreateFolder}
|
||||
disabled={isLoader}
|
||||
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
|
||||
>
|
||||
<i className="fa-solid fa-plus mr-1"></i>
|
||||
<span>Create</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateFolder;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { useDropScript } from "../../hook/useScript";
|
||||
import { useDropScript } from "../../../hook/useScript";
|
||||
|
||||
const DROPBOX_APP_KEY = process.env.REACT_APP_DROPBOX_API_KEY; // App key
|
||||
const DROPBOX_SDK_URL = "https://www.dropbox.com/static/api/2/dropins.js";
|
||||
@@ -41,11 +41,15 @@ export default function DropboxChooser({ children, onSuccess, onCancel }) {
|
||||
[onSuccess, onCancel]
|
||||
);
|
||||
|
||||
const handleChoose = useCallback(() => {
|
||||
if (window.Dropbox) {
|
||||
window.Dropbox.choose(options);
|
||||
}
|
||||
}, [options]);
|
||||
const handleChoose = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
if (window.Dropbox) {
|
||||
window.Dropbox.choose(options);
|
||||
}
|
||||
},
|
||||
[options]
|
||||
);
|
||||
|
||||
return (
|
||||
<div onClick={handleChoose}>
|
||||
@@ -0,0 +1,234 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import CreateFolder from "./CreateFolder";
|
||||
import ModalUi from "../../../primitives/ModalUi";
|
||||
|
||||
const FolderModal = (props) => {
|
||||
const [clickFolder, setClickFolder] = useState("");
|
||||
const [folderList, setFolderList] = useState([]);
|
||||
const [tabList, setTabList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isAdd, setIsAdd] = useState(false);
|
||||
// below useEffect is called when user open popup
|
||||
useEffect(() => {
|
||||
if (props.isOpenModal) {
|
||||
fetchFolder();
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.isOpenModal]);
|
||||
|
||||
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
|
||||
const fetchFolder = async (folderPtr) => {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const FolderQuery = new Parse.Query(props.folderCls);
|
||||
if (folderPtr) {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
if (res) {
|
||||
const result = JSON.parse(JSON.stringify(res));
|
||||
if (result) {
|
||||
setFolderList(result);
|
||||
setIsLoader(false);
|
||||
}
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
|
||||
const handleSelect = (item) => {
|
||||
setFolderList([]);
|
||||
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
|
||||
if (tabList.length > 0) {
|
||||
const tab = tabList.some((x) => x.objectId === item.objectId);
|
||||
if (!tab) {
|
||||
setTabList((tabs) => [...tabs, item]);
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: props.folderCls,
|
||||
objectId: item.objectId
|
||||
};
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
} else {
|
||||
setTabList((tabs) => [...tabs, item]);
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: props.folderCls,
|
||||
objectId: item.objectId
|
||||
};
|
||||
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSubmit` is used to pass folderPtr to parent component
|
||||
const handleSubmit = () => {
|
||||
let url = "OpenSign™ Drive";
|
||||
tabList.forEach((t) => {
|
||||
url = url + " / " + t.Name;
|
||||
});
|
||||
if (props.onSuccess) {
|
||||
props.onSuccess(clickFolder);
|
||||
}
|
||||
// SetIsOpen(false);
|
||||
props.setIsOpenMoveModal(false);
|
||||
};
|
||||
|
||||
// `handleCancel` is used to clear list of folder, close popup and folderUrl
|
||||
const handleCancel = () => {
|
||||
// SetIsOpen(false);
|
||||
props.setIsOpenMoveModal(false);
|
||||
setClickFolder({});
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
};
|
||||
|
||||
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||
const removeTabListItem = async (e, i) => {
|
||||
e.preventDefault();
|
||||
// setEditable(false);
|
||||
if (!isAdd) {
|
||||
setIsLoader(true);
|
||||
let folderPtr;
|
||||
if (i) {
|
||||
setFolderList([]);
|
||||
let list = tabList.filter((itm, j) => {
|
||||
if (j <= i) {
|
||||
return itm;
|
||||
}
|
||||
});
|
||||
let _len = list.length - 1;
|
||||
folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: props.folderCls,
|
||||
objectId: list[_len].objectId
|
||||
};
|
||||
setTabList(list);
|
||||
} else {
|
||||
setClickFolder({});
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
}
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
};
|
||||
// `handleCreate` is used to open folder creation form in popup
|
||||
const handleCreate = () => {
|
||||
setIsAdd(!isAdd);
|
||||
};
|
||||
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||
const handleAddFolder = (newFolder) => {
|
||||
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
|
||||
if (clickFolder && clickFolder.ObjectId) {
|
||||
fetchFolder({
|
||||
__type: "Pointer",
|
||||
className: props.folderCls,
|
||||
objectId: clickFolder.ObjectId
|
||||
});
|
||||
} else {
|
||||
fetchFolder();
|
||||
}
|
||||
handleCreate();
|
||||
};
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<ModalUi
|
||||
title={"Select Folder"}
|
||||
isOpen={props.isOpenModal}
|
||||
handleClose={handleCancel}
|
||||
>
|
||||
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title="OpenSign™ Drive"
|
||||
onClick={(e) => removeTabListItem(e)}
|
||||
>
|
||||
OpenSign™ Drive /{" "}
|
||||
</span>
|
||||
{tabList &&
|
||||
tabList.map((tab, i) => (
|
||||
<React.Fragment key={`${tab.objectId}-${i}`}>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title={tab.Name}
|
||||
onClick={(e) => removeTabListItem(e, i)}
|
||||
>
|
||||
{tab.Name}
|
||||
</span>
|
||||
{" / "}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<hr />
|
||||
</div>
|
||||
<div className="mt-2 mb-3">
|
||||
<div className="max-h-[210px] overflow-auto">
|
||||
{!isAdd &&
|
||||
folderList.length > 0 &&
|
||||
folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.Name}
|
||||
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||
onClick={() => handleSelect(folder)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isAdd && (
|
||||
<CreateFolder
|
||||
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||
folderCls={props.folderCls}
|
||||
onSuccess={handleAddFolder}
|
||||
/>
|
||||
)}
|
||||
{isLoader && (
|
||||
<div className="flex justify-center">
|
||||
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||
<div
|
||||
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||
title="Save Here"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{isAdd ? (
|
||||
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||
) : (
|
||||
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="text-[30px] cursor-pointer"
|
||||
title="Save Here"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<i className="fas fa-save" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FolderModal;
|
||||
@@ -0,0 +1,296 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import CreateFolder from "./CreateFolder";
|
||||
import ModalUi from "../../../primitives/ModalUi";
|
||||
import Tooltip from "../../../primitives/Tooltip";
|
||||
|
||||
const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
const [isOpen, SetIsOpen] = useState(false);
|
||||
const [clickFolder, setClickFolder] = useState("");
|
||||
const [selectFolder, setSelectedFolder] = useState({});
|
||||
const [folderList, setFolderList] = useState([]);
|
||||
const [tabList, setTabList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [folderPath, setFolderPath] = useState("");
|
||||
const [isAdd, setIsAdd] = useState(false);
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setIsAdd(false);
|
||||
setClickFolder({});
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
fetchFolder();
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [isOpen]);
|
||||
// below useEffect is used to reset folder selection if user pass isReset = true from parent component
|
||||
useEffect(() => {
|
||||
if (isReset == true) {
|
||||
handleReset();
|
||||
}
|
||||
}, [isReset]);
|
||||
const handleReset = () => {
|
||||
setFolderPath({});
|
||||
setSelectedFolder("");
|
||||
setClickFolder("");
|
||||
setIsAdd(false);
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
};
|
||||
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
|
||||
const fetchFolder = async (folderPtr) => {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const FolderQuery = new Parse.Query(folderCls);
|
||||
if (folderPtr) {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
if (res) {
|
||||
const result = JSON.parse(JSON.stringify(res));
|
||||
if (result) {
|
||||
setFolderList(result);
|
||||
setIsLoader(false);
|
||||
}
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
|
||||
const handleSelect = (item) => {
|
||||
setFolderList([]);
|
||||
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
|
||||
if (tabList.length > 0) {
|
||||
const tab = tabList.some((x) => x.objectId === item.objectId);
|
||||
if (!tab) {
|
||||
setTabList((tabs) => [...tabs, item]);
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: item.objectId
|
||||
};
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
} else {
|
||||
setTabList((tabs) => [...tabs, item]);
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: item.objectId
|
||||
};
|
||||
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
};
|
||||
|
||||
// `handleSubmit` is used to pass folderPtr to parent component
|
||||
const handleSubmit = () => {
|
||||
let url = "OpenSign™ Drive";
|
||||
tabList.forEach((t) => {
|
||||
url = url + " / " + t.Name;
|
||||
});
|
||||
setFolderPath(url);
|
||||
setSelectedFolder(clickFolder);
|
||||
if (onSuccess) {
|
||||
onSuccess(clickFolder);
|
||||
}
|
||||
SetIsOpen(false);
|
||||
};
|
||||
|
||||
// `handleCancel` is used to clear list of folder, close popup and folderUrl
|
||||
const handleCancel = () => {
|
||||
SetIsOpen(false);
|
||||
setClickFolder({});
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
};
|
||||
|
||||
// `handleCancel` is call when user click on folder name from path/tab in popup
|
||||
const removeTabListItem = async (e, i) => {
|
||||
e.preventDefault();
|
||||
// setEditable(false);
|
||||
if (!isAdd) {
|
||||
setIsLoader(true);
|
||||
let folderPtr;
|
||||
if (i) {
|
||||
setFolderList([]);
|
||||
let list = tabList.filter((itm, j) => {
|
||||
if (j <= i) {
|
||||
return itm;
|
||||
}
|
||||
});
|
||||
let _len = list.length - 1;
|
||||
folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: list[_len].objectId
|
||||
};
|
||||
setTabList(list);
|
||||
} else {
|
||||
setClickFolder({});
|
||||
setSelectedFolder({});
|
||||
setFolderList([]);
|
||||
setTabList([]);
|
||||
}
|
||||
fetchFolder(folderPtr);
|
||||
}
|
||||
};
|
||||
// `handleCreate` is used to open folder creation form in popup
|
||||
const handleCreate = () => {
|
||||
setIsAdd(!isAdd);
|
||||
};
|
||||
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
|
||||
const handleAddFolder = () => {
|
||||
setFolderList([]);
|
||||
if (clickFolder && clickFolder.ObjectId) {
|
||||
fetchFolder({
|
||||
__type: "Pointer",
|
||||
className: folderCls,
|
||||
objectId: clickFolder.ObjectId
|
||||
});
|
||||
} else {
|
||||
fetchFolder();
|
||||
}
|
||||
handleCreate();
|
||||
};
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<div>
|
||||
<label className="block">
|
||||
Select Folder
|
||||
{required && <span className="text-red-500 text-[13px]">*</span>}
|
||||
</label>
|
||||
</div>
|
||||
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
|
||||
<div>
|
||||
<i
|
||||
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||
style={{ fontSize: "40px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
<div className="font-semibold ">
|
||||
<div className="flex items-center gap-2">
|
||||
<p>
|
||||
{selectFolder && selectFolder.Name
|
||||
? selectFolder.Name
|
||||
: "OpenSign™ Drive"}
|
||||
</p>
|
||||
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
|
||||
<i
|
||||
className="fa fa-pencil"
|
||||
title="Select Folder"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-[10px] text-gray-400">
|
||||
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute top-1 right-1 cursor-pointer">
|
||||
<Tooltip
|
||||
message={
|
||||
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ModalUi
|
||||
title={"Select Folder"}
|
||||
isOpen={isOpen}
|
||||
handleClose={handleCancel}
|
||||
>
|
||||
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title="OpenSign™ Drive"
|
||||
onClick={(e) => removeTabListItem(e)}
|
||||
>
|
||||
OpenSign™ Drive /{" "}
|
||||
</span>
|
||||
{tabList &&
|
||||
tabList.map((tab, i) => (
|
||||
<React.Fragment key={`${tab.objectId}-${i}`}>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title={tab.Name}
|
||||
onClick={(e) => removeTabListItem(e, i)}
|
||||
>
|
||||
{tab.Name}
|
||||
</span>
|
||||
{" / "}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<hr />
|
||||
</div>
|
||||
<div className="mt-2 mb-3">
|
||||
<div className="max-h-[210px] overflow-auto">
|
||||
{!isAdd &&
|
||||
folderList.length > 0 &&
|
||||
folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.Name}
|
||||
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||
onClick={() => handleSelect(folder)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{isAdd && (
|
||||
<CreateFolder
|
||||
parentFolderId={clickFolder && clickFolder.ObjectId}
|
||||
folderCls={folderCls}
|
||||
onSuccess={handleAddFolder}
|
||||
/>
|
||||
)}
|
||||
{isLoader && (
|
||||
<div className="flex justify-center">
|
||||
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||
<div
|
||||
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||
title="Save Here"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{isAdd ? (
|
||||
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||
) : (
|
||||
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="text-[30px] cursor-pointer"
|
||||
title="Save Here"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<i className="fas fa-save" aria-hidden="true"></i>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectFolder;
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import "../../../styles/AddUser.css";
|
||||
import AsyncSelect from "react-select/async";
|
||||
|
||||
const customStyles = {
|
||||
control: (provided) => ({
|
||||
...provided,
|
||||
fontSize: "13px" // Font size for the control
|
||||
}),
|
||||
option: (provided) => ({
|
||||
...provided,
|
||||
fontSize: "13px" // Font size for the options
|
||||
})
|
||||
};
|
||||
const SelectSigners = (props) => {
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [selected, setSelected] = useState();
|
||||
const [userData, setUserData] = useState({});
|
||||
const [isError, setIsError] = useState(false);
|
||||
// `handleOptions` is used to set just save from quick form to selected option in dropdown
|
||||
const handleOptions = (item) => {
|
||||
setSelected(item);
|
||||
const userData = userList.find((x) => x.objectId === item.value);
|
||||
if (userData) {
|
||||
setUserData(userData);
|
||||
}
|
||||
};
|
||||
const handleAdd = () => {
|
||||
if (userData && userData.objectId) {
|
||||
props.details(userData);
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
} else {
|
||||
setIsError(true);
|
||||
setTimeout(() => setIsError(false), 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const loadOptions = async (inputValue) => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
if (inputValue.length > 1) {
|
||||
contactbook.matches("Name", new RegExp(inputValue, "i"));
|
||||
}
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
// console.log("userList ", res);
|
||||
setUserList(res);
|
||||
return await res.map((item) => ({
|
||||
label: item.Email,
|
||||
value: item.objectId
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="addusercontainer">
|
||||
<div className="form-wrapper">
|
||||
<div className="form-section" style={{ marginBottom: 0 }}>
|
||||
<label style={{ fontSize: 14, fontWeight: "700" }}>Choose User</label>
|
||||
<AsyncSelect
|
||||
cacheOptions
|
||||
defaultOptions
|
||||
value={selected}
|
||||
loadingMessage={() => "Loading..."}
|
||||
noOptionsMessage={() => "User not Found"}
|
||||
loadOptions={loadOptions}
|
||||
onChange={handleOptions}
|
||||
styles={customStyles}
|
||||
/>
|
||||
</div>
|
||||
{isError ? (
|
||||
<p style={{ color: "red", fontSize: "12px", margin: "5px" }}>
|
||||
Please select signer
|
||||
</p>
|
||||
) : (
|
||||
<p style={{ color: "transparent", fontSize: "12px", margin: "5px" }}>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
<button className="submitbutton" onClick={() => handleAdd()}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectSigners;
|
||||
@@ -0,0 +1,201 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Select from "react-select";
|
||||
import AddSigner from "../../AddSigner";
|
||||
import Modal from "react-modal";
|
||||
import Parse from "parse";
|
||||
import Tooltip from "../../../primitives/Tooltip";
|
||||
function arrayMove(array, from, to) {
|
||||
array = array.slice();
|
||||
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* react-sortable-hoc is depcreated not usable from react 18.x.x
|
||||
* need to replace it with @dnd-kit
|
||||
* code changes required
|
||||
*/
|
||||
|
||||
const SignersInput = (props) => {
|
||||
Modal.setAppElement("body");
|
||||
const [state, setState] = useState(undefined);
|
||||
// const [editFormData, setEditFormData] = useState([]);
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [isModal, setIsModel] = useState(false);
|
||||
const onChange = (selectedOptions) => setSelected(selectedOptions);
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||
|
||||
const onSortEnd = ({ oldIndex, newIndex }) => {
|
||||
const newValue = arrayMove(selected, oldIndex, newIndex);
|
||||
setSelected(newValue);
|
||||
};
|
||||
|
||||
const GetSelectListData = async () => {
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||
contactbook.equalTo(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
contactbook.notEqualTo("IsDeleted", true);
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
let list = [];
|
||||
|
||||
// let _selected = [];
|
||||
res.forEach((x) => {
|
||||
let obj = {
|
||||
label: x.Name,
|
||||
value: x.objectId,
|
||||
isChecked: true
|
||||
};
|
||||
|
||||
list.push(obj);
|
||||
});
|
||||
setState(list);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
GetSelectListData();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (props.isReset && props.isReset === true) {
|
||||
setSelected([]);
|
||||
}
|
||||
}, [props.isReset]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selected && selected.length) {
|
||||
let newData = [];
|
||||
selected.forEach((x) => {
|
||||
newData.push(x.value);
|
||||
});
|
||||
if (props.onChange) {
|
||||
props.onChange(newData);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [selected]);
|
||||
|
||||
const handleModalCloseClick = () => {
|
||||
setIsModel(false);
|
||||
setModalIsOpen(false);
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
setModalIsOpen(true);
|
||||
};
|
||||
|
||||
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
|
||||
const handleNewDetails = (data) => {
|
||||
setState([...state, data]);
|
||||
if (selected.length > 0) {
|
||||
setSelected([...selected, data]);
|
||||
} else {
|
||||
setSelected([data]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<label className="block">
|
||||
Signers
|
||||
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
||||
<span className="absolute ml-1 text-xs z-50">
|
||||
<Tooltip
|
||||
id={"signer-tooltip"}
|
||||
message={"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<div style={{ display: "flex", gap: 5 }}>
|
||||
<div style={{ flexWrap: "wrap", width: "100%" }}>
|
||||
<Select
|
||||
onSortEnd={onSortEnd}
|
||||
distance={4}
|
||||
isMulti
|
||||
options={state || []}
|
||||
value={selected}
|
||||
onChange={onChange}
|
||||
closeMenuOnSelect={false}
|
||||
required={props.required}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsModel(true);
|
||||
openModal();
|
||||
}}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
borderRadius: 4,
|
||||
border: "1px solid #ccc",
|
||||
minHeight: 38,
|
||||
minWidth: 48,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
}}
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
</div>
|
||||
<Modal
|
||||
isOpen={modalIsOpen}
|
||||
onRequestClose={handleModalCloseClick}
|
||||
shouldCloseOnOverlayClick={false}
|
||||
id="modal1"
|
||||
contentLabel="Modal"
|
||||
style={{
|
||||
content: {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
right: "auto",
|
||||
bottom: "auto",
|
||||
transform: "translate(-50%, -50%)",
|
||||
border: "1px transparent",
|
||||
padding: 0
|
||||
},
|
||||
overlay: {
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.75)",
|
||||
zIndex: 50
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="min-w-full md:min-w-[500px]">
|
||||
<div
|
||||
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
|
||||
style={{ background: "#32a3ac" }}
|
||||
>
|
||||
<div className="text-[1.2rem] font-normal">Add Signer</div>
|
||||
<button
|
||||
onClick={handleModalCloseClick}
|
||||
className="text-[1.5rem] cursor-pointer focus:outline-none"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isModal && (
|
||||
<AddSigner
|
||||
valueKey={"objectId"}
|
||||
displayKey={"Name"}
|
||||
details={handleNewDetails}
|
||||
closePopup={handleModalCloseClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignersInput;
|
||||
@@ -2,20 +2,24 @@ import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
|
||||
const Menu = ({ item, isOpen, closeSidebar }) => {
|
||||
return (
|
||||
<li key={item.title} role="none">
|
||||
<NavLink
|
||||
to={`/${item.pageType}/${item.objectId}`}
|
||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4"
|
||||
onClick={closeSidebar}
|
||||
tabIndex={isOpen ? 0 : -1}
|
||||
role="menuitem"
|
||||
>
|
||||
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<span className="ml-3 lg:ml-4">{item.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<li key={item.title} role="none">
|
||||
<NavLink
|
||||
to={
|
||||
item.pageType
|
||||
? `/${item.pageType}/${item.objectId}`
|
||||
: `/${item.objectId}`
|
||||
}
|
||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer focus:text-[#0056b3] focus:bg-[#eef1f5]"
|
||||
onClick={closeSidebar}
|
||||
tabIndex={isOpen ? 0 : -1}
|
||||
role="menuitem"
|
||||
>
|
||||
<i className={`${item.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<span className="ml-3 lg:ml-4">{item.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
export default Menu;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Menu from './Menu';
|
||||
import Menu from "./Menu";
|
||||
import Submenu from "./SubMenu";
|
||||
import SocialMedia from './SocialMedia';
|
||||
import SocialMedia from "./SocialMedia";
|
||||
|
||||
import Parse from "parse";
|
||||
import dp from "../../assets/images/dp.png";
|
||||
@@ -20,16 +20,10 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
}, []);
|
||||
|
||||
const menuItem = async () => {
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
|
||||
try {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
var sideMenu = Parse.Object.extend("w_menu");
|
||||
var query = new Parse.Query(sideMenu);
|
||||
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
|
||||
|
||||
const results = await query.first();
|
||||
const resultjson = results.toJSON();
|
||||
let result = resultjson;
|
||||
@@ -37,7 +31,6 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -47,7 +40,11 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
>
|
||||
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
||||
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
|
||||
<img className="w-full h-full object-cover" src={image} alt="Profile" />
|
||||
<img
|
||||
className="w-full h-full object-cover"
|
||||
src={image}
|
||||
alt="Profile"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[14px] font-bold">{username}</p>
|
||||
@@ -57,14 +54,27 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
</div>
|
||||
</div>
|
||||
<nav aria-label="OpenSign Sidebar Navigation">
|
||||
<ul className="text-sm" role="menubar" aria-label="OpenSign Sidebar Navigation">
|
||||
{menuList.map((item) => (
|
||||
<ul
|
||||
className="text-sm"
|
||||
role="menubar"
|
||||
aria-label="OpenSign Sidebar Navigation"
|
||||
>
|
||||
{menuList.map((item) =>
|
||||
!item.children ? (
|
||||
<Menu key={item.title} item={item} isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||
<Menu
|
||||
key={item.title}
|
||||
item={item}
|
||||
isOpen={isOpen}
|
||||
closeSidebar={closeSidebar}
|
||||
/>
|
||||
) : (
|
||||
<Submenu key={item.title} item={item} closeSidebar={closeSidebar} />
|
||||
<Submenu
|
||||
key={item.title}
|
||||
item={item}
|
||||
closeSidebar={closeSidebar}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
<footer className="mt-4 flex justify-center items-center text-[25px] text-black gap-3">
|
||||
|
||||
@@ -13,7 +13,7 @@ const Submenu = ({ item, closeSidebar }) => {
|
||||
<li role="none">
|
||||
<button
|
||||
onClick={toggleSubmenu}
|
||||
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:ring-2 focus:ring-blue-600"
|
||||
className="w-full flex items-center text-left text-[#444] p-3 lg:p-4 hover:bg-[#eef1f5] focus:outline-none focus:text-[#0056b3] focus:bg-[#eef1f5]"
|
||||
aria-expanded={submenuOpen}
|
||||
aria-haspopup="true"
|
||||
aria-controls={`submenu-${title}`}
|
||||
@@ -22,8 +22,9 @@ const Submenu = ({ item, closeSidebar }) => {
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<span className="ml-3 lg:ml-4">{title}</span>
|
||||
<i
|
||||
className={`${submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
||||
}`}
|
||||
className={`${
|
||||
submenuOpen ? "fa-solid fa-angle-down" : "fa-solid fa-angle-right"
|
||||
}`}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
@@ -33,13 +34,20 @@ const Submenu = ({ item, closeSidebar }) => {
|
||||
{children.map((childItem) => (
|
||||
<li key={childItem.title} role="none">
|
||||
<NavLink
|
||||
to={`/${childItem.pageType}/${childItem.objectId}`}
|
||||
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white"
|
||||
to={
|
||||
childItem.pageType
|
||||
? `/${childItem.pageType}/${childItem.objectId}`
|
||||
: `/${childItem.objectId}`
|
||||
}
|
||||
className="block pl-6 md:pl-8 py-2 text-sm text-gray-700 hover:bg-blue-500 hover:text-white focus:text-[#0056b3] focus:bg-[#eef1f5] cursor-pointer"
|
||||
onClick={closeSidebar}
|
||||
role="menuitem"
|
||||
tabIndex={submenuOpen ? 0 : -1}
|
||||
>
|
||||
<i className={`${childItem.icon} text-[18px]`} aria-hidden="true"></i>
|
||||
<i
|
||||
className={`${childItem.icon} text-[18px]`}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="ml-3 lg:ml-4">{childItem.title}</span>
|
||||
</NavLink>
|
||||
</li>
|
||||
|
||||
@@ -3,7 +3,10 @@ export const appInfo = {
|
||||
appTitle: "contracts",
|
||||
applogo: logo,
|
||||
appname: "contracts",
|
||||
baseurl: process.env.REACT_APP_SERVERURL,
|
||||
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
|
||||
baseUrl: process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app",
|
||||
defaultRole: "contracts_User",
|
||||
fbAppId: process.env.REACT_APP_FBAPPID
|
||||
? `${process.env.REACT_APP_FBAPPID}`
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export const contactCls = "contracts_Contactbook";
|
||||
export const templateCls = "contracts_Template";
|
||||
export const documentCls = "contracts_Document";
|
||||
export const rejectBtn =
|
||||
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
|
||||
export const submitBtn =
|
||||
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
|
||||
export const modalSubmitBtnColor = "#17a2b8";
|
||||
export const modalCancelBtnColor = "white";
|
||||
export const themeColor = "#47a3ad";
|
||||
export const iconColor = "#686968";
|
||||
@@ -1,9 +1,6 @@
|
||||
import axios from "axios";
|
||||
import Parse from "parse";
|
||||
const parseAppId = localStorage.getItem("AppID12");
|
||||
const serverUrl = localStorage.getItem("BaseUrl12");
|
||||
Parse.serverURL = serverUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
|
||||
export const SaveFileSize = async (size, imageUrl) => {
|
||||
//checking server url and save file's size
|
||||
|
||||
@@ -4,4 +4,15 @@
|
||||
|
||||
body{
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom CSS to hide scrollbar */
|
||||
.hide-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hide-scrollbar {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
@@ -1 +1,71 @@
|
||||
import("./Bootstrap");
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { TouchBackend } from "react-dnd-touch-backend";
|
||||
import {
|
||||
DndProvider,
|
||||
TouchTransition,
|
||||
MouseTransition,
|
||||
Preview
|
||||
} from "react-dnd-multi-backend";
|
||||
import DragElement from "./components/pdf/DragElement";
|
||||
import TagManager from "react-gtm-module";
|
||||
import Parse from "parse";
|
||||
const appId = process.env.REACT_APP_APPID
|
||||
? process.env.REACT_APP_APPID
|
||||
: "opensign";
|
||||
const serverUrl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
Parse.initialize(appId);
|
||||
Parse.serverURL = serverUrl;
|
||||
|
||||
const HTML5toTouch = {
|
||||
backends: [
|
||||
{
|
||||
id: "html5",
|
||||
backend: HTML5Backend,
|
||||
transition: MouseTransition
|
||||
},
|
||||
{
|
||||
id: "touch",
|
||||
backend: TouchBackend,
|
||||
options: { enableMouseEvents: true },
|
||||
preview: true,
|
||||
transition: TouchTransition
|
||||
}
|
||||
]
|
||||
};
|
||||
const generatePreview = (props) => {
|
||||
const { item, style } = props;
|
||||
const newStyle = {
|
||||
...style
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={newStyle}>
|
||||
<DragElement {...item} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (process.env.REACT_APP_GTM) {
|
||||
const tagManagerArgs = {
|
||||
gtmId: process.env.REACT_APP_GTM
|
||||
};
|
||||
TagManager.initialize(tagManagerArgs);
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
@@ -1,584 +1,26 @@
|
||||
import Parse from "parse";
|
||||
export const formJson = (id) => {
|
||||
let formData;
|
||||
import { documentCls, templateCls } from "../constant/const";
|
||||
export const formJson = {
|
||||
//json form for signYourself
|
||||
if (id === "sHAnZphf69") {
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "Sign Yourself",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["URL", "Name"],
|
||||
properties: {
|
||||
URL: {
|
||||
type: "string",
|
||||
title: "Select Document",
|
||||
filetypes: [],
|
||||
maxfilesizeKB: "10000",
|
||||
uploadtype: "regular",
|
||||
helpbody: "",
|
||||
helplink: ""
|
||||
},
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Title",
|
||||
maxLength: 50
|
||||
},
|
||||
Description: {
|
||||
type: "string",
|
||||
title: "Description",
|
||||
maxLength: 500
|
||||
},
|
||||
Folder: {
|
||||
title: "Folder",
|
||||
parent: null,
|
||||
data: {
|
||||
ClassName: "contracts_Document",
|
||||
FolderNameField: "Name",
|
||||
FolderTypeField: "Type",
|
||||
FolderTypeValue: "Folder",
|
||||
FolderDescription: "Description",
|
||||
ParentFolderField: "Folder",
|
||||
FormId: "YjIB7W7Xs6",
|
||||
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
|
||||
}
|
||||
},
|
||||
ExtUserPtr: {
|
||||
data: {
|
||||
valueKey: "objectId",
|
||||
isPointer: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
URL: {
|
||||
"ui:field": "FileUpload"
|
||||
},
|
||||
Description: {
|
||||
"ui:widget": "textarea",
|
||||
"ui:options": {
|
||||
rows: 2
|
||||
}
|
||||
},
|
||||
Note: {
|
||||
"ui:widget": "textarea",
|
||||
"ui:options": {
|
||||
rows: 2
|
||||
}
|
||||
},
|
||||
Folder: {
|
||||
"ui:field": "FolderComponent"
|
||||
},
|
||||
ExtUserPtr: {
|
||||
"ui:field": "HiddenField"
|
||||
}
|
||||
},
|
||||
userSchema: {},
|
||||
rules: null,
|
||||
noValidate: false,
|
||||
liveValidate: false,
|
||||
isRegisterForm: false,
|
||||
help: { htmlbody: "" },
|
||||
description: "",
|
||||
class: "contracts_Document",
|
||||
buttons: {
|
||||
add: {
|
||||
resetText: "Reset",
|
||||
submitText: "Submit"
|
||||
},
|
||||
edit: { submitText: "Update", cancelText: "Cancel" }
|
||||
},
|
||||
formACL: {
|
||||
"#currentUser#": { read: true, write: true },
|
||||
"*": { read: true, write: true }
|
||||
},
|
||||
redirect_id:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/signaturePdf",
|
||||
success_message: "Success!",
|
||||
success_redirect: "Microapp",
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
sHAnZphf69: {
|
||||
title: "Sign Yourself",
|
||||
redirectRoute: "signaturePdf",
|
||||
msgVar: "Document",
|
||||
Cls: documentCls
|
||||
},
|
||||
|
||||
return formData;
|
||||
}
|
||||
//json form for request signature
|
||||
else if (id === "8mZzFxbG1z") {
|
||||
const user = Parse.User.current();
|
||||
const userPtr = JSON.stringify({
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: user.id
|
||||
});
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "New Document",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["URL", "Name", "Note", "TimeToCompleteDays", "Signers"],
|
||||
properties: {
|
||||
URL: {
|
||||
type: "string",
|
||||
title: "Select Document",
|
||||
filetypes: [],
|
||||
maxfilesizeKB: "5000",
|
||||
uploadtype: "regular",
|
||||
helpbody: "",
|
||||
helplink: ""
|
||||
},
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Title",
|
||||
maxLength: 100
|
||||
},
|
||||
Description: {
|
||||
type: "string",
|
||||
title: "Description",
|
||||
maxLength: 500
|
||||
},
|
||||
Signers: {
|
||||
type: "array",
|
||||
title: "Signers",
|
||||
isSortable: false,
|
||||
selectAll: false,
|
||||
selectedData: null,
|
||||
data: {
|
||||
class: "contracts_Contactbook",
|
||||
displayKey: "Name",
|
||||
valueKey: "objectId",
|
||||
query: `where={"CreatedBy":${userPtr},"IsDeleted":{"$ne":true}}&keys=Name`,
|
||||
isPointer: true,
|
||||
helpbody: "",
|
||||
helplink: "",
|
||||
quickAddFormId: "qUGywSWd8e"
|
||||
}
|
||||
},
|
||||
Note: {
|
||||
type: "string",
|
||||
title: "Note",
|
||||
default: "Please review and sign this document",
|
||||
maxLength: 500
|
||||
},
|
||||
Folder: {
|
||||
title: "Folder",
|
||||
data: {
|
||||
ClassName: "contracts_Document",
|
||||
FolderNameField: "Name",
|
||||
FolderTypeField: "Type",
|
||||
FolderTypeValue: "Folder",
|
||||
FolderDescription: "Description",
|
||||
ParentFolderField: "Folder",
|
||||
FormId: "YjIB7W7Xs6",
|
||||
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
|
||||
}
|
||||
},
|
||||
TimeToCompleteDays: {
|
||||
type: "number",
|
||||
title: "Time To Complete (Days)",
|
||||
default: 15,
|
||||
maxLength: 5000
|
||||
},
|
||||
ExtUserPtr: {
|
||||
data: {
|
||||
valueKey: "objectId",
|
||||
isPointer: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
URL: {
|
||||
"ui:field": "FileUpload"
|
||||
},
|
||||
Description: {
|
||||
"ui:widget": "textarea",
|
||||
"ui:options": {
|
||||
rows: 2
|
||||
}
|
||||
},
|
||||
Note: {
|
||||
"ui:widget": "textarea",
|
||||
"ui:options": {
|
||||
rows: 2
|
||||
}
|
||||
},
|
||||
Signers: {
|
||||
"ui:field": "MultiSelectField"
|
||||
},
|
||||
Folder: {
|
||||
"ui:field": "FolderComponent"
|
||||
},
|
||||
TimeToCompleteDays: {
|
||||
"ui:placeholder": "No. of days",
|
||||
"ui:widget": "updown"
|
||||
},
|
||||
ExtUserPtr: {
|
||||
"ui:field": "HiddenField"
|
||||
}
|
||||
},
|
||||
userSchema: {},
|
||||
rules: null,
|
||||
noValidate: false,
|
||||
liveValidate: false,
|
||||
isRegisterForm: false,
|
||||
help: { htmlbody: "" },
|
||||
description: "",
|
||||
class: "contracts_Document",
|
||||
buttons: {
|
||||
add: {
|
||||
resetText: "Reset",
|
||||
submitText: "Submit"
|
||||
},
|
||||
edit: { submitText: "Update", cancelText: "Cancel" }
|
||||
},
|
||||
formACL: {
|
||||
"#currentUser#": { read: true, write: true },
|
||||
"*": { read: true, write: true }
|
||||
},
|
||||
redirect_id:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/placeHolderSign",
|
||||
success_message: "Success!",
|
||||
success_redirect: "Microapp",
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
|
||||
return formData;
|
||||
}
|
||||
//json form for create new folder
|
||||
else if (id === "YjIB7W7Xs6") {
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "New Folder",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["Name"],
|
||||
properties: {
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Name",
|
||||
maxLength: 50
|
||||
},
|
||||
Type: {
|
||||
type: "string",
|
||||
data: {
|
||||
valueKey: "Type",
|
||||
isPointer: false
|
||||
},
|
||||
default: "Folder"
|
||||
},
|
||||
Folder: {
|
||||
title: "Parent Folder",
|
||||
data: {
|
||||
class: "contracts_Document",
|
||||
displayKey: "Name",
|
||||
valueKey: "objectId",
|
||||
query: 'where={"Type":"Folder"}&keys=Name',
|
||||
isPointer: true,
|
||||
savePointerClass: "",
|
||||
helpbody: "",
|
||||
helplink: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
Type: {
|
||||
"ui:field": "HiddenField"
|
||||
},
|
||||
Folder: {
|
||||
"ui:field": "Level1Dropdown"
|
||||
}
|
||||
},
|
||||
userSchema: {},
|
||||
rules: null,
|
||||
noValidate: false,
|
||||
liveValidate: false,
|
||||
isRegisterForm: false,
|
||||
help: { htmlbody: "" },
|
||||
description: "",
|
||||
class: "contracts_Document",
|
||||
buttons: {
|
||||
add: {
|
||||
resetText: "Reset",
|
||||
submitText: "Submit"
|
||||
},
|
||||
edit: { submitText: "Update", cancelText: "Cancel" }
|
||||
},
|
||||
formACL: {
|
||||
"#currentUser#": { read: true, write: true },
|
||||
"*": { read: true, write: true }
|
||||
},
|
||||
redirect_id: "",
|
||||
success_message: "Success!",
|
||||
success_redirect: "",
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
return formData;
|
||||
}
|
||||
//json form for add signers
|
||||
else if (id === "fICciRuUcB") {
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "Add Signer",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["Name", "Email", "Phone", "Company"],
|
||||
properties: {
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Name",
|
||||
maxLength: 50
|
||||
},
|
||||
Email: {
|
||||
type: "string",
|
||||
title: "Email",
|
||||
maxLength: 50
|
||||
},
|
||||
Phone: {
|
||||
type: "string",
|
||||
title: "Phone",
|
||||
maxLength: 50
|
||||
},
|
||||
Company: {
|
||||
type: "string",
|
||||
title: "Company",
|
||||
maxLength: 50
|
||||
},
|
||||
JobTitle: {
|
||||
type: "string",
|
||||
title: "JobTitle",
|
||||
maxLength: 50
|
||||
},
|
||||
IsContactEntry: {
|
||||
type: "boolean",
|
||||
data: {
|
||||
valueKey: "IsContactEntry",
|
||||
isPointer: false
|
||||
},
|
||||
default: true
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
Email: {
|
||||
"ui:widget": "email"
|
||||
},
|
||||
IsContactEntry: {
|
||||
"ui:field": "HiddenField"
|
||||
}
|
||||
},
|
||||
userSchema: {
|
||||
email: "$Email",
|
||||
name: "$Name",
|
||||
password: "$Phone",
|
||||
phone: "$Phone",
|
||||
role: "contracts_User",
|
||||
username: "$Email"
|
||||
},
|
||||
rules: null,
|
||||
noValidate: false,
|
||||
liveValidate: false,
|
||||
isRegisterForm: true,
|
||||
help: {},
|
||||
description: "",
|
||||
class: "contracts_Users",
|
||||
buttons: {
|
||||
add: {
|
||||
resetText: "Reset",
|
||||
submitText: "Submit"
|
||||
},
|
||||
edit: { submitText: "Update", cancelText: "Cancel" }
|
||||
},
|
||||
formACL: {
|
||||
"#currentUser#": { read: true, write: true },
|
||||
"*": { read: true, write: true }
|
||||
},
|
||||
redirect_id: "",
|
||||
success_message: "Success!",
|
||||
success_redirect: "",
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
return formData;
|
||||
}
|
||||
//json form for add users
|
||||
else if (id === "lM0xRnM3iE") {
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "Add User",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["Name", "Email", "Phone", "JobTitle"],
|
||||
properties: {
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Name",
|
||||
maxLength: 50
|
||||
},
|
||||
Email: {
|
||||
type: "string",
|
||||
title: "Email",
|
||||
maxLength: 50
|
||||
},
|
||||
Phone: {
|
||||
type: "string",
|
||||
title: "Phone",
|
||||
maxLength: 50
|
||||
},
|
||||
JobTitle: {
|
||||
type: "string",
|
||||
title: "Job Title",
|
||||
maxLength: 50
|
||||
},
|
||||
Company: {
|
||||
type: "string",
|
||||
title: "Company",
|
||||
maxLength: 50
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
Email: {
|
||||
"ui:widget": "email"
|
||||
},
|
||||
Phone: {
|
||||
"ui:options": {
|
||||
inputType: "tel"
|
||||
}
|
||||
}
|
||||
},
|
||||
userSchema: {
|
||||
email: "$Email",
|
||||
name: "$Name",
|
||||
password: "$Phone",
|
||||
phone: "$Phone",
|
||||
role: "contracts_User",
|
||||
username: "$Email"
|
||||
},
|
||||
rules: null,
|
||||
noValidate: false,
|
||||
liveValidate: false,
|
||||
isRegisterForm: true,
|
||||
help: {},
|
||||
description: "",
|
||||
class: "contracts_Users",
|
||||
buttons: {
|
||||
add: {
|
||||
resetText: "Reset",
|
||||
submitText: "Submit"
|
||||
},
|
||||
edit: { submitText: "Update", cancelText: "Cancel" }
|
||||
},
|
||||
formACL: {
|
||||
"#currentUser#": { read: true, write: true },
|
||||
"*": { read: true, write: true }
|
||||
},
|
||||
redirect_id: "",
|
||||
success_message: "Success!",
|
||||
success_redirect: "",
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
return formData;
|
||||
} else if (id === "qUGywSWd8e") {
|
||||
formData = {
|
||||
jsonSchema: {
|
||||
title: "Add Contact",
|
||||
description: "",
|
||||
type: "object",
|
||||
required: ["Name", "Email", "Phone"],
|
||||
properties: {
|
||||
Name: {
|
||||
type: "string",
|
||||
title: "Name",
|
||||
maxLength: 50
|
||||
},
|
||||
Email: {
|
||||
type: "string",
|
||||
title: "Email",
|
||||
maxLength: 50
|
||||
},
|
||||
Phone: {
|
||||
type: "string",
|
||||
title: "Phone",
|
||||
maxLength: 50
|
||||
}
|
||||
}
|
||||
},
|
||||
uiSchema: {
|
||||
Email: {
|
||||
"ui:widget": "email"
|
||||
},
|
||||
Phone: {
|
||||
"ui:options": {
|
||||
inputType: "tel"
|
||||
}
|
||||
}
|
||||
},
|
||||
userSchema: {
|
||||
name: "$Name",
|
||||
username: "$Email",
|
||||
email: "$Email",
|
||||
password: "$Phone",
|
||||
phone: "$Phone",
|
||||
role: "contracts_Guest"
|
||||
},
|
||||
restrict_form_entry: {
|
||||
enable_captcha: false,
|
||||
allow_one_entry_per_ip_address: false,
|
||||
allow_one_entry_per_user: false,
|
||||
enable_geo_fence: false
|
||||
},
|
||||
restrict_form_access: {
|
||||
entryCount: ""
|
||||
},
|
||||
help: {},
|
||||
email_template: {
|
||||
from: "",
|
||||
to: "",
|
||||
subject: "",
|
||||
message: ""
|
||||
},
|
||||
buttons: {
|
||||
add: {
|
||||
submitText: "Submit",
|
||||
resetText: "Reset"
|
||||
},
|
||||
edit: {
|
||||
submitText: "Update",
|
||||
cancelText: "Cancel"
|
||||
}
|
||||
},
|
||||
appId: {
|
||||
__type: "Pointer",
|
||||
className: "w_appinfo",
|
||||
objectId: "aIPmIvMzGM"
|
||||
},
|
||||
formACL: {
|
||||
"*": {
|
||||
read: true,
|
||||
write: true
|
||||
},
|
||||
"#currentUser#": {
|
||||
read: true,
|
||||
write: true
|
||||
}
|
||||
},
|
||||
class: "contracts_Contactbook",
|
||||
description: "",
|
||||
success_message: "Success!",
|
||||
form_permission: "private",
|
||||
success_redirect: "",
|
||||
redirect_id: "",
|
||||
isRegisterForm: true,
|
||||
liveValidate: false,
|
||||
noValidate: false,
|
||||
validFunction:
|
||||
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
|
||||
};
|
||||
return formData;
|
||||
//json form for request signatures
|
||||
"8mZzFxbG1z": {
|
||||
title: "Request Signatures",
|
||||
msgVar: "Document",
|
||||
redirectRoute: "placeHolderSign",
|
||||
Cls: documentCls,
|
||||
signers: true
|
||||
},
|
||||
//json form for template
|
||||
template: {
|
||||
title: "New Template",
|
||||
redirectRoute: "template",
|
||||
msgVar: "Template",
|
||||
Cls: templateCls
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
export default function reportJson(id) {
|
||||
// console.log("json ", json);
|
||||
const head = ["Sr.No", "Name", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const contactbook = ["Sr.No", "Name", "Email", "Phone"];
|
||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
|
||||
switch (id) {
|
||||
// draft documents report
|
||||
case "ByHuevtCFY":
|
||||
@@ -14,10 +16,10 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg: "This are documents you have started but have not finalized for sending."
|
||||
};
|
||||
// Need your sign report
|
||||
case "4Hhwbp482K":
|
||||
@@ -30,10 +32,11 @@ export default function reportJson(id) {
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg:
|
||||
"This is a list of documents that are waiting for your signature"
|
||||
};
|
||||
// In progess report
|
||||
case "1MwEuxLEkF":
|
||||
@@ -46,10 +49,11 @@ export default function reportJson(id) {
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg:
|
||||
"This is a list of documents you've sent to other parties for signature."
|
||||
};
|
||||
// completed documents report
|
||||
case "kQUoW4hUXz":
|
||||
@@ -62,10 +66,10 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg: "This is a list of documents that have been signed by all parties."
|
||||
};
|
||||
// declined documents report
|
||||
case "UPr2Fm5WY3":
|
||||
@@ -78,10 +82,10 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg: "This is a list of documents that have been declined by one of the signers."
|
||||
};
|
||||
// Expired Documents report
|
||||
case "zNqBHXHsYH":
|
||||
@@ -94,24 +98,23 @@ export default function reportJson(id) {
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
],
|
||||
helpMsg: "This is a list of documents that have reached their expiration date."
|
||||
};
|
||||
// Recently sent for signatures report show on dashboard
|
||||
case "d9k3UfYHBc":
|
||||
return {
|
||||
reportName: "Recently sent for signatures",
|
||||
heading: head,
|
||||
heading: dashboardReportHead,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -119,15 +122,14 @@ export default function reportJson(id) {
|
||||
case "5Go51Q7T8r":
|
||||
return {
|
||||
reportName: "Recent signature requests",
|
||||
heading: head,
|
||||
heading: dashboardReportHead,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "Sign",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -135,15 +137,14 @@ export default function reportJson(id) {
|
||||
case "kC5mfynCi4":
|
||||
return {
|
||||
reportName: "Drafts",
|
||||
heading: head,
|
||||
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "sign",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl:
|
||||
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/draftDocument"
|
||||
redirectUrl: "draftDocument"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -159,7 +160,33 @@ export default function reportJson(id) {
|
||||
textColor: "white",
|
||||
btnIcon: "fa-solid fa-trash"
|
||||
}
|
||||
]
|
||||
],
|
||||
form: "ContactBook",
|
||||
helpMsg: "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
|
||||
};
|
||||
// template report
|
||||
case "6TeaPr321t":
|
||||
return {
|
||||
reportName: "Templates",
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "Use",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "placeHolderSign"
|
||||
},
|
||||
{
|
||||
btnLabel: "Edit",
|
||||
btnColor: "#00c9d5",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "template"
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
"planName": "THANKS PLAN",
|
||||
"currency": "$",
|
||||
"price": "29.99",
|
||||
"subtitle": "Use code 'FREEBETA' to get this for free(First 1000 users)",
|
||||
"subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)",
|
||||
"btnText": "Subscribe",
|
||||
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
|
||||
"target": "_self",
|
||||
"benefits": [
|
||||
"Sign Unlimited Documents",
|
||||
"Unlimited Secure Doc Storage with OpenSignDrive",
|
||||
"Unlimited Secure Doc Storage with OpenSign™ Drive",
|
||||
"Unlimited Guest Signers",
|
||||
"Unlimited completion certificates",
|
||||
"Unique Code(OTP) verification for guest signers",
|
||||
|
||||
@@ -7,10 +7,37 @@ import Tour from "reactour";
|
||||
import axios from "axios";
|
||||
import { useSelector } from "react-redux";
|
||||
import Parse from "parse";
|
||||
const HomeLayout = ({ children }) => {
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useNavigate, useLocation, Outlet } from "react-router-dom";
|
||||
|
||||
const HomeLayout = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { width } = useWindowSize();
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const arr = useSelector((state) => state.TourSteps);
|
||||
const [isUserValid, setIsUserValid] = useState(true);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
// Use the session token to validate the user
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(Parse.User.current().id, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
setIsUserValid(false);
|
||||
}
|
||||
} catch (error) {
|
||||
// Session token is invalid or there was an error
|
||||
setIsUserValid(false);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
// reactour state
|
||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||
@@ -19,7 +46,7 @@ const HomeLayout = ({ children }) => {
|
||||
const [tourConfigs, setTourConfigs] = useState([]);
|
||||
|
||||
const showSidebar = () => {
|
||||
setIsOpen(value => !value);
|
||||
setIsOpen((value) => !value);
|
||||
};
|
||||
useEffect(() => {
|
||||
if (width && width <= 768) {
|
||||
@@ -28,9 +55,7 @@ const HomeLayout = ({ children }) => {
|
||||
}, [width]);
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("domain") === "sign" && arr && arr.length > 0) {
|
||||
handleDynamicSteps();
|
||||
} else if (
|
||||
if (
|
||||
localStorage.getItem("domain") === "contracts" &&
|
||||
arr &&
|
||||
arr.length > 0
|
||||
@@ -69,10 +94,16 @@ const HomeLayout = ({ children }) => {
|
||||
position: "top"
|
||||
// style: { backgroundColor: "#abd4d2" },
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="tourbutton"]',
|
||||
content: `To upload documents for self-signing or to request others’ signatures, simply select the respective buttons.`,
|
||||
position: "top"
|
||||
// style: { backgroundColor: "#abd4d2" },
|
||||
},
|
||||
...resArr,
|
||||
{
|
||||
selector: '[data-tut="reactourLast"]',
|
||||
content: `You are good to go`,
|
||||
content: `You are ready to start using OpenSign! If you need support feel free to contact us.`,
|
||||
position: "top"
|
||||
// style: { backgroundColor: "#abd4d2" },
|
||||
}
|
||||
@@ -85,7 +116,7 @@ const HomeLayout = ({ children }) => {
|
||||
// console.log("closeTour");
|
||||
setIsTour(false);
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const appId = localStorage.getItem("AppID12");
|
||||
const appId = localStorage.getItem("parseAppId");
|
||||
const extUserClass = localStorage.getItem("extended_class");
|
||||
const json = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const extUserId = json && json.length > 0 && json[0].objectId;
|
||||
@@ -153,32 +184,83 @@ const HomeLayout = ({ children }) => {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
try {
|
||||
Parse.User.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
localStorage.removeItem("accesstoken");
|
||||
navigate("/", { replace: true, state: { from: location } });
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-50">
|
||||
<Header showSidebar={showSidebar} />
|
||||
</div>
|
||||
<div className="flex md:flex-row flex-col z-50">
|
||||
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||
{isUserValid ? (
|
||||
<>
|
||||
{isLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex md:flex-row flex-col z-50">
|
||||
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||
|
||||
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
|
||||
<div className="bg-[#eef1f5] p-3">{children}</div>
|
||||
<div className="z-30">
|
||||
<Footer />
|
||||
<div
|
||||
id="renderList"
|
||||
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
|
||||
>
|
||||
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
|
||||
<div className="z-30">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
// disableInteraction={true}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
|
||||
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
|
||||
<p className="text-xl font-normal">Your session has expired.</p>
|
||||
<button
|
||||
onClick={handleLoginBtn}
|
||||
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfigs}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
// disableInteraction={true}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
showCloseButton={isCloseBtn}
|
||||
/>
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import React from "react";
|
||||
import stamp from "./stamp2.png";
|
||||
import sign from "./sign3.png";
|
||||
import { themeColor } from "./IconColor.js";
|
||||
|
||||
function DragElement({ id }) {
|
||||
const selectedId = id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{selectedId === 3 ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
userSelect: "none"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="sign img"
|
||||
style={{
|
||||
width: "25px",
|
||||
height: "23px",
|
||||
background: themeColor()
|
||||
}}
|
||||
src={sign}
|
||||
/>
|
||||
<span style={{ color: themeColor(), fontSize: "12px" }}>
|
||||
Signature
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
userSelect: "none"
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="stamp img"
|
||||
style={{
|
||||
width: "25px",
|
||||
height: "23px",
|
||||
background: themeColor()
|
||||
}}
|
||||
src={stamp}
|
||||
/>
|
||||
<span style={{ color: themeColor(), fontSize: "12px" }}>Stamp</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DragElement;
|
||||
@@ -1,3 +0,0 @@
|
||||
export const themeColor = () => {
|
||||
return "#47a3ad";
|
||||
};
|
||||
|
Before Width: | Height: | Size: 804 B |
|
Before Width: | Height: | Size: 561 B |
@@ -11,8 +11,6 @@ function ChangePassword() {
|
||||
evt.preventDefault();
|
||||
try {
|
||||
if (newpassword === confirmpassword) {
|
||||
Parse.serverURL = localStorage.getItem("baseUrl");
|
||||
Parse.initialize(localStorage.getItem("parseAppId"));
|
||||
Parse.User.logIn(localStorage.getItem("userEmail"), currentpassword)
|
||||
.then(async (user) => {
|
||||
if (user) {
|
||||
@@ -4,16 +4,15 @@ import "../styles/loader.css";
|
||||
import GetDashboard from "../components/dashboard/GetDashboard";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
import { save_tourSteps } from "../redux/actions";
|
||||
import { connect } from "react-redux";
|
||||
const Dashboard = (props) => {
|
||||
import { useDispatch } from "react-redux";
|
||||
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
|
||||
const Dashboard = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { id } = useParams();
|
||||
const [dashboard, setdashboard] = useState([]);
|
||||
const [classnameArray, setclassnameArray] = useState([]);
|
||||
const [loading, setloading] = useState(false);
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [defaultQuery, setDefaultQuery] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
@@ -24,7 +23,7 @@ const Dashboard = (props) => {
|
||||
getDashboard(localStorage.getItem("PageLanding"));
|
||||
}
|
||||
} else {
|
||||
navigate("/", { replace: true });
|
||||
navigate("/", { replace: true, state: { from: location } });
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [id]);
|
||||
@@ -32,8 +31,6 @@ const Dashboard = (props) => {
|
||||
const getDashboard = async (id) => {
|
||||
setloading(true);
|
||||
try {
|
||||
Parse.serverURL = parseBaseUrl;
|
||||
Parse.initialize(parseAppId);
|
||||
var forms = Parse.Object.extend("w_dashboard");
|
||||
var query = new Parse.Query(forms);
|
||||
query.equalTo("objectId", id);
|
||||
@@ -92,7 +89,7 @@ const Dashboard = (props) => {
|
||||
// style: { backgroundColor: "#abd4d2" },
|
||||
};
|
||||
});
|
||||
props.save_tourSteps(arr);
|
||||
dispatch(saveTourSteps(arr));
|
||||
// console.log("arr ", arr);
|
||||
setclassnameArray(classArray);
|
||||
if (localStorage.getItem("DashboardDefaultFilter")) {
|
||||
@@ -137,6 +134,4 @@ const Dashboard = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(null, {
|
||||
save_tourSteps
|
||||
})(Dashboard);
|
||||
export default Dashboard;
|
||||
@@ -0,0 +1,429 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import "../styles/signature.css";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import RenderDebugPdf from "../components/RenderDebugPdf";
|
||||
import { pdfjs } from "react-pdf";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Alert from "../primitives/Alert";
|
||||
import HandleError from "../primitives/HandleError";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
|
||||
function processDimensions(x, y, width, height) {
|
||||
if (width < 0) {
|
||||
x -= Math.abs(width);
|
||||
width = Math.abs(width);
|
||||
}
|
||||
|
||||
if (height < 0) {
|
||||
y -= Math.abs(height);
|
||||
height = Math.abs(height);
|
||||
}
|
||||
|
||||
return {
|
||||
x: Math.floor(x),
|
||||
y: Math.floor(y),
|
||||
width: Math.floor(width),
|
||||
height: Math.floor(height)
|
||||
};
|
||||
}
|
||||
const DebugPdf = () => {
|
||||
const { width } = useWindowSize();
|
||||
const [pdf, setPdf] = useState("");
|
||||
const [isModal, setIsModal] = useState(true);
|
||||
const [allPages, setAllPages] = useState(null);
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||
status: false,
|
||||
type: "load"
|
||||
});
|
||||
const [pdfDetails, setPdfDetails] = useState({
|
||||
name: "",
|
||||
pdftype: "",
|
||||
totalPages: "",
|
||||
currentPage: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
base64: ""
|
||||
});
|
||||
const [hoverCoordinates, setHoverCoordinates] = useState({ x: 0, y: 0 });
|
||||
const [pdfDimension, setPdfDimension] = useState({ width: 0, height: 0 });
|
||||
const [annotations, setAnnotations] = useState([]);
|
||||
const [newAnnotation, setNewAnnotation] = useState([]);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (pdf && pdf.name) {
|
||||
const fetchPdfMetadata = async () => {
|
||||
try {
|
||||
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
|
||||
console.log("pdfDataURL ", pdfDataURL);
|
||||
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
|
||||
const pdfType = await inferPdfType(pdfInfo);
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
pdftype: pdfType
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error("Error fetching PDF metadata:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchPdfMetadata();
|
||||
}
|
||||
}, [pdf]);
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(1);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
|
||||
// Assuming a standard DPI of 72, you can adjust this value if needed
|
||||
const dpi = 72;
|
||||
|
||||
const widthInInches = width / dpi;
|
||||
const heightInInches = height / dpi;
|
||||
|
||||
const isA1 = widthInInches > 23.39 && heightInInches > 16.54;
|
||||
const isA2 = widthInInches > 16.54 && heightInInches > 11.69;
|
||||
const isA3 = widthInInches > 11.69 && heightInInches > 8.27;
|
||||
const isA4 = widthInInches > 8.27 && heightInInches > 5.83;
|
||||
const isLegal = widthInInches > 8.5 && heightInInches > 14;
|
||||
const isLetter = widthInInches > 8.5 && heightInInches > 11;
|
||||
const isLedger = widthInInches > 11 && heightInInches > 17;
|
||||
|
||||
if (isA1) return "A1";
|
||||
if (isA2) return "A2";
|
||||
if (isA3) return "A3";
|
||||
if (isA4) return "A4";
|
||||
if (isLegal) return "Legal";
|
||||
if (isLetter) return "Letter";
|
||||
if (isLedger) return "Ledger";
|
||||
|
||||
return "Unknown";
|
||||
} catch (error) {
|
||||
console.error("Error getting page dimensions:", error);
|
||||
return "Unknown";
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
setIsModal(false);
|
||||
setPdfDetails((prevData) => ({ ...prevData, name: pdf?.name }));
|
||||
};
|
||||
|
||||
//function for get pdf page details
|
||||
const pageDetails = async ({ numPages }) => {
|
||||
const load = {
|
||||
status: true
|
||||
};
|
||||
setPdfDetails((prevDetails) => ({ ...prevDetails, totalPages: numPages }));
|
||||
setPdfLoadFail(load);
|
||||
};
|
||||
|
||||
const handleMouseMoveDiv = (event) => {
|
||||
setHoverCoordinates({
|
||||
x: event.nativeEvent.offsetX,
|
||||
y: event.nativeEvent.offsetY
|
||||
});
|
||||
};
|
||||
const handleMouseDown = (event) => {
|
||||
if (newAnnotation.length === 0) {
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
setNewAnnotation([
|
||||
{ x, y, width: 0, height: 0, key: "0", page: pageNumber }
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (event) => {
|
||||
if (newAnnotation.length === 1) {
|
||||
const sx = newAnnotation[0].x;
|
||||
const sy = newAnnotation[0].y;
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
const result = processDimensions(sx, sy, x - sx, y - sy);
|
||||
const annotationToAdd = {
|
||||
...result,
|
||||
key: annotations.length + 1,
|
||||
page: pageNumber
|
||||
};
|
||||
annotations.push(annotationToAdd);
|
||||
setNewAnnotation([]);
|
||||
setAnnotations(annotations);
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
x: result.x,
|
||||
y: result.y
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
if (newAnnotation.length === 1) {
|
||||
const sx = newAnnotation[0].x;
|
||||
const sy = newAnnotation[0].y;
|
||||
const { x, y } = event.target.getStage().getPointerPosition();
|
||||
setNewAnnotation([
|
||||
{
|
||||
x: sx,
|
||||
y: sy,
|
||||
width: x - sx,
|
||||
height: y - sy,
|
||||
page: pageNumber,
|
||||
key: "0"
|
||||
}
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const annotationsToDraw = [...annotations, ...newAnnotation];
|
||||
|
||||
const handlePageLoadSuccess = (page) => {
|
||||
setPdfDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
currentPage: page.pageNumber
|
||||
}));
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleFileChange = (files) => {
|
||||
const file = files[0];
|
||||
setPdf(file);
|
||||
|
||||
if (file && file.type === "application/pdf") {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onloadend = () => {
|
||||
const base64String = reader.result.split(",")[1];
|
||||
setPdfDetails((prevdata) => ({ ...prevdata, base64: base64String }));
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
const handleDelete = (key) => {
|
||||
const updateAnnotations = annotations.filter((x) => x.key !== key);
|
||||
setAnnotations(updateAnnotations);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
<Title title={"Debug Pdf"} />
|
||||
{width < 800 ? (
|
||||
<HandleError handleError={"Debug PDF only availble for PC"} />
|
||||
) : (
|
||||
<>
|
||||
{!isModal && (
|
||||
<div className="signatureContainer">
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdf}
|
||||
allPages={allPages}
|
||||
setAllPages={setAllPages}
|
||||
setPageNumber={setPageNumber}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
{/* pdf render view */}
|
||||
<div>
|
||||
<div data-tut="reactourThird">
|
||||
<RenderDebugPdf
|
||||
pdfUrl={pdf}
|
||||
pageDetails={pageDetails}
|
||||
pageNumber={pageNumber}
|
||||
setPdfLoadFail={setPdfLoadFail}
|
||||
pdfLoadFail={pdfLoadFail}
|
||||
handlePageLoadSuccess={handlePageLoadSuccess}
|
||||
handleMouseMove={handleMouseMove}
|
||||
handleMouseUp={handleMouseUp}
|
||||
handleMouseDown={handleMouseDown}
|
||||
hoverCoordinates={hoverCoordinates}
|
||||
annotations={annotationsToDraw}
|
||||
pdfDimension={pdfDimension}
|
||||
handleMouseMoveDiv={handleMouseMoveDiv}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ backgroundColor: "white", width: 220 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
PDF details
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Name: {pdfDetails?.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Pdf type: {pdfDetails?.pdftype}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Total Pages: {pdfDetails?.totalPages}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Current Page: {pdfDetails?.currentPage}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
Base64 : {pdfDetails?.base64.slice(0, 10)}...
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => copytoclipboard(pdfDetails?.base64)}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
Last click
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
x co-ordinate: {pdfDetails?.x}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
y co-ordinate: {pdfDetails?.y}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey",
|
||||
borderTop: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
Annotation
|
||||
</div>
|
||||
<ul
|
||||
style={{
|
||||
listStyle: "none",
|
||||
padding: 10,
|
||||
height: 500,
|
||||
overflowY: "auto",
|
||||
scrollbarWidth: 5
|
||||
}}
|
||||
>
|
||||
{annotations.map((coord, index) => (
|
||||
<li key={index}>
|
||||
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
|
||||
index + 1
|
||||
}:`}</span>
|
||||
<code
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: "black",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
|
||||
</code>
|
||||
<div>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() =>
|
||||
copytoclipboard(
|
||||
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
onClick={() => handleDelete(coord.key)}
|
||||
>
|
||||
<i className="fa-solid fa-trash-can"></i>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi title={"Select PDF"} isOpen={isModal}>
|
||||
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => handleFileChange(e.target.files)}
|
||||
style={{
|
||||
width: "100%",
|
||||
border: "1px solid grey",
|
||||
borderRadius: "4px",
|
||||
padding: "5px",
|
||||
fontSize: 12
|
||||
}}
|
||||
accept=".pdf"
|
||||
required
|
||||
/>
|
||||
<div
|
||||
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
background: "#32a3ac",
|
||||
borderRadius: "2px",
|
||||
boxShadow:
|
||||
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
|
||||
border: "none",
|
||||
textTransform: "uppercase",
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
padding: "0.375rem 0.75rem",
|
||||
textAlign: "center",
|
||||
color: "#ffffff",
|
||||
outline: "none"
|
||||
}}
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DebugPdf;
|
||||
@@ -1,16 +1,21 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { fetchAppInfo, forgetPassword } from "../redux/actions";
|
||||
import Title from "../components/Title";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import Parse from "parse";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
|
||||
function ForgotPassword(props) {
|
||||
function ForgotPassword() {
|
||||
const dispatch = useDispatch();
|
||||
const [state, setState] = useState({
|
||||
email: "",
|
||||
password: "",
|
||||
hideNav: ""
|
||||
});
|
||||
const [sentStatus, setSentStatus] = useState("");
|
||||
const handleChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setState({ ...state, [name]: value });
|
||||
@@ -23,34 +28,47 @@ function ForgotPassword(props) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
localStorage.setItem("appLogo", props.appInfo.applogo);
|
||||
localStorage.setItem("appName", props.appInfo.appname);
|
||||
localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
|
||||
localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
|
||||
localStorage.setItem(
|
||||
"userSettings",
|
||||
JSON.stringify(props.appInfo.settings)
|
||||
);
|
||||
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("appName", appInfo.appname);
|
||||
// localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
|
||||
// localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
props.forgetPassword(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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
props.fetchAppInfo(localStorage.getItem("domain"));
|
||||
dispatch(fetchAppInfo());
|
||||
resize();
|
||||
window.addEventListener("resize", resize);
|
||||
return () => window.removeEventListener("resize", resize);
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
let image = props.appInfo.applogo;
|
||||
let image = appInfo.applogo;
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<Title title="Forgot password page" />
|
||||
{sentStatus === "success" && (
|
||||
<Alert type="success">
|
||||
Reset password link has been sent to your email id
|
||||
</Alert>
|
||||
)}
|
||||
{sentStatus === "failed" && (
|
||||
<Alert type={"danger"}>Please setup email adapter </Alert>
|
||||
)}
|
||||
<div>
|
||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
||||
<div className="w-[250px] h-[66px] inline-block">
|
||||
@@ -111,10 +129,4 @@ function ForgotPassword(props) {
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return { appInfo: state.appInfo, forgotPassword: state.forgotPassword };
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, { fetchAppInfo, forgetPassword })(
|
||||
ForgotPassword
|
||||
);
|
||||
export default ForgotPassword;
|
||||
@@ -0,0 +1,440 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { formJson } from "../json/FormJson";
|
||||
import AddUser from "../components/AddUser";
|
||||
import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||
import Parse from "parse";
|
||||
import DropboxChooser from "../components/shared/fields/DropboxChoose";
|
||||
import Alert from "../primitives/Alert";
|
||||
import SelectFolder from "../components/shared/fields/SelectFolder";
|
||||
import SignersInput from "../components/shared/fields/SignersInput";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
const { id } = useParams();
|
||||
|
||||
if (id === "lM0xRnM3iE") {
|
||||
return <AddUser />;
|
||||
} else {
|
||||
const config = formJson[id];
|
||||
if (config) {
|
||||
return <Forms {...config} />;
|
||||
} else {
|
||||
return <PageNotFound prefix={"Form"} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Forms = (props) => {
|
||||
const maxFileSize = 20;
|
||||
const navigate = useNavigate();
|
||||
const [signers, setSigners] = useState([]);
|
||||
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||
const [formData, setFormData] = useState({
|
||||
Name: "",
|
||||
Description: "",
|
||||
Note: "",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "false"
|
||||
});
|
||||
const [fileupload, setFileUpload] = useState([]);
|
||||
const [fileload, setfileload] = useState(false);
|
||||
const [percentage, setpercentage] = useState(0);
|
||||
const [isReset, setIsReset] = useState(false);
|
||||
const [isAlert, setIsAlert] = useState(false);
|
||||
const [isSubmit, setIsSubmit] = useState(false);
|
||||
const [isErr, setIsErr] = useState("");
|
||||
const handleStrInput = (e) => {
|
||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||
};
|
||||
useEffect(() => {
|
||||
handleReset();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.title]);
|
||||
const handleFileInput = (e) => {
|
||||
setpercentage(0);
|
||||
try {
|
||||
let files = e.target.files;
|
||||
if (typeof files[0] !== "undefined") {
|
||||
const mb = Math.round(files[0].size / Math.pow(1024, 2));
|
||||
if (mb > maxFileSize) {
|
||||
alert(
|
||||
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
|
||||
);
|
||||
setFileUpload([]);
|
||||
e.target.value = "";
|
||||
return;
|
||||
} else {
|
||||
handleFileUpload(files[0]);
|
||||
}
|
||||
} else {
|
||||
alert("Please select file.");
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = async (file) => {
|
||||
setfileload(true);
|
||||
const fileName = file.name;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const pdfFile = file;
|
||||
const parseFile = new Parse.File(name, pdfFile);
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// The response object will contain information about the uploaded file
|
||||
// You can access the URL of the uploaded file using response.url()
|
||||
setFileUpload(response.url());
|
||||
setfileload(false);
|
||||
if (response.url()) {
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const dropboxSuccess = async (files) => {
|
||||
setfileload(true);
|
||||
const file = files[0];
|
||||
const url = file.link;
|
||||
const mb = Math.round(file.bytes / Math.pow(1024, 2));
|
||||
|
||||
if (mb > maxFileSize) {
|
||||
setTimeout(() => {
|
||||
alert(
|
||||
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
|
||||
);
|
||||
}, 500);
|
||||
return;
|
||||
} else {
|
||||
const name = sanitizeFileName(file.name);
|
||||
|
||||
const parseFile = new Parse.File(name, { uri: url });
|
||||
|
||||
try {
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
const percentCompleted = Math.round((loaded * 100) / total);
|
||||
setpercentage(percentCompleted);
|
||||
}
|
||||
}
|
||||
});
|
||||
setFileUpload(response.url());
|
||||
setfileload(false);
|
||||
|
||||
if (response.url()) {
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
setfileload(false);
|
||||
setpercentage(0);
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const dropboxCancel = async () => {};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmit(true);
|
||||
try {
|
||||
const currentUser = Parse.User.current();
|
||||
const object = new Parse.Object(props.Cls);
|
||||
object.set("Name", formData?.Name);
|
||||
object.set("Description", formData?.Description);
|
||||
object.set("Note", formData?.Note);
|
||||
if (props.title === "Request Signatures") {
|
||||
object.set(
|
||||
"TimeToCompleteDays",
|
||||
parseInt(formData?.TimeToCompleteDays)
|
||||
);
|
||||
}
|
||||
if (props.title !== "Sign Yourself") {
|
||||
const isChecked = formData.SendinOrder === "false" ? false : true;
|
||||
object.set("SendinOrder", isChecked);
|
||||
}
|
||||
object.set("URL", fileupload);
|
||||
object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
||||
if (folder && folder.ObjectId) {
|
||||
object.set("Folder", {
|
||||
__type: "Pointer",
|
||||
className: props.Cls,
|
||||
objectId: folder.ObjectId
|
||||
});
|
||||
}
|
||||
if (signers && signers.length > 0) {
|
||||
object.set("Signers", signers);
|
||||
}
|
||||
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
object.set("ExtUserPtr", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Users",
|
||||
objectId: ExtCls[0].objectId
|
||||
});
|
||||
|
||||
const res = await object.save();
|
||||
if (res) {
|
||||
setSigners([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
setFormData({
|
||||
Name: "",
|
||||
Description: "",
|
||||
Note: ""
|
||||
});
|
||||
setFileUpload([]);
|
||||
setpercentage(0);
|
||||
navigate(`/${props?.redirectRoute}/${res.id}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
setIsErr(true);
|
||||
} finally {
|
||||
setIsAlert(true);
|
||||
setTimeout(() => {
|
||||
setIsAlert(false);
|
||||
}, 1000);
|
||||
setIsSubmit(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFolder = (data) => {
|
||||
setFolder(data);
|
||||
};
|
||||
const handleSigners = (data) => {
|
||||
if (data && data.length > 0) {
|
||||
const updateSigners = data.map((x) => ({
|
||||
__type: "Pointer",
|
||||
className: "contracts_Contactbook",
|
||||
objectId: x
|
||||
}));
|
||||
setSigners(updateSigners);
|
||||
}
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setIsReset(true);
|
||||
setSigners([]);
|
||||
setFolder({ ObjectId: "", Name: "" });
|
||||
setFormData({
|
||||
Name: "",
|
||||
Description: "",
|
||||
Note:
|
||||
props.title === "Sign Yourself"
|
||||
? "Note to myself"
|
||||
: "Please review and sign this document",
|
||||
TimeToCompleteDays: 15,
|
||||
SendinOrder: "true"
|
||||
});
|
||||
setFileUpload([]);
|
||||
setpercentage(0);
|
||||
setTimeout(() => setIsReset(false), 50);
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<Title title={props?.title} />
|
||||
{isAlert && (
|
||||
<Alert type={isErr ? "danger" : "success"}>
|
||||
{isErr
|
||||
? "Something went wrong please try again!"
|
||||
: `${props.msgVar} created successfully!`}
|
||||
</Alert>
|
||||
)}
|
||||
{isSubmit ? (
|
||||
<div style={{ height: "300px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: "45%",
|
||||
marginTop: "150px",
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
||||
{fileload && (
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
||||
<div
|
||||
className="h-2 rounded-full bg-blue-500"
|
||||
style={{ width: `${percentage}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="text-black text-sm">{percentage}%</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs">
|
||||
<label className="block">
|
||||
File<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected :{" "}
|
||||
{fileupload?.split("/")?.pop()?.split("_")[1]}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setFileUpload([]);
|
||||
}}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<input
|
||||
type="file"
|
||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
onChange={(e) => handleFileInput(e)}
|
||||
accept="application/pdf"
|
||||
required
|
||||
/>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
<DropboxChooser
|
||||
onSuccess={dropboxSuccess}
|
||||
onCancel={dropboxCancel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
{props.title === "New Template"
|
||||
? "Template Title"
|
||||
: "Document Title"}
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Name"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Name}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.signers && (
|
||||
<SignersInput onChange={handleSigners} isReset={isReset} required />
|
||||
)}
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Note<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="Note"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.Note}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<SelectFolder
|
||||
onSuccess={handleFolder}
|
||||
folderCls={props.Cls}
|
||||
isReset={isReset}
|
||||
/>
|
||||
|
||||
{props.title === "Request Signatures" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Time To Complete (Days)
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
name="TimeToCompleteDays"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
value={formData.TimeToCompleteDays}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.title !== "Sign Yourself" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Send In Order</label>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"true"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "true"}
|
||||
className=""
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">Yes</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||
<input
|
||||
type="radio"
|
||||
value={"false"}
|
||||
name="SendinOrder"
|
||||
checked={formData.SendinOrder === "false"}
|
||||
onChange={handleStrInput}
|
||||
/>
|
||||
<div className="text-center">No</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
className={`${
|
||||
isSubmit && "cursor-progress"
|
||||
} bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
|
||||
type="submit"
|
||||
disabled={isSubmit}
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
<div
|
||||
className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
onClick={() => handleReset()}
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default Form;
|
||||
@@ -0,0 +1,192 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import axios from "axios";
|
||||
import Alert from "../primitives/Alert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { rejectBtn, submitBtn } from "../constant/const";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
|
||||
function GenerateToken() {
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [apiToken, SetApiToken] = useState("");
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isGenerate, setIsGenerate] = useState(false);
|
||||
const [isErr, setIsErr] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchToken();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const fetchToken = async () => {
|
||||
try {
|
||||
const url = parseBaseUrl + "functions/getapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const res = await axios.post(url, {}, { headers: headers });
|
||||
if (res) {
|
||||
SetApiToken(res.data.result.result);
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
SetApiToken();
|
||||
setIsLoader(false);
|
||||
console.log("Err", err);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
try {
|
||||
const url = parseBaseUrl + "functions/generateapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.post(url, {}, { headers: headers }).then((res) => {
|
||||
if (res) {
|
||||
SetApiToken(res.data.result.token);
|
||||
// localStorage.setItem("apiToken", res.data.result.token);
|
||||
setIsGenerate(true);
|
||||
setTimeout(() => {
|
||||
setIsGenerate(false);
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
console.error("Error while generating Token");
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
|
||||
console.log("err", error);
|
||||
}
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleModal = () => setIsModal(!isModal);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"API Token"} />
|
||||
{isGenerate && (
|
||||
<Alert type="success">Token generated successfully!</Alert>
|
||||
)}
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
||||
{isLoader ? (
|
||||
<div
|
||||
style={{
|
||||
height: "100vh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "45px",
|
||||
color: "#3dd3e0"
|
||||
}}
|
||||
className="loader-37"
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-white flex flex-col justify-center shadow rounded">
|
||||
<PremiumAlertHeader />
|
||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
||||
API Token{" "}
|
||||
<Tooltip
|
||||
url={"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"}
|
||||
/>
|
||||
</h1>
|
||||
<ul className="w-full flex flex-col p-2 text-sm">
|
||||
<li
|
||||
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<span className="w-[40%]">Api Token:</span>{" "}
|
||||
<span
|
||||
id="token"
|
||||
className="w-[60%] md:text-end cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
{apiToken && apiToken}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
)
|
||||
}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
View Docs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={"Regenerate Token"}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
Are you sure you want to regenerate token it will expire old
|
||||
token?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className={submitBtn + "ml-[2px]"}
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button onClick={handleModal} className={rejectBtn}>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default GenerateToken;
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useState } from "react";
|
||||
import "../css/LoginPage.css";
|
||||
import loader from "../assests/loader2.gif";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import "../styles/loginPage.css";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import axios from "axios";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { themeColor } from "../utils/ThemeColor/backColor";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../constant/const";
|
||||
import { contractUsers } from "../constant/Utils";
|
||||
|
||||
function Login() {
|
||||
function GuestLogin() {
|
||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
||||
let navigate = useNavigate();
|
||||
const [email, setEmail] = useState(userMail);
|
||||
@@ -18,6 +17,7 @@ function Login() {
|
||||
|
||||
useEffect(() => {
|
||||
handleServerUrl();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||
@@ -103,24 +103,37 @@ function Login() {
|
||||
setLoading(false);
|
||||
} else {
|
||||
let _user = user.data.result;
|
||||
const parseId = localStorage.getItem("parseAppId");
|
||||
const contractUserDetails = await contractUsers(_user.email);
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem(
|
||||
`Parse/${parseId}/currentUser`,
|
||||
JSON.stringify(_user)
|
||||
);
|
||||
if (contractUserDetails) {
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
JSON.stringify(contractUserDetails)
|
||||
);
|
||||
}
|
||||
|
||||
localStorage.setItem("username", _user.name);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||
localStorage.setItem("isGuestSigner", true);
|
||||
setLoading(false);
|
||||
navigate(
|
||||
`/loadmf/signmicroapp/recipientSignPdf/${id}/${contactBookId}`
|
||||
);
|
||||
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
}
|
||||
} else {
|
||||
alert("Please Enter OTP!");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "2rem" }}>
|
||||
<div style={{ padding: "2rem", background: "white" }}>
|
||||
{isLoading ? (
|
||||
<div
|
||||
style={{
|
||||
@@ -184,7 +197,7 @@ function Login() {
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
background: themeColor(),
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
@@ -201,7 +214,7 @@ function Login() {
|
||||
<button
|
||||
className="verifyBtn"
|
||||
style={{
|
||||
background: themeColor(),
|
||||
background: themeColor,
|
||||
color: "white",
|
||||
marginLeft: "0px !important"
|
||||
}}
|
||||
@@ -237,7 +250,7 @@ function Login() {
|
||||
{loading ? (
|
||||
<button
|
||||
style={{
|
||||
background: themeColor(),
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
@@ -256,7 +269,7 @@ function Login() {
|
||||
type="button"
|
||||
onClick={(e) => VerifyOTP(e)}
|
||||
style={{
|
||||
background: themeColor(),
|
||||
background: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="verifyBtn"
|
||||
@@ -274,4 +287,4 @@ function Login() {
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
export default GuestLogin;
|
||||
@@ -1,24 +1,29 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import "../styles/toast.css";
|
||||
import "../styles/loader.css";
|
||||
import { connect } from "react-redux";
|
||||
import { fetchAppInfo, showTenantName } from "../redux/actions";
|
||||
import { useDispatch } from "react-redux";
|
||||
import axios from "axios";
|
||||
import Title from "../components/Title";
|
||||
import GoogleSignInBtn from "../components/LoginGoogle";
|
||||
// import LoginFacebook from "../components/LoginFacebook";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { NavLink, useNavigate, useLocation } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
|
||||
function Login(props) {
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
function Login() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const { width } = useWindowSize();
|
||||
const [state, setState] = useState({
|
||||
email: "",
|
||||
toastColor: "#5cb85c",
|
||||
toastDescription: "",
|
||||
alertType: "success",
|
||||
alertMsg: "",
|
||||
password: "",
|
||||
passwordVisible: false,
|
||||
mobile: "",
|
||||
@@ -34,18 +39,14 @@ function Login(props) {
|
||||
Destination: ""
|
||||
});
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const image = props?.appInfo?.applogo || undefined;
|
||||
const image = appInfo?.applogo || undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("accesstoken")) {
|
||||
setState({ ...state, loading: true });
|
||||
GetLoginData();
|
||||
}
|
||||
props.fetchAppInfo(
|
||||
localStorage.getItem("domain"),
|
||||
localStorage.getItem("BaseUrl12"),
|
||||
localStorage.getItem("AppID12")
|
||||
);
|
||||
dispatch(fetchAppInfo());
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
const handleChange = (event) => {
|
||||
@@ -62,10 +63,8 @@ function Login(props) {
|
||||
setState({ ...state, loading: true });
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let parseAppId = localStorage.getItem("parseAppId");
|
||||
localStorage.setItem("appLogo", props.appInfo.applogo);
|
||||
localStorage.setItem("appName", props.appInfo.appname);
|
||||
Parse.serverURL = localStorage.getItem("baseUrl");
|
||||
Parse.initialize(localStorage.getItem("parseAppId"));
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("appName", appInfo.appname);
|
||||
// Pass the username and password to logIn function
|
||||
await Parse.User.logIn(email, password)
|
||||
.then(async (user) => {
|
||||
@@ -84,8 +83,8 @@ function Login(props) {
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
let userRoles = [];
|
||||
if (props.appInfo.settings) {
|
||||
let userSettings = props.appInfo.settings;
|
||||
if (appInfo.settings) {
|
||||
let userSettings = appInfo.settings;
|
||||
|
||||
//Get Current user roles
|
||||
let url = `${baseUrl}functions/UserGroups`;
|
||||
@@ -132,6 +131,9 @@ function Login(props) {
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
userSettings.forEach(async (element) => {
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${element.pageType}/${element.pageId}`;
|
||||
if (element.role === _currentRole) {
|
||||
let _role = _currentRole.replace(
|
||||
`${localStorage.getItem("_appName")}_`,
|
||||
@@ -143,11 +145,6 @@ function Login(props) {
|
||||
"extended_class",
|
||||
element.extended_class
|
||||
);
|
||||
localStorage.setItem(
|
||||
"userpointer",
|
||||
element.userpointer
|
||||
);
|
||||
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
@@ -177,8 +174,10 @@ function Login(props) {
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
props.showTenantName(
|
||||
tenentInfo[0].tenentName || ""
|
||||
dispatch(
|
||||
showTenant(
|
||||
tenentInfo[0].tenentName || ""
|
||||
)
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
@@ -217,8 +216,10 @@ function Login(props) {
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
props.showTenantName(
|
||||
tenentInfo[0].tenentName || ""
|
||||
dispatch(
|
||||
showTenant(
|
||||
tenentInfo[0].tenentName || ""
|
||||
)
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
@@ -260,9 +261,8 @@ function Login(props) {
|
||||
localStorage.removeItem(
|
||||
"userDetails"
|
||||
);
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
@@ -274,9 +274,8 @@ function Login(props) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -313,9 +312,8 @@ function Login(props) {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -351,13 +349,16 @@ function Login(props) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
toastColor: "#d9534f",
|
||||
toastDescription: `${error.message}`
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
const x = document.getElementById("snackbar");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
console.log(error);
|
||||
}
|
||||
@@ -367,13 +368,16 @@ function Login(props) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
toastColor: "#d9534f",
|
||||
toastDescription: "Login failed: Invalid username or password."
|
||||
alertType: "danger",
|
||||
alertMsg: "Invalid username or password!"
|
||||
});
|
||||
const x = document.getElementById("snackbar");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
console.error("Error while logging in user", error);
|
||||
});
|
||||
@@ -388,8 +392,8 @@ function Login(props) {
|
||||
setState({ ...state, thirdpartyLoader: value });
|
||||
};
|
||||
const thirdpartyLoginfn = async (sessionToken, billingDate) => {
|
||||
const baseUrl = localStorage.getItem("BaseUrl12");
|
||||
const parseAppId = localStorage.getItem("AppID12");
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const res = await axios.get(baseUrl + "users/me", {
|
||||
headers: {
|
||||
"X-Parse-Session-Token": sessionToken,
|
||||
@@ -414,8 +418,8 @@ function Login(props) {
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
let userRoles = [];
|
||||
if (props.appInfo.settings) {
|
||||
let userSettings = props.appInfo.settings;
|
||||
if (appInfo.settings) {
|
||||
let userSettings = appInfo.settings;
|
||||
|
||||
//Get Current user roles
|
||||
let url = `${baseUrl}functions/UserGroups`;
|
||||
@@ -462,6 +466,9 @@ function Login(props) {
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
userSettings.forEach(async (element) => {
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${element.pageType}/${element.pageId}`;
|
||||
if (element.role === _currentRole) {
|
||||
let _role = _currentRole.replace(
|
||||
`${localStorage.getItem("_appName")}_`,
|
||||
@@ -474,7 +481,6 @@ function Login(props) {
|
||||
"extended_class",
|
||||
element.extended_class
|
||||
);
|
||||
localStorage.setItem("userpointer", element.userpointer);
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
@@ -503,8 +509,8 @@ function Login(props) {
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
props.showTenantName(
|
||||
tenentInfo[0].tenentName || ""
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
@@ -543,8 +549,8 @@ function Login(props) {
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
props.showTenantName(
|
||||
tenentInfo[0].tenentName || ""
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
@@ -569,9 +575,7 @@ function Login(props) {
|
||||
if (billingDate) {
|
||||
if (billingDate > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
@@ -581,9 +585,7 @@ function Login(props) {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -599,9 +601,8 @@ function Login(props) {
|
||||
if (billingDate) {
|
||||
if (billingDate > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
@@ -609,9 +610,7 @@ function Login(props) {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -648,13 +647,16 @@ function Login(props) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
toastColor: "#d9534f",
|
||||
toastDescription: `${error.message}`
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
const x = document.getElementById("snackbar");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
console.log(error);
|
||||
}
|
||||
@@ -664,8 +666,6 @@ function Login(props) {
|
||||
const GetLoginData = async () => {
|
||||
setState({ ...state, loading: true });
|
||||
try {
|
||||
Parse.serverURL = localStorage.getItem("baseUrl");
|
||||
Parse.initialize(localStorage.getItem("parseAppId"));
|
||||
const user = await Parse.User.become(localStorage.getItem("accesstoken"));
|
||||
let _usss = user.toJSON();
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_usss));
|
||||
@@ -718,13 +718,17 @@ function Login(props) {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
toastColor: "#d9534f",
|
||||
toastDescription: `Does not have permissions to access this application.`
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"Does not have permissions to access this application!"
|
||||
});
|
||||
const x = document.getElementById("snackbar");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
// _currentRole = userRoles[0];
|
||||
@@ -732,92 +736,90 @@ function Login(props) {
|
||||
} else {
|
||||
_currentRole = userRoles[0];
|
||||
}
|
||||
let SettingsUser = JSON.parse(userSettings);
|
||||
SettingsUser.forEach(async (element) => {
|
||||
if (element.role === _currentRole) {
|
||||
let _role = _currentRole.replace(
|
||||
`${localStorage.getItem("_appName")}_`,
|
||||
""
|
||||
);
|
||||
localStorage.setItem("_user_role", _role);
|
||||
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem(
|
||||
"extended_class",
|
||||
element.extended_class
|
||||
);
|
||||
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
}).then(
|
||||
(result) => {
|
||||
let tenentInfo = [];
|
||||
const results = [result];
|
||||
if (results) {
|
||||
let extendedInfo_stringify = JSON.stringify(results);
|
||||
let extendedInfo = JSON.parse(extendedInfo_stringify);
|
||||
if (extendedInfo.length > 1) {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
|
||||
localStorage.setItem("showpopup", true);
|
||||
localStorage.setItem("PageLanding", element.pageId);
|
||||
localStorage.setItem("defaultmenuid", element.menuId);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
navigate("/");
|
||||
if (_currentRole && _currentRole !== "contracts_Guest") {
|
||||
let SettingsUser = JSON.parse(userSettings);
|
||||
SettingsUser.forEach(async (item) => {
|
||||
if (item.role === _currentRole) {
|
||||
let _role = _currentRole.replace(`${appInfo.appname}_`, "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem("extended_class", item.extended_class);
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
}).then(
|
||||
(result) => {
|
||||
let tenentInfo = [];
|
||||
const results = [result];
|
||||
if (results) {
|
||||
let extendedInfo_stringify = JSON.stringify(results);
|
||||
let extendedInfo = JSON.parse(extendedInfo_stringify);
|
||||
if (extendedInfo.length > 1) {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
localStorage.setItem("showpopup", true);
|
||||
localStorage.setItem("PageLanding", item.pageId);
|
||||
localStorage.setItem("defaultmenuid", item.menuId);
|
||||
localStorage.setItem("pageType", item.pageType);
|
||||
navigate("/");
|
||||
} else {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
localStorage.setItem("PageLanding", item.pageId);
|
||||
localStorage.setItem("defaultmenuid", item.menuId);
|
||||
localStorage.setItem("pageType", item.pageType);
|
||||
navigate(`/${item.pageType}/${item.pageId}`);
|
||||
}
|
||||
} else {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
localStorage.setItem("PageLanding", element.pageId);
|
||||
localStorage.setItem("defaultmenuid", element.menuId);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
navigate(`/${element.pageType}/${element.pageId}`);
|
||||
localStorage.setItem("PageLanding", item.pageId);
|
||||
localStorage.setItem("defaultmenuid", item.menuId);
|
||||
localStorage.setItem("pageType", item.pageType);
|
||||
navigate(`/${item.pageType}/${item.pageId}`);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem("PageLanding", element.pageId);
|
||||
localStorage.setItem("defaultmenuid", element.menuId);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
navigate(`/${element.pageType}/${element.pageId}`);
|
||||
},
|
||||
(error) => {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "You don`t have access to this application!"
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
localStorage.setItem("accesstoken", null);
|
||||
console.error("Error while fetching Follow", error);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
setState({
|
||||
...state,
|
||||
loading: false,
|
||||
toastColor: "#d9534f",
|
||||
toastDescription:
|
||||
"You don`t have access to this application."
|
||||
});
|
||||
const x = document.getElementById("snackbar");
|
||||
x.className = "show";
|
||||
setTimeout(function () {
|
||||
x.className = x.className.replace("show", "");
|
||||
}, 2000);
|
||||
localStorage.setItem("accesstoken", null);
|
||||
console.error("Error while fetching Follow", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setState({ ...state, loading: false });
|
||||
handleCloseModal();
|
||||
}
|
||||
} else {
|
||||
console.log("User Role Not Found.");
|
||||
}
|
||||
@@ -896,8 +898,8 @@ function Login(props) {
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("BaseUrl12");
|
||||
let appid = localStorage.getItem("AppID12");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
@@ -908,8 +910,8 @@ function Login(props) {
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("BaseUrl12", baseUrl);
|
||||
localStorage.setItem("AppID12", appid);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
};
|
||||
return (
|
||||
<div className="bg-white">
|
||||
@@ -943,7 +945,7 @@ function Login(props) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{props.isloginVisible && props.isloginVisible ? (
|
||||
{appInfo && appInfo.appId ? (
|
||||
<>
|
||||
<div aria-labelledby="loginHeading" role="region">
|
||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-4 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
||||
@@ -1051,7 +1053,7 @@ function Login(props) {
|
||||
</div>
|
||||
</form>
|
||||
<br />
|
||||
{(props.appInfo.fbAppId || props.appInfo.googleClietId) && (
|
||||
{(appInfo.fbAppId || appInfo.googleClietId) && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
@@ -1082,9 +1084,9 @@ function Login(props) {
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
{/* {props.appInfo.fbAppId && props.appInfo.fbAppId !== "" ? (
|
||||
{/* {appInfo.fbAppId && appInfo.fbAppId !== "" ? (
|
||||
<LoginFacebook
|
||||
FBCred={props.appInfo.fbAppId}
|
||||
FBCred={appInfo.fbAppId}
|
||||
thirdpartyLoginfn={thirdpartyLoginfn}
|
||||
thirdpartyLoader={state.thirdpartyLoader}
|
||||
setThirdpartyLoader={setThirdpartyLoader}
|
||||
@@ -1100,10 +1102,9 @@ function Login(props) {
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
{props.appInfo.googleClietId &&
|
||||
props.appInfo.googleClietId !== "" ? (
|
||||
{appInfo.googleClietId && appInfo.googleClietId !== "" ? (
|
||||
<GoogleSignInBtn
|
||||
GoogleCred={props.appInfo.googleClietId}
|
||||
GoogleCred={appInfo.googleClietId}
|
||||
thirdpartyLoginfn={thirdpartyLoginfn}
|
||||
thirdpartyLoader={state.thirdpartyLoader}
|
||||
setThirdpartyLoader={setThirdpartyLoader}
|
||||
@@ -1125,109 +1126,78 @@ function Login(props) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="snackbar"
|
||||
role="alert"
|
||||
style={{ backgroundColor: state.toastColor }}
|
||||
>
|
||||
{state.toastDescription}
|
||||
</div>
|
||||
<Alert type={state.alertType}>{state.alertMsg}</Alert>
|
||||
</div>
|
||||
{isModal && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="exampleModal"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{
|
||||
display: "block",
|
||||
zIndex: 1,
|
||||
backgroundColor: "rgba(0,0,0,0.5)"
|
||||
}}
|
||||
>
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<h5 className="modal-title font-semibold">
|
||||
Additional Info
|
||||
</h5>
|
||||
<span>
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<form className="text-sm">
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="Company"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Company{" "}
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="Company"
|
||||
value={userDetails.Company}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Company: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label
|
||||
htmlFor="JobTitle"
|
||||
style={{ display: "flex" }}
|
||||
className="col-form-label"
|
||||
>
|
||||
Job Title
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
id="JobTitle"
|
||||
value={userDetails.Destination}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Destination: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#17a2b8] text-sm p-2 text-white rounded uppercase"
|
||||
onClick={(e) => handleSubmitbtn(e)}
|
||||
style={{ marginRight: 10 }}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-[#6c757d] text-sm p-2 text-white rounded uppercase"
|
||||
onClick={handleCloseModal}
|
||||
style={{ width: 90 }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
|
||||
<form className="px-4 py-3">
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="Company"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
id="Company"
|
||||
value={userDetails.Company}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Company: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="JobTitle"
|
||||
style={{ display: "flex" }}
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Job Title
|
||||
<span style={{ fontSize: 13, color: "red" }}>*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
id="JobTitle"
|
||||
value={userDetails.Destination}
|
||||
onChange={(e) =>
|
||||
setUserDetails({
|
||||
...userDetails,
|
||||
Destination: e.target.value
|
||||
})
|
||||
}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||
onClick={(e) => handleSubmitbtn(e)}
|
||||
style={{
|
||||
marginRight: 10,
|
||||
backgroundColor: modalSubmitBtnColor
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
|
||||
onClick={handleCloseModal}
|
||||
style={{ width: 75, backgroundColor: modalCancelBtnColor }}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalUi>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
@@ -1244,16 +1214,4 @@ function Login(props) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
if (Object.keys(state.appInfo).length !== 0) {
|
||||
return { appInfo: state.appInfo, isloginVisible: true };
|
||||
} else {
|
||||
return { appInfo: state.appInfo, isloginVisible: false };
|
||||
}
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
fetchAppInfo,
|
||||
showTenantName
|
||||
})(Login);
|
||||
export default Login;
|
||||