mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-11 12:17:41 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e073e83ad6 | ||
|
|
69140d2139 | ||
|
|
5518d6932e | ||
|
|
f14e301b6c | ||
|
|
901ddf35bb | ||
|
|
9aa3166528 | ||
|
|
a90e679228 | ||
|
|
90b5e3c860 | ||
|
|
74f6612271 | ||
|
|
5c3f967128 | ||
|
|
2900464272 | ||
|
|
302f38d472 | ||
|
|
0be1816046 | ||
|
|
feb162e03e | ||
|
|
a793fc3722 | ||
|
|
ffdabe4a00 | ||
|
|
ba1c3145f3 | ||
|
|
4898c4a851 | ||
|
|
85aa6b2947 | ||
|
|
3b02d2bcdb | ||
|
|
ed01f4a7a0 | ||
|
|
1f395cf25f | ||
|
|
cf98e73d13 | ||
|
|
10112ac20d | ||
|
|
9a0183274f | ||
|
|
6b2e16c21d | ||
|
|
3c51baf1c9 | ||
|
|
3f4be1c4f3 | ||
|
|
62b36f58f7 | ||
|
|
ba3591c7bc | ||
|
|
1ec11b8733 | ||
|
|
ab89d3b71f | ||
|
|
1811173648 | ||
|
|
414e2d819c | ||
|
|
0769101f28 | ||
|
|
5dc86119d3 | ||
|
|
4999dc2b18 | ||
|
|
962d36c088 | ||
|
|
d071986aac | ||
|
|
afb4de9d0c | ||
|
|
bc5c8cac33 | ||
|
|
be93e6a214 | ||
|
|
37748057e3 | ||
|
|
eb3bb77692 | ||
|
|
df0ce3f086 | ||
|
|
0a99b5181e | ||
|
|
8a5d3a0368 | ||
|
|
c00406e401 | ||
|
|
aeead23c86 | ||
|
|
83fe88911f | ||
|
|
527bca32d2 | ||
|
|
9bf4940c2e | ||
|
|
a653df6935 | ||
|
|
dfc6da388c | ||
|
|
3e2c822bbc | ||
|
|
1a20841a94 | ||
|
|
c6be81923b | ||
|
|
6b47f6a27b | ||
|
|
33b0111ed0 | ||
|
|
48fd9e52b3 | ||
|
|
d85074b127 | ||
|
|
5ada9a411b | ||
|
|
6f50de1ef1 | ||
|
|
612150cf4b | ||
|
|
634db83e8e | ||
|
|
b32c8068ef | ||
|
|
ae464274b0 | ||
|
|
c5ceba8d57 | ||
|
|
903d626c27 | ||
|
|
e5b4fb123f | ||
|
|
116893c1b4 | ||
|
|
592254a9a3 | ||
|
|
f52c6e24fe | ||
|
|
70ae1e24fc | ||
|
|
c491595fc8 | ||
|
|
6875135ede | ||
|
|
662049d878 | ||
|
|
1764d7f9ba | ||
|
|
07933b56b1 | ||
|
|
f6ba4db62d | ||
|
|
5bd36f91d9 | ||
|
|
c199c3019e | ||
|
|
facbc8e76e | ||
|
|
c3c6f6bab3 | ||
|
|
f5e216fedc | ||
|
|
c40a923388 | ||
|
|
a86f4fd9b7 | ||
|
|
59f6d022cf | ||
|
|
b7beeee260 | ||
|
|
31c861cbe4 | ||
|
|
6a9408b7b4 | ||
|
|
a9fd264a01 | ||
|
|
6c77bbfe75 | ||
|
|
890c3d2b1f | ||
|
|
2433ad5957 | ||
|
|
fffd907d63 | ||
|
|
bf307abe9a | ||
|
|
08a97707b7 | ||
|
|
d99fecd46e | ||
|
|
6c59c0ec9a | ||
|
|
32c75d1fc3 | ||
|
|
7f0f81c334 | ||
|
|
a882f07f57 | ||
|
|
74b81a5363 | ||
|
|
9b75764717 | ||
|
|
c1468112b0 | ||
|
|
950b74789c | ||
|
|
7e4f12e41a | ||
|
|
d75c1d7c0c | ||
|
|
7eacbedf59 | ||
|
|
2e5947279f | ||
|
|
ef5ac7d626 | ||
|
|
658c37cc4d | ||
|
|
ffc8e4b23e | ||
|
|
3fb96fc792 | ||
|
|
71016ba5e0 | ||
|
|
cd1a6fdaf6 | ||
|
|
c2afeed448 | ||
|
|
37fd7bbeda | ||
|
|
71fbde0099 | ||
|
|
ca6cb60d78 | ||
|
|
df1c0ea66c | ||
|
|
46279efc2c | ||
|
|
aca2848406 | ||
|
|
eaec3cf607 | ||
|
|
ad25d73f26 | ||
|
|
481b9ac1e0 | ||
|
|
4cd3791feb | ||
|
|
7de8699437 | ||
|
|
4c162676b1 | ||
|
|
ca390bdfa6 | ||
|
|
22d064fd38 | ||
|
|
444c9bbfdf | ||
|
|
b289686d42 | ||
|
|
eeb878b340 | ||
|
|
7894d69cc9 | ||
|
|
b992d7db24 | ||
|
|
50b72f4d85 | ||
|
|
44639f66b0 | ||
|
|
ae740efced | ||
|
|
66c020d903 | ||
|
|
562a7d6fc8 | ||
|
|
090c17c2c0 | ||
|
|
70637d6143 | ||
|
|
f38e8705de | ||
|
|
ba06ff72a0 | ||
|
|
82312a1e0d | ||
|
|
1a8cf11a3e | ||
|
|
3deacbabc3 | ||
|
|
c28805add9 | ||
|
|
80fb320ae4 | ||
|
|
76c9c1fa53 | ||
|
|
f38d83dbe0 | ||
|
|
ee9411729d | ||
|
|
f6f7c6cecd | ||
|
|
a45fc0dd60 | ||
|
|
348f7810b9 | ||
|
|
45bec128a9 | ||
|
|
4da307315c | ||
|
|
c87aa415a9 | ||
|
|
b1786ec16a | ||
|
|
6ad35e979e | ||
|
|
9d996d5cb7 | ||
|
|
008e3e8783 | ||
|
|
8458906b0b | ||
|
|
6933aa309b | ||
|
|
067ea68d3b | ||
|
|
e4e8a0b721 | ||
|
|
dc392adc1d | ||
|
|
93b0d00ab0 | ||
|
|
d731951159 | ||
|
|
24bf08c14f | ||
|
|
c03819f5a9 | ||
|
|
4521d16891 | ||
|
|
f77525cd8b | ||
|
|
c934f8ccd6 | ||
|
|
4e128e5a5a | ||
|
|
b202d78059 | ||
|
|
e15136ce51 | ||
|
|
39c9211167 | ||
|
|
64864e3f54 | ||
|
|
af0bef6d29 | ||
|
|
130704b32f | ||
|
|
5b139b66a9 | ||
|
|
f9c5041af1 | ||
|
|
1e58b35db5 | ||
|
|
245bf8046b | ||
|
|
94eb5f9efe | ||
|
|
044128f36a | ||
|
|
594383fe14 | ||
|
|
90a585367e | ||
|
|
0b8382df00 | ||
|
|
a54c7e7fd7 | ||
|
|
45a2c88e90 | ||
|
|
e4526f026b | ||
|
|
11a2f6b8ec | ||
|
|
3bf35166fc | ||
|
|
cd50c8cf20 | ||
|
|
055ef3f209 | ||
|
|
964ec0d422 | ||
|
|
ec0e291604 | ||
|
|
836e952ba0 | ||
|
|
c5f39e2a24 | ||
|
|
56dd060531 | ||
|
|
960fbdb0eb | ||
|
|
f8145feebc | ||
|
|
df38d15611 | ||
|
|
bb69518674 | ||
|
|
fee53fb020 | ||
|
|
d086e60ae5 | ||
|
|
51150d4e47 | ||
|
|
1d3cd02f9d | ||
|
|
d03515efc4 | ||
|
|
30e6e4b3fd | ||
|
|
cbdd729e95 | ||
|
|
ac82209d52 | ||
|
|
d5cc6f6758 | ||
|
|
3aa211dc1a | ||
|
|
33d5eecbbf | ||
|
|
1d6c108ba4 | ||
|
|
79da0f285d | ||
|
|
e8eef32958 | ||
|
|
dc1fab5250 | ||
|
|
3e185ef796 | ||
|
|
8cf5adb273 | ||
|
|
803faed97b | ||
|
|
660eaeb714 | ||
|
|
79126700c4 | ||
|
|
6355c02485 | ||
|
|
bce028877b | ||
|
|
e47211a8c0 | ||
|
|
4a4c9eaa2d | ||
|
|
0a5653f53d | ||
|
|
5a90a0f484 | ||
|
|
a3a398bd92 | ||
|
|
7d70162137 | ||
|
|
e4c62c4eeb | ||
|
|
3eadca01fa | ||
|
|
659b7fe0f4 | ||
|
|
ade6694427 | ||
|
|
f8160665a4 | ||
|
|
5778bdecae | ||
|
|
9122c71c90 | ||
|
|
2c2065aca8 | ||
|
|
b1dd4df3c0 | ||
|
|
64fd3580ac | ||
|
|
01273777b7 | ||
|
|
23eaa1ae81 | ||
|
|
3fd0613407 | ||
|
|
d45566257f | ||
|
|
e9eb59aa7a | ||
|
|
9e3ba2cf5c | ||
|
|
81afc92c14 | ||
|
|
d3bf9e9e9b | ||
|
|
18e25f6cd9 | ||
|
|
a6d97d587f | ||
|
|
ff9a495775 | ||
|
|
b29c7d5c7f | ||
|
|
fddeba8d44 | ||
|
|
c564b5741c | ||
|
|
179c3c5dfe | ||
|
|
221f71c061 | ||
|
|
a26e65f76f | ||
|
|
9ba05d23fd | ||
|
|
0f67013005 | ||
|
|
65d5c7f337 | ||
|
|
a7dd47f1b1 | ||
|
|
350212c02f | ||
|
|
d9126d6701 | ||
|
|
51c3a0f71a | ||
|
|
a4bf9ff915 | ||
|
|
28a2a113e2 | ||
|
|
07382822bd | ||
|
|
06677d923c | ||
|
|
534dc3d3f5 | ||
|
|
a69682cebf | ||
|
|
2ae7e8ced2 | ||
|
|
e20b75cfc1 | ||
|
|
37b73e36ca | ||
|
|
cc6b64d140 | ||
|
|
4b765a8e1d | ||
|
|
4a7108e4f6 | ||
|
|
66bf577676 | ||
|
|
13e2d92ce6 | ||
|
|
4b9f22a5bc | ||
|
|
77c61c89a2 | ||
|
|
d04b79001a | ||
|
|
e14b2a2a17 | ||
|
|
6afa97a76b | ||
|
|
3a28b1cdd7 | ||
|
|
23b48838ae | ||
|
|
bddebf2758 | ||
|
|
5a68e18c48 | ||
|
|
280b812e5b | ||
|
|
082f0569e2 | ||
|
|
b01618c8ca | ||
|
|
7b0f0d5b9f | ||
|
|
1381a65c0b | ||
|
|
961ec2b2ba | ||
|
|
022cb20b64 | ||
|
|
7b4fa4fe23 | ||
|
|
c85c21ef8a | ||
|
|
09fb01862d | ||
|
|
f6ce1b45fb | ||
|
|
8fd2d4210e | ||
|
|
0f7f1f1ba2 | ||
|
|
8693ef29f6 | ||
|
|
dc67d2c23d | ||
|
|
761925fced | ||
|
|
c240eb133d | ||
|
|
9daed4a5ed | ||
|
|
ee18f82fa9 | ||
|
|
4085f8259d | ||
|
|
98f84f6cc8 | ||
|
|
da9363f588 | ||
|
|
5afb60a687 | ||
|
|
a18733ad79 | ||
|
|
400e132f11 | ||
|
|
c56830b7be | ||
|
|
f988c25491 | ||
|
|
53bd13e03b | ||
|
|
68edecae18 | ||
|
|
b421c0f1a9 | ||
|
|
3858746d64 | ||
|
|
41e8aa18a7 | ||
|
|
fb8915e9db | ||
|
|
ba2bb07aa9 | ||
|
|
74083d0606 | ||
|
|
0f2862c52e | ||
|
|
61e11b7105 | ||
|
|
d7de8648e9 | ||
|
|
c279fd6525 | ||
|
|
3599fbee21 | ||
|
|
9ad51a1d6a | ||
|
|
03e22dbf33 | ||
|
|
5693fa230b | ||
|
|
8cd90911a8 | ||
|
|
7cf820d205 | ||
|
|
4d01547fd5 | ||
|
|
6597f67f0a | ||
|
|
14ce91350e | ||
|
|
7a5f75ea79 | ||
|
|
a44cb7168f | ||
|
|
1c6b056d6c | ||
|
|
cf6759ce79 | ||
|
|
d422f46f47 | ||
|
|
85f94c8ffa | ||
|
|
91da331ce6 | ||
|
|
3db6946198 | ||
|
|
94c8caa3e6 | ||
|
|
ed1120927c | ||
|
|
79b4c38587 | ||
|
|
2cc5517bc1 | ||
|
|
eddf70f102 | ||
|
|
b6774519f9 | ||
|
|
d6bb83f997 | ||
|
|
55e8042b0a | ||
|
|
d5bc2df20f | ||
|
|
c623e868f7 | ||
|
|
174b9b9941 | ||
|
|
0eb247ae0c | ||
|
|
f96df699e4 | ||
|
|
dcaca98aec | ||
|
|
d70f4dddb6 | ||
|
|
db34c4248c | ||
|
|
1d39db6d7d | ||
|
|
654dadce93 | ||
|
|
e578981791 | ||
|
|
c4297a260e | ||
|
|
ecc453957c | ||
|
|
f14c98505a | ||
|
|
3f23bdd372 | ||
|
|
493731a6af | ||
|
|
28fd98c0db | ||
|
|
b04d2abbab | ||
|
|
387154c07b | ||
|
|
f7606bce35 | ||
|
|
f08758fd71 | ||
|
|
d3b1a37a7c | ||
|
|
4718c653f7 | ||
|
|
f9b8437981 | ||
|
|
b0c73bc63e | ||
|
|
269246659b | ||
|
|
51fe5d133e | ||
|
|
36a1123535 | ||
|
|
3c1ceb4995 | ||
|
|
53d154fa59 |
@@ -205,6 +205,33 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ugoconsonni",
|
||||||
|
"name": "ugoconsonni",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/13661702?v=4",
|
||||||
|
"profile": "https://github.com/ugoconsonni",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "daniel-mutwiri",
|
||||||
|
"name": "Daniel Mutwiri",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/8936960?v=4",
|
||||||
|
"profile": "https://github.com/daniel-mutwiri",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Zathiel",
|
||||||
|
"name": "Zathiel",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/26553418?v=4",
|
||||||
|
"profile": "https://github.com/Zathiel",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ DO_SECRET_ACCESS_KEY=
|
|||||||
# Digital ocean spaces region or AWS s3 region
|
# Digital ocean spaces region or AWS s3 region
|
||||||
DO_REGION=us-west
|
DO_REGION=us-west
|
||||||
# local storage
|
# local storage
|
||||||
USE_LOCAL=TRUE
|
USE_LOCAL=true
|
||||||
|
|
||||||
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
# Email mailgun or set SMTP_ENABLE true to use SMTP config (The app will not initialize if any of these 3 variables are not set) *********************************************************************************************************************
|
||||||
MAILGUN_API_KEY=
|
MAILGUN_API_KEY=
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ We would like to thank all our contributors and users for their support and feed
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/VikramNagwal"><img src="https://avatars.githubusercontent.com/u/123088024?v=4?s=100" width="100px;" alt="Vikram"/><br /><sub><b>Vikram</b></sub></a><br /><a href="#code-VikramNagwal" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ugoconsonni"><img src="https://avatars.githubusercontent.com/u/13661702?v=4?s=100" width="100px;" alt="ugoconsonni"/><br /><sub><b>ugoconsonni</b></sub></a><br /><a href="#code-ugoconsonni" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/daniel-mutwiri"><img src="https://avatars.githubusercontent.com/u/8936960?v=4?s=100" width="100px;" alt="Daniel Mutwiri"/><br /><sub><b>Daniel Mutwiri</b></sub></a><br /><a href="#code-daniel-mutwiri" title="Code">💻</a></td>
|
||||||
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Zathiel"><img src="https://avatars.githubusercontent.com/u/26553418?v=4?s=100" width="100px;" alt="Zathiel"/><br /><sub><b>Zathiel</b></sub></a><br /><a href="#code-Zathiel" title="Code">💻</a><a href="#security-Zathiel" title="Security">🛡️</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Generated
+4074
-3585
File diff suppressed because it is too large
Load Diff
+24
-19
@@ -3,22 +3,24 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^0.8.1",
|
"@formkit/auto-animate": "^0.8.2",
|
||||||
"@radix-ui/themes": "^2.0.3",
|
"@radix-ui/themes": "^3.0.5",
|
||||||
"@react-pdf/renderer": "^3.4.0",
|
"@react-pdf/renderer": "^3.4.4",
|
||||||
"@reduxjs/toolkit": "^2.2.1",
|
"@reduxjs/toolkit": "^2.2.5",
|
||||||
"axios": "^1.6.8",
|
"axios": "^1.7.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"parse": "^4.3.1",
|
"nth-check": "^2.1.1",
|
||||||
|
"parse": "^5.1.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"radix-ui": "^1.0.1",
|
"radix-ui": "^1.0.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.10.2",
|
"react-bootstrap": "^2.10.2",
|
||||||
|
"react-confetti": "^6.1.0",
|
||||||
"react-cookie": "^7.1.4",
|
"react-cookie": "^7.1.4",
|
||||||
"react-datepicker": "^6.4.0",
|
"react-datepicker": "^6.9.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dnd-multi-backend": "^8.0.3",
|
"react-dnd-multi-backend": "^8.0.3",
|
||||||
@@ -28,24 +30,24 @@
|
|||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-konva": "^18.2.10",
|
"react-konva": "^18.2.10",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-pdf": "^7.7.1",
|
"react-pdf": "^9.0.0",
|
||||||
"react-quill": "^2.0.0",
|
"react-quill": "^2.0.0",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.2",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.11",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-scrollbars-custom": "^4.1.1",
|
"react-scrollbars-custom": "^4.1.1",
|
||||||
"react-select": "^5.8.0",
|
"react-select": "^5.8.0",
|
||||||
"react-signature-canvas": "^1.0.6",
|
"react-signature-canvas": "^1.0.6",
|
||||||
"react-tooltip": "^5.26.3",
|
"react-tooltip": "^5.26.4",
|
||||||
"react-web-share": "^2.0.2",
|
"react-web-share": "^2.0.2",
|
||||||
"reactour": "^1.19.2",
|
"reactour": "^1.19.4",
|
||||||
"redux": "^5.0.1",
|
"redux": "^5.0.1",
|
||||||
"redux-thunk": "^3.1.0",
|
"redux-thunk": "^3.1.0",
|
||||||
"regex-parser": "^2.3.0",
|
"regex-parser": "^2.3.0",
|
||||||
"serve": "^14.2.1",
|
"serve": "^14.2.3",
|
||||||
"styled-components": "^4.4.1",
|
"styled-components": "^4.4.1",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^4.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "serve -s build",
|
"start": "serve -s build",
|
||||||
@@ -83,16 +85,19 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/runtime-corejs2": "^7.24.1",
|
"@babel/runtime-corejs2": "^7.24.6",
|
||||||
"autoprefixer": "^10.4.18",
|
"autoprefixer": "^10.4.19",
|
||||||
"commitizen": "^4.3.0",
|
"commitizen": "^4.3.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.5",
|
||||||
"postcss": "^8.4.37",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"pretty-quick": "^3.3.1",
|
"pretty-quick": "^3.3.1",
|
||||||
"tailwindcss": "^3.4.1"
|
"tailwindcss": "^3.4.3"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"nth-check": "$nth-check"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import PlaceHolderSign from "./pages/PlaceHolderSign";
|
|||||||
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
||||||
import LazyPage from "./primitives/LazyPage";
|
import LazyPage from "./primitives/LazyPage";
|
||||||
import { isEnableSubscription } from "./constant/const";
|
import { isEnableSubscription } from "./constant/const";
|
||||||
|
import SSOVerify from "./pages/SSOVerify";
|
||||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||||
@@ -28,8 +29,7 @@ const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
|||||||
const ManageSign = lazy(() => import("./pages/Managesign"));
|
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||||
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||||
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
|
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.min.mjs`;
|
||||||
|
|
||||||
const Loader = () => {
|
const Loader = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -109,6 +109,10 @@ function App() {
|
|||||||
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
path="/login/:id/:userMail/:contactBookId/:serverUrl"
|
||||||
element={<LazyPage Page={GuestLogin} />}
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/login/:base64url"
|
||||||
|
element={<LazyPage Page={GuestLogin} />}
|
||||||
|
/>
|
||||||
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
|
||||||
<Route
|
<Route
|
||||||
path="/forgetpassword"
|
path="/forgetpassword"
|
||||||
@@ -177,6 +181,7 @@ function App() {
|
|||||||
element={<PdfRequestFiles />}
|
element={<PdfRequestFiles />}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/sso" element={<SSOVerify />} />
|
||||||
<Route path="*" element={<PageNotFound />} />
|
<Route path="*" element={<PageNotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const AddSigner = (props) => {
|
|||||||
);
|
);
|
||||||
if (savedUserDetails && addYourself) {
|
if (savedUserDetails && addYourself) {
|
||||||
setName(savedUserDetails.name);
|
setName(savedUserDetails.name);
|
||||||
setPhone(savedUserDetails.phone);
|
setPhone(savedUserDetails?.phone || "");
|
||||||
setEmail(savedUserDetails.email);
|
setEmail(savedUserDetails.email);
|
||||||
}
|
}
|
||||||
}, [addYourself]);
|
}, [addYourself]);
|
||||||
@@ -49,144 +49,162 @@ const AddSigner = (props) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsLoader(true);
|
setIsLoader(true);
|
||||||
try {
|
if (localStorage.getItem("TenantId")) {
|
||||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
|
||||||
contactQuery.set("Name", name);
|
|
||||||
contactQuery.set("Phone", phone);
|
|
||||||
contactQuery.set("Email", email);
|
|
||||||
contactQuery.set("UserRole", "contracts_Guest");
|
|
||||||
|
|
||||||
if (localStorage.getItem("TenantId")) {
|
|
||||||
contactQuery.set("TenantId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "partners_Tenant",
|
|
||||||
objectId: localStorage.getItem("TenantId")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const _users = Parse.Object.extend("User");
|
const user = Parse.User.current();
|
||||||
const _user = new _users();
|
const query = new Parse.Query("contracts_Contactbook");
|
||||||
_user.set("name", name);
|
query.equalTo("CreatedBy", user);
|
||||||
_user.set("username", email);
|
query.notEqualTo("IsDeleted", true);
|
||||||
_user.set("email", email);
|
query.equalTo("Email", email);
|
||||||
_user.set("phone", phone);
|
const res = await query.first();
|
||||||
_user.set("password", phone);
|
// console.log(res);
|
||||||
|
if (!res) {
|
||||||
const user = await _user.save();
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
if (user) {
|
contactQuery.set("Name", name);
|
||||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
if (phone) {
|
||||||
const headers = {
|
contactQuery.set("Phone", phone);
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
const body = {
|
|
||||||
appName: localStorage.getItem("_appName"),
|
|
||||||
roleName: "contracts_Guest",
|
|
||||||
userId: user.id
|
|
||||||
};
|
|
||||||
await axios.post(roleurl, body, { headers: headers });
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
contactQuery.set(
|
|
||||||
"CreatedBy",
|
|
||||||
Parse.User.createWithoutData(currentUser.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
contactQuery.set("UserId", user);
|
|
||||||
const acl = new Parse.ACL();
|
|
||||||
acl.setPublicReadAccess(true);
|
|
||||||
acl.setPublicWriteAccess(true);
|
|
||||||
acl.setReadAccess(currentUser.id, true);
|
|
||||||
acl.setWriteAccess(currentUser.id, true);
|
|
||||||
|
|
||||||
contactQuery.setACL(acl);
|
|
||||||
|
|
||||||
const res = await contactQuery.save();
|
|
||||||
|
|
||||||
const parseData = JSON.parse(JSON.stringify(res));
|
|
||||||
if (props.details) {
|
|
||||||
props.details({
|
|
||||||
value: parseData[props.valueKey],
|
|
||||||
label: parseData[props.displayKey]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (props.closePopup) {
|
|
||||||
props.closePopup();
|
|
||||||
}
|
|
||||||
if (props.handleUserData) {
|
|
||||||
props.handleUserData(parseData);
|
|
||||||
}
|
}
|
||||||
|
contactQuery.set("Email", email);
|
||||||
|
contactQuery.set("UserRole", "contracts_Guest");
|
||||||
|
|
||||||
|
contactQuery.set("TenantId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "partners_Tenant",
|
||||||
|
objectId: localStorage.getItem("TenantId")
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const _users = Parse.Object.extend("User");
|
||||||
|
const _user = new _users();
|
||||||
|
_user.set("name", name);
|
||||||
|
_user.set("username", email);
|
||||||
|
_user.set("email", email);
|
||||||
|
_user.set("password", email);
|
||||||
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
const user = await _user.save();
|
||||||
|
if (user) {
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_Guest",
|
||||||
|
userId: user.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", user);
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoader(false);
|
||||||
|
// Reset the form fields
|
||||||
|
setAddYourself(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err ", err);
|
||||||
|
if (err.code === 202) {
|
||||||
|
const params = { email: email };
|
||||||
|
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||||
|
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const body = {
|
||||||
|
appName: localStorage.getItem("_appName"),
|
||||||
|
roleName: "contracts_Guest",
|
||||||
|
userId: userRes.id
|
||||||
|
};
|
||||||
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
contactQuery.set(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactQuery.set("UserId", {
|
||||||
|
__type: "Pointer",
|
||||||
|
className: "_User",
|
||||||
|
objectId: userRes.id
|
||||||
|
});
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setPublicReadAccess(true);
|
||||||
|
acl.setPublicWriteAccess(true);
|
||||||
|
acl.setReadAccess(currentUser.id, true);
|
||||||
|
acl.setWriteAccess(currentUser.id, true);
|
||||||
|
|
||||||
|
contactQuery.setACL(acl);
|
||||||
|
const res = await contactQuery.save();
|
||||||
|
|
||||||
|
const parseData = JSON.parse(JSON.stringify(res));
|
||||||
|
if (props.details) {
|
||||||
|
props.details({
|
||||||
|
value: parseData[props.valueKey],
|
||||||
|
label: parseData[props.displayKey]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (props.closePopup) {
|
||||||
|
props.closePopup();
|
||||||
|
}
|
||||||
|
if (props.handleUserData) {
|
||||||
|
props.handleUserData(parseData);
|
||||||
|
}
|
||||||
|
setIsLoader(false);
|
||||||
|
// Reset the form fields
|
||||||
|
setAddYourself(false);
|
||||||
|
setName("");
|
||||||
|
setPhone("");
|
||||||
|
setEmail("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Contact already exist!");
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
// Reset the form fields
|
|
||||||
setAddYourself(false);
|
|
||||||
setName("");
|
|
||||||
setPhone("");
|
|
||||||
setEmail("");
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err ", err);
|
console.log("err in fetch contact", err);
|
||||||
if (err.code === 202) {
|
setIsLoader(false);
|
||||||
const params = { email: email };
|
alert("something went wrong, please try again later");
|
||||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
|
||||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseAppId,
|
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
|
||||||
};
|
|
||||||
const body = {
|
|
||||||
appName: localStorage.getItem("_appName"),
|
|
||||||
roleName: "contracts_Guest",
|
|
||||||
userId: userRes.id
|
|
||||||
};
|
|
||||||
await axios.post(roleurl, body, { headers: headers });
|
|
||||||
const currentUser = Parse.User.current();
|
|
||||||
contactQuery.set(
|
|
||||||
"CreatedBy",
|
|
||||||
Parse.User.createWithoutData(currentUser.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
contactQuery.set("UserId", {
|
|
||||||
__type: "Pointer",
|
|
||||||
className: "_User",
|
|
||||||
objectId: userRes.id
|
|
||||||
});
|
|
||||||
const acl = new Parse.ACL();
|
|
||||||
acl.setPublicReadAccess(true);
|
|
||||||
acl.setPublicWriteAccess(true);
|
|
||||||
acl.setReadAccess(currentUser.id, true);
|
|
||||||
acl.setWriteAccess(currentUser.id, true);
|
|
||||||
|
|
||||||
contactQuery.setACL(acl);
|
|
||||||
const res = await contactQuery.save();
|
|
||||||
|
|
||||||
const parseData = JSON.parse(JSON.stringify(res));
|
|
||||||
if (props.details) {
|
|
||||||
props.details({
|
|
||||||
value: parseData[props.valueKey],
|
|
||||||
label: parseData[props.displayKey]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (props.closePopup) {
|
|
||||||
props.closePopup();
|
|
||||||
}
|
|
||||||
if (props.handleUserData) {
|
|
||||||
props.handleUserData(parseData);
|
|
||||||
}
|
|
||||||
setIsLoader(false);
|
|
||||||
// Reset the form fields
|
|
||||||
setAddYourself(false);
|
|
||||||
setName("");
|
|
||||||
setPhone("");
|
|
||||||
setEmail("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} else {
|
||||||
// console.log("err", err);
|
|
||||||
setIsLoader(false);
|
setIsLoader(false);
|
||||||
alert("something went wrong!");
|
alert("something went wrong, please try again later");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -264,10 +282,10 @@ const AddSigner = (props) => {
|
|||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs lowercase"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
@@ -276,14 +294,12 @@ const AddSigner = (props) => {
|
|||||||
className="block text-xs text-gray-700 font-semibold"
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
>
|
>
|
||||||
Phone
|
Phone
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="phone"
|
id="phone"
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -62,8 +62,10 @@ const AddUser = (props) => {
|
|||||||
_user.set("name", name);
|
_user.set("name", name);
|
||||||
_user.set("username", email);
|
_user.set("username", email);
|
||||||
_user.set("email", email);
|
_user.set("email", email);
|
||||||
_user.set("phone", phone);
|
_user.set("password", email);
|
||||||
_user.set("password", phone);
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await _user.save();
|
const user = await _user.save();
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import axios from "axios";
|
||||||
|
import SuggestionInput from "./shared/fields/SuggestionInput";
|
||||||
|
|
||||||
|
const BulkSendUi = (props) => {
|
||||||
|
const [forms, setForms] = useState([]);
|
||||||
|
const [formId, setFormId] = useState(2);
|
||||||
|
const formRef = useRef(null);
|
||||||
|
const [scrollOnNextUpdate, setScrollOnNextUpdate] = useState(false);
|
||||||
|
const [isSubmit, setIsSubmit] = useState(false);
|
||||||
|
const [allowedForm, setAllowedForm] = useState(0);
|
||||||
|
const [isSignatureExist, setIsSignatureExist] = useState();
|
||||||
|
const allowedSigners = 50;
|
||||||
|
useEffect(() => {
|
||||||
|
signatureExist();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
//function to check atleast one signature field exist
|
||||||
|
const signatureExist = () => {
|
||||||
|
const getPlaceholder = props.item?.Placeholders;
|
||||||
|
const checkIsSignatureExistt = getPlaceholder?.every((placeholderObj) =>
|
||||||
|
placeholderObj?.placeHolder?.some((holder) =>
|
||||||
|
holder?.pos?.some((posItem) => posItem?.type === "signature")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
setIsSignatureExist(checkIsSignatureExistt);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollOnNextUpdate && formRef.current) {
|
||||||
|
formRef.current.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "end",
|
||||||
|
inline: "nearest"
|
||||||
|
});
|
||||||
|
setScrollOnNextUpdate(false);
|
||||||
|
}
|
||||||
|
}, [forms, scrollOnNextUpdate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(() => {
|
||||||
|
if (props?.Placeholders?.length > 0) {
|
||||||
|
let users = [];
|
||||||
|
props?.Placeholders?.forEach((element) => {
|
||||||
|
if (!element.signerObjId) {
|
||||||
|
users = [
|
||||||
|
...users,
|
||||||
|
{
|
||||||
|
fieldId: element.Id,
|
||||||
|
email: "",
|
||||||
|
label: element.Role,
|
||||||
|
signer: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setForms((prevForms) => [...prevForms, { Id: 1, fields: users }]);
|
||||||
|
const totalForms = Math.floor(allowedSigners / users?.length);
|
||||||
|
setAllowedForm(totalForms);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
const handleInputChange = (index, signer, fieldIndex) => {
|
||||||
|
const newForms = [...forms];
|
||||||
|
newForms[index].fields[fieldIndex].email = signer?.Email
|
||||||
|
? signer?.Email
|
||||||
|
: signer || "";
|
||||||
|
newForms[index].fields[fieldIndex].signer = signer?.objectId ? signer : "";
|
||||||
|
setForms(newForms);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddForm = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Check if the quick send limit has been reached
|
||||||
|
if (forms?.length < allowedForm) {
|
||||||
|
if (props?.Placeholders.length > 0) {
|
||||||
|
let newForm = [];
|
||||||
|
props?.Placeholders?.forEach((element) => {
|
||||||
|
if (!element.signerObjId) {
|
||||||
|
newForm = [
|
||||||
|
...newForm,
|
||||||
|
{
|
||||||
|
fieldId: element.Id,
|
||||||
|
email: "",
|
||||||
|
label: element.Role,
|
||||||
|
signer: {}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setForms([...forms, { Id: formId, fields: newForm }]);
|
||||||
|
}
|
||||||
|
setFormId(formId + 1);
|
||||||
|
setScrollOnNextUpdate(true);
|
||||||
|
} else {
|
||||||
|
// If the limit has been reached, throw an error with the appropriate message
|
||||||
|
alert("Quick send reached limit.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemoveForm = (index) => {
|
||||||
|
const updatedForms = forms.filter((_, i) => i !== index);
|
||||||
|
setForms(updatedForms);
|
||||||
|
};
|
||||||
|
const handleSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsSubmit(true);
|
||||||
|
|
||||||
|
// Create a copy of Placeholders array from props.item
|
||||||
|
let Placeholders = [...props.item.Placeholders];
|
||||||
|
// Initialize an empty array to store updated documents
|
||||||
|
let Documents = [];
|
||||||
|
|
||||||
|
// Loop through each form
|
||||||
|
forms.forEach((form) => {
|
||||||
|
// Map through the copied Placeholders array to update email values
|
||||||
|
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||||
|
// Find the field in the current form that matches the placeholder Id
|
||||||
|
const field = form.fields.find(
|
||||||
|
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||||
|
);
|
||||||
|
// If a matching field is found, update the email value in the placeholder
|
||||||
|
const signer = field?.signer?.objectId ? field.signer : {};
|
||||||
|
if (field) {
|
||||||
|
return {
|
||||||
|
...placeholder,
|
||||||
|
email: field.email,
|
||||||
|
signerObjId: field?.signer?.objectId || "",
|
||||||
|
signerPtr: signer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// If no matching field is found, keep the placeholder as is
|
||||||
|
return placeholder;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Push a new document object with updated Placeholders into the Documents array
|
||||||
|
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
|
||||||
|
});
|
||||||
|
// console.log("Documents ", Documents);
|
||||||
|
await batchQuery(Documents);
|
||||||
|
};
|
||||||
|
|
||||||
|
const batchQuery = async (Documents) => {
|
||||||
|
const serverUrl = localStorage.getItem("baseUrl");
|
||||||
|
const functionsUrl = `${serverUrl}functions/batchdocuments`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
};
|
||||||
|
const params = {
|
||||||
|
Documents: JSON.stringify(Documents)
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const res = await axios.post(functionsUrl, params, { headers: headers });
|
||||||
|
// console.log("res ", res);
|
||||||
|
if (res.data && res.data.result) {
|
||||||
|
props.handleClose("success", Documents?.length);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err ", err);
|
||||||
|
props.handleClose("error", 0);
|
||||||
|
} finally {
|
||||||
|
setIsSubmit(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{isSubmit && (
|
||||||
|
<div className="absolute z-[999] h-full w-full flex justify-center items-center bg-black bg-opacity-40">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "45px",
|
||||||
|
color: "#3dd3e0"
|
||||||
|
}}
|
||||||
|
className="loader-37 "
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.Placeholders?.length > 0 ? (
|
||||||
|
isSignatureExist ? (
|
||||||
|
<>
|
||||||
|
{props.Placeholders?.some((x) => !x.signerObjId) ? (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className=" min-h-max max-h-[250px] overflow-y-auto">
|
||||||
|
{forms?.map((form, index) => (
|
||||||
|
<div
|
||||||
|
key={form.Id}
|
||||||
|
className="p-3 rounded-xl border-[1px] border-gray-400 m-4 bg-white text-black grid grid-cols-1 md:grid-cols-2 gap-2 relative"
|
||||||
|
>
|
||||||
|
{form?.fields?.map((field, fieldIndex) => (
|
||||||
|
<div className="flex flex-col " key={field.fieldId}>
|
||||||
|
<label>{field.label}</label>
|
||||||
|
<SuggestionInput
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
value={field.value}
|
||||||
|
index={fieldIndex}
|
||||||
|
onChange={(signer) =>
|
||||||
|
handleInputChange(index, signer, fieldIndex)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{forms?.length > 1 && (
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveForm(index)}
|
||||||
|
className="absolute right-3 top-1 border border-gray-300 rounded-lg px-2 py-1"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<div ref={formRef}></div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col mx-4 mb-4 gap-3">
|
||||||
|
<button
|
||||||
|
onClick={handleAddForm}
|
||||||
|
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-plus"></i> <span>Add new</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="bg-[#32a3ac] p-2 text-white w-full rounded-full focus:outline-none"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-paper-plane"></i>{" "}
|
||||||
|
<span>Send</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
All roles in this document are currently linked to contacts. To
|
||||||
|
'quick send' copies of this template to multiple
|
||||||
|
signers, please ensure that at least one role is not linked to
|
||||||
|
any contact.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
Please ensure there's at least one signature widget added for
|
||||||
|
all recipients.
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div className="text-black p-3 bg-white w-full text-sm md:text-base flex justify-center items-center">
|
||||||
|
Please add at least one role to this template in order to 'quick
|
||||||
|
send' copies of it to multiple signers.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BulkSendUi;
|
||||||
@@ -44,7 +44,9 @@ const Header = ({ showSidebar }) => {
|
|||||||
|
|
||||||
const closeDropdown = () => {
|
const closeDropdown = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
Parse.User.logOut();
|
if (Parse?.User?.current()) {
|
||||||
|
Parse.User.logOut();
|
||||||
|
}
|
||||||
let appdata = localStorage.getItem("userSettings");
|
let appdata = localStorage.getItem("userSettings");
|
||||||
let applogo = localStorage.getItem("appLogo");
|
let applogo = localStorage.getItem("appLogo");
|
||||||
let appName = localStorage.getItem("appName");
|
let appName = localStorage.getItem("appName");
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const LoginFacebook = ({
|
|||||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
// console.log("extRes ", extRes);
|
// console.log("extRes ", extRes);
|
||||||
if (extRes) {
|
if (extRes) {
|
||||||
const params = { ...details, Phone: extRes.get("Phone") };
|
const params = { ...details, Phone: extRes?.get("Phone") || "" };
|
||||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
if (payload && payload.sessiontoken) {
|
if (payload && payload.sessiontoken) {
|
||||||
@@ -59,7 +59,7 @@ const LoginFacebook = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: details.Name,
|
name: details.Name,
|
||||||
email: details.Email,
|
email: details.Email,
|
||||||
phone: extRes.get("Phone"),
|
phone: extRes?.get("Phone") || "",
|
||||||
company: extRes.get("Company")
|
company: extRes.get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
@@ -75,7 +75,7 @@ const LoginFacebook = ({
|
|||||||
const handleSubmitbtn = async () => {
|
const handleSubmitbtn = async () => {
|
||||||
if (userDetails.Phone && userDetails.Company) {
|
if (userDetails.Phone && userDetails.Company) {
|
||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
const params = { ...fBDetails, Phone: userDetails.Phone };
|
const params = { ...fBDetails, Phone: userDetails?.Phone || "" };
|
||||||
const payload = await Parse.Cloud.run("facebooksign", params);
|
const payload = await Parse.Cloud.run("facebooksign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ const LoginFacebook = ({
|
|||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
// "passsword":userDetails.Phone,
|
// "passsword":userDetails.Phone,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
role: "contracts_User",
|
role: "contracts_User",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ const LoginFacebook = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
|
|||||||
@@ -110,22 +110,22 @@ const GoogleSignInBtn = ({
|
|||||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
// console.log("extRes ", extRes);
|
// console.log("extRes ", extRes);
|
||||||
if (extRes) {
|
if (extRes) {
|
||||||
const params = { ...details, Phone: extRes.get("Phone") };
|
const params = { ...details, Phone: extRes?.get("Phone") || "" };
|
||||||
const payload = await Parse.Cloud.run("googlesign", params);
|
const payload = await Parse.Cloud.run("googlesign", params);
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
if (payload && payload.sessiontoken) {
|
if (payload && payload.sessiontoken) {
|
||||||
// setThirdpartyLoader(true);
|
// setThirdpartyLoader(true);
|
||||||
const billingDate =
|
// const billingDate =
|
||||||
extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
|
// extRes.get("Next_billing_date") && extRes.get("Next_billing_date");
|
||||||
// console.log("billingDate expired", billingDate > new Date());
|
// console.log("billingDate expired", billingDate > new Date());
|
||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: details.Name,
|
name: details.Name,
|
||||||
email: details.Gmail,
|
email: details.Gmail,
|
||||||
phone: extRes.get("Phone"),
|
phone: extRes?.get("Phone") || "",
|
||||||
company: extRes.get("Company")
|
company: extRes.get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
thirdpartyLoginfn(payload.sessiontoken, billingDate);
|
thirdpartyLoginfn(payload.sessiontoken);
|
||||||
}
|
}
|
||||||
return { msg: "exist" };
|
return { msg: "exist" };
|
||||||
} else {
|
} else {
|
||||||
@@ -139,7 +139,7 @@ const GoogleSignInBtn = ({
|
|||||||
setThirdpartyLoader(true);
|
setThirdpartyLoader(true);
|
||||||
// e.preventDefault()
|
// e.preventDefault()
|
||||||
// console.log("handelSubmit", userDetails);
|
// console.log("handelSubmit", userDetails);
|
||||||
const params = { ...googleDetails, Phone: userDetails.Phone };
|
const params = { ...googleDetails, Phone: userDetails?.Phone };
|
||||||
const payload = await Parse.Cloud.run("googlesign", params);
|
const payload = await Parse.Cloud.run("googlesign", params);
|
||||||
|
|
||||||
// console.log("payload ", payload);
|
// console.log("payload ", payload);
|
||||||
@@ -149,7 +149,7 @@ const GoogleSignInBtn = ({
|
|||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
// "passsword":userDetails.Phone,
|
// "passsword":userDetails.Phone,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
role: "contracts_User",
|
role: "contracts_User",
|
||||||
company: userDetails.Company,
|
company: userDetails.Company,
|
||||||
jobTitle: userDetails.Destination
|
jobTitle: userDetails.Destination
|
||||||
@@ -161,7 +161,7 @@ const GoogleSignInBtn = ({
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: userDetails.Name,
|
name: userDetails.Name,
|
||||||
email: userDetails.Email,
|
email: userDetails.Email,
|
||||||
phone: userDetails.Phone,
|
phone: userDetails?.Phone || "",
|
||||||
company: userDetails.Company
|
company: userDetails.Company
|
||||||
// jobTitle: userDetails.JobTitle
|
// jobTitle: userDetails.JobTitle
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ const RenderDebugPdf = (props) => {
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
border: "1px solid grey",
|
border: "1px solid grey",
|
||||||
overflow: "auto"
|
overflow: "auto",
|
||||||
|
cursor: "crosshair"
|
||||||
}}
|
}}
|
||||||
onMouseMove={props.handleMouseMoveDiv}
|
onMouseMove={props.handleMouseMoveDiv}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div data-tut="reactourThird">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: themeColor,
|
background: themeColor,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import "../../styles/AddUser.css";
|
import "../../styles/AddUser.css";
|
||||||
import { getFileName } from "../../constant/Utils";
|
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||||
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
// import SelectFolder from "../../premitives/SelectFolder";
|
// import SelectFolder from "../../premitives/SelectFolder";
|
||||||
|
|
||||||
const EditTemplate = ({ template, onSuccess }) => {
|
const EditTemplate = ({ template, onSuccess }) => {
|
||||||
@@ -9,9 +12,21 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
Name: template?.Name || "",
|
Name: template?.Name || "",
|
||||||
Note: template?.Note || "",
|
Note: template?.Note || "",
|
||||||
Description: template?.Description || "",
|
Description: template?.Description || "",
|
||||||
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
|
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false",
|
||||||
|
AutomaticReminders: template?.AutomaticReminders || false,
|
||||||
|
RemindOnceInEvery: template?.RemindOnceInEvery || 5
|
||||||
});
|
});
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
fetchSubscription();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
const fetchSubscription = async () => {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
|
};
|
||||||
const handleStrInput = (e) => {
|
const handleStrInput = (e) => {
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
};
|
};
|
||||||
@@ -25,12 +40,26 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const isChecked = formData.SendinOrder === "true" ? true : false;
|
const isChecked = formData.SendinOrder === "true" ? true : false;
|
||||||
const data = { ...formData, SendinOrder: isChecked };
|
const AutoReminder = formData?.AutomaticReminders || false;
|
||||||
|
|
||||||
|
let reminderDate = {};
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = parseInt(formData?.RemindOnceInEvery);
|
||||||
|
const ReminderDate = new Date(template?.createdAt);
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
reminderDate = { NextReminderDate: ReminderDate };
|
||||||
|
}
|
||||||
|
const data = { ...formData, SendinOrder: isChecked, ...reminderDate };
|
||||||
onSuccess(data);
|
onSuccess(data);
|
||||||
};
|
};
|
||||||
|
const handleAutoReminder = () => {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
AutomaticReminders: !formData.AutomaticReminders
|
||||||
|
}));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="addusercontainer">
|
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||||
<div className="form-wrapper">
|
<div className="form-wrapper">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
@@ -128,6 +157,57 @@ const EditTemplate = ({ template, onSuccess }) => {
|
|||||||
<div style={{ fontSize: 12 }}>No</div>
|
<div style={{ fontSize: 12 }}>No</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
{!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Disable Auto reminder is free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "font-semibold"
|
||||||
|
: "font-semibold text-gray-300"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Auto reminder{" "}
|
||||||
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</span>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "cursor-pointer "
|
||||||
|
: "pointer-events-none opacity-50"
|
||||||
|
} relative block items-center mb-0`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
checked={formData.AutomaticReminders}
|
||||||
|
onChange={handleAutoReminder}
|
||||||
|
type="checkbox"
|
||||||
|
value=""
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600 mt-2"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{isSubscribe && formData?.AutomaticReminders === true && (
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
Remind once in every (Days)
|
||||||
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.RemindOnceInEvery}
|
||||||
|
name="RemindOnceInEvery"
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
onChange={handleStrInput}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
|
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
|
||||||
<div className="buttoncontainer">
|
<div className="buttoncontainer">
|
||||||
<button type="submit" className="submitbutton">
|
<button type="submit" className="submitbutton">
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function EmailBody(props) {
|
|||||||
required
|
required
|
||||||
value={props.requestSubject}
|
value={props.requestSubject}
|
||||||
onChange={(e) => props.setRequestSubject(e.target.value)}
|
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||||
placeholder="${senderName} has requested you to sign ${documentName}"
|
placeholder='${senderName} has requested you to sign "${documentName}"'
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
/>
|
/>
|
||||||
<label className="text-sm ml-2 mt-3">
|
<label className="text-sm ml-2 mt-3">
|
||||||
|
|||||||
@@ -10,20 +10,22 @@ import loader from "../../assets/images/loader2.gif";
|
|||||||
function EmailComponent({
|
function EmailComponent({
|
||||||
isEmail,
|
isEmail,
|
||||||
pdfUrl,
|
pdfUrl,
|
||||||
isCeleb,
|
|
||||||
setIsEmail,
|
setIsEmail,
|
||||||
setSuccessEmail,
|
setSuccessEmail,
|
||||||
pdfName,
|
pdfName,
|
||||||
sender,
|
sender,
|
||||||
setIsAlert,
|
setIsAlert,
|
||||||
extUserId
|
extUserId,
|
||||||
|
activeMailAdapter
|
||||||
}) {
|
}) {
|
||||||
const [emailList, setEmailList] = useState([]);
|
const [emailList, setEmailList] = useState([]);
|
||||||
const [emailValue, setEmailValue] = useState();
|
const [emailValue, setEmailValue] = useState();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isEmailCelebration, setIsEmailCelebration] = useState(false);
|
||||||
//function for send email
|
//function for send email
|
||||||
const sendEmail = async () => {
|
const sendEmail = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
let sendMail;
|
let sendMail;
|
||||||
for (let i = 0; i < emailList.length; i++) {
|
for (let i = 0; i < emailList.length; i++) {
|
||||||
try {
|
try {
|
||||||
@@ -39,6 +41,7 @@ function EmailComponent({
|
|||||||
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||||
const themeBGcolor = themeColor;
|
const themeBGcolor = themeColor;
|
||||||
let params = {
|
let params = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
extUserId: extUserId,
|
extUserId: extUserId,
|
||||||
pdfName: pdfName,
|
pdfName: pdfName,
|
||||||
url: pdfUrl,
|
url: pdfUrl,
|
||||||
@@ -59,6 +62,10 @@ function EmailComponent({
|
|||||||
" target=_blank>here</a> </p></div></div></body></html>"
|
" target=_blank>here</a> </p></div></div></body></html>"
|
||||||
};
|
};
|
||||||
sendMail = await axios.post(url, params, { headers: headers });
|
sendMail = await axios.post(url, params, { headers: headers });
|
||||||
|
setIsEmailCelebration(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsEmailCelebration(false);
|
||||||
|
}, 3000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
console.log("error", error);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@@ -242,22 +249,17 @@ function EmailComponent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div style={{ height: "100%", padding: 20 }}>
|
||||||
{isCeleb && (
|
{isEmailCelebration && (
|
||||||
<div
|
<div className="absolute w-[100%] flex justify-center items-center">
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
marginLeft: "50px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
alt="print img"
|
alt="print img"
|
||||||
width={300}
|
width={300}
|
||||||
height={250}
|
height={250}
|
||||||
// style={styles.gifCeleb}
|
|
||||||
src={celebration}
|
src={celebration}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p
|
<p
|
||||||
style={{
|
style={{
|
||||||
fontFamily: "system-ui",
|
fontFamily: "system-ui",
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import PrevNext from "./PrevNext";
|
import PrevNext from "./PrevNext";
|
||||||
import printModule from "print-js";
|
import {
|
||||||
import { getBase64FromUrl } from "../../constant/Utils";
|
handleDownloadCertificate,
|
||||||
import { saveAs } from "file-saver";
|
handleDownloadPdf,
|
||||||
|
handleToPrint
|
||||||
|
} from "../../constant/Utils";
|
||||||
import "../../styles/signature.css";
|
import "../../styles/signature.css";
|
||||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { themeColor } from "../../constant/const";
|
import { themeColor } from "../../constant/const";
|
||||||
import Certificate from "./Certificate";
|
import ModalUi from "../../primitives/ModalUi";
|
||||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
|
||||||
|
|
||||||
function Header({
|
function Header({
|
||||||
isPdfRequestFiles,
|
isPdfRequestFiles,
|
||||||
@@ -39,6 +40,7 @@ function Header({
|
|||||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||||
const isMobile = window.innerWidth < 767;
|
const isMobile = window.innerWidth < 767;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const [isDownloading, setIsDownloading] = useState("");
|
||||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||||
//for go to previous page
|
//for go to previous page
|
||||||
function previousPage() {
|
function previousPage() {
|
||||||
@@ -57,100 +59,6 @@ function Header({
|
|||||||
setIsDecline(currentDecline);
|
setIsDecline(currentDecline);
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for print digital sign pdf
|
|
||||||
const handleToPrint = async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const pdf = await getBase64FromUrl(pdfUrl);
|
|
||||||
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
|
||||||
const isAppleDevice =
|
|
||||||
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
|
||||||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
|
||||||
!window.MSStream;
|
|
||||||
if (isAndroidDevice || isAppleDevice) {
|
|
||||||
const byteArray = Uint8Array.from(
|
|
||||||
atob(pdf)
|
|
||||||
.split("")
|
|
||||||
.map((char) => char.charCodeAt(0))
|
|
||||||
);
|
|
||||||
const blob = new Blob([byteArray], { type: "application/pdf" });
|
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
|
||||||
window.open(blobUrl, "_blank");
|
|
||||||
} else {
|
|
||||||
printModule({ printable: pdf, type: "pdf", base64: true });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//handle download signed pdf
|
|
||||||
const handleDownloadPdf = () => {
|
|
||||||
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
|
||||||
saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const sanitizeFileName = (pdfName) => {
|
|
||||||
// Replace spaces with underscore
|
|
||||||
return pdfName.replace(/ /g, "_");
|
|
||||||
};
|
|
||||||
|
|
||||||
//handle download signed pdf
|
|
||||||
const handleDownloadCertificate = async () => {
|
|
||||||
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
|
||||||
try {
|
|
||||||
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
|
||||||
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
|
||||||
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
|
||||||
} catch (err) {
|
|
||||||
console.log("err in download in certificate", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const GenerateCertificate = () => {
|
|
||||||
//after generate download certifcate pdf
|
|
||||||
const handleDownload = (pdfBlob, fileName) => {
|
|
||||||
if (pdfBlob) {
|
|
||||||
const url = window.URL.createObjectURL(pdfBlob);
|
|
||||||
// Create a temporary anchor element
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = fileName;
|
|
||||||
// Append the anchor to the body
|
|
||||||
document.body.appendChild(link);
|
|
||||||
// Programmatically click the anchor to trigger the download
|
|
||||||
link.click();
|
|
||||||
// Remove the anchor from the body
|
|
||||||
document.body.removeChild(link);
|
|
||||||
// Release the object URL to free up resources
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PDFDownloadLink
|
|
||||||
onClick={(e) => e.preventDefault()}
|
|
||||||
style={{ textDecoration: "none", zIndex: "35" }}
|
|
||||||
document={<Certificate pdfData={pdfDetails} />}
|
|
||||||
>
|
|
||||||
{({ blob, loading }) => (
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
handleDownload(
|
|
||||||
blob,
|
|
||||||
`completion certificate-${
|
|
||||||
pdfDetails[0] && pdfDetails[0].Name
|
|
||||||
}.pdf`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
disabled={loading}
|
|
||||||
className=" md:bg-[#08bc66] border-none focus:outline-none flex flex-row items-center md:shadow md:rounded-[3px] py-[3px] md:px-[11px] md:text-white text-black md:font-[500] text-[13px] mr-[5px]"
|
|
||||||
>
|
|
||||||
<i className="fa-solid fa-award py-[3px]" aria-hidden="true"></i>
|
|
||||||
<span className="md:hidden lg:block ml-1">Certificate</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</PDFDownloadLink>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||||
{isMobile && isShowHeader ? (
|
{isMobile && isShowHeader ? (
|
||||||
@@ -223,7 +131,9 @@ function Header({
|
|||||||
>
|
>
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
className="DropdownMenuItem"
|
className="DropdownMenuItem"
|
||||||
onClick={() => handleDownloadPdf()}
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -240,32 +150,29 @@ function Header({
|
|||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
{isCompleted && (
|
{isCompleted && (
|
||||||
<>
|
<DropdownMenu.Item
|
||||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
className="DropdownMenuItem"
|
||||||
<DropdownMenu.Item
|
onClick={() =>
|
||||||
className="DropdownMenuItem"
|
handleDownloadCertificate(
|
||||||
onClick={() => handleDownloadCertificate()}
|
pdfDetails,
|
||||||
>
|
setIsDownloading
|
||||||
<div
|
)
|
||||||
style={{
|
}
|
||||||
border: "none",
|
>
|
||||||
backgroundColor: "#fff"
|
<div
|
||||||
}}
|
style={{
|
||||||
>
|
border: "none",
|
||||||
<i
|
backgroundColor: "#fff"
|
||||||
className="fa-solid fa-award"
|
}}
|
||||||
style={{ marginRight: "2px" }}
|
>
|
||||||
aria-hidden="true"
|
<i
|
||||||
></i>
|
className="fa-solid fa-award"
|
||||||
Certificate
|
style={{ marginRight: "2px" }}
|
||||||
</div>
|
aria-hidden="true"
|
||||||
</DropdownMenu.Item>
|
></i>
|
||||||
) : (
|
Certificate
|
||||||
<DropdownMenu.Item className="DropdownMenuItem">
|
</div>
|
||||||
<GenerateCertificate />
|
</DropdownMenu.Item>
|
||||||
</DropdownMenu.Item>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{isSignYourself && (
|
{isSignYourself && (
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
@@ -289,7 +196,9 @@ function Header({
|
|||||||
)}
|
)}
|
||||||
<DropdownMenu.Item
|
<DropdownMenu.Item
|
||||||
className="DropdownMenuItem"
|
className="DropdownMenuItem"
|
||||||
onClick={handleToPrint}
|
onClick={(e) =>
|
||||||
|
handleToPrint(e, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -309,24 +218,16 @@ function Header({
|
|||||||
</DropdownMenu.Portal>
|
</DropdownMenu.Portal>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ display: "flex", justifyContent: "space-around" }}>
|
<div className="flex justify-around items-center">
|
||||||
{/* current signer is checking user send request and check status of pdf sign than if current
|
{/* current signer is checking user send request and check status of pdf sign than if current
|
||||||
user exist than show finish button else no
|
user exist than show finish button else no
|
||||||
*/}
|
*/}
|
||||||
{currentSigner && (
|
{currentSigner && (
|
||||||
<>
|
<div className="flex items-center" data-tut="reactourFifth">
|
||||||
{decline && (
|
{decline && (
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
handleDeclinePdfAlert();
|
className="text-[red] border-none font-[650] text-[14px] mr-2"
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
color: "red",
|
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px",
|
|
||||||
marginRight: "10px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Decline
|
Decline
|
||||||
</div>
|
</div>
|
||||||
@@ -338,12 +239,8 @@ function Header({
|
|||||||
alertSendEmail();
|
alertSendEmail();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
className="border-none font-[650] text-[14px]"
|
||||||
color: isMailSend ? "gray" : themeColor,
|
style={{ color: isMailSend ? "gray" : themeColor }}
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px"
|
|
||||||
}}
|
|
||||||
data-tut={dataTut4}
|
data-tut={dataTut4}
|
||||||
>
|
>
|
||||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||||
@@ -358,17 +255,49 @@ function Header({
|
|||||||
embedWidgetsData();
|
embedWidgetsData();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{ color: themeColor }}
|
||||||
color: themeColor,
|
className="border-none font-[650] text-[14px]"
|
||||||
border: "none",
|
|
||||||
fontWeight: "650",
|
|
||||||
fontSize: "16px"
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild>
|
||||||
|
<div
|
||||||
|
className="border-none font-[650] text-[18px] px-2 ml-[4px]"
|
||||||
|
style={{ color: themeColor }}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-ellipsis-v"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="bg-white shadow-md rounded px-2 py-1"
|
||||||
|
sideOffset={5}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-down mr-[5px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-[500]">Download</span>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -433,38 +362,32 @@ function Header({
|
|||||||
{completeBtnTitle
|
{completeBtnTitle
|
||||||
? completeBtnTitle
|
? completeBtnTitle
|
||||||
: isMailSend
|
: isMailSend
|
||||||
? "Sent"
|
? "Sent"
|
||||||
: "Send"}
|
: "Send"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : isPdfRequestFiles ? (
|
) : isPdfRequestFiles ? (
|
||||||
alreadySign ? (
|
alreadySign ? (
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
<div className="flex flex-row">
|
||||||
{isCompleted && (
|
{isCompleted && (
|
||||||
<>
|
<button
|
||||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
type="button"
|
||||||
<button
|
onClick={() =>
|
||||||
type="button"
|
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||||
onClick={() => handleDownloadCertificate()}
|
}
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-award py-[3px]"
|
className="fa-solid fa-award py-[3px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span className="hidden lg:block ml-1">
|
<span className="hidden lg:block ml-1">Certificate</span>
|
||||||
Certificate
|
</button>
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<GenerateCertificate />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleToPrint}
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||||
>
|
>
|
||||||
@@ -475,14 +398,16 @@ function Header({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
|
||||||
onClick={() => handleDownloadPdf()}
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||||
<span className="hidden lg:block ml-1">Download</span>
|
<span className="hidden lg:block ml-1">Download</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex">
|
<div className="flex" data-tut="reactourFifth">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate(-1)}
|
onClick={() => navigate(-1)}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -493,53 +418,77 @@ function Header({
|
|||||||
{currentSigner && (
|
{currentSigner && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
style={{ background: "#de4337" }}
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#de4337]"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
|
onClick={() => handleDeclinePdfAlert()}
|
||||||
onClick={() => {
|
|
||||||
handleDeclinePdfAlert();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Decline
|
Decline
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
style={{
|
|
||||||
background: "#188ae2"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||||
onClick={() => {
|
onClick={() => embedWidgetsData()}
|
||||||
embedWidgetsData();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Finish
|
Finish
|
||||||
</button>
|
</button>
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger asChild>
|
||||||
|
<div
|
||||||
|
className="border-none font-[650] text-[16px] py-[3px] px-[11px] shadow rounded bg-white"
|
||||||
|
style={{ color: themeColor }}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-ellipsis-v"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="bg-white shadow-md rounded px-2 py-1"
|
||||||
|
sideOffset={5}
|
||||||
|
>
|
||||||
|
<DropdownMenu.Item
|
||||||
|
className="flex flex-row justify-center items-center text-[13px] focus:outline-none cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
handleDownloadPdf(
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-arrow-down mr-[5px]"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span className="font-[500]">Download</span>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : isCompleted ? (
|
) : isCompleted ? (
|
||||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
<div className="flex flex-row">
|
||||||
{isCompleted && (
|
{isCompleted && (
|
||||||
<>
|
<button
|
||||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
type="button"
|
||||||
<button
|
onClick={() =>
|
||||||
type="button"
|
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||||
onClick={() => handleDownloadCertificate()}
|
}
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-award py-[3px]"
|
className="fa-solid fa-award py-[3px]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span className="hidden lg:block ml-1">Certificate</span>
|
<span className="hidden lg:block ml-1">Certificate</span>
|
||||||
</button>
|
</button>
|
||||||
) : (
|
|
||||||
<GenerateCertificate />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={handleToPrint}
|
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||||
>
|
>
|
||||||
@@ -549,7 +498,9 @@ function Header({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#f14343]"
|
||||||
onClick={() => handleDownloadPdf()}
|
onClick={() =>
|
||||||
|
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||||
<span className="hidden lg:block ml-1">Download</span>
|
<span className="hidden lg:block ml-1">Download</span>
|
||||||
@@ -575,9 +526,6 @@ function Header({
|
|||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
style={{
|
|
||||||
background: "#188ae2"
|
|
||||||
}}
|
|
||||||
type="button"
|
type="button"
|
||||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -592,6 +540,34 @@ function Header({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isDownloading === "pdf" && (
|
||||||
|
<div className="fixed z-[200] inset-0 flex justify-center items-center bg-black bg-opacity-30">
|
||||||
|
<div
|
||||||
|
style={{ fontSize: "45px", color: "#3dd3e0" }}
|
||||||
|
className="loader-37"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<ModalUi
|
||||||
|
isOpen={isDownloading === "certificate"}
|
||||||
|
title={
|
||||||
|
isDownloading === "certificate"
|
||||||
|
? "Generating certificate"
|
||||||
|
: "PDF Download"
|
||||||
|
}
|
||||||
|
handleClose={() => setIsDownloading("")}
|
||||||
|
>
|
||||||
|
<div className="p-3 md:p-5 text-[13px] md:text-base text-center">
|
||||||
|
{isDownloading === "certificate"}{" "}
|
||||||
|
<p>
|
||||||
|
Your completion certificate is being generated. Please wait
|
||||||
|
momentarily.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If the download doesn't start shortly, click the button again.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function PlaceholderType(props) {
|
|||||||
"December"
|
"December"
|
||||||
];
|
];
|
||||||
const validateExpression = (regexValidation) => {
|
const validateExpression = (regexValidation) => {
|
||||||
if (textValue) {
|
if (textValue && regexValidation) {
|
||||||
let regexObject = regexValidation;
|
let regexObject = regexValidation;
|
||||||
if (props.pos?.options?.validation?.type === "regex") {
|
if (props.pos?.options?.validation?.type === "regex") {
|
||||||
regexObject = RegexParser(regexValidation);
|
regexObject = RegexParser(regexValidation);
|
||||||
@@ -56,7 +56,7 @@ function PlaceholderType(props) {
|
|||||||
props.setDraggingEnabled(true);
|
props.setDraggingEnabled(true);
|
||||||
const validateType = props.pos?.options?.validation?.type;
|
const validateType = props.pos?.options?.validation?.type;
|
||||||
let regexValidation;
|
let regexValidation;
|
||||||
if (validateType) {
|
if (validateType && validateType !== "text") {
|
||||||
switch (validateType) {
|
switch (validateType) {
|
||||||
case "email":
|
case "email":
|
||||||
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
@@ -66,12 +66,8 @@ function PlaceholderType(props) {
|
|||||||
regexValidation = /^[0-9\s]*$/;
|
regexValidation = /^[0-9\s]*$/;
|
||||||
validateExpression(regexValidation);
|
validateExpression(regexValidation);
|
||||||
break;
|
break;
|
||||||
case "text":
|
|
||||||
regexValidation = /^[a-zA-Z\s]+$/;
|
|
||||||
validateExpression(regexValidation);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
regexValidation = props.pos?.options.validation.pattern;
|
regexValidation = props.pos?.options?.validation?.pattern || "";
|
||||||
validateExpression(regexValidation);
|
validateExpression(regexValidation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import RSC from "react-scrollbars-custom";
|
import RSC from "react-scrollbars-custom";
|
||||||
import { Document, Page } from "react-pdf";
|
import { Document, Page } from "react-pdf";
|
||||||
import { themeColor } from "../../constant/const";
|
import { themeColor } from "../../constant/const";
|
||||||
@@ -9,13 +9,54 @@ function RenderAllPdfPage({
|
|||||||
setAllPages,
|
setAllPages,
|
||||||
setPageNumber,
|
setPageNumber,
|
||||||
setSignBtnPosition,
|
setSignBtnPosition,
|
||||||
pageNumber
|
pageNumber,
|
||||||
|
signerPos,
|
||||||
|
signerObjectId
|
||||||
}) {
|
}) {
|
||||||
|
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||||
|
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||||
//set all number of pages after load pdf
|
//set all number of pages after load pdf
|
||||||
function onDocumentLoad({ numPages }) {
|
function onDocumentLoad({ numPages }) {
|
||||||
setAllPages(numPages);
|
setAllPages(numPages);
|
||||||
|
//check if signerPos array exist then save page number exist in signerPos array to show bookmark icon
|
||||||
|
if (signerPos) {
|
||||||
|
const checkUser = signerPos.filter(
|
||||||
|
(data) => data.signerObjId === signerObjectId
|
||||||
|
);
|
||||||
|
setBookmarkColor(checkUser[0]?.blockColor);
|
||||||
|
let pageNumberArr = [];
|
||||||
|
if (checkUser?.length > 0) {
|
||||||
|
checkUser[0]?.placeHolder?.map((data) => {
|
||||||
|
pageNumberArr.push(data?.pageNumber);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSignPageNumber(pageNumberArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
|
||||||
|
const addSignatureBookmark = (index) => {
|
||||||
|
const ispageNumber = signPageNumber.includes(index + 1);
|
||||||
|
return (
|
||||||
|
ispageNumber && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
zIndex: 2,
|
||||||
|
top: -12,
|
||||||
|
right: -7,
|
||||||
|
transform: "translate(50% -50%)"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
style={{ color: bookmarkColor || "red" }}
|
||||||
|
className="fa-solid fa-bookmark"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="showPages">
|
<div className="showPages">
|
||||||
<div
|
<div
|
||||||
@@ -66,7 +107,8 @@ function RenderAllPdfPage({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
contain: "content"
|
|
||||||
|
position: "relative"
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPageNumber(index + 1);
|
setPageNumber(index + 1);
|
||||||
@@ -75,15 +117,25 @@ function RenderAllPdfPage({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Page
|
{signerPos && addSignatureBookmark(index)}
|
||||||
key={`page_${index + 1}`}
|
|
||||||
pageNumber={index + 1}
|
<div
|
||||||
width={100}
|
style={{
|
||||||
height={100}
|
position: "relative",
|
||||||
scale={1}
|
zIndex: 1,
|
||||||
renderAnnotationLayer={false}
|
overflow: "hidden"
|
||||||
renderTextLayer={false}
|
}}
|
||||||
/>
|
>
|
||||||
|
<Page
|
||||||
|
key={`page_${index + 1}`}
|
||||||
|
pageNumber={index + 1}
|
||||||
|
width={100}
|
||||||
|
height={100}
|
||||||
|
scale={1}
|
||||||
|
renderAnnotationLayer={false}
|
||||||
|
renderTextLayer={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Document>
|
</Document>
|
||||||
|
|||||||
@@ -204,34 +204,34 @@ function RenderPdf({
|
|||||||
};
|
};
|
||||||
//function for render placeholder block over pdf document
|
//function for render placeholder block over pdf document
|
||||||
const checkSignedSignes = (data) => {
|
const checkSignedSignes = (data) => {
|
||||||
const checkSign = signedSigners.filter(
|
let checkSign = [];
|
||||||
(sign) => sign.objectId === data.signerObjId
|
//condition to handle quick send flow and using normal request sign flow
|
||||||
|
checkSign = signedSigners.filter(
|
||||||
|
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
|
||||||
);
|
);
|
||||||
if (data.signerObjId === signerObjectId) {
|
if (data.signerObjId === signerObjectId) {
|
||||||
setCurrentSigner(true);
|
setCurrentSigner(true);
|
||||||
}
|
}
|
||||||
const handleAllUserName = (Id, Role, type) => {
|
const handleAllUserName = (Id, Role, type) => {
|
||||||
return pdfDetails[0].Signers.map((signerData, key) => {
|
return (
|
||||||
return (
|
<React.Fragment>
|
||||||
signerData.objectId === data.signerObjId && (
|
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||||
<React.Fragment key={key}>
|
{
|
||||||
{signerData?.Name && (
|
pdfDetails[0].Signers?.find(
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
(signer) => signer.objectId === data.signerObjId
|
||||||
{signerData.Name}
|
)?.Name
|
||||||
</div>
|
}
|
||||||
)}
|
</div>
|
||||||
{type && (
|
{type && (
|
||||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||||
)}
|
)}
|
||||||
{Role && (
|
{Role && (
|
||||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||||
{`(${Role})`}
|
{`(${Role})`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
);
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -334,6 +334,7 @@ function RenderPdf({
|
|||||||
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
||||||
{isMobile && scale ? (
|
{isMobile && scale ? (
|
||||||
<div
|
<div
|
||||||
|
data-tut="reactourForth"
|
||||||
style={{
|
style={{
|
||||||
border: "0.1px solid #ebe8e8",
|
border: "0.1px solid #ebe8e8",
|
||||||
marginTop: isGuestSigner && "30px"
|
marginTop: isGuestSigner && "30px"
|
||||||
@@ -517,6 +518,7 @@ function RenderPdf({
|
|||||||
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
data-tut="reactourForth"
|
||||||
style={{
|
style={{
|
||||||
border: "0.1px solid #ebe8e8",
|
border: "0.1px solid #ebe8e8",
|
||||||
width: pdfOriginalWidth
|
width: pdfOriginalWidth
|
||||||
|
|||||||
@@ -214,8 +214,8 @@ function SignPad({
|
|||||||
const fontfamily = fontStyle
|
const fontfamily = fontStyle
|
||||||
? fontStyle
|
? fontStyle
|
||||||
: fontSelect
|
: fontSelect
|
||||||
? fontSelect
|
? fontSelect
|
||||||
: "Fasthand";
|
: "Fasthand";
|
||||||
|
|
||||||
//creating span for getting text content width
|
//creating span for getting text content width
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
@@ -267,10 +267,10 @@ function SignPad({
|
|||||||
key === 0 && penColor === "blue"
|
key === 0 && penColor === "blue"
|
||||||
? "2px solid blue"
|
? "2px solid blue"
|
||||||
: key === 1 && penColor === "red"
|
: key === 1 && penColor === "red"
|
||||||
? "2px solid red"
|
? "2px solid red"
|
||||||
: key === 2 && penColor === "black"
|
: key === 2 && penColor === "black"
|
||||||
? "2px solid black"
|
? "2px solid black"
|
||||||
: "2px solid white"
|
: "2px solid white"
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props?.convertToImg &&
|
props?.convertToImg &&
|
||||||
@@ -536,7 +536,6 @@ function SignPad({
|
|||||||
style={{
|
style={{
|
||||||
border: "1.3px solid #007bff",
|
border: "1.3px solid #007bff",
|
||||||
borderRadius: "2px",
|
borderRadius: "2px",
|
||||||
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
@@ -553,7 +552,7 @@ function SignPad({
|
|||||||
type="file"
|
type="file"
|
||||||
onChange={onImageChange}
|
onChange={onImageChange}
|
||||||
className="filetype"
|
className="filetype"
|
||||||
accept="image/*"
|
accept="image/png,image/jpeg"
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
hidden
|
hidden
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { rejectBtn, submitBtn, themeColor } from "../../constant/const";
|
||||||
|
|
||||||
|
function VerifyEmail(props) {
|
||||||
|
return (
|
||||||
|
<div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded">
|
||||||
|
<div className="bg-white rounded outline-none md:w-[40%] w-[80%]">
|
||||||
|
<div
|
||||||
|
style={{ backgroundColor: themeColor }}
|
||||||
|
className=" text-white p-[10px] rounded-t"
|
||||||
|
>
|
||||||
|
OTP verification
|
||||||
|
</div>
|
||||||
|
{props.isVerifyModal ? (
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
props.setIsVerifyModal(false);
|
||||||
|
props.handleVerifyEmail(e);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="px-6 py-3">
|
||||||
|
<label className="mb-2">Enter OTP</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="tel"
|
||||||
|
pattern="[0-9]{4}"
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
placeholder="Enter OTP received over email"
|
||||||
|
value={props.otp}
|
||||||
|
onChange={(e) => props.setOtp(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="px-6 my-3">
|
||||||
|
<button type="submit" className={submitBtn}>
|
||||||
|
Verify
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${rejectBtn} ml-2`}
|
||||||
|
onClick={(e) => props.handleResend(e)}
|
||||||
|
>
|
||||||
|
Resend
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
) : props.otpLoader ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "150px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "45px",
|
||||||
|
color: "#3dd3e0"
|
||||||
|
}}
|
||||||
|
className="loader-37"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-6 py-3">
|
||||||
|
<p className="mb-2">Please verify your email !</p>
|
||||||
|
<hr />
|
||||||
|
<div className="px-0 mt-3">
|
||||||
|
<button
|
||||||
|
className={submitBtn}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
props.handleVerifyBtn();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send OTP
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VerifyEmail;
|
||||||
@@ -6,6 +6,7 @@ import { textInputWidget } from "../../constant/Utils";
|
|||||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||||
import Upgrade from "../../primitives/Upgrade";
|
import Upgrade from "../../primitives/Upgrade";
|
||||||
import { isEnableSubscription } from "../../constant/const";
|
import { isEnableSubscription } from "../../constant/const";
|
||||||
|
import Tooltip from "../../primitives/Tooltip";
|
||||||
|
|
||||||
const WidgetNameModal = (props) => {
|
const WidgetNameModal = (props) => {
|
||||||
const [formdata, setFormdata] = useState({
|
const [formdata, setFormdata] = useState({
|
||||||
@@ -53,7 +54,11 @@ const WidgetNameModal = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
if (e) {
|
||||||
|
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||||
|
} else {
|
||||||
|
setFormdata({ ...formdata, textvalidate: "" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handledefaultChange = (e) => {
|
const handledefaultChange = (e) => {
|
||||||
@@ -136,16 +141,21 @@ const WidgetNameModal = (props) => {
|
|||||||
props.widgetName === textInputWidget) && (
|
props.widgetName === textInputWidget) && (
|
||||||
<>
|
<>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label
|
<div className="flex items-center gap-1">
|
||||||
htmlFor="textvalidate"
|
<label
|
||||||
className={
|
htmlFor="textvalidate"
|
||||||
!props.isSubscribe && isEnableSubscription && "disabled"
|
className={
|
||||||
}
|
!props.isSubscribe && isEnableSubscription && "disabled"
|
||||||
style={{ fontSize: 13 }}
|
}
|
||||||
>
|
style={{ fontSize: 13 }}
|
||||||
Validation
|
>
|
||||||
</label>
|
Validation
|
||||||
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
</label>
|
||||||
|
<Tooltip
|
||||||
|
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
|
||||||
|
/>
|
||||||
|
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
!props.isSubscribe && isEnableSubscription && "disabled"
|
!props.isSubscribe && isEnableSubscription && "disabled"
|
||||||
@@ -161,7 +171,7 @@ const WidgetNameModal = (props) => {
|
|||||||
<input
|
<input
|
||||||
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
|
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
|
||||||
name="textvalidate"
|
name="textvalidate"
|
||||||
placeholder="Enter custom expression"
|
placeholder="Enter custom regular expression"
|
||||||
value={formdata.textvalidate}
|
value={formdata.textvalidate}
|
||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
// onBlur={() => handleBlurRegex()}
|
// onBlur={() => handleBlurRegex()}
|
||||||
@@ -173,7 +183,10 @@ const WidgetNameModal = (props) => {
|
|||||||
onChange={(e) => handleChange(e)}
|
onChange={(e) => handleChange(e)}
|
||||||
onBlur={() => handleBlurRegex()}
|
onBlur={() => handleBlurRegex()}
|
||||||
>
|
>
|
||||||
<option disabled style={{ fontSize: "13px" }}>
|
<option
|
||||||
|
disabled={formdata?.textvalidate}
|
||||||
|
style={{ fontSize: "13px" }}
|
||||||
|
>
|
||||||
Select...
|
Select...
|
||||||
</option>
|
</option>
|
||||||
{inputOpt.map((data, ind) => {
|
{inputOpt.map((data, ind) => {
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|||||||
if (parentFolderId) {
|
if (parentFolderId) {
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
} else {
|
} else {
|
||||||
FolderQuery.doesNotExist("Folder");
|
FolderQuery.doesNotExist("Folder");
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
const res = await FolderQuery.find();
|
||||||
@@ -49,6 +51,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
|||||||
const exsitQuery = new Parse.Query(folderCls);
|
const exsitQuery = new Parse.Query(folderCls);
|
||||||
exsitQuery.equalTo("Name", name);
|
exsitQuery.equalTo("Name", name);
|
||||||
exsitQuery.equalTo("Type", "Folder");
|
exsitQuery.equalTo("Type", "Folder");
|
||||||
|
exsitQuery.notEqualTo("IsArchive", true);
|
||||||
if (parentFolderId) {
|
if (parentFolderId) {
|
||||||
exsitQuery.equalTo("Folder", folderPtr);
|
exsitQuery.equalTo("Folder", folderPtr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,11 @@ const FolderModal = (props) => {
|
|||||||
if (folderPtr) {
|
if (folderPtr) {
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
} else {
|
} else {
|
||||||
FolderQuery.doesNotExist("Folder");
|
FolderQuery.doesNotExist("Folder");
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
const res = await FolderQuery.find();
|
||||||
|
|||||||
@@ -45,9 +45,11 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
if (folderPtr) {
|
if (folderPtr) {
|
||||||
FolderQuery.equalTo("Folder", folderPtr);
|
FolderQuery.equalTo("Folder", folderPtr);
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
} else {
|
} else {
|
||||||
FolderQuery.doesNotExist("Folder");
|
FolderQuery.doesNotExist("Folder");
|
||||||
FolderQuery.equalTo("Type", "Folder");
|
FolderQuery.equalTo("Type", "Folder");
|
||||||
|
FolderQuery.notEqualTo("IsArchive", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await FolderQuery.find();
|
const res = await FolderQuery.find();
|
||||||
@@ -169,32 +171,40 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
|||||||
{required && <span className="text-red-500 text-[13px]">*</span>}
|
{required && <span className="text-red-500 text-[13px]">*</span>}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center">
|
<div className="relative max-w-sm">
|
||||||
<div>
|
<div
|
||||||
<i
|
onClick={() => SetIsOpen(true)}
|
||||||
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
className=" cursor-pointer rounded px-[20px] py-[20px] bg-white border border-gray-200 shadow flex max-w-sm gap-8 items-center"
|
||||||
style={{ fontSize: "40px" }}
|
>
|
||||||
aria-hidden="true"
|
<div>
|
||||||
></i>
|
<i
|
||||||
</div>
|
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||||
<div className="font-semibold ">
|
style={{ fontSize: "40px" }}
|
||||||
<div className="flex items-center gap-2">
|
aria-hidden="true"
|
||||||
<p>
|
></i>
|
||||||
{selectFolder && selectFolder.Name
|
</div>
|
||||||
? selectFolder.Name
|
<div className="font-semibold ">
|
||||||
: "OpenSign™ Drive"}
|
<div className="flex items-center gap-2">
|
||||||
</p>
|
<p>
|
||||||
<div className="text-black text-sm" onClick={() => SetIsOpen(true)}>
|
{selectFolder && selectFolder.Name
|
||||||
<i
|
? selectFolder.Name
|
||||||
className="fa fa-pencil"
|
: "OpenSign™ Drive"}
|
||||||
title="Select Folder"
|
</p>
|
||||||
aria-hidden="true"
|
<div
|
||||||
></i>
|
className="text-black text-sm"
|
||||||
</div>
|
// onClick={() => SetIsOpen(true)}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fa fa-pencil cursor-pointer"
|
||||||
|
title="Select Folder"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-[10px] text-gray-400">
|
||||||
|
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-gray-400">
|
|
||||||
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-2 right-1 cursor-pointer">
|
<div className="absolute top-2 right-1 cursor-pointer">
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import { findContact } from "../../../constant/Utils";
|
||||||
|
const SuggestionInput = (props) => {
|
||||||
|
const [inputValue, setInputValue] = useState(props?.value || "");
|
||||||
|
const [suggestions, setSuggestions] = useState([]);
|
||||||
|
const [showSuggestions, setShowSuggestions] = useState(false);
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener("mousedown", Clickout);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", Clickout);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const Clickout = (event) => {
|
||||||
|
if (ref.current && !ref.current.contains(event.target)) {
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// create debounce to avoid unnecessay api calls
|
||||||
|
useEffect(() => {
|
||||||
|
let timer;
|
||||||
|
if (inputValue) {
|
||||||
|
if (timer) clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
(async () => {
|
||||||
|
const res = await findContact(inputValue);
|
||||||
|
if (res?.length > 0) {
|
||||||
|
setSuggestions(res);
|
||||||
|
setShowSuggestions(true);
|
||||||
|
} else {
|
||||||
|
setSuggestions(res);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [inputValue]);
|
||||||
|
|
||||||
|
const handleInputChange = async (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
setInputValue(value?.toLowerCase());
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.trim() === "") {
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleSuggestionClick = (suggestion) => {
|
||||||
|
setInputValue(suggestion.Email);
|
||||||
|
setSuggestions([]);
|
||||||
|
setShowSuggestions(false);
|
||||||
|
if (props.onChange) {
|
||||||
|
props.onChange(suggestion);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center relative">
|
||||||
|
<input
|
||||||
|
type={props?.type || "text"}
|
||||||
|
value={inputValue}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Email..."
|
||||||
|
className="w-full border-[1px] border-gray-400 p-2 text-black rounded lowercase"
|
||||||
|
required={props.required}
|
||||||
|
/>
|
||||||
|
{showSuggestions && (
|
||||||
|
<ul
|
||||||
|
ref={ref}
|
||||||
|
className="absolute z-50 left-0 top-[2.55rem] w-full max-h-[100px] overflow-auto bg-white border border-gray-300 rounded shadow-md"
|
||||||
|
>
|
||||||
|
{suggestions.map((suggestion, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className="py-2 px-2 w-full text-sm cursor-pointer hover:bg-gray-100"
|
||||||
|
onClick={() => handleSuggestionClick(suggestion)}
|
||||||
|
>
|
||||||
|
{suggestion.Email}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SuggestionInput;
|
||||||
@@ -10,7 +10,11 @@ const Menu = ({ item, isOpen, closeSidebar }) => {
|
|||||||
? `/${item.pageType}/${item.objectId}`
|
? `/${item.pageType}/${item.objectId}`
|
||||||
: `/${item.objectId}`
|
: `/${item.objectId}`
|
||||||
}
|
}
|
||||||
className="mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer focus:text-[#0056b3] focus:bg-[#eef1f5]"
|
className={({ isActive }) =>
|
||||||
|
`${
|
||||||
|
isActive ? "text-[#0056b3] bg-[#eef1f5]" : ""
|
||||||
|
} mx-auto flex items-center hover:bg-[#eef1f5] p-3 lg:p-4 cursor-pointer`
|
||||||
|
}
|
||||||
onClick={closeSidebar}
|
onClick={closeSidebar}
|
||||||
tabIndex={isOpen ? 0 : -1}
|
tabIndex={isOpen ? 0 : -1}
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
|
|||||||
@@ -4,22 +4,34 @@ import { isEnableSubscription, themeColor } from "./const";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { rgb } from "pdf-lib";
|
import { rgb } from "pdf-lib";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
|
import { appInfo } from "./appinfo";
|
||||||
|
import { saveAs } from "file-saver";
|
||||||
|
import printModule from "print-js";
|
||||||
|
|
||||||
export const isMobile = window.innerWidth < 767;
|
export const isMobile = window.innerWidth < 767;
|
||||||
export const textInputWidget = "text input";
|
export const textInputWidget = "text input";
|
||||||
export const textWidget = "text";
|
export const textWidget = "text";
|
||||||
export const radioButtonWidget = "radio button";
|
export const radioButtonWidget = "radio button";
|
||||||
export const openInNewTab = (url) => {
|
export const openInNewTab = (url, target) => {
|
||||||
window.open(url, "_blank", "noopener,noreferrer");
|
if (target) {
|
||||||
|
window.open(url, target, "noopener,noreferrer");
|
||||||
|
} else {
|
||||||
|
window.open(url, "_blank", "noopener,noreferrer");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function fetchSubscription(extUserId) {
|
export async function fetchSubscription(
|
||||||
|
extUserId,
|
||||||
|
contactObjId,
|
||||||
|
isGuestSign = false
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const extClass = localStorage.getItem("Extand_Class");
|
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||||
|
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||||
|
// console.log("extClass ", extClass);
|
||||||
let extUser;
|
let extUser;
|
||||||
if (extClass) {
|
if (extClass && extClass.length > 0) {
|
||||||
const jsonSender = JSON.parse(extClass);
|
extUser = extClass[0].objectId;
|
||||||
extUser = jsonSender[0].objectId;
|
|
||||||
} else {
|
} else {
|
||||||
extUser = extUserId;
|
extUser = extUserId;
|
||||||
}
|
}
|
||||||
@@ -30,11 +42,19 @@ export async function fetchSubscription(extUserId) {
|
|||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
};
|
};
|
||||||
const params = { extUserId: extUser };
|
const params = isGuestSign
|
||||||
|
? { contactId: contactObjId }
|
||||||
|
: { extUserId: extUser };
|
||||||
const tenatRes = await axios.post(url, params, { headers: headers });
|
const tenatRes = await axios.post(url, params, { headers: headers });
|
||||||
const plan = tenatRes.data?.result?.result?.PlanCode;
|
let plan, status, billingDate;
|
||||||
const billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
|
if (isGuestSign) {
|
||||||
return { plan, billingDate };
|
plan = tenatRes.data?.result?.result?.plan;
|
||||||
|
status = tenatRes.data?.result?.result?.isSubscribed;
|
||||||
|
} else {
|
||||||
|
plan = tenatRes.data?.result?.result?.PlanCode;
|
||||||
|
billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
|
||||||
|
}
|
||||||
|
return { plan, billingDate, status };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Err in fetch subscription", err);
|
console.log("Err in fetch subscription", err);
|
||||||
return { plan: "", billingDate: "" };
|
return { plan: "", billingDate: "" };
|
||||||
@@ -159,11 +179,10 @@ export const contractUsers = async (email) => {
|
|||||||
.then((Listdata) => {
|
.then((Listdata) => {
|
||||||
const json = Listdata.data;
|
const json = Listdata.data;
|
||||||
let data = [];
|
let data = [];
|
||||||
|
|
||||||
if (json && json.result) {
|
if (json && json.result) {
|
||||||
data.push(json.result);
|
data.push(json.result);
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
return data;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("Err ", err);
|
console.log("Err ", err);
|
||||||
@@ -528,53 +547,94 @@ export const signPdfFun = async (
|
|||||||
signerObjectId,
|
signerObjectId,
|
||||||
setIsAlert,
|
setIsAlert,
|
||||||
objectId,
|
objectId,
|
||||||
isSubscribed
|
isSubscribed,
|
||||||
|
activeMailAdapter,
|
||||||
|
xyPosition
|
||||||
) => {
|
) => {
|
||||||
let singleSign,
|
let singleSign,
|
||||||
isCustomCompletionMail = false;
|
isCustomCompletionMail = false;
|
||||||
|
|
||||||
//get tenant details
|
try {
|
||||||
const tenantDetails = await getTenantDetails(objectId);
|
//get tenant details
|
||||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
const tenantDetails = await getTenantDetails(objectId);
|
||||||
alert("User does not exist");
|
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||||
} else {
|
alert("User does not exist");
|
||||||
if (
|
} else {
|
||||||
tenantDetails?.CompletionBody &&
|
if (
|
||||||
tenantDetails?.CompletionSubject &&
|
tenantDetails?.CompletionBody &&
|
||||||
(!isEnableSubscription || isSubscribed)
|
tenantDetails?.CompletionSubject &&
|
||||||
) {
|
(!isEnableSubscription || isSubscribed)
|
||||||
isCustomCompletionMail = true;
|
) {
|
||||||
}
|
isCustomCompletionMail = true;
|
||||||
}
|
|
||||||
|
|
||||||
singleSign = {
|
|
||||||
pdfFile: base64Url,
|
|
||||||
docId: documentId,
|
|
||||||
userId: signerObjectId,
|
|
||||||
isCustomCompletionMail: isCustomCompletionMail
|
|
||||||
};
|
|
||||||
const response = await axios
|
|
||||||
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.then((Listdata) => {
|
|
||||||
const json = Listdata.data;
|
|
||||||
const res = json.result;
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("Err ", err);
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "something went wrong"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
// below for loop is used to get first signature of user to send if to signpdf
|
||||||
|
// for adding it in completion certificate
|
||||||
|
let getSignature;
|
||||||
|
for (let item of xyPosition) {
|
||||||
|
if (!getSignature) {
|
||||||
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let base64Sign = getSignature.SignUrl;
|
||||||
|
//check https type signature (default signature exist) then convert in base64
|
||||||
|
const isUrl = base64Sign.includes("https");
|
||||||
|
if (isUrl) {
|
||||||
|
try {
|
||||||
|
base64Sign = await fetchImageBase64(base64Sign);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//change image width and height to 104/44 in png base64
|
||||||
|
const getNewse64 = await changeImageWH(base64Sign);
|
||||||
|
//remove suffiix of base64
|
||||||
|
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||||
|
|
||||||
|
singleSign = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
pdfFile: base64Url,
|
||||||
|
docId: documentId,
|
||||||
|
userId: signerObjectId,
|
||||||
|
isCustomCompletionMail: isCustomCompletionMail,
|
||||||
|
signature: suffixbase64
|
||||||
|
};
|
||||||
|
const response = await axios
|
||||||
|
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
// sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((Listdata) => {
|
||||||
|
const json = Listdata.data;
|
||||||
|
const res = json.result;
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("Err ", err);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong"
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const randomId = () => {
|
export const randomId = () => {
|
||||||
@@ -609,6 +669,7 @@ export const createDocument = async (template, placeholders, signerData) => {
|
|||||||
Name: Doc.Name,
|
Name: Doc.Name,
|
||||||
URL: Doc.URL,
|
URL: Doc.URL,
|
||||||
SignedUrl: Doc.SignedUrl,
|
SignedUrl: Doc.SignedUrl,
|
||||||
|
SentToOthers: Doc.SentToOthers,
|
||||||
Description: Doc.Description,
|
Description: Doc.Description,
|
||||||
Note: Doc.Note,
|
Note: Doc.Note,
|
||||||
Placeholders: placeholdersArr,
|
Placeholders: placeholdersArr,
|
||||||
@@ -623,7 +684,9 @@ export const createDocument = async (template, placeholders, signerData) => {
|
|||||||
objectId: Doc.CreatedBy.objectId
|
objectId: Doc.CreatedBy.objectId
|
||||||
},
|
},
|
||||||
Signers: signers,
|
Signers: signers,
|
||||||
SendinOrder: Doc?.SendinOrder || false
|
SendinOrder: Doc?.SendinOrder || false,
|
||||||
|
AutomaticReminders: Doc?.AutomaticReminders || false,
|
||||||
|
RemindOnceInEvery: parseInt(Doc?.RemindOnceInEvery || 5)
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1111,7 +1174,6 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
|||||||
const imageType = event.target.files[0].type;
|
const imageType = event.target.files[0].type;
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(event.target.files[0]);
|
reader.readAsDataURL(event.target.files[0]);
|
||||||
|
|
||||||
reader.onloadend = function (e) {
|
reader.onloadend = function (e) {
|
||||||
let width, height;
|
let width, height;
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
@@ -1139,6 +1201,50 @@ export const onImageSelect = (event, setImgWH, setImage) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//convert https url to base64
|
||||||
|
export const fetchImageBase64 = async (imageUrl) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(imageUrl);
|
||||||
|
const blob = await response.blob();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
const base64data = reader.result;
|
||||||
|
resolve(base64data);
|
||||||
|
};
|
||||||
|
reader.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Error converting URL to base64:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function for select image and upload image
|
||||||
|
export const changeImageWH = async (base64Image) => {
|
||||||
|
const newWidth = 100;
|
||||||
|
const newHeight = 40;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = base64Image;
|
||||||
|
img.onload = async () => {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
canvas.width = newWidth;
|
||||||
|
canvas.height = newHeight;
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
ctx.drawImage(img, 0, 0, newWidth, newHeight);
|
||||||
|
const resizedBase64 = canvas.toDataURL("image/png", 1);
|
||||||
|
resolve(resizedBase64);
|
||||||
|
};
|
||||||
|
img.onerror = (error) => {
|
||||||
|
reject(error);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
//function for embed multiple signature using pdf-lib
|
//function for embed multiple signature using pdf-lib
|
||||||
export const multiSignEmbed = async (
|
export const multiSignEmbed = async (
|
||||||
pngUrl,
|
pngUrl,
|
||||||
@@ -1917,3 +2023,165 @@ export const convertPdfArrayBuffer = async (url) => {
|
|||||||
return "Error";
|
return "Error";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function
|
||||||
|
export const handleSendOTP = async (email) => {
|
||||||
|
try {
|
||||||
|
let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId")
|
||||||
|
};
|
||||||
|
const body = { email: email };
|
||||||
|
await axios.post(url, body, { headers: headers });
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//handle download signed pdf
|
||||||
|
export const handleDownloadPdf = async (
|
||||||
|
pdfDetails,
|
||||||
|
pdfUrl,
|
||||||
|
setIsDownloading
|
||||||
|
) => {
|
||||||
|
const pdfName = pdfDetails[0] && pdfDetails[0].Name;
|
||||||
|
setIsDownloading("pdf");
|
||||||
|
try {
|
||||||
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
|
saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`);
|
||||||
|
setIsDownloading("");
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in getsignedurl", err);
|
||||||
|
setIsDownloading("");
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sanitizeFileName = (pdfName) => {
|
||||||
|
// Replace spaces with underscore
|
||||||
|
return pdfName.replace(/ /g, "_");
|
||||||
|
};
|
||||||
|
//function for print digital sign pdf
|
||||||
|
export const handleToPrint = async (event, pdfUrl, setIsDownloading) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setIsDownloading("pdf");
|
||||||
|
try {
|
||||||
|
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
|
||||||
|
const axiosRes = await axios.post(
|
||||||
|
`${appInfo.baseUrl}/functions/getsignedurl`,
|
||||||
|
{ url: pdfUrl },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"content-type": "Application/json",
|
||||||
|
"X-Parse-Application-Id": appInfo.appId,
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const url = axiosRes.data.result;
|
||||||
|
const pdf = await getBase64FromUrl(url);
|
||||||
|
const isAndroidDevice = navigator.userAgent.match(/Android/i);
|
||||||
|
const isAppleDevice =
|
||||||
|
(/iPad|iPhone|iPod/.test(navigator.platform) ||
|
||||||
|
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) &&
|
||||||
|
!window.MSStream;
|
||||||
|
if (isAndroidDevice || isAppleDevice) {
|
||||||
|
const byteArray = Uint8Array.from(
|
||||||
|
atob(pdf)
|
||||||
|
.split("")
|
||||||
|
.map((char) => char.charCodeAt(0))
|
||||||
|
);
|
||||||
|
const blob = new Blob([byteArray], { type: "application/pdf" });
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
window.open(blobUrl, "_blank");
|
||||||
|
setIsDownloading("");
|
||||||
|
} else {
|
||||||
|
printModule({ printable: pdf, type: "pdf", base64: true });
|
||||||
|
setIsDownloading("");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setIsDownloading("");
|
||||||
|
console.log("err in getsignedurl", err);
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//handle download signed pdf
|
||||||
|
export const handleDownloadCertificate = async (
|
||||||
|
pdfDetails,
|
||||||
|
setIsDownloading
|
||||||
|
) => {
|
||||||
|
if (pdfDetails?.length > 0 && pdfDetails[0]?.CertificateUrl) {
|
||||||
|
try {
|
||||||
|
await fetch(pdfDetails[0] && pdfDetails[0]?.CertificateUrl);
|
||||||
|
const certificateUrl = pdfDetails[0] && pdfDetails[0]?.CertificateUrl;
|
||||||
|
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in download in certificate", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsDownloading("certificate");
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
docId: pdfDetails[0]?.objectId
|
||||||
|
};
|
||||||
|
const docDetails = await axios.post(
|
||||||
|
`${localStorage.getItem("baseUrl")}functions/getDocument`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (docDetails.data && docDetails.data.result) {
|
||||||
|
const doc = docDetails.data.result;
|
||||||
|
if (doc?.CertificateUrl) {
|
||||||
|
await fetch(doc?.CertificateUrl);
|
||||||
|
const certificateUrl = doc?.CertificateUrl;
|
||||||
|
saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`);
|
||||||
|
setIsDownloading("");
|
||||||
|
} else {
|
||||||
|
setIsDownloading("certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in download in certificate", err);
|
||||||
|
alert("something went wrong, please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export async function findContact(value) {
|
||||||
|
try {
|
||||||
|
const currentUser = Parse.User.current();
|
||||||
|
const contactbook = new Parse.Query("contracts_Contactbook");
|
||||||
|
contactbook.equalTo(
|
||||||
|
"CreatedBy",
|
||||||
|
Parse.User.createWithoutData(currentUser.id)
|
||||||
|
);
|
||||||
|
contactbook.notEqualTo("IsDeleted", true);
|
||||||
|
contactbook.matches("Email", new RegExp(value, "i"));
|
||||||
|
|
||||||
|
const contactRes = await contactbook.find();
|
||||||
|
if (contactRes) {
|
||||||
|
const res = JSON.parse(JSON.stringify(contactRes));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching suggestions:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default function reportJson(id) {
|
|||||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||||
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
||||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||||
|
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||||
switch (id) {
|
switch (id) {
|
||||||
// draft documents report
|
// draft documents report
|
||||||
case "ByHuevtCFY":
|
case "ByHuevtCFY":
|
||||||
@@ -85,6 +85,14 @@ export default function reportJson(id) {
|
|||||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||||
action: "option",
|
action: "option",
|
||||||
subaction: [
|
subaction: [
|
||||||
|
{
|
||||||
|
btnId: "1630",
|
||||||
|
btnLabel: "Resend",
|
||||||
|
hoverLabel: "Resend",
|
||||||
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "resend"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
btnId: "1688",
|
btnId: "1688",
|
||||||
btnLabel: "Revoke",
|
btnLabel: "Revoke",
|
||||||
@@ -223,6 +231,14 @@ export default function reportJson(id) {
|
|||||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||||
action: "option",
|
action: "option",
|
||||||
subaction: [
|
subaction: [
|
||||||
|
{
|
||||||
|
btnId: "1631",
|
||||||
|
btnLabel: "Resend",
|
||||||
|
hoverLabel: "Resend",
|
||||||
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "resend"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
btnId: "6788",
|
btnId: "6788",
|
||||||
btnLabel: "Revoke",
|
btnLabel: "Revoke",
|
||||||
@@ -310,7 +326,7 @@ export default function reportJson(id) {
|
|||||||
case "6TeaPr321t":
|
case "6TeaPr321t":
|
||||||
return {
|
return {
|
||||||
reportName: "Templates",
|
reportName: "Templates",
|
||||||
heading: head,
|
heading: templateReport,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
btnId: "2234",
|
btnId: "2234",
|
||||||
@@ -325,6 +341,17 @@ export default function reportJson(id) {
|
|||||||
message:
|
message:
|
||||||
"Click the 'Use' button to create a new document from an existing template. "
|
"Click the 'Use' button to create a new document from an existing template. "
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
btnId: "1631",
|
||||||
|
btnLabel: "Quick send",
|
||||||
|
hoverLabel: "Quick send",
|
||||||
|
btnIcon: "fa-solid fa-envelope",
|
||||||
|
redirectUrl: "",
|
||||||
|
action: "bulksend",
|
||||||
|
selector: "tourbulksend",
|
||||||
|
message:
|
||||||
|
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
btnId: "2234",
|
btnId: "2234",
|
||||||
hoverLabel: "option",
|
hoverLabel: "option",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const plans = [
|
|||||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||||
target: "_self",
|
target: "_self",
|
||||||
benefits: [
|
benefits: [
|
||||||
"Everything in OpenSign™ professional",
|
"Everything in OpenSign™ free",
|
||||||
"Field validations",
|
"Field validations",
|
||||||
"Regular expression validations",
|
"Regular expression validations",
|
||||||
"Organize docs in OpenSign™ Drive",
|
"Organize docs in OpenSign™ Drive",
|
||||||
@@ -52,7 +52,8 @@ const plans = [
|
|||||||
"API Access",
|
"API Access",
|
||||||
"100 API signatures included",
|
"100 API signatures included",
|
||||||
"DocumentId removal from signed docs",
|
"DocumentId removal from signed docs",
|
||||||
"Custom email templates(coming soon)"
|
"Custom email templates",
|
||||||
|
"Auto reminders"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,29 +28,34 @@ const HomeLayout = () => {
|
|||||||
const [tourConfigs, setTourConfigs] = useState([]);
|
const [tourConfigs, setTourConfigs] = useState([]);
|
||||||
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
||||||
|
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
if (!tenantId) {
|
||||||
try {
|
setIsUserValid(false);
|
||||||
// Use the session token to validate the user
|
} else {
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
(async () => {
|
||||||
const user = await userQuery.get(Parse.User.current().id, {
|
try {
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
// Use the session token to validate the user
|
||||||
});
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
if (user) {
|
const user = await userQuery.get(Parse.User.current().id, {
|
||||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
checkIsSubscribed();
|
});
|
||||||
} else {
|
if (user) {
|
||||||
|
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||||
|
checkIsSubscribed();
|
||||||
|
} else {
|
||||||
|
setIsUserValid(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Session token is invalid or there was an error
|
||||||
setIsUserValid(false);
|
setIsUserValid(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})();
|
||||||
// Session token is invalid or there was an error
|
saveCookies();
|
||||||
setIsUserValid(false);
|
}
|
||||||
}
|
|
||||||
})();
|
|
||||||
saveCookies();
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, [tenantId]);
|
||||||
//function to use save data in cookies storage
|
//function to use save data in cookies storage
|
||||||
const saveCookies = () => {
|
const saveCookies = () => {
|
||||||
const main_Domain = window.location.origin;
|
const main_Domain = window.location.origin;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const Dashboard = () => {
|
|||||||
getDashboard(localStorage.getItem("PageLanding"));
|
getDashboard(localStorage.getItem("PageLanding"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
navigate("/", { replace: true, state: { from: location } });
|
navigate("/", { replace: true, state: { from: "" } });
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ function ForgotPassword() {
|
|||||||
</button>
|
</button>
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/"
|
to="/"
|
||||||
className="rounded-sm bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-3 text-xs font-semibold shadow uppercase"
|
className="rounded-sm bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] px-4 py-3 text-xs font-semibold shadow uppercase cursor-pointer"
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|||||||
+675
-203
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import { formJson } from "../json/FormJson";
|
import { formJson } from "../json/FormJson";
|
||||||
import AddUser from "../components/AddUser";
|
import AddUser from "../components/AddUser";
|
||||||
@@ -11,7 +11,14 @@ import SignersInput from "../components/shared/fields/SignersInput";
|
|||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import PageNotFound from "./PageNotFound";
|
import PageNotFound from "./PageNotFound";
|
||||||
import { SaveFileSize } from "../constant/saveFileSize";
|
import { SaveFileSize } from "../constant/saveFileSize";
|
||||||
import { getFileName } from "../constant/Utils";
|
import { checkIsSubscribed, getFileName, toDataUrl } from "../constant/Utils";
|
||||||
|
import { PDFDocument } from "pdf-lib";
|
||||||
|
import axios from "axios";
|
||||||
|
import { isEnableSubscription, submitBtn } from "../constant/const";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
|
import Upgrade from "../primitives/Upgrade";
|
||||||
|
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||||
|
|
||||||
// `Form` render all type of Form on this basis of their provided in path
|
// `Form` render all type of Form on this basis of their provided in path
|
||||||
function Form() {
|
function Form() {
|
||||||
@@ -31,6 +38,8 @@ function Form() {
|
|||||||
|
|
||||||
const Forms = (props) => {
|
const Forms = (props) => {
|
||||||
const maxFileSize = 20;
|
const maxFileSize = 20;
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const inputFileRef = useRef(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [signers, setSigners] = useState([]);
|
const [signers, setSigners] = useState([]);
|
||||||
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
|
||||||
@@ -39,37 +48,248 @@ const Forms = (props) => {
|
|||||||
Description: "",
|
Description: "",
|
||||||
Note: "",
|
Note: "",
|
||||||
TimeToCompleteDays: 15,
|
TimeToCompleteDays: 15,
|
||||||
SendinOrder: "false"
|
SendinOrder: "false",
|
||||||
|
password: "",
|
||||||
|
file: "",
|
||||||
|
remindOnceInEvery: 5,
|
||||||
|
autoreminder: false
|
||||||
});
|
});
|
||||||
const [fileupload, setFileUpload] = useState([]);
|
const [fileupload, setFileUpload] = useState("");
|
||||||
const [fileload, setfileload] = useState(false);
|
const [fileload, setfileload] = useState(false);
|
||||||
const [percentage, setpercentage] = useState(0);
|
const [percentage, setpercentage] = useState(0);
|
||||||
const [isReset, setIsReset] = useState(false);
|
const [isReset, setIsReset] = useState(false);
|
||||||
const [isAlert, setIsAlert] = useState(false);
|
const [isAlert, setIsAlert] = useState(false);
|
||||||
const [isSubmit, setIsSubmit] = useState(false);
|
const [isSubmit, setIsSubmit] = useState(false);
|
||||||
const [isErr, setIsErr] = useState("");
|
const [isErr, setIsErr] = useState("");
|
||||||
|
const [isPassword, setIsPassword] = useState(false);
|
||||||
|
const [isDecrypting, setIsDecrypting] = useState(false);
|
||||||
|
const [isCorrectPass, setIsCorrectPass] = useState(true);
|
||||||
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
const handleStrInput = (e) => {
|
const handleStrInput = (e) => {
|
||||||
|
setIsCorrectPass(true);
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleReset();
|
handleReset();
|
||||||
|
return () => abortController.abort();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.title]);
|
}, [props.title]);
|
||||||
const handleFileInput = (e) => {
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchSubscription();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
const fetchSubscription = async () => {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const getIsSubscribe = await checkIsSubscribed();
|
||||||
|
setIsSubscribe(getIsSubscribe);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getFileAsArrayBuffer(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = (e) => {
|
||||||
|
resolve(e.target.result);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = (e) => {
|
||||||
|
reject(e.target.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const handleFileInput = async (e) => {
|
||||||
setpercentage(0);
|
setpercentage(0);
|
||||||
try {
|
try {
|
||||||
let files = e.target.files;
|
let files = e.target.files;
|
||||||
|
setFormData((prev) => ({ ...prev, file: e.target.files[0] }));
|
||||||
if (typeof files[0] !== "undefined") {
|
if (typeof files[0] !== "undefined") {
|
||||||
const mb = Math.round(files[0].size / Math.pow(1024, 2));
|
const mb = Math.round(files[0].size / Math.pow(1024, 2));
|
||||||
if (mb > maxFileSize) {
|
if (mb > maxFileSize) {
|
||||||
alert(
|
alert(
|
||||||
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
|
`The selected file size is too large. Please select a file less than ${maxFileSize} MB`
|
||||||
);
|
);
|
||||||
setFileUpload([]);
|
setFileUpload("");
|
||||||
e.target.value = "";
|
e.target.value = "";
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
handleFileUpload(files[0]);
|
if (files?.[0]?.type === "application/pdf") {
|
||||||
|
try {
|
||||||
|
const res = await getFileAsArrayBuffer(files[0]);
|
||||||
|
await PDFDocument.load(res);
|
||||||
|
handleFileUpload(files[0]);
|
||||||
|
} catch (err) {
|
||||||
|
if (err?.message?.includes("is encrypted")) {
|
||||||
|
try {
|
||||||
|
await Parse.Cloud.run("encryptedpdf", {
|
||||||
|
email: Parse.User.current().getEmail()
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in sending posthog encryptedpdf", err);
|
||||||
|
}
|
||||||
|
// console.log("err ", err);
|
||||||
|
try {
|
||||||
|
setIsDecrypting(true);
|
||||||
|
const fileName = files?.[0].name;
|
||||||
|
const size = files?.[0].size;
|
||||||
|
const name = sanitizeFileName(fileName);
|
||||||
|
const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; //
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", files[0]);
|
||||||
|
formData.append("password", "");
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
"content-type": "multipart/form-data"
|
||||||
|
// sessiontoken: Parse.User.current().getSessionToken()
|
||||||
|
},
|
||||||
|
responseType: "blob"
|
||||||
|
};
|
||||||
|
const response = await axios.post(url, formData, config);
|
||||||
|
const pdfBlob = new Blob([response.data], {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
const pdfFile = new File([pdfBlob], name, {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
setIsDecrypting(false);
|
||||||
|
setfileload(true);
|
||||||
|
|
||||||
|
// Upload the file to Parse Server
|
||||||
|
const parseFile = new Parse.File(
|
||||||
|
name,
|
||||||
|
pdfFile,
|
||||||
|
"application/pdf"
|
||||||
|
);
|
||||||
|
|
||||||
|
await parseFile.save({
|
||||||
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
|
if (type === "upload" && progressValue !== null) {
|
||||||
|
const percentCompleted = Math.round(
|
||||||
|
(loaded * 100) / total
|
||||||
|
);
|
||||||
|
setpercentage(percentCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retrieve the URL of the uploaded file
|
||||||
|
if (parseFile.url()) {
|
||||||
|
setFileUpload(parseFile.url());
|
||||||
|
setfileload(false);
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, parseFile.url(), tenantId);
|
||||||
|
return parseFile.url();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setfileload(false);
|
||||||
|
setpercentage(0);
|
||||||
|
if (err?.response?.status === 401) {
|
||||||
|
setIsPassword(true);
|
||||||
|
} else {
|
||||||
|
console.log("Error uploading file: ", err?.response);
|
||||||
|
setIsDecrypting(false);
|
||||||
|
e.target.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("err ", err);
|
||||||
|
setFileUpload("");
|
||||||
|
e.target.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const isImage = files?.[0]?.type.includes("image/");
|
||||||
|
if (isImage) {
|
||||||
|
const image = await toDataUrl(files[0]);
|
||||||
|
const pdfDoc = await PDFDocument.create();
|
||||||
|
let embedImg;
|
||||||
|
if (files?.[0]?.type === "image/png") {
|
||||||
|
embedImg = await pdfDoc.embedPng(image);
|
||||||
|
} else {
|
||||||
|
embedImg = await pdfDoc.embedJpg(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get image dimensions
|
||||||
|
const imageWidth = embedImg.width;
|
||||||
|
const imageHeight = embedImg.height;
|
||||||
|
const page = pdfDoc.addPage([imageWidth, imageHeight]);
|
||||||
|
page.drawImage(embedImg, {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: imageWidth,
|
||||||
|
height: imageHeight
|
||||||
|
});
|
||||||
|
const getFile = await pdfDoc.save({
|
||||||
|
useObjectStreams: false
|
||||||
|
});
|
||||||
|
setfileload(true);
|
||||||
|
const fileName = files[0].name;
|
||||||
|
const size = files[0].size;
|
||||||
|
const name = sanitizeFileName(fileName);
|
||||||
|
const pdfName = `${name?.split(".")[0]}.pdf`;
|
||||||
|
const parseFile = new Parse.File(
|
||||||
|
pdfName,
|
||||||
|
[...getFile],
|
||||||
|
"application/pdf"
|
||||||
|
);
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, response.url(), tenantId);
|
||||||
|
return response.url();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
e.target.value = "";
|
||||||
|
setfileload(false);
|
||||||
|
setpercentage(0);
|
||||||
|
console.error("Error uploading file:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
try {
|
||||||
|
setfileload(true);
|
||||||
|
const url = "https://ai.nxglabs.in/docxtopdf";
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", files[0]);
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
"content-type": "multipart/form-data",
|
||||||
|
sessiontoken: Parse.User.current().getSessionToken()
|
||||||
|
},
|
||||||
|
signal: abortController.signal
|
||||||
|
};
|
||||||
|
const res = await axios.post(url, formData, config);
|
||||||
|
if (res.data) {
|
||||||
|
setFileUpload(res.data.url);
|
||||||
|
setfileload(false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
e.target.value = "";
|
||||||
|
setfileload(false);
|
||||||
|
setpercentage(0);
|
||||||
|
console.log("err in libreconverter ", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
alert("Please select file.");
|
alert("Please select file.");
|
||||||
@@ -162,64 +382,69 @@ const Forms = (props) => {
|
|||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setIsSubmit(true);
|
if (fileupload) {
|
||||||
try {
|
setIsSubmit(true);
|
||||||
const currentUser = Parse.User.current();
|
try {
|
||||||
const object = new Parse.Object(props.Cls);
|
const currentUser = Parse.User.current();
|
||||||
object.set("Name", formData?.Name);
|
const object = new Parse.Object(props.Cls);
|
||||||
object.set("Description", formData?.Description);
|
object.set("Name", formData?.Name);
|
||||||
object.set("Note", formData?.Note);
|
object.set("Description", formData?.Description);
|
||||||
if (props.title === "Request Signatures") {
|
object.set("Note", formData?.Note);
|
||||||
object.set(
|
if (props.title === "Request Signatures") {
|
||||||
"TimeToCompleteDays",
|
object.set(
|
||||||
parseInt(formData?.TimeToCompleteDays)
|
"TimeToCompleteDays",
|
||||||
);
|
parseInt(formData?.TimeToCompleteDays)
|
||||||
}
|
);
|
||||||
if (props.title !== "Sign Yourself") {
|
}
|
||||||
const isChecked = formData.SendinOrder === "false" ? false : true;
|
if (props.title !== "Sign Yourself") {
|
||||||
object.set("SendinOrder", isChecked);
|
const isChecked = formData.SendinOrder === "false" ? false : true;
|
||||||
}
|
object.set("SendinOrder", isChecked);
|
||||||
object.set("URL", fileupload);
|
object.set("AutomaticReminders", formData.autoreminder);
|
||||||
object.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
|
object.set("RemindOnceInEvery", parseInt(formData.remindOnceInEvery));
|
||||||
if (folder && folder.ObjectId) {
|
}
|
||||||
object.set("Folder", {
|
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",
|
__type: "Pointer",
|
||||||
className: props.Cls,
|
className: "contracts_Users",
|
||||||
objectId: folder.ObjectId
|
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);
|
||||||
}
|
}
|
||||||
if (signers && signers.length > 0) {
|
} else {
|
||||||
object.set("Signers", signers);
|
alert("Please wait until the PDF has been uploading.");
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -249,13 +474,92 @@ const Forms = (props) => {
|
|||||||
? "Note to myself"
|
? "Note to myself"
|
||||||
: "Please review and sign this document",
|
: "Please review and sign this document",
|
||||||
TimeToCompleteDays: 15,
|
TimeToCompleteDays: 15,
|
||||||
SendinOrder: "true"
|
SendinOrder: "true",
|
||||||
|
password: "",
|
||||||
|
file: "",
|
||||||
|
remindOnceInEvery: 5,
|
||||||
|
autoreminder: false
|
||||||
});
|
});
|
||||||
setFileUpload([]);
|
setFileUpload("");
|
||||||
setpercentage(0);
|
setpercentage(0);
|
||||||
setTimeout(() => setIsReset(false), 50);
|
setTimeout(() => setIsReset(false), 50);
|
||||||
};
|
};
|
||||||
|
const handlePasswordSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsPassword(false);
|
||||||
|
setfileload(true);
|
||||||
|
try {
|
||||||
|
const fileName = formData?.file?.name;
|
||||||
|
const size = formData?.file?.size;
|
||||||
|
const name = sanitizeFileName(fileName);
|
||||||
|
const url = "https://ai.nxglabs.in/decryptpdf"; //
|
||||||
|
let Data = new FormData();
|
||||||
|
Data.append("file", formData?.file);
|
||||||
|
Data.append("password", formData.password);
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
"content-type": "multipart/form-data"
|
||||||
|
// sessiontoken: Parse.User.current().getSessionToken()
|
||||||
|
},
|
||||||
|
responseType: "blob"
|
||||||
|
};
|
||||||
|
const response = await axios.post(url, Data, config);
|
||||||
|
const pdfBlob = new Blob([response.data], {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
const pdfFile = new File([pdfBlob], name, {
|
||||||
|
type: "application/pdf"
|
||||||
|
});
|
||||||
|
setIsDecrypting(false);
|
||||||
|
// Upload the file to Parse Server
|
||||||
|
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
|
||||||
|
|
||||||
|
await parseFile.save({
|
||||||
|
progress: (progressValue, loaded, total, { type }) => {
|
||||||
|
if (type === "upload" && progressValue !== null) {
|
||||||
|
const percentCompleted = Math.round((loaded * 100) / total);
|
||||||
|
setpercentage(percentCompleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Retrieve the URL of the uploaded file
|
||||||
|
if (parseFile.url()) {
|
||||||
|
setFormData((prev) => ({ ...prev, password: "" }));
|
||||||
|
setFileUpload(parseFile.url());
|
||||||
|
setfileload(false);
|
||||||
|
const tenantId = localStorage.getItem("TenantId");
|
||||||
|
SaveFileSize(size, parseFile.url(), tenantId);
|
||||||
|
return parseFile.url();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setfileload(false);
|
||||||
|
setpercentage(0);
|
||||||
|
if (err?.response?.status === 401) {
|
||||||
|
setIsPassword(true);
|
||||||
|
setIsCorrectPass(false);
|
||||||
|
} else {
|
||||||
|
console.log("Error uploading file: ", err?.response);
|
||||||
|
setFormData((prev) => ({ ...prev, password: "" }));
|
||||||
|
setIsDecrypting(false);
|
||||||
|
if (inputFileRef.current) {
|
||||||
|
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handeCloseModal = () => {
|
||||||
|
setIsPassword(false);
|
||||||
|
setFormData((prev) => ({ ...prev, file: "", password: "" }));
|
||||||
|
setIsDecrypting(false);
|
||||||
|
setfileload(false);
|
||||||
|
if (inputFileRef.current) {
|
||||||
|
inputFileRef.current.value = ""; // Set file input value to empty string
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleAutoReminder = () => {
|
||||||
|
setFormData((prev) => ({ ...prev, autoreminder: !formData.autoreminder }));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||||
<Title title={props?.title} />
|
<Title title={props?.title} />
|
||||||
@@ -279,166 +583,334 @@ const Forms = (props) => {
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit}>
|
<>
|
||||||
<h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1>
|
<ModalUi
|
||||||
{fileload && (
|
isOpen={isPassword}
|
||||||
<div className="flex items-center gap-x-2">
|
handleClose={() => handeCloseModal()}
|
||||||
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
|
title={"Enter Pdf Password"}
|
||||||
<div
|
>
|
||||||
className="h-2 rounded-full bg-blue-500"
|
<form onSubmit={handlePasswordSubmit}>
|
||||||
style={{ width: `${percentage}%` }}
|
<div className="px-6 pt-3 pb-2">
|
||||||
></div>
|
<label className="mb-2 text-xs">Password</label>
|
||||||
</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 : {getFileName(fileupload)}
|
|
||||||
</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
|
<input
|
||||||
type="file"
|
type="text"
|
||||||
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
name="password"
|
||||||
onChange={(e) => handleFileInput(e)}
|
value={formData.password}
|
||||||
accept="application/pdf"
|
onChange={(e) => handleStrInput(e)}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
placeholder="Enter pdf password"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
{!isCorrectPass ? (
|
||||||
<DropboxChooser
|
<p className="ml-2 text-[11px] text-red-600">
|
||||||
onSuccess={dropboxSuccess}
|
Please provide correct password
|
||||||
onCancel={dropboxCancel}
|
</p>
|
||||||
/>
|
) : (
|
||||||
|
<p className="ml-2 text-[11px] text-transparent text-red-600">
|
||||||
|
.
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="px-6 my-3">
|
||||||
|
<button type="submit" className={submitBtn}>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</ModalUi>
|
||||||
|
<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>
|
{isDecrypting && (
|
||||||
<div className="text-xs mt-2">
|
<div className="flex items-center gap-x-2">
|
||||||
<label className="block">
|
<span className="text-black text-sm">
|
||||||
{props.title === "New Template"
|
Decrypting pdf please wait...
|
||||||
? "Template Title"
|
</span>
|
||||||
: "Document Title"}
|
</div>
|
||||||
<span className="text-red-500 text-[13px]">*</span>
|
)}
|
||||||
</label>
|
<div className="text-xs">
|
||||||
<input
|
<label className="block">
|
||||||
name="Name"
|
{`File (pdf, png, jpg, jpeg${
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
isEnableSubscription ? ", docx)" : ")"
|
||||||
value={formData.Name}
|
}`}
|
||||||
onChange={(e) => handleStrInput(e)}
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
required
|
</label>
|
||||||
/>
|
{fileupload.length > 0 ? (
|
||||||
</div>
|
<div className="flex gap-2 justify-center items-center">
|
||||||
<div className="text-xs mt-2">
|
<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]">
|
||||||
<label className="block">Description</label>
|
<div className="break-all">
|
||||||
<input
|
file selected : {getFileName(fileupload)}
|
||||||
name="Description"
|
</div>
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
<div
|
||||||
value={formData.Description}
|
onClick={() => setFileUpload("")}
|
||||||
onChange={(e) => handleStrInput(e)}
|
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||||
/>
|
>
|
||||||
</div>
|
<i className="fa-solid fa-xmark"></i>
|
||||||
{props.signers && (
|
</div>
|
||||||
<SignersInput onChange={handleSigners} isReset={isReset} required />
|
</div>
|
||||||
)}
|
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||||
<div className="text-xs mt-2">
|
<DropboxChooser
|
||||||
<label className="block">
|
onSuccess={dropboxSuccess}
|
||||||
Note<span className="text-red-500 text-[13px]">*</span>
|
onCancel={dropboxCancel}
|
||||||
</label>
|
/>
|
||||||
<input
|
)}
|
||||||
name="Note"
|
</div>
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
) : (
|
||||||
value={formData.Note}
|
<div className="flex gap-2 justify-center items-center">
|
||||||
onChange={(e) => handleStrInput(e)}
|
<input
|
||||||
required
|
type="file"
|
||||||
/>
|
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
</div>
|
onChange={(e) => handleFileInput(e)}
|
||||||
<SelectFolder
|
ref={inputFileRef}
|
||||||
onSuccess={handleFolder}
|
accept={
|
||||||
folderCls={props.Cls}
|
isEnableSubscription
|
||||||
isReset={isReset}
|
? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
|
||||||
/>
|
: "application/pdf,image/png,image/jpeg"
|
||||||
|
}
|
||||||
{props.title === "Request Signatures" && (
|
required
|
||||||
|
/>
|
||||||
|
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||||
|
<DropboxChooser
|
||||||
|
onSuccess={dropboxSuccess}
|
||||||
|
onCancel={dropboxCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="text-xs mt-2">
|
<div className="text-xs mt-2">
|
||||||
<label className="block">
|
<label className="block">
|
||||||
Time To Complete (Days)
|
{props.title === "New Template"
|
||||||
|
? "Template Title"
|
||||||
|
: "Document Title"}
|
||||||
<span className="text-red-500 text-[13px]">*</span>
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
name="Name"
|
||||||
name="TimeToCompleteDays"
|
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
value={formData.TimeToCompleteDays}
|
value={formData.Name}
|
||||||
onChange={(e) => handleStrInput(e)}
|
onChange={(e) => handleStrInput(e)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{props.title !== "Sign Yourself" && (
|
|
||||||
<div className="text-xs mt-2">
|
<div className="text-xs mt-2">
|
||||||
<label className="block">Send In Order</label>
|
<label className="block">Description</label>
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
<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>
|
||||||
|
{props.title !== "New Template" && (
|
||||||
|
<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
|
<input
|
||||||
type="radio"
|
type="number"
|
||||||
value={"true"}
|
name="TimeToCompleteDays"
|
||||||
name="SendinOrder"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
checked={formData.SendinOrder === "true"}
|
value={formData.TimeToCompleteDays}
|
||||||
className=""
|
onChange={(e) => handleStrInput(e)}
|
||||||
onChange={handleStrInput}
|
required
|
||||||
/>
|
/>
|
||||||
<div className="text-center">Yes</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 ml-2 mb-1">
|
)}
|
||||||
<input
|
{props.title !== "Sign Yourself" && (
|
||||||
type="radio"
|
<>
|
||||||
value={"false"}
|
<div className="text-xs mt-2">
|
||||||
name="SendinOrder"
|
<label className="block">
|
||||||
checked={formData.SendinOrder === "false"}
|
Send In Order
|
||||||
onChange={handleStrInput}
|
<a data-tooltip-id="sendInOrder-tooltip" className="ml-1">
|
||||||
/>
|
<sup>
|
||||||
<div className="text-center">No</div>
|
<i
|
||||||
|
className="fa-solid fa-question rounded-full"
|
||||||
|
style={{
|
||||||
|
borderColor: "#33bbff",
|
||||||
|
color: "#33bbff",
|
||||||
|
fontSize: 13,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
padding: "1.5px 4px"
|
||||||
|
}}
|
||||||
|
></i>
|
||||||
|
</sup>
|
||||||
|
</a>
|
||||||
|
<Tooltip id="sendInOrder-tooltip" className="z-50">
|
||||||
|
<div className="max-w-[200px] md:max-w-[450px]">
|
||||||
|
<p className="font-bold">Send in Order</p>
|
||||||
|
<p>
|
||||||
|
Choose how you want the signing requests to be sent to
|
||||||
|
the document signers:
|
||||||
|
</p>
|
||||||
|
<p className="p-[5px]">
|
||||||
|
<ol className="list-disc">
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">Yes:</span>
|
||||||
|
<span>
|
||||||
|
{" "}
|
||||||
|
Selecting this option will send the signing
|
||||||
|
request to the first signer initially. Once the
|
||||||
|
first signer completes their part, the next
|
||||||
|
signer in the sequence will receive the request.
|
||||||
|
This process continues until all signers have
|
||||||
|
signed the document. This method ensures that
|
||||||
|
the document is signed in a specific order.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className="font-bold">No: </span>
|
||||||
|
<span>
|
||||||
|
Selecting this option will send the signing
|
||||||
|
links to all signers simultaneously. Every
|
||||||
|
signer can sign the document at their
|
||||||
|
convenience, regardless of whether other signers
|
||||||
|
have completed their signatures. This method is
|
||||||
|
faster but does not enforce any signing order
|
||||||
|
among the participants.
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Select the option that best suits the needs of your
|
||||||
|
document processing.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</label>
|
||||||
|
<div className="flex items-center gap-2 ml-2 mb-1">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
value={"true"}
|
||||||
|
name="SendinOrder"
|
||||||
|
checked={formData.SendinOrder === "true"}
|
||||||
|
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="text-xs mt-2">
|
||||||
|
{!isEnableSubscription && (
|
||||||
|
<PremiumAlertHeader
|
||||||
|
message={
|
||||||
|
"Disable Auto reminder is free in beta, this feature will incur a fee later."
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "font-semibold"
|
||||||
|
: "font-semibold text-gray-300"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Auto reminder{" "}
|
||||||
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
|
</span>
|
||||||
|
<label
|
||||||
|
className={`${
|
||||||
|
isSubscribe || !isEnableSubscription
|
||||||
|
? "cursor-pointer "
|
||||||
|
: "pointer-events-none opacity-50"
|
||||||
|
} relative block items-center mb-0`}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
checked={formData.autoreminder}
|
||||||
|
onChange={handleAutoReminder}
|
||||||
|
type="checkbox"
|
||||||
|
value=""
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600 mt-2"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{formData?.autoreminder === true && (
|
||||||
|
<div className="text-xs mt-2">
|
||||||
|
<label className="block">
|
||||||
|
Remind once in every (Days)
|
||||||
|
<span className="text-red-500 text-[13px]">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={formData.remindOnceInEvery}
|
||||||
|
name="remindOnceInEvery"
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
onChange={handleStrInput}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</form>
|
||||||
<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>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -144,29 +144,32 @@ function GenerateToken() {
|
|||||||
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<li
|
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
|
||||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5 ">
|
||||||
>
|
|
||||||
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
|
|
||||||
<span className="">Api Token:</span>{" "}
|
<span className="">Api Token:</span>{" "}
|
||||||
<span
|
<span id="token" className="md:text-end py-2 md:py-0">
|
||||||
id="token"
|
<span
|
||||||
className=" md:text-end cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => copytoclipboard(apiToken)}
|
onClick={() => copytoclipboard(apiToken)}
|
||||||
>
|
>
|
||||||
{apiToken ? apiToken : "_____"}
|
{apiToken ? apiToken : "_____"}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-2.5 py-2 focus:outline-none ml-2"
|
||||||
|
onClick={() => copytoclipboard(apiToken)}
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-copy"></i>
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
|
||||||
onClick={apiToken ? handleModal : handleSubmit}
|
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"
|
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white text-xs md:text-sm lg:text-base px-2 py-2 focus:outline-none"
|
||||||
>
|
>
|
||||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="flex items-center justify-center ">
|
<div className="flex items-center justify-center ">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,25 +1,32 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useNavigate, useParams } from "react-router-dom";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
import "../styles/loginPage.css";
|
|
||||||
import loader from "../assets/images/loader2.gif";
|
import loader from "../assets/images/loader2.gif";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
import {
|
||||||
|
isEnableSubscription,
|
||||||
|
modalSubmitBtnColor,
|
||||||
|
themeColor
|
||||||
|
} from "../constant/const";
|
||||||
import { contractUsers, getAppLogo } from "../constant/Utils";
|
import { contractUsers, getAppLogo } from "../constant/Utils";
|
||||||
import logo from "../assets/images/logo.png";
|
import logo from "../assets/images/logo.png";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import Parse from "parse";
|
||||||
|
|
||||||
function GuestLogin() {
|
function GuestLogin() {
|
||||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
const { id, userMail, contactBookId, base64url } = useParams();
|
||||||
let navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [email, setEmail] = useState(userMail);
|
const [email, setEmail] = useState(userMail);
|
||||||
const [OTP, setOTP] = useState("");
|
const [OTP, setOTP] = useState("");
|
||||||
const [EnterOTP, setEnterOtp] = useState(false);
|
const [EnterOTP, setEnterOtp] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [appLogo, setAppLogo] = useState("");
|
const [appLogo, setAppLogo] = useState("");
|
||||||
|
const [documentId, setDocumentId] = useState(id);
|
||||||
|
const [contactId, setContactId] = useState(contactBookId);
|
||||||
|
const [sendmail, setSendmail] = useState();
|
||||||
|
const [contact, setContact] = useState({ name: "", phone: "", email: "" });
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleServerUrl();
|
handleServerUrl();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -36,60 +43,65 @@ function GuestLogin() {
|
|||||||
setAppLogo(logo);
|
setAppLogo(logo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//split url in array from '&'
|
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
const checkSplit = serverUrl.split("&");
|
const parseId = appInfo.appId;
|
||||||
const server = checkSplit[0];
|
const newServer = `${appInfo.baseUrl}/`;
|
||||||
const parseId = checkSplit[1];
|
const appName = appInfo.appname;
|
||||||
const appName = checkSplit[2];
|
|
||||||
|
|
||||||
const newServer = server.replaceAll("%2F", "/");
|
|
||||||
localStorage.setItem("baseUrl", newServer);
|
localStorage.setItem("baseUrl", newServer);
|
||||||
localStorage.setItem("parseAppId", parseId);
|
localStorage.setItem("parseAppId", parseId);
|
||||||
localStorage.setItem("_appName", appName);
|
localStorage.setItem("_appName", appName);
|
||||||
|
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
|
||||||
|
if (!id) {
|
||||||
|
//`atob` function is used to decode base64
|
||||||
|
const decodebase64 = atob(base64url);
|
||||||
|
//split url in array from '/'
|
||||||
|
const checkSplit = decodebase64.split("/");
|
||||||
|
setDocumentId(checkSplit[0]);
|
||||||
|
setContact((prev) => ({ ...prev, email: checkSplit[1] }));
|
||||||
|
setEmail(checkSplit[1]);
|
||||||
|
const contactId = checkSplit?.[2];
|
||||||
|
setSendmail(checkSplit[3]);
|
||||||
|
if (!contactId) {
|
||||||
|
const params = { email: checkSplit[1], docId: checkSplit[0] };
|
||||||
|
try {
|
||||||
|
const linkContactRes = await Parse.Cloud.run(
|
||||||
|
"linkcontacttodoc",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes?.contactId);
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setContactId(checkSplit[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (event) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
setOTP(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
//send email OTP function
|
//send email OTP function
|
||||||
const SendOtp = async (e) => {
|
const SendOtp = async () => {
|
||||||
const serverUrl =
|
setLoading(true);
|
||||||
localStorage.getItem("baseUrl") && localStorage.getItem("baseUrl");
|
setEmail(email);
|
||||||
const parseId =
|
try {
|
||||||
localStorage.getItem("parseAppId") && localStorage.getItem("parseAppId");
|
const params = { email: email.toString(), docId: documentId };
|
||||||
if (serverUrl && localStorage) {
|
const Otp = await Parse.Cloud.run("SendOTPMailV1", params);
|
||||||
setLoading(true);
|
if (Otp) {
|
||||||
e.preventDefault();
|
setLoading(false);
|
||||||
setEmail(email);
|
setEnterOtp(true);
|
||||||
|
|
||||||
try {
|
|
||||||
let url = `${serverUrl}functions/SendOTPMailV1/`;
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": parseId
|
|
||||||
};
|
|
||||||
let body = {
|
|
||||||
email: email.toString(),
|
|
||||||
docId: id
|
|
||||||
};
|
|
||||||
let Otp = await axios.post(url, body, { headers: headers });
|
|
||||||
|
|
||||||
if (Otp) {
|
|
||||||
setLoading(false);
|
|
||||||
setEnterOtp(true);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
alert("something went wrong!");
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
alert("something went wrong!");
|
alert("something went wrong!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSendOTPBtn = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
await SendOtp();
|
||||||
|
};
|
||||||
|
|
||||||
//verify OTP send on via email
|
//verify OTP send on via email
|
||||||
const VerifyOTP = async (e) => {
|
const VerifyOTP = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -125,7 +137,8 @@ function GuestLogin() {
|
|||||||
`Parse/${parseId}/currentUser`,
|
`Parse/${parseId}/currentUser`,
|
||||||
JSON.stringify(_user)
|
JSON.stringify(_user)
|
||||||
);
|
);
|
||||||
if (contractUserDetails) {
|
// console.log("contractUserDetails ", contractUserDetails);
|
||||||
|
if (contractUserDetails && contractUserDetails.length > 0) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"Extand_Class",
|
"Extand_Class",
|
||||||
JSON.stringify(contractUserDetails)
|
JSON.stringify(contractUserDetails)
|
||||||
@@ -137,7 +150,13 @@ function GuestLogin() {
|
|||||||
//save isGuestSigner true in local to handle login flow header in mobile view
|
//save isGuestSigner true in local to handle login flow header in mobile view
|
||||||
localStorage.setItem("isGuestSigner", true);
|
localStorage.setItem("isGuestSigner", true);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
navigate(`/load/recipientSignPdf/${id}/${contactBookId}`);
|
if (sendmail === "false") {
|
||||||
|
navigate(
|
||||||
|
`/load/recipientSignPdf/${documentId}/${contactId}?sendmail=${sendmail}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
navigate(`/load/recipientSignPdf/${documentId}/${contactId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("err ", error);
|
console.log("err ", error);
|
||||||
@@ -146,156 +165,171 @@ function GuestLogin() {
|
|||||||
alert("Please Enter OTP!");
|
alert("Please Enter OTP!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const handleUserData = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const params = { ...contact, docId: documentId };
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const linkContactRes = await Parse.Cloud.run("linkcontacttodoc", params);
|
||||||
|
// console.log("linkContactRes ", linkContactRes);
|
||||||
|
setContactId(linkContactRes.contactId);
|
||||||
|
setEnterOtp(true);
|
||||||
|
await SendOtp();
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
alert("something went wron, please try agian later.");
|
||||||
|
console.log("Err in link ext contact", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
setContact((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: "2rem", background: "white" }}>
|
<div style={{ padding: "2rem", background: "white" }}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div
|
<div className="flex flex-col justify-center items-center h-[100vh]">
|
||||||
style={{
|
<img className="w-[80px] h-[80px]" alt="loader" src={loader} />
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100vh",
|
|
||||||
flexDirection: "column"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
alt="no img"
|
|
||||||
src={loader}
|
|
||||||
style={{ width: "80px", height: "80px" }}
|
|
||||||
/>
|
|
||||||
<span style={{ fontSize: "13px", color: "gray" }}>
|
<span style={{ fontSize: "13px", color: "gray" }}>
|
||||||
{isLoading.message}
|
{isLoading.message}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div className="m-1 md:m-2 border-[0.5px] border-[#c5c7c9] p-[30px] shadow-md">
|
||||||
style={{
|
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
|
||||||
margin: "10px",
|
{appLogo && (
|
||||||
border: "0.5px solid #c5c7c9",
|
<img src={appLogo} className="object-contain h-full" alt="logo" />
|
||||||
padding: "30px",
|
)}
|
||||||
boxShadow: "rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="main_head">
|
|
||||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
|
||||||
{appLogo && (
|
|
||||||
<img
|
|
||||||
src={appLogo}
|
|
||||||
className="object-contain h-full"
|
|
||||||
alt="logo"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
{contactId ? (
|
||||||
{!EnterOTP ? (
|
<>
|
||||||
<div className="row">
|
{!EnterOTP ? (
|
||||||
<div className="col-sm-6 KLO">
|
<div className="w-full md:w-[50%]">
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
<br />
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
<span className="KNLO">
|
Verification code is sent to your email
|
||||||
Verification code is sent to your email
|
</legend>
|
||||||
</span>
|
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
||||||
<div className="card card-box" style={{ borderRadius: "0px" }}>
|
|
||||||
<div className="card-body">
|
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="mobile"
|
name="mobile"
|
||||||
value={email}
|
value={email}
|
||||||
|
className="outline-none px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs disabled:bg-[#e0e5f5]"
|
||||||
disabled
|
disabled
|
||||||
className="loginInput"
|
|
||||||
/>
|
/>
|
||||||
<br />
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
className="w-[100px] text-white text-sm font-medium py-1 rounded-sm shadow-md hover:shadow-lg focus:outline-none"
|
||||||
|
style={{ background: themeColor }}
|
||||||
|
onClick={(e) => handleSendOTPBtn(e)}
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Send OTP"}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="btnContainer">
|
) : (
|
||||||
{loading ? (
|
<form className="w-full md:w-[50%]" onSubmit={VerifyOTP}>
|
||||||
<button
|
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||||
type="button"
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
style={{
|
You will get a OTP via Email
|
||||||
background: themeColor,
|
</legend>
|
||||||
color: "white"
|
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
||||||
}}
|
<p>Enter Verification Code</p>
|
||||||
className="verifyBtn"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
className="verifyBtn"
|
|
||||||
style={{
|
|
||||||
background: themeColor,
|
|
||||||
color: "white",
|
|
||||||
marginLeft: "0px !important"
|
|
||||||
}}
|
|
||||||
onClick={(e) => SendOtp(e)}
|
|
||||||
>
|
|
||||||
Send OTP
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="row">
|
|
||||||
<div className="col-sm-6 KLO">
|
|
||||||
<span className="welcomeText">Welcome Back !</span>
|
|
||||||
<br />
|
|
||||||
<span className="KNLO">You will get a OTP via Email</span>
|
|
||||||
<div className="card card-box">
|
|
||||||
<div className="card-body">
|
|
||||||
<label>Enter Verification Code</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
className="loginInput"
|
className="mt-1 outline-none px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs bg-[#e0e5f5]"
|
||||||
name="OTP"
|
name="OTP"
|
||||||
value={OTP}
|
value={OTP}
|
||||||
onChange={handleChange}
|
onChange={(e) => setOTP(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-4">
|
||||||
<div>
|
|
||||||
{loading ? (
|
|
||||||
<button
|
<button
|
||||||
style={{
|
style={{ background: themeColor }}
|
||||||
background: themeColor,
|
className="w-[100px] text-white text-sm font-medium py-1 rounded-sm shadow-md hover:shadow-lg focus:outline-none"
|
||||||
color: "white"
|
type="submit"
|
||||||
}}
|
disabled={loading}
|
||||||
className="verifyBtn"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
>
|
>
|
||||||
<span
|
{loading ? "Loading..." : "Verify"}
|
||||||
className="spinner-border spinner-border-sm "
|
|
||||||
role="status"
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
Loading...
|
|
||||||
</button>
|
</button>
|
||||||
) : (
|
</div>
|
||||||
<button
|
</form>
|
||||||
type="button"
|
)}
|
||||||
onClick={(e) => VerifyOTP(e)}
|
</>
|
||||||
style={{
|
) : (
|
||||||
background: themeColor,
|
<div className="w-full md:w-[50%]">
|
||||||
color: "white"
|
<h1 className="text-2xl md:text-[30px]">Welcome</h1>
|
||||||
}}
|
<legend className="text-[12px] text-[#878787] mt-2">
|
||||||
className="verifyBtn"
|
Provide your details
|
||||||
>
|
</legend>
|
||||||
Verify
|
<form
|
||||||
</button>
|
className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 my-2 rounded shadow-md"
|
||||||
)}
|
onSubmit={handleUserData}
|
||||||
|
>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="name"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Name
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
value={contact.name}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded disabled:bg-[#e0e5f5] focus:outline-none text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="email"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Email
|
||||||
|
<span className="text-[red] text-[13px]"> *</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
value={contact.email}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 disabled:bg-[#e0e5f5] rounded focus:outline-none text-xs"
|
||||||
|
required
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="phone"
|
||||||
|
className="block text-xs text-gray-700 font-semibold"
|
||||||
|
>
|
||||||
|
Phone
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="phone"
|
||||||
|
value={contact.phone}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex justify-start">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
style={{ backgroundColor: modalSubmitBtnColor }}
|
||||||
|
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Loading..." : "Next"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+158
-149
@@ -19,7 +19,7 @@ import Alert from "../primitives/Alert";
|
|||||||
import { appInfo } from "../constant/appinfo";
|
import { appInfo } from "../constant/appinfo";
|
||||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
import { fetchSubscription, getAppLogo } from "../constant/Utils";
|
import { fetchSubscription, getAppLogo, openInNewTab } from "../constant/Utils";
|
||||||
function Login() {
|
function Login() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -45,6 +45,7 @@ function Login() {
|
|||||||
});
|
});
|
||||||
const [isModal, setIsModal] = useState(false);
|
const [isModal, setIsModal] = useState(false);
|
||||||
const [image, setImage] = useState();
|
const [image, setImage] = useState();
|
||||||
|
const [isLoginSSO, setIsLoginSSO] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (localStorage.getItem("accesstoken")) {
|
if (localStorage.getItem("accesstoken")) {
|
||||||
@@ -263,7 +264,7 @@ function Login() {
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: results[0].get("Name"),
|
name: results[0].get("Name"),
|
||||||
email: results[0].get("Email"),
|
email: results[0].get("Email"),
|
||||||
phone: results[0].get("Phone"),
|
phone: results[0]?.get("Phone") || "",
|
||||||
company: results[0].get("Company")
|
company: results[0].get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@@ -317,7 +318,7 @@ function Login() {
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: _user.name,
|
name: _user.name,
|
||||||
email: email,
|
email: email,
|
||||||
phone: _user.phone
|
phone: _user?.phone || ""
|
||||||
// company: results.get("Company"),
|
// company: results.get("Company"),
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@@ -422,7 +423,7 @@ function Login() {
|
|||||||
alert("Somenthing went wrong, please try again later!");
|
alert("Somenthing went wrong, please try again later!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const thirdpartyLoginfn = async (sessionToken, billingDate) => {
|
const thirdpartyLoginfn = async (sessionToken) => {
|
||||||
const baseUrl = localStorage.getItem("baseUrl");
|
const baseUrl = localStorage.getItem("baseUrl");
|
||||||
const parseAppId = localStorage.getItem("parseAppId");
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
const res = await axios.get(baseUrl + "users/me", {
|
const res = await axios.get(baseUrl + "users/me", {
|
||||||
@@ -521,7 +522,7 @@ function Login() {
|
|||||||
await Parse.Cloud.run("getUserDetails", {
|
await Parse.Cloud.run("getUserDetails", {
|
||||||
email: currentUser.get("email")
|
email: currentUser.get("email")
|
||||||
}).then(
|
}).then(
|
||||||
(result) => {
|
async (result) => {
|
||||||
let tenentInfo = [];
|
let tenentInfo = [];
|
||||||
const results = [result];
|
const results = [result];
|
||||||
if (results) {
|
if (results) {
|
||||||
@@ -571,7 +572,7 @@ function Login() {
|
|||||||
setState({ ...state, loading: false });
|
setState({ ...state, loading: false });
|
||||||
navigate("/");
|
navigate("/");
|
||||||
} else {
|
} else {
|
||||||
extendedInfo.forEach((x) => {
|
extendedInfo.forEach(async (x) => {
|
||||||
if (x.TenantId) {
|
if (x.TenantId) {
|
||||||
let obj = {
|
let obj = {
|
||||||
tenentId: x.TenantId.objectId,
|
tenentId: x.TenantId.objectId,
|
||||||
@@ -608,8 +609,13 @@ function Login() {
|
|||||||
setThirdpartyLoader(false);
|
setThirdpartyLoader(false);
|
||||||
setState({ ...state, loading: false });
|
setState({ ...state, loading: false });
|
||||||
if (isEnableSubscription) {
|
if (isEnableSubscription) {
|
||||||
if (billingDate) {
|
const res = await fetchSubscription();
|
||||||
if (billingDate > new Date()) {
|
const freeplan = res.plan;
|
||||||
|
const billingDate = res.billingDate;
|
||||||
|
if (freeplan === "freeplan") {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else if (billingDate) {
|
||||||
|
if (new Date(billingDate) > new Date()) {
|
||||||
localStorage.removeItem("userDetails");
|
localStorage.removeItem("userDetails");
|
||||||
navigate(redirectUrl);
|
navigate(redirectUrl);
|
||||||
} else {
|
} else {
|
||||||
@@ -644,8 +650,13 @@ function Login() {
|
|||||||
setState({ ...state, loading: false });
|
setState({ ...state, loading: false });
|
||||||
setThirdpartyLoader(false);
|
setThirdpartyLoader(false);
|
||||||
if (isEnableSubscription) {
|
if (isEnableSubscription) {
|
||||||
if (billingDate) {
|
const res = await fetchSubscription();
|
||||||
if (billingDate > new Date()) {
|
const freeplan = res.plan;
|
||||||
|
const billingDate = res.billingDate;
|
||||||
|
if (freeplan === "freeplan") {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else if (billingDate) {
|
||||||
|
if (new Date(billingDate) > new Date()) {
|
||||||
localStorage.removeItem("userDetails");
|
localStorage.removeItem("userDetails");
|
||||||
// Redirect to the appropriate URL after successful login
|
// Redirect to the appropriate URL after successful login
|
||||||
navigate(redirectUrl);
|
navigate(redirectUrl);
|
||||||
@@ -858,7 +869,7 @@ function Login() {
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: results[0].get("Name"),
|
name: results[0].get("Name"),
|
||||||
email: results[0].get("Email"),
|
email: results[0].get("Email"),
|
||||||
phone: results[0].get("Phone"),
|
phone: results[0]?.get("Phone") || "",
|
||||||
company: results[0].get("Company")
|
company: results[0].get("Company")
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@@ -895,7 +906,7 @@ function Login() {
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: results[0].get("Name"),
|
name: results[0].get("Name"),
|
||||||
email: results[0].get("Email"),
|
email: results[0].get("Email"),
|
||||||
phone: results[0].get("Phone")
|
phone: results[0]?.get("Phone") || ""
|
||||||
// company: results.get("Company"),
|
// company: results.get("Company"),
|
||||||
};
|
};
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@@ -972,7 +983,7 @@ function Login() {
|
|||||||
userDetails: {
|
userDetails: {
|
||||||
name: userInformation.name,
|
name: userInformation.name,
|
||||||
email: userInformation.email,
|
email: userInformation.email,
|
||||||
phone: userInformation.phone,
|
phone: userInformation?.phone || "",
|
||||||
role: "contracts_User",
|
role: "contracts_User",
|
||||||
company: userDetails.Company,
|
company: userDetails.Company,
|
||||||
jobTitle: userDetails.Destination
|
jobTitle: userDetails.Destination
|
||||||
@@ -984,7 +995,7 @@ function Login() {
|
|||||||
const LocalUserDetails = {
|
const LocalUserDetails = {
|
||||||
name: userInformation.name,
|
name: userInformation.name,
|
||||||
email: userInformation.email,
|
email: userInformation.email,
|
||||||
phone: userInformation.phone,
|
phone: userInformation?.phone || "",
|
||||||
company: userDetails.Company,
|
company: userDetails.Company,
|
||||||
jobTitle: userDetails.JobTitle
|
jobTitle: userDetails.JobTitle
|
||||||
};
|
};
|
||||||
@@ -1006,8 +1017,9 @@ function Login() {
|
|||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleCloseModal = () => {
|
||||||
setIsModal(false);
|
setIsModal(false);
|
||||||
Parse.User.logOut();
|
if (Parse?.User?.current()) {
|
||||||
|
Parse.User.logOut();
|
||||||
|
}
|
||||||
let appdata = localStorage.getItem("userSettings");
|
let appdata = localStorage.getItem("userSettings");
|
||||||
let applogo = localStorage.getItem("appLogo");
|
let applogo = localStorage.getItem("appLogo");
|
||||||
let appName = localStorage.getItem("appName");
|
let appName = localStorage.getItem("appName");
|
||||||
@@ -1030,6 +1042,26 @@ function Login() {
|
|||||||
localStorage.setItem("baseUrl", baseUrl);
|
localStorage.setItem("baseUrl", baseUrl);
|
||||||
localStorage.setItem("parseAppId", appid);
|
localStorage.setItem("parseAppId", appid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `handleSignInWithSSO` is trigger when user click sign in with sso and open sso authorize endpoint
|
||||||
|
const handleSignInWithSSO = () => {
|
||||||
|
if (isLoginSSO === false) {
|
||||||
|
if (state?.email) {
|
||||||
|
setIsLoginSSO(true);
|
||||||
|
const encodedEmail = encodeURIComponent(state.email);
|
||||||
|
const clientUrl = window.location.origin;
|
||||||
|
const domain = state.email.split("@")?.pop();
|
||||||
|
const ssoApiUrl =
|
||||||
|
process.env.SSO_API_URL || "https://sso.opensignlabs.com/api";
|
||||||
|
openInNewTab(
|
||||||
|
`${ssoApiUrl}/oauth/authorize?response_type=code&provider=saml&tenant=${domain}&product=OpenSign&redirect_uri=${clientUrl}/sso&state=${encodedEmail}`,
|
||||||
|
"_self"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
alert("Please provide email.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="bg-white">
|
<div className="bg-white">
|
||||||
<Title title={"Login Page"} />
|
<Title title={"Login Page"} />
|
||||||
@@ -1077,154 +1109,131 @@ function Login() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<form onSubmit={handleSubmit} aria-label="Login Form">
|
||||||
<form onSubmit={handleSubmit} aria-label="Login Form">
|
<h1 className="text-[30px] mt-6">Welcome Back!</h1>
|
||||||
<h1 className="text-[30px] mt-6">Welcome Back!</h1>
|
<fieldset>
|
||||||
<fieldset>
|
<legend className="text-[12px] text-[#878787]">
|
||||||
<legend className="text-[12px] text-[#878787]">
|
Login to your account
|
||||||
Login to your account
|
</legend>
|
||||||
</legend>
|
<div className="px-6 py-4 outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
||||||
<div className="px-6 py-4 outline outline-1 outline-slate-300/50 my-2 rounded shadow-md">
|
<label className="block text-xs" htmlFor="email">
|
||||||
<label className="block text-xs" htmlFor="email">
|
Email
|
||||||
Username
|
</label>
|
||||||
</label>
|
<input
|
||||||
<input
|
id="email"
|
||||||
id="email"
|
type="text"
|
||||||
type="text"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
|
name="email"
|
||||||
name="email"
|
value={state.email}
|
||||||
value={state.email}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
required
|
||||||
required
|
/>
|
||||||
/>
|
<hr className="my-2 border-none" />
|
||||||
<hr className="my-2 border-none" />
|
{!isLoginSSO && (
|
||||||
<label className="block text-xs" htmlFor="password">
|
<>
|
||||||
Password
|
<label className="block text-xs" htmlFor="password">
|
||||||
</label>
|
Password
|
||||||
<div className="relative">
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
id="password"
|
<input
|
||||||
type={state.passwordVisible ? "text" : "password"}
|
id="password"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
|
type={
|
||||||
name="password"
|
state.passwordVisible ? "text" : "password"
|
||||||
value={state.password}
|
}
|
||||||
onChange={handleChange}
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs"
|
||||||
required
|
name="password"
|
||||||
/>
|
value={state.password}
|
||||||
<span
|
onChange={handleChange}
|
||||||
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
|
required
|
||||||
state.passwordVisible
|
/>
|
||||||
? "text-[#007bff]"
|
<span
|
||||||
: "text-black"
|
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${
|
||||||
}`}
|
state.passwordVisible
|
||||||
onClick={togglePasswordVisibility}
|
? "text-[#007bff]"
|
||||||
>
|
: "text-black"
|
||||||
{state.passwordVisible ? (
|
}`}
|
||||||
<i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon
|
onClick={togglePasswordVisibility}
|
||||||
) : (
|
>
|
||||||
<i className="fa fa-eye text-xs pb-1 " /> // Open eye icon
|
{state.passwordVisible ? (
|
||||||
)}
|
<i className="fa fa-eye-slash text-xs pb-1" /> // Close eye icon
|
||||||
</span>
|
) : (
|
||||||
</div>
|
<i className="fa fa-eye text-xs pb-1 " /> // Open eye icon
|
||||||
</div>
|
)}
|
||||||
</fieldset>
|
</span>
|
||||||
<div className="flex flex-row justify-between items-center ml-4 py-2">
|
</div>
|
||||||
<div>
|
</>
|
||||||
<label
|
)}
|
||||||
className="form-check-label inline-block cursor-pointer"
|
<div className="relative mt-1">
|
||||||
htmlFor="rememberpassword"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input mr-1"
|
|
||||||
value=""
|
|
||||||
id="rememberpassword"
|
|
||||||
/>
|
|
||||||
<span className="text-[13px]">
|
|
||||||
Remember Password
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/forgetpassword"
|
to="/forgetpassword"
|
||||||
className="text-[13px] underline focus:outline-none focus:ring-2 focus:ring-blue-600"
|
className="text-[13px] text-[#002864] hover:underline underline-offset-1 focus:outline-none cursor-pointer ml-1"
|
||||||
>
|
>
|
||||||
Forgot Password ?
|
Forgot Password?
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
|
</fieldset>
|
||||||
<button
|
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
|
||||||
type="submit"
|
<button
|
||||||
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600"
|
type="submit"
|
||||||
disabled={state.loading}
|
className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600"
|
||||||
>
|
disabled={state.loading}
|
||||||
{state.loading ? "Loading..." : "Login"}
|
>
|
||||||
</button>
|
{state.loading ? "Loading..." : "Login"}
|
||||||
<NavLink
|
</button>
|
||||||
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
|
<NavLink
|
||||||
to={
|
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
|
||||||
location.search
|
to={
|
||||||
? "/signup" + location.search
|
location.search
|
||||||
: "/signup"
|
? "/signup" + location.search
|
||||||
}
|
: "/signup"
|
||||||
style={width < 768 ? { textAlign: "center" } : {}}
|
}
|
||||||
>
|
style={width < 768 ? { textAlign: "center" } : {}}
|
||||||
Create Account
|
>
|
||||||
</NavLink>
|
Create Account
|
||||||
</div>
|
</NavLink>
|
||||||
</form>
|
</div>
|
||||||
<br />
|
</form>
|
||||||
{(appInfo.fbAppId || appInfo.googleClietId) && (
|
<br />
|
||||||
<div className="text-sm flex justify-center items-center">
|
{appInfo.googleClietId && (
|
||||||
<hr className="border-[1px] border-gray-300 w-full" />
|
<div className="text-sm flex justify-center items-center">
|
||||||
<span className="px-2 text-gray-500 cursor-default">
|
<hr className="border-[1px] border-gray-300 w-full" />
|
||||||
OR
|
<span className="px-2 text-gray-500 cursor-default">
|
||||||
</span>
|
OR
|
||||||
<hr className="border-[1px] border-gray-300 w-full" />
|
</span>
|
||||||
</div>
|
<hr className="border-[1px] border-gray-300 w-full" />
|
||||||
)}
|
</div>
|
||||||
<br />
|
)}
|
||||||
<div
|
<br />
|
||||||
style={{
|
<div className="flex flex-col justify-center items-center gap-y-3">
|
||||||
textAlign: "center",
|
{/* {appInfo?.fbAppId && (
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* {appInfo.fbAppId && appInfo.fbAppId !== "" ? (
|
|
||||||
<LoginFacebook
|
<LoginFacebook
|
||||||
FBCred={appInfo.fbAppId}
|
FBCred={appInfo.fbAppId}
|
||||||
thirdpartyLoginfn={thirdpartyLoginfn}
|
thirdpartyLoginfn={thirdpartyLoginfn}
|
||||||
thirdpartyLoader={state.thirdpartyLoader}
|
thirdpartyLoader={state.thirdpartyLoader}
|
||||||
setThirdpartyLoader={setThirdpartyLoader}
|
setThirdpartyLoader={setThirdpartyLoader}
|
||||||
/>
|
/>
|
||||||
) : null} */}
|
)} */}
|
||||||
</div>
|
{appInfo?.googleClietId && (
|
||||||
<div style={{ margin: "10px 0" }}></div>
|
<GoogleSignInBtn
|
||||||
<div
|
GoogleCred={appInfo.googleClietId}
|
||||||
style={{
|
thirdpartyLoginfn={thirdpartyLoginfn}
|
||||||
textAlign: "center",
|
thirdpartyLoader={state.thirdpartyLoader}
|
||||||
display: "flex",
|
setThirdpartyLoader={setThirdpartyLoader}
|
||||||
alignItems: "center",
|
/>
|
||||||
justifyContent: "center"
|
)}
|
||||||
}}
|
{isEnableSubscription && (
|
||||||
>
|
<div
|
||||||
{appInfo.googleClietId && appInfo.googleClietId !== "" ? (
|
className="cursor-pointer border-[1px] border-gray-300 rounded px-[40px] py-2 font-semibold text-sm hover:border-[#d2e3fc] hover:bg-[#ecf3feb7]"
|
||||||
<GoogleSignInBtn
|
onClick={() => handleSignInWithSSO()}
|
||||||
GoogleCred={appInfo.googleClietId}
|
>
|
||||||
thirdpartyLoginfn={thirdpartyLoginfn}
|
Sign in with SSO
|
||||||
thirdpartyLoader={state.thirdpartyLoader}
|
</div>
|
||||||
setThirdpartyLoader={setThirdpartyLoader}
|
)}
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{width >= 768 && (
|
{width >= 768 && (
|
||||||
<div className="self-center">
|
<div className="place-self-center">
|
||||||
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
|
<div className="mx-auto md:w-[300px] lg:w-[400px] xl:w-[500px]">
|
||||||
<img
|
<img
|
||||||
src={login_img}
|
src={login_img}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
|||||||
import Tour from "reactour";
|
import Tour from "reactour";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const DriveBody = React.lazy(
|
const DriveBody = React.lazy(() =>
|
||||||
() => import("../components/opensigndrive/DriveBody")
|
import("../components/opensigndrive/DriveBody")
|
||||||
);
|
);
|
||||||
const Loader = () => {
|
const Loader = () => {
|
||||||
return (
|
return (
|
||||||
@@ -64,6 +64,7 @@ function Opensigndrive() {
|
|||||||
const sortingValue = ["Name", "Date"];
|
const sortingValue = ["Name", "Date"];
|
||||||
const [isDontShow, setIsDontShow] = useState(false);
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
const [tourData, setTourData] = useState();
|
const [tourData, setTourData] = useState();
|
||||||
|
const [showTourFirstTIme, setShowTourFirstTime] = useState(true);
|
||||||
const orderName = {
|
const orderName = {
|
||||||
Ascending: "Ascending",
|
Ascending: "Ascending",
|
||||||
Descending: "Descending",
|
Descending: "Descending",
|
||||||
@@ -134,7 +135,9 @@ function Opensigndrive() {
|
|||||||
//function for get all pdf document list
|
//function for get all pdf document list
|
||||||
const getPdfDocumentList = async (disbaleLoading) => {
|
const getPdfDocumentList = async (disbaleLoading) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
checkTourStatus();
|
if (showTourFirstTIme) {
|
||||||
|
checkTourStatus();
|
||||||
|
}
|
||||||
if (!disbaleLoading) {
|
if (!disbaleLoading) {
|
||||||
setIsLoading({ isLoad: true, message: "This might take some time" });
|
setIsLoading({ isLoad: true, message: "This might take some time" });
|
||||||
}
|
}
|
||||||
@@ -250,17 +253,23 @@ function Opensigndrive() {
|
|||||||
}, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes
|
}, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes
|
||||||
|
|
||||||
//function for handle folder name path
|
//function for handle folder name path
|
||||||
const handleRoute = (index) => {
|
const handleRoute = (index, folderData) => {
|
||||||
setPdfData([]);
|
|
||||||
setSkip(0);
|
setSkip(0);
|
||||||
|
// after onclick on route filter route from that index
|
||||||
const updateFolderName = folderName.filter((x, i) => {
|
const updateFolderName = folderName.filter((x, i) => {
|
||||||
if (i <= index) {
|
if (i <= index) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setFolderName(updateFolderName);
|
setFolderName(updateFolderName);
|
||||||
|
//get route details after onclick path of folder name
|
||||||
|
const getCurrentId = folderData[index];
|
||||||
const getLastId = updateFolderName[updateFolderName.length - 1];
|
const getLastId = updateFolderName[updateFolderName.length - 1];
|
||||||
setDocId(getLastId.objectId);
|
//below condition is used to check if user click on same route which already open then don't change any thing
|
||||||
|
if (docId !== getCurrentId.objectId) {
|
||||||
|
setPdfData([]);
|
||||||
|
setDocId(getLastId.objectId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//function for add new folder name
|
//function for add new folder name
|
||||||
@@ -291,6 +300,7 @@ function Opensigndrive() {
|
|||||||
const exsitQuery = new Parse.Query(foldercls);
|
const exsitQuery = new Parse.Query(foldercls);
|
||||||
exsitQuery.equalTo("Name", newFolderName);
|
exsitQuery.equalTo("Name", newFolderName);
|
||||||
exsitQuery.equalTo("Type", "Folder");
|
exsitQuery.equalTo("Type", "Folder");
|
||||||
|
exsitQuery.notEqualTo("IsArchive", true);
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
exsitQuery.equalTo("Folder", folderPtr);
|
exsitQuery.equalTo("Folder", folderPtr);
|
||||||
}
|
}
|
||||||
@@ -439,7 +449,7 @@ function Opensigndrive() {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={id}>
|
<React.Fragment key={id}>
|
||||||
<span
|
<span
|
||||||
onClick={() => handleRoute(id)}
|
onClick={() => handleRoute(id, folderData)}
|
||||||
style={{
|
style={{
|
||||||
color: "#a64b4e",
|
color: "#a64b4e",
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
@@ -474,6 +484,7 @@ function Opensigndrive() {
|
|||||||
|
|
||||||
const closeTour = async () => {
|
const closeTour = async () => {
|
||||||
setIsTour(false);
|
setIsTour(false);
|
||||||
|
setShowTourFirstTime(false);
|
||||||
if (isDontShow) {
|
if (isDontShow) {
|
||||||
const serverUrl = localStorage.getItem("baseUrl");
|
const serverUrl = localStorage.getItem("baseUrl");
|
||||||
const appId = localStorage.getItem("parseAppId");
|
const appId = localStorage.getItem("parseAppId");
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -127,7 +127,9 @@ const PgSignUp = () => {
|
|||||||
user.set("username", zohoRes.data.result.email);
|
user.set("username", zohoRes.data.result.email);
|
||||||
user.set("password", obj.password);
|
user.set("password", obj.password);
|
||||||
user.set("email", zohoRes.data.result.email);
|
user.set("email", zohoRes.data.result.email);
|
||||||
user.set("phone", zohoRes.data.result.phone);
|
if (zohoRes?.data?.result?.phone) {
|
||||||
|
user.set("phone", zohoRes.data.result.phone);
|
||||||
|
}
|
||||||
user.set("name", zohoRes.data.result.name);
|
user.set("name", zohoRes.data.result.name);
|
||||||
|
|
||||||
const res = await user.signUp();
|
const res = await user.signUp();
|
||||||
@@ -140,7 +142,7 @@ const PgSignUp = () => {
|
|||||||
company: zohoRes.data.result.company,
|
company: zohoRes.data.result.company,
|
||||||
name: zohoRes.data.result.name,
|
name: zohoRes.data.result.name,
|
||||||
email: zohoRes.data.result.email,
|
email: zohoRes.data.result.email,
|
||||||
phone: zohoRes.data.result.phone,
|
phone: zohoRes?.data?.result?.phone || "",
|
||||||
role: obj.role
|
role: obj.role
|
||||||
},
|
},
|
||||||
subscription: zohoRes.data.result.subscription
|
subscription: zohoRes.data.result.subscription
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ function PlaceHolderSign() {
|
|||||||
const [requestSubject, setRequestSubject] = useState("");
|
const [requestSubject, setRequestSubject] = useState("");
|
||||||
const [requestBody, setRequestBody] = useState("");
|
const [requestBody, setRequestBody] = useState("");
|
||||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||||
|
const [activeMailAdapter, setActiveMailAdapter] = useState("");
|
||||||
const [isAlreadyPlace, setIsAlreadyPlace] = useState({
|
const [isAlreadyPlace, setIsAlreadyPlace] = useState({
|
||||||
status: false,
|
status: false,
|
||||||
message: ""
|
message: ""
|
||||||
@@ -172,15 +173,6 @@ function PlaceHolderSign() {
|
|||||||
: rowLevel?.objectId && rowLevel.objectId;
|
: rowLevel?.objectId && rowLevel.objectId;
|
||||||
|
|
||||||
const documentId = docId ? docId : objectId && objectId;
|
const documentId = docId ? docId : objectId && objectId;
|
||||||
const senderUser =
|
|
||||||
localStorage.getItem(
|
|
||||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
|
||||||
) &&
|
|
||||||
localStorage.getItem(
|
|
||||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
|
||||||
);
|
|
||||||
const jsonSender = JSON.parse(senderUser);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (documentId) {
|
if (documentId) {
|
||||||
getDocumentDetails();
|
getDocumentDetails();
|
||||||
@@ -209,16 +201,16 @@ function PlaceHolderSign() {
|
|||||||
|
|
||||||
setDefaultBody(defaultRequestBody);
|
setDefaultBody(defaultRequestBody);
|
||||||
setDefaultSubject(
|
setDefaultSubject(
|
||||||
`{{sender_name}} has requested you to sign {{document_title}}`
|
`{{sender_name}} has requested you to sign "{{document_title}}"`
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setRequestBody(defaultRequestBody);
|
setRequestBody(defaultRequestBody);
|
||||||
setRequestSubject(
|
setRequestSubject(
|
||||||
`{{sender_name}} has requested you to sign {{document_title}}`
|
`{{sender_name}} has requested you to sign "{{document_title}}"`
|
||||||
);
|
);
|
||||||
setDefaultBody(defaultRequestBody);
|
setDefaultBody(defaultRequestBody);
|
||||||
setDefaultSubject(
|
setDefaultSubject(
|
||||||
`{{sender_name}}has requested you to sign {{document_title}}`
|
`{{sender_name}}has requested you to sign "{{document_title}}"`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,11 +397,22 @@ function PlaceHolderSign() {
|
|||||||
};
|
};
|
||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
}
|
}
|
||||||
const res = await contractUsers(jsonSender.email);
|
const currentUserEmail = documentData[0]?.ExtUserPtr?.Email;
|
||||||
if (res[0] && res.length) {
|
const res = await contractUsers(currentUserEmail);
|
||||||
|
if (res === "Error: Something went wrong!") {
|
||||||
|
const loadObj = {
|
||||||
|
isLoad: false
|
||||||
|
};
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
setIsLoading(loadObj);
|
||||||
|
} else if (res.length && res[0]?.objectId) {
|
||||||
|
setActiveMailAdapter(res[0]?.active_mail_adapter);
|
||||||
setSignerUserId(res[0].objectId);
|
setSignerUserId(res[0].objectId);
|
||||||
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
const tourstatus = res[0].TourStatus && res[0].TourStatus;
|
||||||
if (tourstatus && tourstatus.length > 0) {
|
const alreadyPlaceholder = documentData[0]?.SignedUrl;
|
||||||
|
if (alreadyPlaceholder) {
|
||||||
|
setCheckTourStatus(true);
|
||||||
|
} else if (tourstatus && tourstatus.length > 0) {
|
||||||
setTourStatus(tourstatus);
|
setTourStatus(tourstatus);
|
||||||
const checkTourRecipients = tourstatus.filter(
|
const checkTourRecipients = tourstatus.filter(
|
||||||
(data) => data.placeholder
|
(data) => data.placeholder
|
||||||
@@ -422,12 +425,6 @@ function PlaceHolderSign() {
|
|||||||
isLoad: false
|
isLoad: false
|
||||||
};
|
};
|
||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
} else if (res === "Error: Something went wrong!") {
|
|
||||||
const loadObj = {
|
|
||||||
isLoad: false
|
|
||||||
};
|
|
||||||
setHandleError("Error: Something went wrong!");
|
|
||||||
setIsLoading(loadObj);
|
|
||||||
} else if (res.length === 0) {
|
} else if (res.length === 0) {
|
||||||
setHandleError("No Data Found!");
|
setHandleError("No Data Found!");
|
||||||
const loadObj = {
|
const loadObj = {
|
||||||
@@ -836,11 +833,11 @@ function PlaceHolderSign() {
|
|||||||
return pdfDetails[0].URL;
|
return pdfDetails[0].URL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const alertSendEmail = async () => {
|
const alertSendEmail = async () => {
|
||||||
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
|
const filterPrefill = signerPos?.filter((data) => data.Role !== "prefill");
|
||||||
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
|
const getPrefill = signerPos?.filter((data) => data.Role === "prefill");
|
||||||
let isLabel = false;
|
let isLabel = false;
|
||||||
|
//condition is used to check text widget data is empty or have response
|
||||||
if (getPrefill && getPrefill.length > 0) {
|
if (getPrefill && getPrefill.length > 0) {
|
||||||
const prefillPlaceholder = getPrefill[0].placeHolder;
|
const prefillPlaceholder = getPrefill[0].placeHolder;
|
||||||
if (prefillPlaceholder) {
|
if (prefillPlaceholder) {
|
||||||
@@ -851,6 +848,29 @@ function PlaceHolderSign() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let isSignatureExist = true; // variable is used to check a signature widget exit or not then execute other code
|
||||||
|
//for loop is used to check signature widget exist or not
|
||||||
|
for (let item of filterPrefill) {
|
||||||
|
let signatureExist = false; // Reset for each iteration
|
||||||
|
for (let x of item.placeHolder) {
|
||||||
|
if (!signatureExist) {
|
||||||
|
const typeExist = x.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
signatureExist = x.pos.some((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
signatureExist = x.pos.some((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!signatureExist) {
|
||||||
|
isSignatureExist = false;
|
||||||
|
const alert = {
|
||||||
|
mssg: "sure",
|
||||||
|
alert: true
|
||||||
|
};
|
||||||
|
setIsSendAlert(alert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (getPrefill && isLabel) {
|
if (getPrefill && isLabel) {
|
||||||
const alert = {
|
const alert = {
|
||||||
@@ -858,23 +878,26 @@ function PlaceHolderSign() {
|
|||||||
alert: true
|
alert: true
|
||||||
};
|
};
|
||||||
setIsSendAlert(alert);
|
setIsSendAlert(alert);
|
||||||
} else if (filterPrefill.length === signersdata.length) {
|
} else if (isSignatureExist) {
|
||||||
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
if (filterPrefill.length === signersdata.length) {
|
||||||
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId);
|
||||||
setSignerExistModal(true);
|
if (IsSignerNotExist && IsSignerNotExist?.length > 0) {
|
||||||
setSelectWidgetId(IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key);
|
setSignerExistModal(true);
|
||||||
|
setSelectWidgetId(
|
||||||
|
IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
saveDocumentDetails();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
saveDocumentDetails();
|
const alert = {
|
||||||
|
mssg: "sure",
|
||||||
|
alert: true
|
||||||
|
};
|
||||||
|
setIsSendAlert(alert);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const alert = {
|
|
||||||
mssg: "sure",
|
|
||||||
alert: true
|
|
||||||
};
|
|
||||||
setIsSendAlert(alert);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//function to use save placeholder details in contracts_document
|
//function to use save placeholder details in contracts_document
|
||||||
const saveDocumentDetails = async () => {
|
const saveDocumentDetails = async () => {
|
||||||
setIsUiLoading(true);
|
setIsUiLoading(true);
|
||||||
@@ -913,6 +936,7 @@ function PlaceHolderSign() {
|
|||||||
Placeholders: filterPrefill,
|
Placeholders: filterPrefill,
|
||||||
SignedUrl: pdfUrl,
|
SignedUrl: pdfUrl,
|
||||||
Signers: signers,
|
Signers: signers,
|
||||||
|
SentToOthers: true,
|
||||||
ExpiryDate: {
|
ExpiryDate: {
|
||||||
iso: updateExpiryDate,
|
iso: updateExpiryDate,
|
||||||
__type: "Date"
|
__type: "Date"
|
||||||
@@ -922,7 +946,8 @@ function PlaceHolderSign() {
|
|||||||
data = {
|
data = {
|
||||||
Placeholders: filterPrefill,
|
Placeholders: filterPrefill,
|
||||||
SignedUrl: pdfUrl,
|
SignedUrl: pdfUrl,
|
||||||
Signers: signers
|
Signers: signers,
|
||||||
|
SentToOthers: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await axios
|
await axios
|
||||||
@@ -973,15 +998,14 @@ function PlaceHolderSign() {
|
|||||||
const shareLinkList = [];
|
const shareLinkList = [];
|
||||||
let signerMail = signersdata;
|
let signerMail = signersdata;
|
||||||
for (let i = 0; i < signerMail.length; i++) {
|
for (let i = 0; i < signerMail.length; i++) {
|
||||||
const serverUrl = localStorage.getItem("baseUrl");
|
|
||||||
const newServer = serverUrl.replaceAll("/", "%2F");
|
|
||||||
const objectId = signerMail[i].objectId;
|
const objectId = signerMail[i].objectId;
|
||||||
const serverParams = `${newServer}&${localStorage.getItem(
|
|
||||||
"parseAppId"
|
|
||||||
)}&${localStorage.getItem("_appName")}`;
|
|
||||||
|
|
||||||
const hostUrl = window.location.origin;
|
const hostUrl = window.location.origin;
|
||||||
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
|
const sendMail = false;
|
||||||
|
//encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(
|
||||||
|
`${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${sendMail}`
|
||||||
|
);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
shareLinkList.push({ signerEmail: signerMail[i].Email, url: signPdf });
|
shareLinkList.push({ signerEmail: signerMail[i].Email, url: signPdf });
|
||||||
}
|
}
|
||||||
return shareLinkList.map((data, ind) => {
|
return shareLinkList.map((data, ind) => {
|
||||||
@@ -1055,15 +1079,13 @@ function PlaceHolderSign() {
|
|||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
};
|
};
|
||||||
const serverUrl = localStorage.getItem("baseUrl");
|
|
||||||
const newServer = serverUrl.replaceAll("/", "%2F");
|
|
||||||
const objectId = signerMail[i].objectId;
|
const objectId = signerMail[i].objectId;
|
||||||
const serverParams = `${newServer}&${localStorage.getItem(
|
|
||||||
"parseAppId"
|
|
||||||
)}&${localStorage.getItem("_appName")}`;
|
|
||||||
|
|
||||||
const hostUrl = window.location.origin;
|
const hostUrl = window.location.origin;
|
||||||
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`;
|
//encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(
|
||||||
|
`${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}`
|
||||||
|
);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
const openSignUrl = "https://www.opensignlabs.com/";
|
const openSignUrl = "https://www.opensignlabs.com/";
|
||||||
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
const orgName = pdfDetails[0]?.ExtUserPtr.Company
|
||||||
? pdfDetails[0].ExtUserPtr.Company
|
? pdfDetails[0].ExtUserPtr.Company
|
||||||
@@ -1088,10 +1110,10 @@ function PlaceHolderSign() {
|
|||||||
document_title: documentName,
|
document_title: documentName,
|
||||||
sender_name: senderName,
|
sender_name: senderName,
|
||||||
sender_mail: senderEmail,
|
sender_mail: senderEmail,
|
||||||
sender_phone: senderPhone,
|
sender_phone: senderPhone || "",
|
||||||
receiver_name: signerMail[i].Name,
|
receiver_name: signerMail[i].Name,
|
||||||
receiver_email: signerMail[i].Email,
|
receiver_email: signerMail[i].Email,
|
||||||
receiver_phone: signerMail[i].Phone,
|
receiver_phone: signerMail[i]?.Phone || "",
|
||||||
expiry_date: localExpireDate,
|
expiry_date: localExpireDate,
|
||||||
company_name: orgName,
|
company_name: orgName,
|
||||||
signing_url: `<a href=${signPdf}>Sign here</a>`
|
signing_url: `<a href=${signPdf}>Sign here</a>`
|
||||||
@@ -1104,11 +1126,12 @@ function PlaceHolderSign() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let params = {
|
let params = {
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
extUserId: extUserId,
|
extUserId: extUserId,
|
||||||
recipient: signerMail[i].Email,
|
recipient: signerMail[i].Email,
|
||||||
subject: isCustomize
|
subject: isCustomize
|
||||||
? replaceVar?.subject
|
? replaceVar?.subject
|
||||||
: `${senderName} has requested you to sign ${documentName}`,
|
: `${senderName} has requested you to sign "${documentName}"`,
|
||||||
from: senderEmail,
|
from: senderEmail,
|
||||||
html: isCustomize
|
html: isCustomize
|
||||||
? replaceVar?.body
|
? replaceVar?.body
|
||||||
@@ -1142,40 +1165,45 @@ function PlaceHolderSign() {
|
|||||||
}
|
}
|
||||||
if (sendMail.data.result.status === "success") {
|
if (sendMail.data.result.status === "success") {
|
||||||
setMailStatus("success");
|
setMailStatus("success");
|
||||||
|
try {
|
||||||
if (
|
let data;
|
||||||
requestBody &&
|
if (
|
||||||
requestSubject &&
|
requestBody &&
|
||||||
isCustomize &&
|
requestSubject &&
|
||||||
(isSubscribe || !isEnableSubscription)
|
isCustomize &&
|
||||||
) {
|
(isSubscribe || !isEnableSubscription)
|
||||||
try {
|
) {
|
||||||
const data = {
|
data = {
|
||||||
RequestBody: htmlReqBody,
|
RequestBody: htmlReqBody,
|
||||||
RequestSubject: requestSubject
|
RequestSubject: requestSubject,
|
||||||
|
SendMail: true
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
SendMail: true
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios
|
|
||||||
.put(
|
|
||||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
|
||||||
"_appName"
|
|
||||||
)}_Document/${documentId}`,
|
|
||||||
data,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.log("error", e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.put(
|
||||||
|
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||||
|
"_appName"
|
||||||
|
)}_Document/${documentId}`,
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("axois err ", err);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsSend(true);
|
setIsSend(true);
|
||||||
@@ -1696,8 +1724,8 @@ function PlaceHolderSign() {
|
|||||||
>
|
>
|
||||||
{isSendAlert.mssg === "sure" ? (
|
{isSendAlert.mssg === "sure" ? (
|
||||||
<span>
|
<span>
|
||||||
Please add at least one signature field for all
|
Please ensure there's at least one signature widget
|
||||||
recipients.
|
added for all recipients.
|
||||||
</span>
|
</span>
|
||||||
) : isSendAlert.mssg === textWidget ? (
|
) : isSendAlert.mssg === textWidget ? (
|
||||||
<p>Please confirm that you have filled the text field.</p>
|
<p>Please confirm that you have filled the text field.</p>
|
||||||
@@ -1778,10 +1806,7 @@ function PlaceHolderSign() {
|
|||||||
|
|
||||||
{!isSubscribe && isEnableSubscription && (
|
{!isSubscribe && isEnableSubscription && (
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Upgrade
|
<Upgrade message="Upgrade to customize Email" />
|
||||||
message="Upgrade to customize Email"
|
|
||||||
newWindow={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,8 +16,17 @@ const PlanSubscriptions = () => {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [yearlyVisible, setYearlyVisible] = useState(false);
|
const [yearlyVisible, setYearlyVisible] = useState(false);
|
||||||
const [isLoader, setIsLoader] = useState(true);
|
const [isLoader, setIsLoader] = useState(true);
|
||||||
|
const extUser =
|
||||||
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
|
localStorage.getItem("Extand_Class") &&
|
||||||
|
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||||
|
const user = {
|
||||||
|
name: extUser?.Name,
|
||||||
|
email: extUser?.Email,
|
||||||
|
company: extUser?.Company,
|
||||||
|
phone: extUser?.Phone
|
||||||
|
};
|
||||||
|
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
|
||||||
|
// console.log("userDetails ", userDetails);
|
||||||
const name =
|
const name =
|
||||||
userDetails && userDetails.name
|
userDetails && userDetails.name
|
||||||
? "first_name=" + encodeURIComponent(userDetails.name)
|
? "first_name=" + encodeURIComponent(userDetails.name)
|
||||||
|
|||||||
@@ -0,0 +1,413 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import Parse from "parse";
|
||||||
|
import axios from "axios";
|
||||||
|
import { appInfo } from "../constant/appinfo";
|
||||||
|
import {
|
||||||
|
isEnableSubscription,
|
||||||
|
modalCancelBtnColor,
|
||||||
|
modalSubmitBtnColor
|
||||||
|
} from "../constant/const";
|
||||||
|
import { fetchSubscription } from "../constant/Utils";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
import loader from "../assets/images/loader2.gif";
|
||||||
|
|
||||||
|
const SSOVerify = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isModal, setIsModal] = useState(false);
|
||||||
|
const [message, setMessage] = useState("Verifying SSO...");
|
||||||
|
const [isLoader, setIsLoader] = useState(false);
|
||||||
|
const [userDetails, setUserDetails] = useState({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
Destination: "",
|
||||||
|
Company: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
linkUserWithSSO();
|
||||||
|
// eslint-disable-next-line
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// `linkUserWithSSO` is used to sign in or sign up a user using an SSO code and check if the user is present in the extended class
|
||||||
|
const linkUserWithSSO = async () => {
|
||||||
|
const param = new URLSearchParams(location.search);
|
||||||
|
const code = param?.get("code");
|
||||||
|
const state = param?.get("state");
|
||||||
|
try {
|
||||||
|
// The `ssosign` cloud function is used to sign in or sign up a user
|
||||||
|
const ssosign = await Parse.Cloud.run("ssosign", {
|
||||||
|
code: code,
|
||||||
|
email: state
|
||||||
|
});
|
||||||
|
localStorage.setItem("accesstoken", ssosign.sessiontoken);
|
||||||
|
// `checkExtUser` checks if the user is present in the extended class `contracts_Users` and if not, initiates the new user flow
|
||||||
|
await checkExtUser(ssosign);
|
||||||
|
} catch (err) {
|
||||||
|
setMessage("Error: " + err.message);
|
||||||
|
console.log("err", err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const checkExtUser = async (ssosign) => {
|
||||||
|
const params = { email: ssosign?.email };
|
||||||
|
try {
|
||||||
|
// `isextenduser` checks if the current user is present in the extended class
|
||||||
|
const extRes = await Parse.Cloud.run("isextenduser", params);
|
||||||
|
if (extRes?.isUserExist) {
|
||||||
|
// `getUserDetails` retrieves the current user's details from the extended class
|
||||||
|
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||||
|
if (ssosign && ssosign.sessiontoken) {
|
||||||
|
const LocalUserDetails = {
|
||||||
|
name: extRes.Name,
|
||||||
|
email: extRes.email,
|
||||||
|
phone: extRes?.get("Phone") || "",
|
||||||
|
company: extRes.get("Company")
|
||||||
|
};
|
||||||
|
localStorage.setItem("userDetails", JSON.stringify(LocalUserDetails));
|
||||||
|
thirdpartyLoginfn(ssosign.sessiontoken);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setIsModal(true);
|
||||||
|
setUserDetails((prev) => ({
|
||||||
|
...prev,
|
||||||
|
name: ssosign.name,
|
||||||
|
email: ssosign.email,
|
||||||
|
phone: ssosign?.phone || ""
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in isextenduser or getuserdetails cloud function", err);
|
||||||
|
setMessage("Error: " + err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleSubmitbtn` is used to create a user in the extended class
|
||||||
|
const handleSubmitbtn = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsLoader(true);
|
||||||
|
let phone;
|
||||||
|
if (userDetails?.phone) {
|
||||||
|
phone = validateInput(userDetails?.phone);
|
||||||
|
} else {
|
||||||
|
phone = true;
|
||||||
|
}
|
||||||
|
if (userDetails.Destination && userDetails.Company && phone) {
|
||||||
|
const payload = { sessionToken: localStorage.getItem("accesstoken") };
|
||||||
|
if (payload && payload.sessionToken) {
|
||||||
|
const params = {
|
||||||
|
userDetails: {
|
||||||
|
name: userDetails.name,
|
||||||
|
email: userDetails.email,
|
||||||
|
phone: userDetails?.phone || "",
|
||||||
|
role: "contracts_User",
|
||||||
|
company: userDetails.Company,
|
||||||
|
jobTitle: userDetails.Destination
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const userSignUp = await Parse.Cloud.run("usersignup", params);
|
||||||
|
if (userSignUp && userSignUp.sessionToken) {
|
||||||
|
const LocalUserDetails = params.userDetails;
|
||||||
|
localStorage.setItem(
|
||||||
|
"userDetails",
|
||||||
|
JSON.stringify(LocalUserDetails)
|
||||||
|
);
|
||||||
|
await thirdpartyLoginfn(userSignUp.sessionToken);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else {
|
||||||
|
alert(userSignUp.message);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("error in usersignup", err);
|
||||||
|
localStorage.removeItem("accesstoken");
|
||||||
|
alert("Something went wrong.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("accesstoken");
|
||||||
|
alert("Internal server error.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Please fill required details correctly.");
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
|
||||||
|
const thirdpartyLoginfn = async (sessionToken) => {
|
||||||
|
const baseUrl = localStorage.getItem("baseUrl");
|
||||||
|
const parseAppId = localStorage.getItem("parseAppId");
|
||||||
|
try {
|
||||||
|
const validUser = await Parse.User.become(sessionToken);
|
||||||
|
if (validUser) {
|
||||||
|
localStorage.setItem("accesstoken", sessionToken);
|
||||||
|
const _user = JSON.parse(JSON.stringify(validUser));
|
||||||
|
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||||
|
if (_user.ProfilePic) {
|
||||||
|
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem("profileImg", "");
|
||||||
|
}
|
||||||
|
// Check extended class user role and tenentId
|
||||||
|
try {
|
||||||
|
let userRoles = [];
|
||||||
|
const userSettings = appInfo.settings;
|
||||||
|
//Get Current user roles
|
||||||
|
const url = `${baseUrl}functions/UserGroups`;
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-Parse-Application-Id": parseAppId,
|
||||||
|
sessionToken: _user.sessionToken
|
||||||
|
};
|
||||||
|
const body = { appname: appInfo.appname };
|
||||||
|
const UserGroupsRes = await axios.post(url, JSON.stringify(body), {
|
||||||
|
headers: headers
|
||||||
|
});
|
||||||
|
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || [];
|
||||||
|
if (userRoles) {
|
||||||
|
let _currentRole = "";
|
||||||
|
const excludeRoles = ["contracts_Guest", `contracts_appeditor`];
|
||||||
|
const filteredRole = userRoles.filter(
|
||||||
|
(x) => !excludeRoles.includes(x)
|
||||||
|
);
|
||||||
|
if (filteredRole?.length > 1) {
|
||||||
|
console.log("user has two roles");
|
||||||
|
// _currentRole = filteredRole.filter((x) => x === "contracts_User");
|
||||||
|
} else {
|
||||||
|
_currentRole = filteredRole?.[0] || "";
|
||||||
|
}
|
||||||
|
if (_currentRole) {
|
||||||
|
const roleSetting = userSettings?.find(
|
||||||
|
(setting) => setting.role === _currentRole
|
||||||
|
);
|
||||||
|
const redirectUrl =
|
||||||
|
location?.state?.from ||
|
||||||
|
`/${roleSetting.pageType}/${roleSetting.pageId}`;
|
||||||
|
const _role = _currentRole.replace(`${appInfo.appname}_`, "");
|
||||||
|
localStorage.setItem("_user_role", _role);
|
||||||
|
// Get TenentID from Extendend Class
|
||||||
|
localStorage.setItem(
|
||||||
|
"extended_class",
|
||||||
|
roleSetting.extended_class
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||||
|
email: _user.email
|
||||||
|
});
|
||||||
|
if (extUser) {
|
||||||
|
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||||
|
localStorage.setItem("userEmail", _extUser.Email);
|
||||||
|
localStorage.setItem("username", _extUser.Name);
|
||||||
|
localStorage.setItem("scriptId", true);
|
||||||
|
let tenentInfo = [];
|
||||||
|
const results = [extUser];
|
||||||
|
if (results) {
|
||||||
|
let extendedInfo_stringify = JSON.stringify(results);
|
||||||
|
localStorage.setItem(
|
||||||
|
"Extand_Class",
|
||||||
|
extendedInfo_stringify
|
||||||
|
);
|
||||||
|
if (_extUser.TenantId) {
|
||||||
|
const obj = {
|
||||||
|
tenentId: _extUser.TenantId.objectId,
|
||||||
|
tenentName: _extUser.TenantId.TenantName || ""
|
||||||
|
};
|
||||||
|
localStorage.setItem("TenantId", obj.tenentId);
|
||||||
|
tenentInfo.push(obj);
|
||||||
|
dispatch(showTenant(obj.tenentName || ""));
|
||||||
|
localStorage.setItem("TenantName", obj.tenentName || "");
|
||||||
|
}
|
||||||
|
localStorage.setItem("PageLanding", roleSetting.pageId);
|
||||||
|
localStorage.setItem("defaultmenuid", roleSetting.menuId);
|
||||||
|
localStorage.setItem("pageType", roleSetting.pageType);
|
||||||
|
if (isEnableSubscription) {
|
||||||
|
const res = await fetchSubscription();
|
||||||
|
const plan = res.plan;
|
||||||
|
const billingDate = res.billingDate;
|
||||||
|
if (plan === "freeplan") {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else if (billingDate) {
|
||||||
|
if (new Date(billingDate) > new Date()) {
|
||||||
|
localStorage.removeItem("userDetails");
|
||||||
|
navigate(redirectUrl);
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(`/subscription`, {
|
||||||
|
replace: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
alert("user not exist.");
|
||||||
|
setMessage("Error: User not exist.");
|
||||||
|
console.log("err in get extUser", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Role does not exists.");
|
||||||
|
setMessage("Error: Role does not exists.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Role does not exists.");
|
||||||
|
setMessage("Error: Role does not exists.");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in usergroups", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Err in become method", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// `handleCloseModal` is triggered when the user wants to close the new user flow modal
|
||||||
|
const handleCloseModal = () => {
|
||||||
|
setIsModal(false);
|
||||||
|
if (Parse?.User?.current()) {
|
||||||
|
Parse.User.logOut();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// `validateInput` is used to verify the phone pattern
|
||||||
|
function validateInput(input) {
|
||||||
|
if (input) {
|
||||||
|
const pattern = /^(?!.*\+.*\+)[\d+-]*$/;
|
||||||
|
return pattern.test(input);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="w-full h-screen flex flex-col justify-center items-center text-sm md:text-xl ">
|
||||||
|
{message === "Verifying SSO..." && (
|
||||||
|
<img alt="loader" src={loader} className="w-[80px] h-[80px]" />
|
||||||
|
)}
|
||||||
|
<div className="text-[gray]">{message}</div>
|
||||||
|
</div>
|
||||||
|
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
|
||||||
|
<div className="relative">
|
||||||
|
{isLoader && (
|
||||||
|
<div className="absolute w-full h-full bg-black bg-opacity-25 flex justify-center items-center">
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "45px",
|
||||||
|
color: "#3dd3e0"
|
||||||
|
}}
|
||||||
|
className="loader-37"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<form className="px-4 py-3" onSubmit={handleSubmitbtn}>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="Company"
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<p className="text-[10px] text-[red] ml-2">
|
||||||
|
{!validateInput(userDetails.phone) && "Invalid phone value"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="mb-3">
|
||||||
|
<label
|
||||||
|
htmlFor="Company"
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={isLoader}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
|
||||||
|
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>
|
||||||
|
</div>
|
||||||
|
</ModalUi>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SSOVerify;
|
||||||
@@ -11,7 +11,7 @@ import { useDispatch } from "react-redux";
|
|||||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||||
import { isEnableSubscription } from "../constant/const";
|
import { isEnableSubscription } from "../constant/const";
|
||||||
import { getAppLogo } from "../constant/Utils";
|
import { getAppLogo, openInNewTab } from "../constant/Utils";
|
||||||
const Signup = () => {
|
const Signup = () => {
|
||||||
const { width } = useWindowSize();
|
const { width } = useWindowSize();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -33,7 +33,7 @@ const Signup = () => {
|
|||||||
const [lengthValid, setLengthValid] = useState(false);
|
const [lengthValid, setLengthValid] = useState(false);
|
||||||
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
||||||
const [specialCharValid, setSpecialCharValid] = useState(false);
|
const [specialCharValid, setSpecialCharValid] = useState(false);
|
||||||
|
const [isAuthorize, setIsAuthorize] = useState(false);
|
||||||
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
||||||
|
|
||||||
const clearStorage = async () => {
|
const clearStorage = async () => {
|
||||||
@@ -562,9 +562,11 @@ const Signup = () => {
|
|||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
type="text"
|
type="text"
|
||||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs lowercase"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) =>
|
||||||
|
setEmail(e.target.value?.toLowerCase())
|
||||||
|
}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<hr className="my-2 border-none" />
|
<hr className="my-2 border-none" />
|
||||||
@@ -658,10 +660,34 @@ const Signup = () => {
|
|||||||
{specialCharValid ? "✓" : "✗"} Password should
|
{specialCharValid ? "✓" : "✗"} Password should
|
||||||
contain special character
|
contain special character
|
||||||
</p>
|
</p>
|
||||||
{/* </>
|
|
||||||
)} */}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="mt-2.5 ml-1 flex flex-row items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="termsandcondition"
|
||||||
|
checked={isAuthorize}
|
||||||
|
onChange={(e) => setIsAuthorize(e.target.checked)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="text-xs cursor-pointer ml-1 mb-0"
|
||||||
|
htmlFor="termsandcondition"
|
||||||
|
>
|
||||||
|
I agree to the
|
||||||
|
</label>
|
||||||
|
<span
|
||||||
|
className="underline cursor-pointer ml-1"
|
||||||
|
onClick={() =>
|
||||||
|
openInNewTab(
|
||||||
|
"https://www.opensignlabs.com/terms-and-conditions"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Terms of Service
|
||||||
|
</span>
|
||||||
|
<span>.</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
|
<div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2">
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { PDFDocument } from "pdf-lib";
|
import { PDFDocument } from "pdf-lib";
|
||||||
import "../styles/signature.css";
|
import "../styles/signature.css";
|
||||||
|
import Parse from "parse";
|
||||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||||
|
import Confetti from "react-confetti";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import Loader from "../primitives/LoaderWithMsg";
|
import Loader from "../primitives/LoaderWithMsg";
|
||||||
import loader from "../assets/images/loader2.gif";
|
import loader from "../assets/images/loader2.gif";
|
||||||
@@ -29,7 +31,10 @@ import {
|
|||||||
textWidget,
|
textWidget,
|
||||||
getTenantDetails,
|
getTenantDetails,
|
||||||
checkIsSubscribed,
|
checkIsSubscribed,
|
||||||
convertPdfArrayBuffer
|
convertPdfArrayBuffer,
|
||||||
|
fetchImageBase64,
|
||||||
|
changeImageWH,
|
||||||
|
handleSendOTP
|
||||||
} from "../constant/Utils";
|
} from "../constant/Utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import Tour from "reactour";
|
import Tour from "reactour";
|
||||||
@@ -42,6 +47,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
|||||||
import Title from "../components/Title";
|
import Title from "../components/Title";
|
||||||
import ModalUi from "../primitives/ModalUi";
|
import ModalUi from "../primitives/ModalUi";
|
||||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||||
|
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||||
|
|
||||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||||
function SignYourSelf() {
|
function SignYourSelf() {
|
||||||
@@ -65,7 +71,6 @@ function SignYourSelf() {
|
|||||||
const [dragKey, setDragKey] = useState();
|
const [dragKey, setDragKey] = useState();
|
||||||
const [signKey, setSignKey] = useState();
|
const [signKey, setSignKey] = useState();
|
||||||
const [imgWH, setImgWH] = useState({});
|
const [imgWH, setImgWH] = useState({});
|
||||||
const [isCeleb, setIsCeleb] = useState(false);
|
|
||||||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||||||
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
||||||
const [successEmail, setSuccessEmail] = useState(false);
|
const [successEmail, setSuccessEmail] = useState(false);
|
||||||
@@ -89,6 +94,7 @@ function SignYourSelf() {
|
|||||||
const [containerWH, setContainerWH] = useState({});
|
const [containerWH, setContainerWH] = useState({});
|
||||||
const [isPageCopy, setIsPageCopy] = useState(false);
|
const [isPageCopy, setIsPageCopy] = useState(false);
|
||||||
const [selectWidgetId, setSelectWidgetId] = useState("");
|
const [selectWidgetId, setSelectWidgetId] = useState("");
|
||||||
|
const [otpLoader, setOtpLoader] = useState(false);
|
||||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||||
status: false
|
status: false
|
||||||
});
|
});
|
||||||
@@ -103,7 +109,12 @@ function SignYourSelf() {
|
|||||||
const [isDontShow, setIsDontShow] = useState(false);
|
const [isDontShow, setIsDontShow] = useState(false);
|
||||||
const [extUserId, setExtUserId] = useState("");
|
const [extUserId, setExtUserId] = useState("");
|
||||||
const [isCompleted, setIsCompleted] = useState(false);
|
const [isCompleted, setIsCompleted] = useState(false);
|
||||||
|
const [isCelebration, setIsCelebration] = useState(false);
|
||||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||||
|
const [activeMailAdapter, setActiveMailAdapter] = useState("");
|
||||||
|
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||||
|
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||||
|
const [otp, setOtp] = useState("");
|
||||||
const divRef = useRef(null);
|
const divRef = useRef(null);
|
||||||
const nodeRef = useRef(null);
|
const nodeRef = useRef(null);
|
||||||
const [, drop] = useDrop({
|
const [, drop] = useDrop({
|
||||||
@@ -193,13 +204,18 @@ function SignYourSelf() {
|
|||||||
setPdfDetails(documentData);
|
setPdfDetails(documentData);
|
||||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||||
const url = documentData[0] && documentData[0]?.URL;
|
const url = documentData[0] && documentData[0]?.URL;
|
||||||
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
if (url) {
|
||||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||||||
if (arrayBuffer === "Error") {
|
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||||
setHandleError("Error: Something went wrong!");
|
if (arrayBuffer === "Error") {
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
} else {
|
||||||
|
setPdfArrayBuffer(arrayBuffer);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setPdfArrayBuffer(arrayBuffer);
|
setHandleError("Error: Something went wrong!");
|
||||||
}
|
}
|
||||||
|
|
||||||
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
setIsCompleted(true);
|
setIsCompleted(true);
|
||||||
@@ -214,8 +230,12 @@ function SignYourSelf() {
|
|||||||
setIsUiLoading(false);
|
setIsUiLoading(false);
|
||||||
setIsSignPad(false);
|
setIsSignPad(false);
|
||||||
setIsEmail(true);
|
setIsEmail(true);
|
||||||
|
setIsCelebration(true);
|
||||||
setXyPostion([]);
|
setXyPostion([]);
|
||||||
setSignBtnPosition([]);
|
setSignBtnPosition([]);
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsCelebration(false);
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@@ -266,7 +286,14 @@ function SignYourSelf() {
|
|||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
});
|
});
|
||||||
const contractUsersRes = await contractUsers(jsonSender.email);
|
const contractUsersRes = await contractUsers(jsonSender.email);
|
||||||
if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
if (contractUsersRes === "Error: Something went wrong!") {
|
||||||
|
const loadObj = {
|
||||||
|
isLoad: false
|
||||||
|
};
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
setIsLoading(loadObj);
|
||||||
|
} else if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
||||||
|
setActiveMailAdapter(contractUsersRes[0]?.active_mail_adapter);
|
||||||
setContractName("_Users");
|
setContractName("_Users");
|
||||||
setSignerUserId(contractUsersRes[0].objectId);
|
setSignerUserId(contractUsersRes[0].objectId);
|
||||||
const tourstatuss =
|
const tourstatuss =
|
||||||
@@ -286,12 +313,6 @@ function SignYourSelf() {
|
|||||||
isLoad: false
|
isLoad: false
|
||||||
};
|
};
|
||||||
setIsLoading(loadObj);
|
setIsLoading(loadObj);
|
||||||
} else if (contractUsersRes === "Error: Something went wrong!") {
|
|
||||||
const loadObj = {
|
|
||||||
isLoad: false
|
|
||||||
};
|
|
||||||
setHandleError("Error: Something went wrong!");
|
|
||||||
setIsLoading(loadObj);
|
|
||||||
} else if (contractUsersRes.length === 0) {
|
} else if (contractUsersRes.length === 0) {
|
||||||
const contractContactBook = await contactBook(jsonSender.objectId);
|
const contractContactBook = await contactBook(jsonSender.objectId);
|
||||||
if (contractContactBook && contractContactBook.length > 0) {
|
if (contractContactBook && contractContactBook.length > 0) {
|
||||||
@@ -525,76 +546,163 @@ function SignYourSelf() {
|
|||||||
setSignKey(key);
|
setSignKey(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//`handleResend` function is used to resend otp for email verification
|
||||||
|
const handleResend = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
setOtpLoader(false);
|
||||||
|
alert("OTP sent on you email");
|
||||||
|
};
|
||||||
|
//`handleVerifyEmail` function is used to verify email with otp
|
||||||
|
const handleVerifyEmail = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
try {
|
||||||
|
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||||
|
otp: otp,
|
||||||
|
email: Parse.User.current().getEmail()
|
||||||
|
});
|
||||||
|
if (resEmail?.message === "Email is verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
} else if (resEmail?.message === "Email is already verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
}
|
||||||
|
setOtp("");
|
||||||
|
alert(resEmail.message);
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
// handleRecipientSign();
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
} finally {
|
||||||
|
setOtpLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//`handleVerifyBtn` function is used to send otp on user mail
|
||||||
|
const handleVerifyBtn = async () => {
|
||||||
|
setIsVerifyModal(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
};
|
||||||
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
|
||||||
async function embedWidgetsData() {
|
async function embedWidgetsData() {
|
||||||
let showAlert = false;
|
//check current user email is verified or not
|
||||||
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
for (let i = 0; i < xyPostion?.length; i++) {
|
let isEmailVerified;
|
||||||
const requiredWidgets = xyPostion[i].pos.filter(
|
isEmailVerified = currentUser?.emailVerified;
|
||||||
(position) => position.type !== "checkbox"
|
if (isEmailVerified) {
|
||||||
);
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (requiredWidgets && requiredWidgets?.length > 0) {
|
} else {
|
||||||
let checkSigned;
|
try {
|
||||||
for (let i = 0; i < requiredWidgets?.length; i++) {
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
checkSigned = requiredWidgets[i]?.options?.response;
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
if (!checkSigned) {
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
});
|
||||||
let checkDefaultSigned = requiredWidgets[i]?.options?.defaultValue;
|
if (user) {
|
||||||
if (!checkSignUrl) {
|
isEmailVerified = user?.get("emailVerified");
|
||||||
if (!checkDefaultSigned) {
|
setIsEmailVerified(isEmailVerified);
|
||||||
if (!showAlert) {
|
}
|
||||||
showAlert = true;
|
} catch (e) {
|
||||||
|
setHandleError("Error: Something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isEmailVerified) {
|
||||||
|
let showAlert = false,
|
||||||
|
isSignatureExist = false;
|
||||||
|
try {
|
||||||
|
for (let i = 0; i < xyPostion?.length; i++) {
|
||||||
|
const requiredWidgets = xyPostion[i].pos.filter(
|
||||||
|
(position) => position.type !== "checkbox"
|
||||||
|
);
|
||||||
|
if (requiredWidgets && requiredWidgets?.length > 0) {
|
||||||
|
let checkSigned;
|
||||||
|
for (let i = 0; i < requiredWidgets?.length; i++) {
|
||||||
|
checkSigned = requiredWidgets[i]?.options?.response;
|
||||||
|
if (!checkSigned) {
|
||||||
|
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
|
||||||
|
let checkDefaultSigned =
|
||||||
|
requiredWidgets[i]?.options?.defaultValue;
|
||||||
|
if (!checkSignUrl) {
|
||||||
|
if (!checkDefaultSigned) {
|
||||||
|
if (!showAlert) {
|
||||||
|
showAlert = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//condition to check exist signature widget or not
|
||||||
|
if (!isSignatureExist) {
|
||||||
|
isSignatureExist = xyPostion[i].pos.some(
|
||||||
|
(data) => data?.type === "signature"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (xyPostion.length === 0 || !isSignatureExist) {
|
||||||
|
setIsAlert({
|
||||||
|
header: "Fields required",
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure there's at least one signature widget added"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (showAlert) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage:
|
||||||
|
"Please ensure all field is accurately filled and meets all requirements."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setIsUiLoading(true);
|
||||||
|
const existingPdfBytes = pdfArrayBuffer;
|
||||||
|
// Load a PDFDocument from the existing PDF bytes
|
||||||
|
try {
|
||||||
|
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||||
|
const isSignYourSelfFlow = true;
|
||||||
|
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
||||||
|
const HeaderDocId = extUserPtr?.HeaderDocId;
|
||||||
|
//embed document's object id to all pages in pdf document
|
||||||
|
if (!HeaderDocId) {
|
||||||
|
await embedDocId(pdfDoc, documentId, allPages);
|
||||||
|
}
|
||||||
|
//embed multi signature in pdf
|
||||||
|
const pdfBytes = await multiSignEmbed(
|
||||||
|
xyPostion,
|
||||||
|
pdfDoc,
|
||||||
|
pdfOriginalWidth,
|
||||||
|
isSignYourSelfFlow,
|
||||||
|
containerWH
|
||||||
|
);
|
||||||
|
// console.log("pdf", pdfBytes);
|
||||||
|
//function for call to embed signature in pdf and get digital signature pdf
|
||||||
|
await signPdfFun(pdfBytes, documentId);
|
||||||
|
} catch (err) {
|
||||||
|
setIsUiLoading(false);
|
||||||
|
if (err && err.message.includes("is encrypted.")) {
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Currently encrypted pdf files are not supported.`
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("err in signing", err);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: `Something went wrong.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("err in embedselfsign ", err);
|
||||||
|
setIsUiLoading(false);
|
||||||
|
setIsAlert({
|
||||||
|
isShow: true,
|
||||||
|
alertMessage: "something went wrong, please try again later."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xyPostion.length === 0) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else if (showAlert) {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: true,
|
|
||||||
alertMessage: "Please complete your signature!"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setIsCeleb(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsCeleb(false);
|
|
||||||
}, 3000);
|
|
||||||
setIsUiLoading(true);
|
|
||||||
const existingPdfBytes = pdfArrayBuffer;
|
|
||||||
// Load a PDFDocument from the existing PDF bytes
|
|
||||||
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
|
|
||||||
ignoreEncryption: true
|
|
||||||
});
|
|
||||||
const isSignYourSelfFlow = true;
|
|
||||||
const extUserPtr = pdfDetails[0].ExtUserPtr;
|
|
||||||
const HeaderDocId = extUserPtr?.HeaderDocId;
|
|
||||||
//embed document's object id to all pages in pdf document
|
|
||||||
if (!HeaderDocId) {
|
|
||||||
await embedDocId(pdfDoc, documentId, allPages);
|
|
||||||
}
|
|
||||||
//embed multi signature in pdf
|
|
||||||
const pdfBytes = await multiSignEmbed(
|
|
||||||
xyPostion,
|
|
||||||
pdfDoc,
|
|
||||||
pdfOriginalWidth,
|
|
||||||
isSignYourSelfFlow,
|
|
||||||
containerWH
|
|
||||||
);
|
|
||||||
// console.log("pdf", pdfBytes);
|
|
||||||
//function for call to embed signature in pdf and get digital signature pdf
|
|
||||||
await signPdfFun(pdfBytes, documentId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// console.log("signyourself", xyPostion);
|
|
||||||
//function for get digital signature
|
//function for get digital signature
|
||||||
const signPdfFun = async (base64Url, documentId) => {
|
const signPdfFun = async (base64Url, documentId) => {
|
||||||
let isCustomCompletionMail = false;
|
let isCustomCompletionMail = false;
|
||||||
@@ -611,11 +719,40 @@ function SignYourSelf() {
|
|||||||
isCustomCompletionMail = true;
|
isCustomCompletionMail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// below for loop is used to get first signature of user to send if to signpdf
|
||||||
|
// for adding it in completion certificate
|
||||||
|
let getSignature;
|
||||||
|
for (let item of xyPostion) {
|
||||||
|
if (!getSignature) {
|
||||||
|
const typeExist = item.pos.some((data) => data?.type);
|
||||||
|
if (typeExist) {
|
||||||
|
getSignature = item.pos.find((data) => data?.type === "signature");
|
||||||
|
} else {
|
||||||
|
getSignature = item.pos.find((data) => !data.isStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let base64Sign = getSignature.SignUrl;
|
||||||
|
//check https type signature (default signature exist) then convert in base64
|
||||||
|
const isUrl = base64Sign.includes("https");
|
||||||
|
if (isUrl) {
|
||||||
|
try {
|
||||||
|
base64Sign = await fetchImageBase64(base64Sign);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//change image width and height to 104/44 in png base64
|
||||||
|
const getNewse64 = await changeImageWH(base64Sign);
|
||||||
|
//remove suffiix of base64
|
||||||
|
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||||
|
|
||||||
let singleSign = {
|
let singleSign = {
|
||||||
pdfFile: base64Url,
|
pdfFile: base64Url,
|
||||||
docId: documentId,
|
docId: documentId,
|
||||||
isCustomCompletionMail: isCustomCompletionMail
|
isCustomCompletionMail: isCustomCompletionMail,
|
||||||
|
mailProvider: activeMailAdapter,
|
||||||
|
signature: suffixbase64
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios
|
await axios
|
||||||
@@ -623,7 +760,8 @@ function SignYourSelf() {
|
|||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||||
sessionToken: localStorage.getItem("accesstoken")
|
// sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((Listdata) => {
|
.then((Listdata) => {
|
||||||
@@ -985,8 +1123,25 @@ function SignYourSelf() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{isCelebration && (
|
||||||
|
<div style={{ position: "relative", zIndex: "999" }}>
|
||||||
|
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="signatureContainer" ref={divRef}>
|
<div className="signatureContainer" ref={divRef}>
|
||||||
|
{!isEmailVerified && (
|
||||||
|
<VerifyEmail
|
||||||
|
isVerifyModal={isVerifyModal}
|
||||||
|
setIsVerifyModal={setIsVerifyModal}
|
||||||
|
handleVerifyEmail={handleVerifyEmail}
|
||||||
|
setOtp={setOtp}
|
||||||
|
otp={otp}
|
||||||
|
otpLoader={otpLoader}
|
||||||
|
handleVerifyBtn={handleVerifyBtn}
|
||||||
|
handleResend={handleResend}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{/* this component used for UI interaction and show their functionality */}
|
{/* this component used for UI interaction and show their functionality */}
|
||||||
{pdfLoadFail && !checkTourStatus && (
|
{pdfLoadFail && !checkTourStatus && (
|
||||||
<Tour
|
<Tour
|
||||||
@@ -1021,7 +1176,7 @@ function SignYourSelf() {
|
|||||||
<ModalUi
|
<ModalUi
|
||||||
headerColor={"#dc3545"}
|
headerColor={"#dc3545"}
|
||||||
isOpen={isAlert.isShow}
|
isOpen={isAlert.isShow}
|
||||||
title={"Alert"}
|
title={isAlert?.header || "Alert"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setIsAlert({
|
setIsAlert({
|
||||||
isShow: false,
|
isShow: false,
|
||||||
@@ -1031,35 +1186,13 @@ function SignYourSelf() {
|
|||||||
>
|
>
|
||||||
<div style={{ height: "100%", padding: 20 }}>
|
<div style={{ height: "100%", padding: 20 }}>
|
||||||
<p>{isAlert.alertMessage}</p>
|
<p>{isAlert.alertMessage}</p>
|
||||||
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "1px",
|
|
||||||
backgroundColor: "#9f9f9f",
|
|
||||||
width: "100%",
|
|
||||||
marginTop: "15px",
|
|
||||||
marginBottom: "15px"
|
|
||||||
}}
|
|
||||||
></div>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
setIsAlert({
|
|
||||||
isShow: false,
|
|
||||||
alertMessage: ""
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="finishBtn cancelBtn"
|
|
||||||
>
|
|
||||||
Ok
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
|
|
||||||
{/* this modal is used show this document is already sign */}
|
{/* this modal is used show this document is already sign */}
|
||||||
<ModalUi
|
<ModalUi
|
||||||
isOpen={showAlreadySignDoc.status}
|
isOpen={showAlreadySignDoc.status}
|
||||||
title={"Sign Documents"}
|
title={"Document signed"}
|
||||||
handleClose={() => {
|
handleClose={() => {
|
||||||
setShowAlreadySignDoc({ status: false });
|
setShowAlreadySignDoc({ status: false });
|
||||||
}}
|
}}
|
||||||
@@ -1131,13 +1264,13 @@ function SignYourSelf() {
|
|||||||
<EmailComponent
|
<EmailComponent
|
||||||
isEmail={isEmail}
|
isEmail={isEmail}
|
||||||
pdfUrl={pdfUrl}
|
pdfUrl={pdfUrl}
|
||||||
isCeleb={isCeleb}
|
|
||||||
setIsEmail={setIsEmail}
|
setIsEmail={setIsEmail}
|
||||||
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
||||||
setSuccessEmail={setSuccessEmail}
|
setSuccessEmail={setSuccessEmail}
|
||||||
sender={jsonSender}
|
sender={jsonSender}
|
||||||
setIsAlert={setIsAlert}
|
setIsAlert={setIsAlert}
|
||||||
extUserId={extUserId}
|
extUserId={extUserId}
|
||||||
|
activeMailAdapter={activeMailAdapter}
|
||||||
/>
|
/>
|
||||||
{/* pdf header which contain funish back button */}
|
{/* pdf header which contain funish back button */}
|
||||||
<Header
|
<Header
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import AddRoleModal from "../components/pdf/AddRoleModal";
|
|||||||
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
import PlaceholderCopy from "../components/pdf/PlaceholderCopy";
|
||||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||||
|
import Parse from "parse";
|
||||||
const TemplatePlaceholder = () => {
|
const TemplatePlaceholder = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { templateId } = useParams();
|
const { templateId } = useParams();
|
||||||
@@ -704,35 +705,29 @@ const TemplatePlaceholder = () => {
|
|||||||
Name: pdfDetails[0]?.Name || "",
|
Name: pdfDetails[0]?.Name || "",
|
||||||
Note: pdfDetails[0]?.Note || "",
|
Note: pdfDetails[0]?.Note || "",
|
||||||
Description: pdfDetails[0]?.Description || "",
|
Description: pdfDetails[0]?.Description || "",
|
||||||
SendinOrder: pdfDetails[0]?.SendinOrder || false
|
SendinOrder: pdfDetails[0]?.SendinOrder || false,
|
||||||
|
AutomaticReminders: pdfDetails[0]?.AutomaticReminders,
|
||||||
|
RemindOnceInEvery: parseInt(pdfDetails[0]?.RemindOnceInEvery),
|
||||||
|
NextReminderDate: pdfDetails[0]?.NextReminderDate
|
||||||
};
|
};
|
||||||
|
const updateTemplate = new Parse.Object("contracts_Template");
|
||||||
|
updateTemplate.id = templateId;
|
||||||
|
for (const key in data) {
|
||||||
|
updateTemplate.set(key, data[key]);
|
||||||
|
}
|
||||||
|
await updateTemplate.save(null, {
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
});
|
||||||
|
|
||||||
await axios
|
setIsCreateDocModal(true);
|
||||||
.put(
|
setIsMailSend(true);
|
||||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
const loadObj = {
|
||||||
"_appName"
|
isLoad: false
|
||||||
)}_Template/${templateId}`,
|
};
|
||||||
data,
|
setIsLoading(loadObj);
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
|
||||||
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
setIsCreateDocModal(true);
|
|
||||||
setIsMailSend(true);
|
|
||||||
const loadObj = {
|
|
||||||
isLoad: false
|
|
||||||
};
|
|
||||||
setIsLoading(loadObj);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log("axois err ", err);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
setIsLoading(false);
|
||||||
|
alert("Something went wrong, please try again later.");
|
||||||
console.log("error", e);
|
console.log("error", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1207,7 +1202,6 @@ const TemplatePlaceholder = () => {
|
|||||||
setIsCheckbox(false);
|
setIsCheckbox(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("signerpos", signerPos);
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Title title={"Template"} />
|
<Title title={"Template"} />
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, {
|
||||||
|
useState,
|
||||||
|
useEffect
|
||||||
|
// useRef
|
||||||
|
} from "react";
|
||||||
import { Navigate, useNavigate } from "react-router-dom";
|
import { Navigate, useNavigate } from "react-router-dom";
|
||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import { SaveFileSize } from "../constant/saveFileSize";
|
import { SaveFileSize } from "../constant/saveFileSize";
|
||||||
@@ -8,9 +12,10 @@ import sanitizeFileName from "../primitives/sanitizeFileName";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||||
import Tooltip from "../primitives/Tooltip";
|
import Tooltip from "../primitives/Tooltip";
|
||||||
import { isEnableSubscription } from "../constant/const";
|
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
|
||||||
import { checkIsSubscribed } from "../constant/Utils";
|
import { checkIsSubscribed, handleSendOTP } from "../constant/Utils";
|
||||||
import Upgrade from "../primitives/Upgrade";
|
import Upgrade from "../primitives/Upgrade";
|
||||||
|
import ModalUi from "../primitives/ModalUi";
|
||||||
|
|
||||||
function UserProfile() {
|
function UserProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -26,18 +31,27 @@ function UserProfile() {
|
|||||||
const [percentage, setpercentage] = useState(0);
|
const [percentage, setpercentage] = useState(0);
|
||||||
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
||||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||||
|
// const [publicUserName, setPublicUserName] = useState(
|
||||||
|
// extendUser && extendUser?.[0]?.UserName
|
||||||
|
// );
|
||||||
|
// const previousPublicUserName = useRef(publicUserName);
|
||||||
const [company, setCompany] = useState(
|
const [company, setCompany] = useState(
|
||||||
extendUser && extendUser?.[0]?.Company
|
extendUser && extendUser?.[0]?.Company
|
||||||
);
|
);
|
||||||
const [jobTitle, setJobTitle] = useState(
|
const [jobTitle, setJobTitle] = useState(
|
||||||
extendUser && extendUser?.[0]?.JobTitle
|
extendUser && extendUser?.[0]?.JobTitle
|
||||||
);
|
);
|
||||||
|
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||||
|
const [otp, setOtp] = useState("");
|
||||||
|
const [otpLoader, setOtpLoader] = useState(false);
|
||||||
|
const [isEmailVerified, setIsEmailVerified] = useState(false);
|
||||||
|
// const [userNameError, setUserNameError] = useState("");
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getUserDetail();
|
getUserDetail();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getUserDetail = async () => {
|
const getUserDetail = async () => {
|
||||||
|
setIsLoader(true);
|
||||||
const extClass = localStorage.getItem("Extand_Class");
|
const extClass = localStorage.getItem("Extand_Class");
|
||||||
const jsonSender = JSON.parse(extClass);
|
const jsonSender = JSON.parse(extClass);
|
||||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||||
@@ -48,47 +62,93 @@ function UserProfile() {
|
|||||||
if (HeaderDocId) {
|
if (HeaderDocId) {
|
||||||
setIsDisableDocId(HeaderDocId);
|
setIsDisableDocId(HeaderDocId);
|
||||||
}
|
}
|
||||||
|
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
|
||||||
|
let isEmailVerified = currentUser?.emailVerified || false;
|
||||||
|
if (isEmailVerified) {
|
||||||
|
setIsEmailVerified(isEmailVerified);
|
||||||
|
setIsLoader(false);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
const user = await userQuery.get(currentUser.objectId, {
|
||||||
|
sessionToken: localStorage.getItem("accesstoken")
|
||||||
|
});
|
||||||
|
if (user) {
|
||||||
|
isEmailVerified = user?.get("emailVerified");
|
||||||
|
setIsEmailVerified(isEmailVerified);
|
||||||
|
setIsLoader(false);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert("something went wrong!");
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
//function to check public username already exist
|
||||||
|
// const handleCheckPublicUserName = async () => {
|
||||||
|
// try {
|
||||||
|
// const res = await Parse.Cloud.run("getpublicusername", {
|
||||||
|
// username: publicUserName
|
||||||
|
// });
|
||||||
|
// if (res) {
|
||||||
|
// setIsLoader(false);
|
||||||
|
// setUserNameError("user name already exist");
|
||||||
|
// setTimeout(() => {
|
||||||
|
// setUserNameError("");
|
||||||
|
// }, 3000);
|
||||||
|
// return res;
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// console.log("error in getpublicusername cloud function");
|
||||||
|
// }
|
||||||
|
// };
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoader(true);
|
let phn = Phone,
|
||||||
let phn = Phone;
|
res = "";
|
||||||
|
//condition to call cloud function when user change publicUserName
|
||||||
try {
|
// if (previousPublicUserName.current !== publicUserName) {
|
||||||
const tour = Parse.Object.extend("_User");
|
// res = await handleCheckPublicUserName();
|
||||||
const query = new Parse.Query(tour);
|
// }
|
||||||
|
if (!res) {
|
||||||
await query.get(UserProfile.objectId).then((object) => {
|
setIsLoader(true);
|
||||||
object.set("name", name);
|
try {
|
||||||
object.set("ProfilePic", Image);
|
const userQuery = Parse.Object.extend("_User");
|
||||||
object.set("phone", phn);
|
const query = new Parse.Query(userQuery);
|
||||||
|
await query.get(UserProfile.objectId).then((object) => {
|
||||||
object.save().then(
|
object.set("name", name);
|
||||||
async (response) => {
|
object.set("ProfilePic", Image);
|
||||||
if (response) {
|
object.set("phone", phn || "");
|
||||||
let res = response.toJSON();
|
object.save().then(
|
||||||
let rr = JSON.stringify(res);
|
async (response) => {
|
||||||
localStorage.setItem("UserInformation", rr);
|
if (response) {
|
||||||
SetName(res.name);
|
let res = response.toJSON();
|
||||||
SetPhone(res.phone);
|
let rr = JSON.stringify(res);
|
||||||
setImage(res.ProfilePic);
|
localStorage.setItem("UserInformation", rr);
|
||||||
localStorage.setItem("username", res.name);
|
SetName(res.name);
|
||||||
localStorage.setItem("profileImg", res.ProfilePic);
|
SetPhone(res?.phone || "");
|
||||||
await updateExtUser({ Name: res.name, Phone: res.phone });
|
setImage(res.ProfilePic);
|
||||||
alert("Profile updated successfully.");
|
localStorage.setItem("username", res.name);
|
||||||
setEditMode(false);
|
localStorage.setItem("profileImg", res.ProfilePic);
|
||||||
navigate("/dashboard/35KBoSgoAK");
|
await updateExtUser({
|
||||||
|
Name: res.name,
|
||||||
|
Phone: res?.phone || ""
|
||||||
|
});
|
||||||
|
alert("Profile updated successfully.");
|
||||||
|
setEditMode(false);
|
||||||
|
setIsLoader(false);
|
||||||
|
//navigate("/dashboard/35KBoSgoAK");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
alert("Something went wrong.");
|
||||||
|
console.error("Error while updating tour", error);
|
||||||
|
setIsLoader(false);
|
||||||
}
|
}
|
||||||
},
|
);
|
||||||
(error) => {
|
});
|
||||||
alert("Something went wrong.");
|
} catch (error) {
|
||||||
console.error("Error while updating tour", error);
|
console.log("err", error);
|
||||||
setIsLoader(false);
|
}
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log("err", error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,14 +157,15 @@ function UserProfile() {
|
|||||||
const extClass = localStorage.getItem("extended_class");
|
const extClass = localStorage.getItem("extended_class");
|
||||||
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||||
const ExtUserId = extData[0].objectId;
|
const ExtUserId = extData[0].objectId;
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
Phone: obj.Phone,
|
Phone: obj?.Phone || "",
|
||||||
Name: obj.Name,
|
Name: obj.Name,
|
||||||
HeaderDocId: isDisableDocId,
|
HeaderDocId: isDisableDocId,
|
||||||
JobTitle: jobTitle,
|
JobTitle: jobTitle,
|
||||||
Company: company
|
Company: company
|
||||||
|
// UserName: publicUserName || ""
|
||||||
};
|
};
|
||||||
|
|
||||||
await axios.put(
|
await axios.put(
|
||||||
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
||||||
body,
|
body,
|
||||||
@@ -122,7 +183,6 @@ function UserProfile() {
|
|||||||
|
|
||||||
const json = JSON.parse(JSON.stringify([res]));
|
const json = JSON.parse(JSON.stringify([res]));
|
||||||
const extRes = JSON.stringify(json);
|
const extRes = JSON.stringify(json);
|
||||||
|
|
||||||
localStorage.setItem("Extand_Class", extRes);
|
localStorage.setItem("Extand_Class", extRes);
|
||||||
// console.log("updateRes ", updateRes);
|
// console.log("updateRes ", updateRes);
|
||||||
};
|
};
|
||||||
@@ -177,6 +237,66 @@ function UserProfile() {
|
|||||||
const handleDisableDocId = () => {
|
const handleDisableDocId = () => {
|
||||||
setIsDisableDocId((prevChecked) => !prevChecked);
|
setIsDisableDocId((prevChecked) => !prevChecked);
|
||||||
};
|
};
|
||||||
|
//`handleVerifyBtn` function is used to send otp on user mail
|
||||||
|
const handleVerifyBtn = async () => {
|
||||||
|
setIsVerifyModal(true);
|
||||||
|
await handleSendOTP(Parse.User.current().getEmail());
|
||||||
|
};
|
||||||
|
const handleCloseVerifyModal = async () => {
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
};
|
||||||
|
//`handleVerifyEmail` function is used to verify email with otp
|
||||||
|
const handleVerifyEmail = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
try {
|
||||||
|
const resEmail = await Parse.Cloud.run("verifyemail", {
|
||||||
|
otp: otp,
|
||||||
|
email: Parse.User.current().getEmail()
|
||||||
|
});
|
||||||
|
if (resEmail?.message === "Email is verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
} else if (resEmail?.message === "Email is already verified.") {
|
||||||
|
setIsEmailVerified(true);
|
||||||
|
}
|
||||||
|
setOtp("");
|
||||||
|
alert(resEmail.message);
|
||||||
|
setIsVerifyModal(false);
|
||||||
|
} catch (error) {
|
||||||
|
alert(error.message);
|
||||||
|
} finally {
|
||||||
|
setOtpLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//function to use resend otp for email verification
|
||||||
|
const handleResend = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setOtpLoader(true);
|
||||||
|
await handleSendOTP();
|
||||||
|
setOtpLoader(false);
|
||||||
|
alert("OTP sent on you email");
|
||||||
|
};
|
||||||
|
//function to handle onchange username and restrict 6-characters username for free users
|
||||||
|
// const handleOnchangeUserName = (e) => {
|
||||||
|
// const value = e.target.value;
|
||||||
|
// if (value.length > 6 && !isSubscribe) {
|
||||||
|
// setUserNameError("Please upgrade to allow more than 6 characters.");
|
||||||
|
// setTimeout(() => {
|
||||||
|
// setUserNameError("");
|
||||||
|
// }, 2000);
|
||||||
|
// } else {
|
||||||
|
// setPublicUserName(e.target.value);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
const handleCancel = () => {
|
||||||
|
setEditMode(false);
|
||||||
|
SetName(localStorage.getItem("username"));
|
||||||
|
SetPhone(UserProfile && UserProfile.phone);
|
||||||
|
setImage(localStorage.getItem("profileImg"));
|
||||||
|
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
|
||||||
|
setCompany(extendUser && extendUser?.[0]?.Company);
|
||||||
|
setJobTitle(extendUser?.[0]?.JobTitle);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Title title={"Profile"} />
|
<Title title={"Profile"} />
|
||||||
@@ -198,7 +318,14 @@ function UserProfile() {
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex justify-center items-center w-full">
|
<div className="flex justify-center items-center w-full relative">
|
||||||
|
{/* {userNameError && (
|
||||||
|
<div
|
||||||
|
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
|
||||||
|
>
|
||||||
|
{userNameError}
|
||||||
|
</div>
|
||||||
|
)} */}
|
||||||
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
|
<div className="bg-white flex flex-col justify-center shadow rounded w-[450px]">
|
||||||
<div className="flex flex-col justify-center items-center my-4">
|
<div className="flex flex-col justify-center items-center my-4">
|
||||||
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
||||||
@@ -310,11 +437,53 @@ function UserProfile() {
|
|||||||
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||||
<span className="font-semibold">Is Email verified:</span>{" "}
|
<span className="font-semibold">Is Email verified:</span>{" "}
|
||||||
<span>
|
<span>
|
||||||
{UserProfile && UserProfile.emailVerified
|
{isEmailVerified ? (
|
||||||
? "Verified"
|
"Verified"
|
||||||
: "Not verified"}
|
) : (
|
||||||
|
<span>
|
||||||
|
Not verified(
|
||||||
|
<span
|
||||||
|
onClick={() => handleVerifyBtn()}
|
||||||
|
className="hover:underline text-blue-600 cursor-pointer"
|
||||||
|
>
|
||||||
|
verify
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
{/* {isEnableSubscription && (
|
||||||
|
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||||
|
<span className="font-semibold">
|
||||||
|
Public profile :{" "}
|
||||||
|
<Tooltip
|
||||||
|
message={`this is your public URL. Copy or share it
|
||||||
|
with the signer, and you will be able to see
|
||||||
|
all your publicly set templates.`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span>opensign-me.vercel.app/</span>
|
||||||
|
{editmode ? (
|
||||||
|
<input
|
||||||
|
onChange={handleOnchangeUserName}
|
||||||
|
value={publicUserName}
|
||||||
|
disabled={!editmode}
|
||||||
|
placeholder="enter user name"
|
||||||
|
className="border-[1px] border-gray-200 rounded-[3px]"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
value={extendUser?.[0]?.UserName}
|
||||||
|
disabled
|
||||||
|
placeholder="enter user name"
|
||||||
|
className="border-[1px] border-gray-200 rounded-[3px]"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)} */}
|
||||||
<li className="border-y-[1px] border-gray-300 break-all">
|
<li className="border-y-[1px] border-gray-300 break-all">
|
||||||
<div className="flex justify-between items-center py-2">
|
<div className="flex justify-between items-center py-2">
|
||||||
<span
|
<span
|
||||||
@@ -329,7 +498,6 @@ function UserProfile() {
|
|||||||
url={
|
url={
|
||||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||||
}
|
}
|
||||||
isSubscribe={isSubscribe}
|
|
||||||
/>{" "}
|
/>{" "}
|
||||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
@@ -386,7 +554,7 @@ function UserProfile() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (editmode) {
|
if (editmode) {
|
||||||
setEditMode(false);
|
handleCancel();
|
||||||
} else {
|
} else {
|
||||||
navigate("/changepassword");
|
navigate("/changepassword");
|
||||||
}
|
}
|
||||||
@@ -399,6 +567,60 @@ function UserProfile() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isVerifyModal && (
|
||||||
|
<ModalUi
|
||||||
|
isOpen
|
||||||
|
title={"OTP verification"}
|
||||||
|
handleClose={handleCloseVerifyModal}
|
||||||
|
>
|
||||||
|
{otpLoader ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "150px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "45px",
|
||||||
|
color: "#3dd3e0"
|
||||||
|
}}
|
||||||
|
className="loader-37"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={(e) => handleVerifyEmail(e)}>
|
||||||
|
<div className="px-6 py-3">
|
||||||
|
<label className="mb-2">Enter OTP</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="tel"
|
||||||
|
pattern="[0-9]{4}"
|
||||||
|
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||||
|
placeholder="Enter OTP received over email"
|
||||||
|
value={otp}
|
||||||
|
onChange={(e) => setOtp(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
<div className="px-6 my-3">
|
||||||
|
<button type="submit" className={submitBtn}>
|
||||||
|
Verify
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`${rejectBtn} ml-2`}
|
||||||
|
onClick={(e) => handleResend(e)}
|
||||||
|
>
|
||||||
|
Resend
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</ModalUi>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|||||||
+12
-10
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import Parse from "parse";
|
import Parse from "parse";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import "../styles/AddUser.css";
|
import "../styles/AddUser.css";
|
||||||
const AddUser = (props) => {
|
const AddContact = (props) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
@@ -21,7 +21,7 @@ const AddUser = (props) => {
|
|||||||
);
|
);
|
||||||
if (savedUserDetails && addYourself) {
|
if (savedUserDetails && addYourself) {
|
||||||
setName(savedUserDetails.name);
|
setName(savedUserDetails.name);
|
||||||
setPhone(savedUserDetails.phone);
|
setPhone(savedUserDetails?.phone || "");
|
||||||
setEmail(savedUserDetails.email);
|
setEmail(savedUserDetails.email);
|
||||||
}
|
}
|
||||||
}, [addYourself]);
|
}, [addYourself]);
|
||||||
@@ -50,7 +50,9 @@ const AddUser = (props) => {
|
|||||||
try {
|
try {
|
||||||
const contactQuery = new Parse.Object("contracts_Contactbook");
|
const contactQuery = new Parse.Object("contracts_Contactbook");
|
||||||
contactQuery.set("Name", name);
|
contactQuery.set("Name", name);
|
||||||
contactQuery.set("Phone", phone);
|
if (phone) {
|
||||||
|
contactQuery.set("Phone", phone);
|
||||||
|
}
|
||||||
contactQuery.set("Email", email);
|
contactQuery.set("Email", email);
|
||||||
contactQuery.set("UserRole", "contracts_Guest");
|
contactQuery.set("UserRole", "contracts_Guest");
|
||||||
|
|
||||||
@@ -68,8 +70,10 @@ const AddUser = (props) => {
|
|||||||
_user.set("name", name);
|
_user.set("name", name);
|
||||||
_user.set("username", email);
|
_user.set("username", email);
|
||||||
_user.set("email", email);
|
_user.set("email", email);
|
||||||
_user.set("phone", phone);
|
_user.set("password", email);
|
||||||
_user.set("password", phone);
|
if (phone) {
|
||||||
|
_user.set("phone", phone);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await _user.save();
|
const user = await _user.save();
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -245,23 +249,21 @@ const AddUser = (props) => {
|
|||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||||
required
|
required
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="addUserInput"
|
className="addUserInput lowercase"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<label htmlFor="phone" style={{ fontSize: 13 }}>
|
<label htmlFor="phone" style={{ fontSize: 13 }}>
|
||||||
Phone
|
Phone
|
||||||
<span style={{ color: "red", fontSize: 13 }}> *</span>
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="phone"
|
id="phone"
|
||||||
value={phone}
|
value={phone}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
onChange={(e) => setPhone(e.target.value)}
|
||||||
required
|
|
||||||
disabled={addYourself}
|
disabled={addYourself}
|
||||||
className="addUserInput"
|
className="addUserInput"
|
||||||
/>
|
/>
|
||||||
@@ -285,4 +287,4 @@ const AddUser = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddUser;
|
export default AddContact;
|
||||||
@@ -18,7 +18,7 @@ const Alert = ({ children, type }) => {
|
|||||||
<>
|
<>
|
||||||
{children && (
|
{children && (
|
||||||
<div
|
<div
|
||||||
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] z-50`}
|
className={`z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] z-50`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import SelectSigners from "../components/shared/fields/SelectSigners";
|
import SelectSigners from "../components/shared/fields/SelectSigners";
|
||||||
import AddUser from "./AddUser";
|
import AddContact from "./AddContact";
|
||||||
import ModalUi from "./ModalUi";
|
import ModalUi from "./ModalUi";
|
||||||
|
|
||||||
const LinkUserModal = (props) => {
|
const LinkUserModal = (props) => {
|
||||||
@@ -43,7 +43,7 @@ const LinkUserModal = (props) => {
|
|||||||
}}
|
}}
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<AddUser details={props.handleAddUser} closePopup={props.closePopup} />
|
<AddContact details={props.handleAddUser} closePopup={props.closePopup} />
|
||||||
</ModalUi>
|
</ModalUi>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,19 +10,17 @@ const ModalUi = ({
|
|||||||
handleClose,
|
handleClose,
|
||||||
showHeader = true,
|
showHeader = true,
|
||||||
showClose = true,
|
showClose = true,
|
||||||
styleClass,
|
reduceWidth,
|
||||||
showHeaderMessage
|
showHeaderMessage
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="fixed z-[999] 2 top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
||||||
<div
|
<div
|
||||||
className={
|
className={`${
|
||||||
styleClass
|
reduceWidth || "md:min-w-[500px]"
|
||||||
? styleClass
|
} fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] overflow-y-auto hide-scrollbar `}
|
||||||
: "fixed z-[1000] top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-sm bg-white rounded shadow-md max-h-90 min-w-[90%] md:min-w-[500px] overflow-y-auto hide-scrollbar"
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{showHeader && (
|
{showHeader && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||||
import { openInNewTab } from "../constant/Utils";
|
import { openInNewTab } from "../constant/Utils";
|
||||||
import { isEnableSubscription } from "../constant/const";
|
const Tooltip = ({ id, message, url, iconColor }) =>
|
||||||
const Tooltip = ({ id, message, url, iconColor, isSubscribe }) =>
|
|
||||||
url ? (
|
url ? (
|
||||||
<button
|
<button onClick={() => openInNewTab(url)} className={"text-center"}>
|
||||||
onClick={() => openInNewTab(url)}
|
|
||||||
className={
|
|
||||||
isSubscribe || !isEnableSubscription
|
|
||||||
? "text-center"
|
|
||||||
: "text-center opacity-20 pointer-events-none"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<sup>
|
<sup>
|
||||||
<i
|
<i
|
||||||
className="fa-solid fa-question rounded-full"
|
className="fa-solid fa-question rounded-full"
|
||||||
|
|||||||
@@ -1,20 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import { openInNewTab } from "../constant/Utils";
|
import { openInNewTab } from "../constant/Utils";
|
||||||
|
|
||||||
function Upgrade({ message, newWindow }) {
|
function Upgrade({ message }) {
|
||||||
const navigation = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<sup>
|
<sup>
|
||||||
<span
|
<span
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (newWindow) {
|
const url = window.location.origin + "/subscription";
|
||||||
const url = window.location.origin + "/subscription";
|
openInNewTab(url);
|
||||||
openInNewTab(url);
|
|
||||||
} else {
|
|
||||||
navigation("/subscription");
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
className="text-blue-800 text-sm cursor-pointer underline"
|
className="text-blue-800 text-sm cursor-pointer underline"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,197 +0,0 @@
|
|||||||
.loginInput {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
border: 1px solid #e2dddd;
|
|
||||||
background-color: #e0e5f5;
|
|
||||||
color: black;
|
|
||||||
padding-left: 1rem !important;
|
|
||||||
padding-right: 1rem !important;
|
|
||||||
padding-top: 0.5rem !important;
|
|
||||||
font-family: inherit;
|
|
||||||
font-feature-settings: inherit;
|
|
||||||
font-variation-settings: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
.loginInput:focus {
|
|
||||||
outline: none;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.verifyBtn {
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
|
||||||
padding: 3px 30px;
|
|
||||||
color: white;
|
|
||||||
font-weight: 500 !important;
|
|
||||||
font-size: 14px !important;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.verifyBtn:focus {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.verifyBtn:hover {
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.fakeimg {
|
|
||||||
height: 200px;
|
|
||||||
background: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GTRY {
|
|
||||||
border: 1px solid #dee2e6;
|
|
||||||
margin-top: 75px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main_head {
|
|
||||||
width: 100%;
|
|
||||||
height: 66px;
|
|
||||||
background-color: #fff;
|
|
||||||
margin-bottom: 52px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-logo {
|
|
||||||
width: 250px;
|
|
||||||
height: 66px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-box {
|
|
||||||
background: #fff;
|
|
||||||
min-height: 50px;
|
|
||||||
box-shadow: 0 20px 20px rgba(0, 0, 0, 0.1);
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
transition: 0.5s;
|
|
||||||
border: 1px solid #f2f2f2;
|
|
||||||
border-radius: 7px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.LKJH {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.GTR {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.JUI {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #15b4e9;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-info:hover {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #15b4e9;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-reg {
|
|
||||||
color: #15b4e9;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-reg:hover {
|
|
||||||
color: #15b4e9;
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-color: #15b4e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.SYTU {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI {
|
|
||||||
float: right;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 100% !important;
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 667px) {
|
|
||||||
.MLKI {
|
|
||||||
float: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-btn {
|
|
||||||
width: 210px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI a {
|
|
||||||
color: #212529;
|
|
||||||
}
|
|
||||||
|
|
||||||
.KNLO {
|
|
||||||
color: #878787;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-check-label {
|
|
||||||
margin-left: 17px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
.welcomeText {
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 600px) {
|
|
||||||
.KLO1 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MLKI a {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-check-label {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.MKUY {
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main_head {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
padding-top: 10px;
|
|
||||||
border-bottom: 1px dashed #eeeeee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 366px) {
|
|
||||||
.main-logo {
|
|
||||||
width: 150px;
|
|
||||||
height: 46px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.welcomeText {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
.KNLO {
|
|
||||||
color: #878787;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
.btnContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
const appId = process.env.APP_ID;
|
const appId = process.env.APP_ID;
|
||||||
const serverUrl = process.env.SERVER_URL;
|
const serverUrl = process.env.SERVER_URL;
|
||||||
@@ -243,3 +245,5 @@ export function sanitizeFileName(fileName) {
|
|||||||
const removedot = file.replace(/\.(?=.*\.)/g, '');
|
const removedot = file.replace(/\.(?=.*\.)/g, '');
|
||||||
return removedot.replace(/[^a-zA-Z0-9._-]/g, '');
|
return removedot.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useLocal = process.env.USE_LOCAL ? process.env.USE_LOCAL.toLowerCase() : 'false';
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
dotenv.config();
|
||||||
|
const ssoApiUrl = process.env.SSO_API_URL || 'https://sso.opensignlabs.com/api'; //'https://osl-jacksonv2.vercel.app/api';
|
||||||
|
export const SSOAuth = {
|
||||||
|
// Returns a promise that fulfills if this user mail is valid.
|
||||||
|
validateAuthData: async authData => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(ssoApiUrl + '/oauth/userinfo', {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${authData.access_token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (response.data && response.data.id && response.data.email === authData.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error in sso adapter', error?.response);
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Returns a promise that fulfills if this app id is valid.
|
||||||
|
validateAppId: () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
// `replaceMailVaribles` is used to replace variables from mail with there actual values
|
||||||
|
function replaceMailVaribles(subject, body, variables) {
|
||||||
|
let replacedSubject = subject;
|
||||||
|
let replacedBody = body;
|
||||||
|
|
||||||
|
for (const variable in variables) {
|
||||||
|
const regex = new RegExp(`{{${variable}}}`, 'g');
|
||||||
|
if (subject) {
|
||||||
|
replacedSubject = replacedSubject.replace(regex, variables[variable]);
|
||||||
|
}
|
||||||
|
if (body) {
|
||||||
|
replacedBody = replacedBody.replace(regex, variables[variable]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
subject: replacedSubject,
|
||||||
|
body: replacedBody,
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `sendmail` is used to signing reminder mail to signer
|
||||||
|
async function sendMail(doc, signer) {
|
||||||
|
const subject = `{{sender_name}} has requested you to sign "{{document_title}}"`;
|
||||||
|
const body = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}} has requested you to review and sign <b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`;
|
||||||
|
const url = `${process.env.SERVER_URL}/functions/sendmailv3`;
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': process.env.APP_ID,
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
|
const encodeBase64 = btoa(`${doc.objectId}/${signer.Email}/${signer.objectId}`);
|
||||||
|
const expireDate = doc?.ExpiryDate?.iso;
|
||||||
|
const newDate = new Date(expireDate);
|
||||||
|
const localExpireDate = newDate.toLocaleDateString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
});
|
||||||
|
const signPdf = `${baseUrl.origin}/login/${encodeBase64}`;
|
||||||
|
const variables = {
|
||||||
|
document_title: doc.Name,
|
||||||
|
sender_name: doc.ExtUserPtr.Name,
|
||||||
|
sender_mail: doc.ExtUserPtr.Email,
|
||||||
|
sender_phone: doc.ExtUserPtr?.Phone || '',
|
||||||
|
receiver_name: signer.Name,
|
||||||
|
receiver_email: signer.Email,
|
||||||
|
receiver_phone: signer?.Phone || '',
|
||||||
|
expiry_date: localExpireDate,
|
||||||
|
company_name: doc?.ExtUserPtr?.Company || '',
|
||||||
|
signing_url: `<a href=${signPdf}>Sign here</a>`,
|
||||||
|
};
|
||||||
|
const mail = replaceMailVaribles(subject, body, variables);
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
|
||||||
|
extUserId: doc?.ExtUserPtr?.objectId,
|
||||||
|
recipient: signer.Email,
|
||||||
|
subject: mail.subject,
|
||||||
|
from: doc?.ExtUserPtr?.Email,
|
||||||
|
html: mail.body,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
// The axios request is used to send a signing reminder email.
|
||||||
|
const res = await axios.post(url, params, { headers: headers });
|
||||||
|
// console.log('res ', res.data.result);
|
||||||
|
if (res.data.result.status === 'success') {
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in mail of sendreminder api', err);
|
||||||
|
return { status: 'error' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default async function autoReminder(request, response) {
|
||||||
|
// The query below is used to find documents where the reminder date is less than or equal to the current date, and which have existing signers and a signed URL.
|
||||||
|
try {
|
||||||
|
const docQuery = new Parse.Query('contracts_Document');
|
||||||
|
docQuery.limit(2000);
|
||||||
|
docQuery.lessThanOrEqualTo('NextReminderDate', new Date());
|
||||||
|
docQuery.equalTo('AutomaticReminders', true);
|
||||||
|
docQuery.exists('NextReminderDate');
|
||||||
|
docQuery.exists('Signers');
|
||||||
|
docQuery.exists('SignedUrl');
|
||||||
|
docQuery.descending('createdAt');
|
||||||
|
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr');
|
||||||
|
docQuery.notEqualTo('IsCompleted', true);
|
||||||
|
docQuery.notEqualTo('IsDeclined', true);
|
||||||
|
docQuery.notEqualTo('IsArchive', true);
|
||||||
|
docQuery.greaterThanOrEqualTo('ExpiryDate', new Date());
|
||||||
|
|
||||||
|
const docsArr = await docQuery.find({ useMasterKey: true });
|
||||||
|
|
||||||
|
if (docsArr && docsArr.length > 0) {
|
||||||
|
const _docsArr = JSON.parse(JSON.stringify(docsArr));
|
||||||
|
let mailCount = 0;
|
||||||
|
let docCount = 0;
|
||||||
|
for (const doc of _docsArr) {
|
||||||
|
// The reminderDate variable is used to calculate the next reminder date.
|
||||||
|
const RemindOnceInEvery = doc?.RemindOnceInEvery || 5;
|
||||||
|
const ReminderDate = new Date();
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
// The sendInOrder variable is used to determine whether to send emails in order or not.
|
||||||
|
const SendInOrder = doc?.SendinOrder || false;
|
||||||
|
if (SendInOrder) {
|
||||||
|
// The auditTrail variable is used to get the count of how many users have already signed the document.
|
||||||
|
const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
|
||||||
|
const count = auditTrail?.length || 0;
|
||||||
|
const signer = doc?.Signers?.[count];
|
||||||
|
if (signer) {
|
||||||
|
docCount += 1;
|
||||||
|
const mailRes = await sendMail(doc, signer);
|
||||||
|
if (mailRes && mailRes.status === 'success') {
|
||||||
|
mailCount += 1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = doc.objectId;
|
||||||
|
updateDoc.set('NextReminderDate', ReminderDate);
|
||||||
|
const updateRes = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
// console.log('updateRes ', updateRes);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in update document', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The AuditTrail variable is used to check if there is any user who has already signed the document.
|
||||||
|
const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
|
||||||
|
if (auditTrail?.length > 0) {
|
||||||
|
// The signers variable is used to get the signers who haven't signed the document.
|
||||||
|
const signers = doc?.Signers.filter(signer => {
|
||||||
|
const signedUser = auditTrail?.find(y => y.UserPtr.objectId === signer.objectId);
|
||||||
|
if (!signedUser) {
|
||||||
|
return signer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (signers?.length > 0) {
|
||||||
|
docCount += 1;
|
||||||
|
// The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
|
||||||
|
for (const signer of signers) {
|
||||||
|
const mailRes = await sendMail(doc, signer);
|
||||||
|
if (mailRes && mailRes.status === 'success') {
|
||||||
|
mailCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
|
||||||
|
const signers = doc?.Signers;
|
||||||
|
if (signers?.length > 0) {
|
||||||
|
docCount += 1;
|
||||||
|
for (const signer of signers) {
|
||||||
|
const mailRes = await sendMail(doc, signer);
|
||||||
|
if (mailRes && mailRes.status === 'success') {
|
||||||
|
mailCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
|
||||||
|
try {
|
||||||
|
const updateDoc = new Parse.Object('contracts_Document');
|
||||||
|
updateDoc.id = doc.objectId;
|
||||||
|
updateDoc.set('NextReminderDate', ReminderDate);
|
||||||
|
const updateRes = await updateDoc.save(null, { useMasterKey: true });
|
||||||
|
// console.log('updateRes ', updateRes);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in sendmail', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (docCount > 0) {
|
||||||
|
response.json({ status: 'success', document_count: docCount, mail_count: mailCount });
|
||||||
|
} else {
|
||||||
|
response.json({ status: 'no record found' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response.json({ status: 'no record found' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
const code = err?.code || 400;
|
||||||
|
const message = err?.message || 'Somehting went wrong!';
|
||||||
|
response.status(code).json({ error: message });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,8 @@ import uploadFile from './uploadFile.js';
|
|||||||
import saveSubscription from './saveSubscription.js';
|
import saveSubscription from './saveSubscription.js';
|
||||||
import saveInvoice from './saveInvoice.js';
|
import saveInvoice from './saveInvoice.js';
|
||||||
import savePayments from './savePayments.js';
|
import savePayments from './savePayments.js';
|
||||||
|
import gooogleauth from './googleauth.js';
|
||||||
|
import autoReminder from './autoReminder.js';
|
||||||
export const app = express();
|
export const app = express();
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
@@ -15,8 +17,8 @@ app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
|||||||
|
|
||||||
app.post('/file_upload', uploadFile);
|
app.post('/file_upload', uploadFile);
|
||||||
|
|
||||||
app.post('/savesubscription', saveSubscription)
|
app.post('/savesubscription', saveSubscription);
|
||||||
app.post('/saveinvoice', saveInvoice)
|
app.post('/saveinvoice', saveInvoice);
|
||||||
app.post('/savepayment', savePayments)
|
app.post('/savepayment', savePayments);
|
||||||
|
app.post('/googleauth', gooogleauth);
|
||||||
|
app.post('/sendreminder', autoReminder);
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
const appId = process.env.APP_ID;
|
||||||
|
const serverUrl = process.env.SERVER_URL;
|
||||||
|
export default async function gooogleauth(request, response) {
|
||||||
|
const code = request.body.code;
|
||||||
|
const baseUrl = new URL(process.env.SERVER_URL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||||
|
headers: {
|
||||||
|
'X-Parse-Application-Id': appId,
|
||||||
|
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const userId = userRes.data && userRes.data.objectId;
|
||||||
|
if (userId) {
|
||||||
|
const clientId = process.env.GOOGLE_CLIENT_ID;
|
||||||
|
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
||||||
|
const redirectUri =
|
||||||
|
baseUrl?.hostname === 'localhost'
|
||||||
|
? 'http://localhost:3000'
|
||||||
|
: 'https://console.opensignlabs.com'; // Should match the redirect URI used in the authorization request
|
||||||
|
const tokenEndpoint = 'https://oauth2.googleapis.com/token';
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('code', code);
|
||||||
|
params.append('client_id', clientId);
|
||||||
|
params.append('client_secret', clientSecret);
|
||||||
|
params.append('redirect_uri', redirectUri);
|
||||||
|
params.append('grant_type', 'authorization_code');
|
||||||
|
|
||||||
|
const res = await axios.post(tokenEndpoint, params);
|
||||||
|
// console.log('oauthres ', res.data);
|
||||||
|
const refresh_token = res.data.refresh_token;
|
||||||
|
const extUserCls = new Parse.Query('contracts_Users');
|
||||||
|
extUserCls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||||
|
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
if (extUser) {
|
||||||
|
const extUserCls = new Parse.Object('contracts_Users');
|
||||||
|
extUserCls.id = extUser.id;
|
||||||
|
extUserCls.set('google_refresh_token', refresh_token);
|
||||||
|
extUserCls.set('active_mail_adapter', 'google');
|
||||||
|
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
|
||||||
|
// console.log('updateExtUser ', updateExtUser);
|
||||||
|
}
|
||||||
|
return response.status(200).json({ status: 'success' });
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ message: 'user not found!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in google auth', err);
|
||||||
|
if (err?.response?.data?.error) {
|
||||||
|
return response.status(404).json({ message: err.response.data.error });
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ message: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import multer from 'multer';
|
|||||||
import multerS3 from 'multer-s3';
|
import multerS3 from 'multer-s3';
|
||||||
import aws from 'aws-sdk';
|
import aws from 'aws-sdk';
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
function sanitizeFileName(fileName) {
|
function sanitizeFileName(fileName) {
|
||||||
@@ -51,7 +52,7 @@ async function uploadFile(req, res) {
|
|||||||
const parseBaseUrl = process.env.SERVER_URL;
|
const parseBaseUrl = process.env.SERVER_URL;
|
||||||
const parseAppId = process.env.APP_ID;
|
const parseAppId = process.env.APP_ID;
|
||||||
let fileStorage;
|
let fileStorage;
|
||||||
if (process.env.USE_LOCAL == 'TRUE') {
|
if (useLocal === 'true') {
|
||||||
fileStorage = multer.diskStorage({
|
fileStorage = multer.diskStorage({
|
||||||
destination: function (req, file, cb) {
|
destination: function (req, file, cb) {
|
||||||
cb(null, 'files/files');
|
cb(null, 'files/files');
|
||||||
@@ -147,7 +148,7 @@ async function uploadFile(req, res) {
|
|||||||
const status = 'Success';
|
const status = 'Success';
|
||||||
//res.header("Access-Control-Allow-Headers", "Content-Type");
|
//res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
//res.setHeader("Access-Control-Allow-Origin", "*");
|
//res.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
if (process.env.USE_LOCAL == 'TRUE') {
|
if (useLocal === 'true') {
|
||||||
console.log(req.file);
|
console.log(req.file);
|
||||||
var fileUrl = `${parseBaseUrl}/files/${parseAppId}/${req.file.filename}`;
|
var fileUrl = `${parseBaseUrl}/files/${parseAppId}/${req.file.filename}`;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import getTemplatetList from './routes/getTemplateList.js';
|
|||||||
import updateTemplate from './routes/updateTemplate.js';
|
import updateTemplate from './routes/updateTemplate.js';
|
||||||
import createContact from './routes/createContact.js';
|
import createContact from './routes/createContact.js';
|
||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
// import fs from 'node:fs';
|
|
||||||
import updateDocument from './routes/updateDocument.js';
|
import updateDocument from './routes/updateDocument.js';
|
||||||
import deleteDocument from './routes/deleteDocument.js';
|
import deleteDocument from './routes/deleteDocument.js';
|
||||||
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
|
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
|
||||||
@@ -26,6 +25,12 @@ import deleteWebhook from './routes/deleteWebhook.js';
|
|||||||
import getWebhook from './routes/getWebhook.js';
|
import getWebhook from './routes/getWebhook.js';
|
||||||
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
|
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
|
||||||
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
|
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
|
||||||
|
import resendMail from './routes/resendMail.js';
|
||||||
|
import getFolder from './routes/getFolder.js';
|
||||||
|
import createFolder from './routes/createFolder.js';
|
||||||
|
import updateFolder from './routes/updateFolder.js';
|
||||||
|
import getFolderList from './routes/getFolderList.js';
|
||||||
|
import deleteFolder from './routes/deleteFolder.js';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
const storage = multer.memoryStorage();
|
const storage = multer.memoryStorage();
|
||||||
const upload = multer({ storage: storage });
|
const upload = multer({ storage: storage });
|
||||||
@@ -102,3 +107,21 @@ app.post('/webhook', saveWebhook);
|
|||||||
|
|
||||||
// set and update webhook
|
// set and update webhook
|
||||||
app.delete('/webhook', deleteWebhook);
|
app.delete('/webhook', deleteWebhook);
|
||||||
|
|
||||||
|
// resend mail
|
||||||
|
app.post('/resendmail', resendMail);
|
||||||
|
|
||||||
|
// create folder
|
||||||
|
app.post('/createfolder', createFolder);
|
||||||
|
|
||||||
|
// update folder
|
||||||
|
app.put('/folder/:folder_id', updateFolder);
|
||||||
|
|
||||||
|
// get folder list
|
||||||
|
app.delete('/folder/:folder_id', deleteFolder);
|
||||||
|
|
||||||
|
// get folder
|
||||||
|
app.get('/folder/:folder_id', getFolder);
|
||||||
|
|
||||||
|
// get folder list
|
||||||
|
app.get('/folderlist', getFolderList);
|
||||||
|
|||||||
@@ -50,11 +50,12 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
const folderId = request.body.folderId;
|
const folderId = request.body.folderId;
|
||||||
const templateId = request.params.template_id;
|
const templateId = request.params.template_id;
|
||||||
const protocol = customAPIurl();
|
const protocol = customAPIurl();
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
const send_email = request.body.send_email;
|
const send_email = request.body.send_email;
|
||||||
const email_subject = request.body.email_subject;
|
const email_subject = request.body.email_subject;
|
||||||
const email_body = request.body.email_body;
|
const email_body = request.body.email_body;
|
||||||
const sendInOrder = request.body.sendInOrder || false;
|
const sendInOrder = request.body.sendInOrder || false;
|
||||||
|
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
@@ -88,12 +89,20 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
|
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
|
||||||
// console.log('isValid ', isValid);
|
// console.log('isValid ', isValid);
|
||||||
if (isValid && updateSigners) {
|
if (isValid && updateSigners) {
|
||||||
|
//Check if every item's placeholders contain at least one placeholder with type 'signature'.
|
||||||
|
let isSignature = template?.Placeholders?.every(item =>
|
||||||
|
item?.placeHolder.some(x => x?.pos.some(data => data?.type === 'signature'))
|
||||||
|
);
|
||||||
|
if (!isSignature) {
|
||||||
|
return response
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'Please add at least one signature widget for all signers' });
|
||||||
|
}
|
||||||
const folderPtr = {
|
const folderPtr = {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: 'contracts_Document',
|
className: 'contracts_Document',
|
||||||
objectId: folderId,
|
objectId: folderId,
|
||||||
};
|
};
|
||||||
const template = JSON.parse(JSON.stringify(templateRes));
|
|
||||||
const object = new Parse.Object('contracts_Document');
|
const object = new Parse.Object('contracts_Document');
|
||||||
object.set('Name', template.Name);
|
object.set('Name', template.Name);
|
||||||
if (template?.Note) {
|
if (template?.Note) {
|
||||||
@@ -169,6 +178,11 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
object.set('Signers', templateSigner);
|
object.set('Signers', templateSigner);
|
||||||
}
|
}
|
||||||
object.set('URL', template.URL);
|
object.set('URL', template.URL);
|
||||||
|
object.set('SignedUrl', template.URL);
|
||||||
|
object.set('SentToOthers', true);
|
||||||
|
if (TimeToCompleteDays) {
|
||||||
|
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||||
|
}
|
||||||
object.set('CreatedBy', template.CreatedBy);
|
object.set('CreatedBy', template.CreatedBy);
|
||||||
object.set('ExtUserPtr', {
|
object.set('ExtUserPtr', {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
@@ -195,9 +209,6 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
});
|
});
|
||||||
let sender = template.ExtUserPtr.Email;
|
let sender = template.ExtUserPtr.Email;
|
||||||
let sendMail;
|
let sendMail;
|
||||||
const serverUrl = process.env.SERVER_URL;
|
|
||||||
const newServer = serverUrl.replaceAll('/', '%2F');
|
|
||||||
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
|
|
||||||
if (send_email === false) {
|
if (send_email === false) {
|
||||||
console.log("don't send mail");
|
console.log("don't send mail");
|
||||||
} else {
|
} else {
|
||||||
@@ -217,9 +228,10 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const objectId = contactMail[i].contactPtr.objectId;
|
const objectId = contactMail[i].contactPtr.objectId;
|
||||||
|
|
||||||
const hostUrl = baseUrl.origin;
|
const hostUrl = baseUrl.origin;
|
||||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
//encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||||
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
|
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
|
||||||
const themeBGcolor = '#47a3ad';
|
const themeBGcolor = '#47a3ad';
|
||||||
@@ -251,10 +263,10 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
document_title: template.Name,
|
document_title: template.Name,
|
||||||
sender_name: template.ExtUserPtr.Name,
|
sender_name: template.ExtUserPtr.Name,
|
||||||
sender_mail: template.ExtUserPtr.Email,
|
sender_mail: template.ExtUserPtr.Email,
|
||||||
sender_phone: template.ExtUserPtr.Phone,
|
sender_phone: template.ExtUserPtr?.Phone || '',
|
||||||
receiver_name: contactMail[i].name,
|
receiver_name: contactMail[i].name,
|
||||||
receiver_email: contactMail[i].email,
|
receiver_email: contactMail[i].email,
|
||||||
receiver_phone: contactMail[i].phone,
|
receiver_phone: contactMail[i]?.phone || '',
|
||||||
expiry_date: localExpireDate,
|
expiry_date: localExpireDate,
|
||||||
company_name: orgName,
|
company_name: orgName,
|
||||||
signing_url: signPdf,
|
signing_url: signPdf,
|
||||||
@@ -266,13 +278,13 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
replaceVar = { subject: replaceVar.subject, body: email_html };
|
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||||
} else if (email_body) {
|
} else if (email_body) {
|
||||||
replaceVar = replaceMailVaribles(
|
replaceVar = replaceMailVaribles(
|
||||||
`${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
|
`${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
|
||||||
email_body,
|
email_body,
|
||||||
variables
|
variables
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replaceVar = {
|
replaceVar = {
|
||||||
subject: `${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
|
subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
|
||||||
body: email_html,
|
body: email_html,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -300,7 +312,11 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
name: template?.Name,
|
name: template?.Name,
|
||||||
note: template?.Note || '',
|
note: template?.Note || '',
|
||||||
description: template?.Description || '',
|
description: template?.Description || '',
|
||||||
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x.phone })),
|
signers: contact?.map(x => ({
|
||||||
|
name: x.name,
|
||||||
|
email: x.email,
|
||||||
|
phone: x?.phone || '',
|
||||||
|
})),
|
||||||
createdAt: res.createdAt,
|
createdAt: res.createdAt,
|
||||||
};
|
};
|
||||||
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
|
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
|
||||||
@@ -320,7 +336,9 @@ export default async function createDocumentWithTemplate(request, response) {
|
|||||||
objectId: res.id,
|
objectId: res.id,
|
||||||
signurl: contact.map(x => ({
|
signurl: contact.map(x => ({
|
||||||
email: x.email,
|
email: x.email,
|
||||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
url: `${baseUrl.origin}/login/${btoa(
|
||||||
|
`${res.id}/${x.email}/${x.contactPtr.objectId}`
|
||||||
|
)}`,
|
||||||
})),
|
})),
|
||||||
message: 'Document sent successfully!',
|
message: 'Document sent successfully!',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default async function createContact(request, response) {
|
|||||||
const serverUrl = process.env.SERVER_URL;
|
const serverUrl = process.env.SERVER_URL;
|
||||||
const appId = process.env.APP_ID;
|
const appId = process.env.APP_ID;
|
||||||
const name = request.body.name;
|
const name = request.body.name;
|
||||||
const phone = request.body.phone;
|
const phone = request.body?.phone;
|
||||||
const email = request.body.email;
|
const email = request.body.email;
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
if (!reqToken) {
|
if (!reqToken) {
|
||||||
@@ -49,7 +49,9 @@ export default async function createContact(request, response) {
|
|||||||
|
|
||||||
const contactQuery = new Parse.Object('contracts_Contactbook');
|
const contactQuery = new Parse.Object('contracts_Contactbook');
|
||||||
contactQuery.set('Name', name);
|
contactQuery.set('Name', name);
|
||||||
contactQuery.set('Phone', phone);
|
if (phone) {
|
||||||
|
contactQuery.set('Phone', phone);
|
||||||
|
}
|
||||||
contactQuery.set('Email', email);
|
contactQuery.set('Email', email);
|
||||||
contactQuery.set('UserRole', 'contracts_Guest');
|
contactQuery.set('UserRole', 'contracts_Guest');
|
||||||
if (tenantRes && tenantRes.id) {
|
if (tenantRes && tenantRes.id) {
|
||||||
@@ -65,8 +67,10 @@ export default async function createContact(request, response) {
|
|||||||
_user.set('name', name);
|
_user.set('name', name);
|
||||||
_user.set('username', email);
|
_user.set('username', email);
|
||||||
_user.set('email', email);
|
_user.set('email', email);
|
||||||
_user.set('phone', phone);
|
_user.set('password', email);
|
||||||
_user.set('password', phone);
|
if (phone) {
|
||||||
|
_user.set('phone', phone);
|
||||||
|
}
|
||||||
|
|
||||||
const user = await _user.save();
|
const user = await _user.save();
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -106,7 +110,7 @@ export default async function createContact(request, response) {
|
|||||||
objectId: parseRes.objectId,
|
objectId: parseRes.objectId,
|
||||||
name: parseRes.Name,
|
name: parseRes.Name,
|
||||||
email: parseRes.Email,
|
email: parseRes.Email,
|
||||||
phone: parseRes.Phone,
|
phone: parseRes?.Phone || '',
|
||||||
createdAt: parseRes.createdAt,
|
createdAt: parseRes.createdAt,
|
||||||
updatedAt: parseRes.updatedAt,
|
updatedAt: parseRes.updatedAt,
|
||||||
});
|
});
|
||||||
@@ -128,7 +132,7 @@ export default async function createContact(request, response) {
|
|||||||
userId: userRes.objectId,
|
userId: userRes.objectId,
|
||||||
};
|
};
|
||||||
await axios.post(roleurl, body, { headers: headers });
|
await axios.post(roleurl, body, { headers: headers });
|
||||||
contactQuery.set('CreatedBy', userPtr.objectId);
|
contactQuery.set('CreatedBy', userPtr);
|
||||||
contactQuery.set('UserId', {
|
contactQuery.set('UserId', {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: '_User',
|
className: '_User',
|
||||||
@@ -155,7 +159,7 @@ export default async function createContact(request, response) {
|
|||||||
objectId: parseRes.objectId,
|
objectId: parseRes.objectId,
|
||||||
name: parseRes.Name,
|
name: parseRes.Name,
|
||||||
email: parseRes.Email,
|
email: parseRes.Email,
|
||||||
phone: parseRes.Phone,
|
phone: parseRes?.Phone || '',
|
||||||
createdAt: parseRes.createdAt,
|
createdAt: parseRes.createdAt,
|
||||||
updatedAt: parseRes.updatedAt,
|
updatedAt: parseRes.updatedAt,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ async function sendDoctoWebhook(doc, WebhookUrl, userId) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log('Err send data to webhook', err);
|
console.log('Err send data to webhook', err?.message);
|
||||||
try {
|
try {
|
||||||
const webhook = new Parse.Object('contracts_Webhook');
|
const webhook = new Parse.Object('contracts_Webhook');
|
||||||
webhook.set('Log', err?.status);
|
webhook.set('Log', err?.status);
|
||||||
@@ -47,7 +47,7 @@ async function sendDoctoWebhook(doc, WebhookUrl, userId) {
|
|||||||
});
|
});
|
||||||
webhook.save(null, { useMasterKey: true });
|
webhook.save(null, { useMasterKey: true });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err save in contracts_Webhook', err);
|
console.log('err save in contracts_Webhook', err?.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// console.log('res ', res.data);
|
// console.log('res ', res.data);
|
||||||
@@ -66,9 +66,10 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
const email_subject = request.body.email_subject;
|
const email_subject = request.body.email_subject;
|
||||||
const email_body = request.body.email_body;
|
const email_body = request.body.email_body;
|
||||||
const sendInOrder = request.body.sendInOrder || false;
|
const sendInOrder = request.body.sendInOrder || false;
|
||||||
|
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
|
||||||
// console.log('fileData ', fileData);
|
// console.log('fileData ', fileData);
|
||||||
const protocol = customAPIurl();
|
const protocol = customAPIurl();
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
if (!reqToken) {
|
if (!reqToken) {
|
||||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
@@ -88,6 +89,15 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
objectId: parseUser.userId.objectId,
|
objectId: parseUser.userId.objectId,
|
||||||
};
|
};
|
||||||
if (signers && signers.length > 0) {
|
if (signers && signers.length > 0) {
|
||||||
|
// Check if at least one signature exists among all items in the signers array
|
||||||
|
let isSignExist = signers.every(item =>
|
||||||
|
item.widgets.some(data => data?.type === 'signature')
|
||||||
|
);
|
||||||
|
if (!isSignExist) {
|
||||||
|
return response
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'Please add at least one signature widget for all signers' });
|
||||||
|
}
|
||||||
let fileUrl;
|
let fileUrl;
|
||||||
if (request.files?.[0]) {
|
if (request.files?.[0]) {
|
||||||
const base64 = fileData?.toString('base64');
|
const base64 = fileData?.toString('base64');
|
||||||
@@ -130,8 +140,13 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
object.set('SendinOrder', sendInOrder);
|
object.set('SendinOrder', sendInOrder);
|
||||||
}
|
}
|
||||||
object.set('URL', fileUrl);
|
object.set('URL', fileUrl);
|
||||||
|
object.set('SignedUrl', fileUrl);
|
||||||
|
object.set('SentToOthers', true);
|
||||||
object.set('CreatedBy', userPtr);
|
object.set('CreatedBy', userPtr);
|
||||||
object.set('ExtUserPtr', extUserPtr);
|
object.set('ExtUserPtr', extUserPtr);
|
||||||
|
if (TimeToCompleteDays) {
|
||||||
|
object.set('TimeToCompleteDays', TimeToCompleteDays);
|
||||||
|
}
|
||||||
object.set('IsSendMail', send_email);
|
object.set('IsSendMail', send_email);
|
||||||
let contact = [];
|
let contact = [];
|
||||||
if (signers && signers.length > 0) {
|
if (signers && signers.length > 0) {
|
||||||
@@ -241,7 +256,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
name: name,
|
name: name,
|
||||||
note: note || '',
|
note: note || '',
|
||||||
description: description || '',
|
description: description || '',
|
||||||
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x.phone })),
|
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })),
|
||||||
createdAt: res.createdAt,
|
createdAt: res.createdAt,
|
||||||
};
|
};
|
||||||
if (parseExtUser && parseExtUser.Webhook) {
|
if (parseExtUser && parseExtUser.Webhook) {
|
||||||
@@ -255,9 +270,6 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
});
|
});
|
||||||
let sender = parseExtUser.Email;
|
let sender = parseExtUser.Email;
|
||||||
const serverUrl = process.env.SERVER_URL;
|
|
||||||
const newServer = serverUrl.replaceAll('/', '%2F');
|
|
||||||
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
|
|
||||||
if (send_email === false) {
|
if (send_email === false) {
|
||||||
console.log("don't send mail");
|
console.log("don't send mail");
|
||||||
} else {
|
} else {
|
||||||
@@ -278,7 +290,9 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
|
|
||||||
const objectId = contactMail[i].contactPtr.objectId;
|
const objectId = contactMail[i].contactPtr.objectId;
|
||||||
const hostUrl = baseUrl.origin;
|
const hostUrl = baseUrl.origin;
|
||||||
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
|
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||||
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
|
||||||
const themeBGcolor = '#47a3ad';
|
const themeBGcolor = '#47a3ad';
|
||||||
@@ -309,10 +323,10 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
document_title: name,
|
document_title: name,
|
||||||
sender_name: parseExtUser.Name,
|
sender_name: parseExtUser.Name,
|
||||||
sender_mail: parseExtUser.Email,
|
sender_mail: parseExtUser.Email,
|
||||||
sender_phone: parseExtUser.Phone,
|
sender_phone: parseExtUser?.Phone || '',
|
||||||
receiver_name: contactMail[i].name,
|
receiver_name: contactMail[i].name,
|
||||||
receiver_email: contactMail[i].email,
|
receiver_email: contactMail[i].email,
|
||||||
receiver_phone: contactMail[i].phone,
|
receiver_phone: contactMail[i]?.phone || '',
|
||||||
expiry_date: localExpireDate,
|
expiry_date: localExpireDate,
|
||||||
company_name: orgName,
|
company_name: orgName,
|
||||||
signing_url: signPdf,
|
signing_url: signPdf,
|
||||||
@@ -324,13 +338,13 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
replaceVar = { subject: replaceVar.subject, body: email_html };
|
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||||
} else if (email_body) {
|
} else if (email_body) {
|
||||||
replaceVar = replaceMailVaribles(
|
replaceVar = replaceMailVaribles(
|
||||||
`${parseExtUser.Name} has requested you to sign ${name}`,
|
`${parseExtUser.Name} has requested you to sign "${name}"`,
|
||||||
email_body,
|
email_body,
|
||||||
variables
|
variables
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
replaceVar = {
|
replaceVar = {
|
||||||
subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`,
|
subject: `${parseExtUser.Name} has requested you to sign "${name}"`,
|
||||||
body: email_html,
|
body: email_html,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -363,7 +377,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
|||||||
objectId: res.id,
|
objectId: res.id,
|
||||||
signurl: contact.map(x => ({
|
signurl: contact.map(x => ({
|
||||||
email: x.email,
|
email: x.email,
|
||||||
url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
|
url: `${baseUrl.origin}/login/${btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`)}`,
|
||||||
})),
|
})),
|
||||||
message: 'Document sent successfully!',
|
message: 'Document sent successfully!',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
export default async function createFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folderName = request.body.folderName;
|
||||||
|
const parentFolderId = request.body.parentFolderId;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
const userQuery = new Parse.Query('contracts_Users');
|
||||||
|
userQuery.equalTo('UserId', userPtr);
|
||||||
|
const extUser = await userQuery.first({ useMasterKey: true });
|
||||||
|
|
||||||
|
const folderCls = new Parse.Object('contracts_Document');
|
||||||
|
folderCls.set('Name', folderName);
|
||||||
|
folderCls.set('CreatedBy', userPtr);
|
||||||
|
folderCls.set('Type', 'Folder');
|
||||||
|
folderCls.set('ExtUserPtr', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Users',
|
||||||
|
objectId: extUser.id,
|
||||||
|
});
|
||||||
|
if (parentFolderId) {
|
||||||
|
folderCls.set('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const acl = new Parse.ACL();
|
||||||
|
acl.setReadAccess(parseUser.userId.objectId, true); // Allow read access to the userPtr
|
||||||
|
acl.setWriteAccess(parseUser.userId.objectId, true); // Allow write access to the userPtr
|
||||||
|
|
||||||
|
// Set the ACL to the document
|
||||||
|
folderCls.setACL(acl);
|
||||||
|
|
||||||
|
const _resFolder = await folderCls.save(null, { useMasterKey: true });
|
||||||
|
if (_resFolder) {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_create_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(_resFolder));
|
||||||
|
return response.json({
|
||||||
|
objectId: parseRes.objectId,
|
||||||
|
folderName: parseRes.Name,
|
||||||
|
parentFolderId: parseRes?.Folder?.objectId || '',
|
||||||
|
createdAt: parseRes.createdAt,
|
||||||
|
updatedAt: parseRes.updatedAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ export default async function createTemplate(request, response) {
|
|||||||
const base64File = request.body.file;
|
const base64File = request.body.file;
|
||||||
const SendinOrder = request.body.sendInOrder || false;
|
const SendinOrder = request.body.sendInOrder || false;
|
||||||
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
const fileData = request.files?.[0] ? request.files[0].buffer : null;
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default async function createTemplatewithCoordinate(request, response) {
|
|||||||
let createContactUrl = protocol + '/v1/createcontact';
|
let createContactUrl = protocol + '/v1/createcontact';
|
||||||
|
|
||||||
for (const [index, element] of parseSigners.entries()) {
|
for (const [index, element] of parseSigners.entries()) {
|
||||||
if (element?.name && element.phone && element?.email) {
|
if (element?.name && element?.email) {
|
||||||
const body = {
|
const body = {
|
||||||
name: element?.name || '',
|
name: element?.name || '',
|
||||||
email: element?.email || '',
|
email: element?.email || '',
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
export default async function deleteFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folderId = request.params.folder_id;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const obj = new Parse.Query('contracts_Document');
|
||||||
|
obj.equalTo('objectId', folderId);
|
||||||
|
obj.equalTo('IsArchive', true);
|
||||||
|
obj.equalTo('CreatedBy', userPtr);
|
||||||
|
const isFolderDeleted = await obj.first({ useMasterKey: true });
|
||||||
|
if (!isFolderDeleted) {
|
||||||
|
const folder = new Parse.Query('contracts_Document');
|
||||||
|
folder.equalTo('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: folderId,
|
||||||
|
});
|
||||||
|
|
||||||
|
folder.notEqualTo('IsArchive', true);
|
||||||
|
folder.equalTo('CreatedBy', userPtr);
|
||||||
|
folder.equalTo('Type', 'Folder');
|
||||||
|
const isSubItems = await folder.first({ useMasterKey: true });
|
||||||
|
// console.log('isSubItems ', isSubItems);
|
||||||
|
if (isSubItems) {
|
||||||
|
return response
|
||||||
|
.status(400)
|
||||||
|
.json({ error: 'folder is not empty, contains document or folder.' });
|
||||||
|
} else {
|
||||||
|
const deleteFolder = new Parse.Object('contracts_Document');
|
||||||
|
deleteFolder.id = folderId;
|
||||||
|
deleteFolder.set('IsArchive', true);
|
||||||
|
const deleteRes = await deleteFolder.save(null, { useMasterKey: true });
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_delete_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ objectId: deleteRes.id, deletedAt: deleteRes.updatedAt });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_delete_folder',
|
||||||
|
properties: { response_code: 404 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.status(404).json({ error: 'folder not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err is delete folder API', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { customAPIurl } from '../../../../Utils.js';
|
import { customAPIurl, saveFileUsage } from '../../../../Utils.js';
|
||||||
|
|
||||||
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||||
export default async function draftDocument(request, response) {
|
export default async function draftDocument(request, response) {
|
||||||
@@ -14,7 +14,7 @@ export default async function draftDocument(request, response) {
|
|||||||
const SendinOrder = request.body.sendInOrder || false;
|
const SendinOrder = request.body.sendInOrder || false;
|
||||||
// console.log('fileData ', fileData);
|
// console.log('fileData ', fileData);
|
||||||
const protocol = customAPIurl();
|
const protocol = customAPIurl();
|
||||||
const baseUrl = new URL(process.env.SERVER_URL);
|
const baseUrl = new URL(process.env.PUBLIC_URL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reqToken = request.headers['x-api-token'];
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default async function getContact(request, response) {
|
|||||||
objectId: parseRes.objectId,
|
objectId: parseRes.objectId,
|
||||||
name: parseRes.Name,
|
name: parseRes.Name,
|
||||||
email: parseRes.Email,
|
email: parseRes.Email,
|
||||||
phone: parseRes.Phone,
|
phone: parseRes?.Phone || '',
|
||||||
createdAt: parseRes.createdAt,
|
createdAt: parseRes.createdAt,
|
||||||
updatedAt: parseRes.updatedAt,
|
updatedAt: parseRes.updatedAt,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default async function getContactList(request, response) {
|
|||||||
objectId: x.objectId,
|
objectId: x.objectId,
|
||||||
name: x.Name,
|
name: x.Name,
|
||||||
email: x.Email,
|
email: x.Email,
|
||||||
phone: x.Phone,
|
phone: x?.Phone || '',
|
||||||
createdAt: x.createdAt,
|
createdAt: x.createdAt,
|
||||||
updatedAt: x.updatedAt,
|
updatedAt: x.updatedAt,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default async function getDocument(request, response) {
|
|||||||
note: document.Note || '',
|
note: document.Note || '',
|
||||||
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
|
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
|
||||||
file: document?.SignedUrl || document.URL,
|
file: document?.SignedUrl || document.URL,
|
||||||
|
certificate: document?.CertificateUrl || '',
|
||||||
owner: document?.ExtUserPtr?.Name,
|
owner: document?.ExtUserPtr?.Name,
|
||||||
signers:
|
signers:
|
||||||
document?.Placeholders?.map(y => ({
|
document?.Placeholders?.map(y => ({
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default async function getDocumentList(request, response) {
|
|||||||
title: x.Name,
|
title: x.Name,
|
||||||
note: x.Note || '',
|
note: x.Note || '',
|
||||||
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
|
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
|
||||||
file: x?.SignedUrl || x.URL,
|
// file: x?.SignedUrl || x.URL,
|
||||||
owner: x?.ExtUserPtr?.Name,
|
owner: x?.ExtUserPtr?.Name,
|
||||||
signers:
|
signers:
|
||||||
x?.Placeholders?.map(y => ({
|
x?.Placeholders?.map(y => ({
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
export default async function getFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folder_id = request.params.folder_id;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('objectId', folder_id);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.include('Folder');
|
||||||
|
|
||||||
|
const res = await folderCls.first({ useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(res));
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({
|
||||||
|
objectId: parseRes.objectId,
|
||||||
|
folderName: parseRes.Name,
|
||||||
|
parentFolderId: parseRes?.Folder?.objectId || '',
|
||||||
|
parentFolderName: parseRes?.Folder?.Name || '',
|
||||||
|
createdAt: parseRes.createdAt,
|
||||||
|
updatedAt: parseRes.updatedAt,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_folder',
|
||||||
|
properties: { response_code: 404 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.status(404).json({ error: 'folder not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
export default async function getFolderList(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const parentFolderId = request.query?.parentFolderId || '';
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
const limit = request?.query?.limit ? parseInt(request.query.limit) : 100;
|
||||||
|
const skip = request?.query?.skip ? parseInt(request.query.skip) : 0;
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.include('Folder');
|
||||||
|
if (parentFolderId) {
|
||||||
|
folderCls.equalTo('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
folderCls.doesNotExist('Folder');
|
||||||
|
}
|
||||||
|
folderCls.descending('createdAt');
|
||||||
|
folderCls.limit(limit);
|
||||||
|
folderCls.skip(skip);
|
||||||
|
const res = await folderCls.find({ useMasterKey: true });
|
||||||
|
if (res && res.length > 0) {
|
||||||
|
const parseRes = JSON.parse(JSON.stringify(res));
|
||||||
|
const folderlist = parseRes.map(x => ({
|
||||||
|
objectId: x.objectId,
|
||||||
|
folderName: x.Name,
|
||||||
|
parentFolderId: x?.Folder?.objectId || '',
|
||||||
|
parentFolderName: x?.Folder?.Name || '',
|
||||||
|
createdAt: x.createdAt,
|
||||||
|
updatedAt: x.updatedAt,
|
||||||
|
}));
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_Folder_list',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ result: folderlist });
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_get_Folder_list',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ result: [] });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@ export default async function getUser(request, response) {
|
|||||||
objectId: parseRes.objectId,
|
objectId: parseRes.objectId,
|
||||||
name: parseRes.Name,
|
name: parseRes.Name,
|
||||||
email: parseRes.Email,
|
email: parseRes.Email,
|
||||||
phone: parseRes.Phone,
|
phone: parseRes?.Phone || '',
|
||||||
jobTitle: parseRes.JobTitle,
|
jobTitle: parseRes.JobTitle,
|
||||||
company: parseRes.Company,
|
company: parseRes.Company,
|
||||||
createdAt: parseRes.createdAt,
|
createdAt: parseRes.createdAt,
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
import { replaceMailVaribles } from '../../../../Utils.js';
|
||||||
|
|
||||||
|
export default async function resendMail(request, response) {
|
||||||
|
try {
|
||||||
|
const reqToken = request.headers['x-api-token'];
|
||||||
|
const docId = request.body.document_id;
|
||||||
|
const userMail = request.body.email;
|
||||||
|
const email_subject = request.body.email_subject;
|
||||||
|
const email_body = request.body.email_body;
|
||||||
|
const baseUrl = new URL(process.env.SERVER_URL);
|
||||||
|
|
||||||
|
if (!reqToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', reqToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const docQuery = new Parse.Query('contracts_Document');
|
||||||
|
docQuery.equalTo('objectId', docId);
|
||||||
|
docQuery.equalTo('CreatedBy', userPtr);
|
||||||
|
docQuery.include('Signers,ExtUserPtr');
|
||||||
|
docQuery.notEqualTo('IsCompleted', true);
|
||||||
|
docQuery.notEqualTo('IsDeclined', true);
|
||||||
|
docQuery.notEqualTo('IsArchive', true);
|
||||||
|
docQuery.greaterThanOrEqualTo('ExpiryDate', new Date());
|
||||||
|
docQuery.exists('SignedUrl');
|
||||||
|
|
||||||
|
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||||
|
// console.log("resDoc ",resDoc)
|
||||||
|
if (resDoc) {
|
||||||
|
const _resDoc = resDoc.toJSON();
|
||||||
|
const contact = _resDoc.Signers.find(x => x.Email === userMail);
|
||||||
|
if (contact) {
|
||||||
|
try {
|
||||||
|
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
|
||||||
|
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Parse-Application-Id': process.env.APP_ID,
|
||||||
|
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||||
|
};
|
||||||
|
|
||||||
|
const objectId = contact.objectId;
|
||||||
|
const hostUrl = baseUrl.origin;
|
||||||
|
const title = _resDoc.Name;
|
||||||
|
const receiverMail = contact.Email;
|
||||||
|
//encode this url value `${response.id}/${receiverMail}/${objectId}` to base64 using `btoa` function
|
||||||
|
const encodeBase64 = btoa(`${_resDoc.objectId}/${receiverMail}/${objectId}`);
|
||||||
|
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||||
|
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
|
||||||
|
const orgName = _resDoc.ExtUserPtr.Company ? _resDoc.ExtUserPtr.Company : '';
|
||||||
|
const newDate = new Date(_resDoc.ExpiryDate.iso);
|
||||||
|
newDate.setDate(newDate.getDate() + 15);
|
||||||
|
const sender = _resDoc.ExtUserPtr.Email;
|
||||||
|
const localExpireDate = newDate.toLocaleDateString('en-US', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
});
|
||||||
|
|
||||||
|
const themeBGcolor = '#47a3ad';
|
||||||
|
const 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: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><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;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
|
||||||
|
_resDoc.ExtUserPtr.Name +
|
||||||
|
' has requested you to review and sign <strong> ' +
|
||||||
|
title +
|
||||||
|
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||||
|
sender +
|
||||||
|
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
|
||||||
|
orgName +
|
||||||
|
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
|
||||||
|
localExpireDate +
|
||||||
|
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
|
||||||
|
signPdf +
|
||||||
|
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
|
||||||
|
sender +
|
||||||
|
' 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>';
|
||||||
|
let replaceVar;
|
||||||
|
const variables = {
|
||||||
|
document_title: title,
|
||||||
|
sender_name: _resDoc.ExtUserPtr.Name,
|
||||||
|
sender_mail: _resDoc.ExtUserPtr.Email,
|
||||||
|
sender_phone: _resDoc.ExtUserPtr?.Phone || '',
|
||||||
|
receiver_name: contact.Name,
|
||||||
|
receiver_email: contact.Email,
|
||||||
|
receiver_phone: contact?.Phone || '',
|
||||||
|
expiry_date: localExpireDate,
|
||||||
|
company_name: orgName,
|
||||||
|
signing_url: signPdf,
|
||||||
|
};
|
||||||
|
if (email_subject && email_body) {
|
||||||
|
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
|
||||||
|
} else if (email_subject) {
|
||||||
|
replaceVar = replaceMailVaribles(email_subject, '', variables);
|
||||||
|
replaceVar = { subject: replaceVar.subject, body: email_html };
|
||||||
|
} else if (email_body) {
|
||||||
|
replaceVar = replaceMailVaribles(
|
||||||
|
`${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
|
||||||
|
email_body,
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
replaceVar = {
|
||||||
|
subject: `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
|
||||||
|
body: email_html,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const subject = replaceVar.subject;
|
||||||
|
const html = replaceVar.body;
|
||||||
|
|
||||||
|
let params = {
|
||||||
|
recipient: contact.Email,
|
||||||
|
subject: subject,
|
||||||
|
from: sender,
|
||||||
|
html: html,
|
||||||
|
extUserId: _resDoc.ExtUserPtr.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await axios.post(url, params, { headers: headers });
|
||||||
|
if (res.data.result && res.data.result.status === 'success') {
|
||||||
|
return response.json({ result: 'mail sent successfully.' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('error', error);
|
||||||
|
return response.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ error: 'user not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(404).json({ error: 'document not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token.' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err in resendmail', err);
|
||||||
|
return response.status(400).json({ error: err.message || 'Something went wrong.' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
export default async function updateFolder(request, response) {
|
||||||
|
const apiToken = request.headers['x-api-token'];
|
||||||
|
const folderId = request.params.folder_id;
|
||||||
|
const name = request.body.folderName;
|
||||||
|
const parentFolderId = request.body.parentFolderId;
|
||||||
|
if (!apiToken) {
|
||||||
|
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const tokenQuery = new Parse.Query('appToken');
|
||||||
|
tokenQuery.equalTo('token', apiToken);
|
||||||
|
tokenQuery.include('userId');
|
||||||
|
const token = await tokenQuery.first({ useMasterKey: true });
|
||||||
|
if (token !== undefined) {
|
||||||
|
// Valid Token then proceed request
|
||||||
|
const parseUser = JSON.parse(JSON.stringify(token));
|
||||||
|
const userPtr = {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: parseUser.userId.objectId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const folderCls = new Parse.Query('contracts_Document');
|
||||||
|
folderCls.equalTo('CreatedBy', userPtr);
|
||||||
|
folderCls.equalTo('objectId', folderId);
|
||||||
|
folderCls.equalTo('Type', 'Folder');
|
||||||
|
folderCls.notEqualTo('IsArchive', true);
|
||||||
|
|
||||||
|
const res = await folderCls.first({ useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
const updateFolder = new Parse.Object('contracts_Document');
|
||||||
|
updateFolder.id = res.id;
|
||||||
|
updateFolder.set('Name', name);
|
||||||
|
if (parentFolderId) {
|
||||||
|
updateFolder.set('Folder', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: 'contracts_Document',
|
||||||
|
objectId: parentFolderId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const updateRes = await updateFolder.save(null, { useMasterKey: true });
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_update_folder',
|
||||||
|
properties: { response_code: 200 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json({ objectId: updateRes.id, updatedAt: updateRes.updatedAt });
|
||||||
|
} else {
|
||||||
|
if (request.posthog) {
|
||||||
|
request.posthog?.capture({
|
||||||
|
distinctId: parseUser.userId.email,
|
||||||
|
event: 'api_update_folder',
|
||||||
|
properties: { response_code: 404 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.status(404).json({ error: 'folder not found.' });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response.status(405).json({ error: 'Invalid API Token!' });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err);
|
||||||
|
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import PDF from './parsefunction/pdf/PDF.min.js';
|
import PDF from './parsefunction/pdf/PDF.js';
|
||||||
import sendmailv3 from './parsefunction/sendMailv3.js';
|
import sendmailv3 from './parsefunction/sendMailv3.js';
|
||||||
import GoogleSign from './parsefunction/GoogleSign.js';
|
import GoogleSign from './parsefunction/GoogleSign.js';
|
||||||
import ZohoDetails from './parsefunction/ZohoDetails.js';
|
import ZohoDetails from './parsefunction/ZohoDetails.js';
|
||||||
@@ -33,8 +33,36 @@ import getInvoices from './parsefunction/getInvoices.js';
|
|||||||
import getPayments from './parsefunction/getPayments.js';
|
import getPayments from './parsefunction/getPayments.js';
|
||||||
import getSubscriptions from './parsefunction/getSubscriptions.js';
|
import getSubscriptions from './parsefunction/getSubscriptions.js';
|
||||||
import TenantAterFind from './parsefunction/TenantAfterFind.js';
|
import TenantAterFind from './parsefunction/TenantAfterFind.js';
|
||||||
import saveSubscriptio from './parsefunction/saveSubscription.js';
|
import saveSubscription from './parsefunction/saveSubscription.js';
|
||||||
|
import VerifyEmail from './parsefunction/VerifyEmail.js';
|
||||||
|
import encryptedpdf from './parsefunction/encryptedPdf.js';
|
||||||
|
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
|
||||||
|
import createBatchDocs from './parsefunction/createBatchDocs.js';
|
||||||
|
import linkContactToDoc from './parsefunction/linkContactToDoc.js';
|
||||||
|
import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js';
|
||||||
|
import GetPublicUserName from './parsefunction/GetPublicUserName.js';
|
||||||
|
import GetPublicTemplate from './parsefunction/GetPublicTemplate.js';
|
||||||
|
import ssoSignin from './parsefunction/ssoSignin.js';
|
||||||
|
import isextenduser from './parsefunction/isextenduser.js';
|
||||||
|
|
||||||
|
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
|
||||||
|
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||||
|
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
|
||||||
|
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
|
||||||
|
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
|
||||||
|
|
||||||
|
// This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification.
|
||||||
|
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
|
||||||
|
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
|
||||||
|
|
||||||
|
// This afterFind function triggers after a query retrieves objects from the specified class, allowing for post-processing of the results.
|
||||||
|
Parse.Cloud.afterFind(Parse.User, UserAfterFind);
|
||||||
|
Parse.Cloud.afterFind('contracts_Document', DocumentBeforeFind);
|
||||||
|
Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
|
||||||
|
Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
|
||||||
|
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
|
||||||
|
|
||||||
|
// This define function creates a custom Cloud Function that can be called from the client-side, enabling custom business logic on the server.
|
||||||
Parse.Cloud.define('AddUserToRole', addUserToGroups);
|
Parse.Cloud.define('AddUserToRole', addUserToGroups);
|
||||||
Parse.Cloud.define('UserGroups', getUserGroups);
|
Parse.Cloud.define('UserGroups', getUserGroups);
|
||||||
Parse.Cloud.define('signPdf', PDF);
|
Parse.Cloud.define('signPdf', PDF);
|
||||||
@@ -59,15 +87,14 @@ Parse.Cloud.define('freesubscription', SubscribeFree);
|
|||||||
Parse.Cloud.define('getinvoices', getInvoices);
|
Parse.Cloud.define('getinvoices', getInvoices);
|
||||||
Parse.Cloud.define('getpayments', getPayments);
|
Parse.Cloud.define('getpayments', getPayments);
|
||||||
Parse.Cloud.define('getsubscriptions', getSubscriptions);
|
Parse.Cloud.define('getsubscriptions', getSubscriptions);
|
||||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
Parse.Cloud.define('savesubscription', saveSubscription);
|
||||||
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
|
Parse.Cloud.define('verifyemail', VerifyEmail);
|
||||||
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
|
Parse.Cloud.define('encryptedpdf', encryptedpdf);
|
||||||
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
|
Parse.Cloud.define('getsignedurl', getSignedUrl);
|
||||||
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
|
Parse.Cloud.define('batchdocuments', createBatchDocs);
|
||||||
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
|
Parse.Cloud.define('linkcontacttodoc', linkContactToDoc);
|
||||||
Parse.Cloud.afterFind(Parse.User, UserAfterFind);
|
Parse.Cloud.define('createpublictemplate', CreatePublicTemplate);
|
||||||
Parse.Cloud.afterFind('contracts_Document', DocumentBeforeFind);
|
Parse.Cloud.define('getpublicusername', GetPublicUserName);
|
||||||
Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
|
Parse.Cloud.define('getpublictemplate', GetPublicTemplate);
|
||||||
Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
|
Parse.Cloud.define('ssosign', ssoSignin);
|
||||||
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
|
Parse.Cloud.define('isextenduser', isextenduser);
|
||||||
Parse.Cloud.define('savesubscription', saveSubscriptio);
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ async function ContractUsersAftersave(request) {
|
|||||||
contactbook.set('Name', request.object.get('Name'));
|
contactbook.set('Name', request.object.get('Name'));
|
||||||
contactbook.set('Email', request.object.get('Email'));
|
contactbook.set('Email', request.object.get('Email'));
|
||||||
contactbook.set('ExtUserPtr', request.object.get('objectId'));
|
contactbook.set('ExtUserPtr', request.object.get('objectId'));
|
||||||
contactbook.set('Phone', request.object.get('Phone'));
|
if (request.object?.get('Phone')) {
|
||||||
|
contactbook.set('Phone', request.object.get('Phone'));
|
||||||
|
}
|
||||||
contactbook.set('ExtUserPtr', {
|
contactbook.set('ExtUserPtr', {
|
||||||
__type: 'Pointer',
|
__type: 'Pointer',
|
||||||
className: 'contracts_Users',
|
className: 'contracts_Users',
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
export default async function CreatePublicTemplate(request) {
|
||||||
|
const templateid = request.params.templateid;
|
||||||
|
const ispublic = request.params.ispublic;
|
||||||
|
const publicrole = request.params.publicrole;
|
||||||
|
try {
|
||||||
|
if (!request?.user) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||||
|
} else {
|
||||||
|
const userId = request?.user && request?.user?.id;
|
||||||
|
if (templateid) {
|
||||||
|
const updateTemplate = new Parse.Object('contracts_Template');
|
||||||
|
updateTemplate.id = templateid;
|
||||||
|
if (ispublic) {
|
||||||
|
updateTemplate.set('PublicRole', publicrole);
|
||||||
|
}
|
||||||
|
updateTemplate.set('IsPublic', ispublic);
|
||||||
|
const Acl = new Parse.ACL();
|
||||||
|
if (ispublic) {
|
||||||
|
Acl.setPublicReadAccess(true);
|
||||||
|
}
|
||||||
|
Acl.setReadAccess(userId, true);
|
||||||
|
Acl.setWriteAccess(userId, true);
|
||||||
|
updateTemplate.setACL(Acl);
|
||||||
|
const savedObject = await updateTemplate.save(null, { useMasterKey: true });
|
||||||
|
const res = savedObject.toJSON();
|
||||||
|
if (res) {
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const code = err.code || 400;
|
||||||
|
const msg = err.message;
|
||||||
|
const error = new Parse.Error(code, msg);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,25 @@
|
|||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
import getPresignedUrl from './getSignedUrl.js';
|
import getPresignedUrl from './getSignedUrl.js';
|
||||||
|
|
||||||
async function DocumentAfterFind(request) {
|
async function DocumentAfterFind(request) {
|
||||||
if (request.objects.length === 1) {
|
if (useLocal !== 'true') {
|
||||||
if (request.objects) {
|
if (request.objects.length === 1) {
|
||||||
const obj = request.objects[0];
|
if (request.objects) {
|
||||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
const obj = request.objects[0];
|
||||||
const Url = obj?.get('URL') && obj?.get('URL');
|
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
const Url = obj?.get('URL') && obj?.get('URL');
|
||||||
if (SignedUrl) {
|
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
if (SignedUrl) {
|
||||||
|
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||||
|
}
|
||||||
|
if (Url) {
|
||||||
|
obj.set('URL', getPresignedUrl(Url));
|
||||||
|
}
|
||||||
|
if (certificateUrl) {
|
||||||
|
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||||
|
}
|
||||||
|
return [obj];
|
||||||
}
|
}
|
||||||
if (Url) {
|
|
||||||
obj.set('URL', getPresignedUrl(Url));
|
|
||||||
}
|
|
||||||
if (certificateUrl) {
|
|
||||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [obj];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ async function DocumentAftersave(request) {
|
|||||||
console.log('new entry is insert in contracts_Document');
|
console.log('new entry is insert in contracts_Document');
|
||||||
const createdAt = request.object.get('createdAt');
|
const createdAt = request.object.get('createdAt');
|
||||||
const Folder = request.object.get('Type');
|
const Folder = request.object.get('Type');
|
||||||
|
const ip = request?.headers?.['x-real-ip'] || '';
|
||||||
if (createdAt && Folder === undefined) {
|
if (createdAt && Folder === undefined) {
|
||||||
// console.log("IN If condition")
|
// console.log("IN If condition")
|
||||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
||||||
@@ -12,18 +13,30 @@ async function DocumentAftersave(request) {
|
|||||||
const documentQuery = new Parse.Query('contracts_Document');
|
const documentQuery = new Parse.Query('contracts_Document');
|
||||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||||
|
updateQuery.set('OriginIp', ip);
|
||||||
|
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||||
|
const ReminderDate = new Date(createdAt);
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
updateQuery.set('NextReminderDate', ReminderDate);
|
||||||
|
}
|
||||||
await updateQuery.save(null, { useMasterKey: true });
|
await updateQuery.save(null, { useMasterKey: true });
|
||||||
} else if (createdAt && Folder === 'AIDoc') {
|
} else if (createdAt && Folder === 'AIDoc') {
|
||||||
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
|
||||||
const ExpiryDate = new Date(createdAt);
|
const ExpiryDate = new Date(createdAt);
|
||||||
// console.log("ExpiryDate")
|
|
||||||
// console.log(ExpiryDate)
|
|
||||||
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
|
||||||
// console.log("ExpiryDate date after update")
|
|
||||||
// console.log(ExpiryDate)
|
|
||||||
const documentQuery = new Parse.Query('contracts_Document');
|
const documentQuery = new Parse.Query('contracts_Document');
|
||||||
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
|
||||||
updateQuery.set('ExpiryDate', ExpiryDate);
|
updateQuery.set('ExpiryDate', ExpiryDate);
|
||||||
|
updateQuery.set('OriginIp', ip);
|
||||||
|
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||||
|
const ReminderDate = new Date(createdAt);
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
updateQuery.set('NextReminderDate', ReminderDate);
|
||||||
|
}
|
||||||
await updateQuery.save(null, { useMasterKey: true });
|
await updateQuery.save(null, { useMasterKey: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ async function DocumentBeforesave(request) {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('Error updating document count in contracts_users: ' + error.message);
|
console.log('Error updating document count in contracts_users: ' + error.message);
|
||||||
}
|
}
|
||||||
|
if (document?.get('Signers') && document.get('Signers').length > 0) {
|
||||||
|
document.set('DocSentAt', new Date());
|
||||||
|
document.save(null, { useMasterKey: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err in document beforesave', err.message);
|
console.log('err in document beforesave', err.message);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
|
|
||||||
const serverUrl = process.env.SERVER_URL;
|
const serverUrl = process.env.SERVER_URL;
|
||||||
const APPID = process.env.APP_ID;
|
const APPID = process.env.APP_ID;
|
||||||
@@ -18,23 +18,23 @@ export default async function FacebookSign(request) {
|
|||||||
const userGoogleId = request.params.Id;
|
const userGoogleId = request.params.Id;
|
||||||
const userAccessToken = request.params.AccessToken;
|
const userAccessToken = request.params.AccessToken;
|
||||||
const userEmail = request.params.Email;
|
const userEmail = request.params.Email;
|
||||||
const phone = request.params.Phone;
|
const phone = request.params?.Phone || '';
|
||||||
const name = request.params.Name;
|
const name = request.params.Name;
|
||||||
const authData = {
|
const authData = {
|
||||||
facebook: { id: userGoogleId, access_token: userAccessToken },
|
facebook: { id: userGoogleId, access_token: userAccessToken },
|
||||||
};
|
};
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
userQuery.equalTo("email", userEmail);
|
userQuery.equalTo('email', userEmail);
|
||||||
const res = await userQuery.first({ useMasterKey: true });
|
const res = await userQuery.first({ useMasterKey: true });
|
||||||
if (res) {
|
if (res) {
|
||||||
try {
|
try {
|
||||||
const SignIn = await axios.put(
|
const SignIn = await axios.put(
|
||||||
serverUrl + "/users/" + res.id,
|
serverUrl + '/users/' + res.id,
|
||||||
{ authData: authData },
|
{ authData: authData },
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": APPID,
|
'X-Parse-Application-Id': APPID,
|
||||||
"X-Parse-Master-key": masterKEY,
|
'X-Parse-Master-key': masterKEY,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -42,22 +42,22 @@ export default async function FacebookSign(request) {
|
|||||||
if (SignIn.data) {
|
if (SignIn.data) {
|
||||||
// console.log("google Sign in", SignIn);
|
// console.log("google Sign in", SignIn);
|
||||||
const sessiontoken = SignIn.data.sessionToken;
|
const sessiontoken = SignIn.data.sessionToken;
|
||||||
console.log("Google sessiontoken", sessiontoken);
|
console.log('Google sessiontoken', sessiontoken);
|
||||||
return {
|
return {
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
message: "User Sign In",
|
message: 'User Sign In',
|
||||||
sessiontoken: sessiontoken,
|
sessiontoken: sessiontoken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in user google sign in", err);
|
console.log('err in user google sign in', err);
|
||||||
return { message: "Internal server error" };
|
return { message: 'Internal server error' };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// console.log("in sign up condition");
|
// console.log("in sign up condition");
|
||||||
try {
|
try {
|
||||||
const SignUp = await axios.post(
|
const SignUp = await axios.post(
|
||||||
serverUrl + "/users",
|
serverUrl + '/users',
|
||||||
{
|
{
|
||||||
authData: authData,
|
authData: authData,
|
||||||
username: userEmail,
|
username: userEmail,
|
||||||
@@ -67,8 +67,8 @@ export default async function FacebookSign(request) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": APPID,
|
'X-Parse-Application-Id': APPID,
|
||||||
"X-Parse-Revocable-Session": "1",
|
'X-Parse-Revocable-Session': '1',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -79,14 +79,14 @@ export default async function FacebookSign(request) {
|
|||||||
const sessiontoken = SignUp.data.sessionToken;
|
const sessiontoken = SignUp.data.sessionToken;
|
||||||
const payload = {
|
const payload = {
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
message: "User Sign Up",
|
message: 'User Sign Up',
|
||||||
sessiontoken: sessiontoken,
|
sessiontoken: sessiontoken,
|
||||||
};
|
};
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in user google sign up", err);
|
console.log('err in user google sign up', err);
|
||||||
return { message: "Internal server err" };
|
return { message: 'Internal server err' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
async function GetPublicTemplate(request) {
|
||||||
|
try {
|
||||||
|
const username = request.params.username;
|
||||||
|
if (username) {
|
||||||
|
const extUserQuery = new Parse.Query('contracts_Users');
|
||||||
|
extUserQuery.equalTo('UserName', username);
|
||||||
|
const res = await extUserQuery.first({ useMasterKey: true });
|
||||||
|
const userId = res.get('UserId').id;
|
||||||
|
if (userId) {
|
||||||
|
const templatQuery = new Parse.Query('contracts_Template');
|
||||||
|
templatQuery.equalTo('CreatedBy', {
|
||||||
|
__type: 'Pointer',
|
||||||
|
className: '_User',
|
||||||
|
objectId: userId,
|
||||||
|
});
|
||||||
|
templatQuery.equalTo('IsPublic', true);
|
||||||
|
const getTemplate = await templatQuery.find({ useMasterKey: true });
|
||||||
|
return getTemplate;
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const code = err.code || 400;
|
||||||
|
const msg = err.message;
|
||||||
|
const error = new Parse.Error(code, msg);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default GetPublicTemplate;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
async function GetPublicUserName(request) {
|
||||||
|
try {
|
||||||
|
if (!request?.user) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||||
|
} else {
|
||||||
|
const username = request.params.username;
|
||||||
|
if (username) {
|
||||||
|
const userQuery = new Parse.Query('contracts_Users');
|
||||||
|
userQuery.equalTo('UserName', username);
|
||||||
|
const res = await userQuery.first({ useMasterKey: true });
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const code = err.code || 400;
|
||||||
|
const msg = err.message;
|
||||||
|
const error = new Parse.Error(code, msg);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default GetPublicUserName;
|
||||||
@@ -26,7 +26,7 @@ export default async function GetTemplate(request) {
|
|||||||
if (res) {
|
if (res) {
|
||||||
// console.log("res ",res)
|
// console.log("res ",res)
|
||||||
const acl = res.getACL();
|
const acl = res.getACL();
|
||||||
console.log("acl", acl.getReadAccess(userId))
|
// console.log("acl", acl.getReadAccess(userId))
|
||||||
if (acl && acl.getReadAccess(userId)) {
|
if (acl && acl.getReadAccess(userId)) {
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
|
|
||||||
const serverUrl = process.env.SERVER_URL;
|
const serverUrl = process.env.SERVER_URL;
|
||||||
const APPID = process.env.APP_ID;
|
const APPID = process.env.APP_ID;
|
||||||
@@ -9,8 +9,8 @@ const masterKEY = process.env.MASTER_KEY;
|
|||||||
* @param Id It is google Id
|
* @param Id It is google Id
|
||||||
* @param TokenId It is google token Id
|
* @param TokenId It is google token Id
|
||||||
* @param Gmail It is user's gmail with user sign in/sign up
|
* @param Gmail It is user's gmail with user sign in/sign up
|
||||||
* @param Phone It is user's Phone number
|
* @param Phone It is user's Phone number
|
||||||
* @param Name It is user's Name
|
* @param Name It is user's Name
|
||||||
* @returns if success {email, message, sessiontoken} else on reject {message}
|
* @returns if success {email, message, sessiontoken} else on reject {message}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -18,21 +18,21 @@ export default async function GoogleSign(request) {
|
|||||||
const userGoogleId = request.params.Id;
|
const userGoogleId = request.params.Id;
|
||||||
const userTokenId = request.params.TokenId;
|
const userTokenId = request.params.TokenId;
|
||||||
const userEmail = request.params.Gmail;
|
const userEmail = request.params.Gmail;
|
||||||
const phone = request.params.Phone;
|
const phone = request.params?.Phone || '';
|
||||||
const name = request.params.Name;
|
const name = request.params.Name;
|
||||||
const authData = { google: { id: userGoogleId, id_token: userTokenId } };
|
const authData = { google: { id: userGoogleId, id_token: userTokenId } };
|
||||||
const userQuery = new Parse.Query(Parse.User);
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
userQuery.equalTo("email", userEmail);
|
userQuery.equalTo('email', userEmail);
|
||||||
const res = await userQuery.first({ useMasterKey: true });
|
const res = await userQuery.first({ useMasterKey: true });
|
||||||
if (res) {
|
if (res) {
|
||||||
try {
|
try {
|
||||||
const SignIn = await axios.put(
|
const SignIn = await axios.put(
|
||||||
serverUrl + "/users/" + res.id,
|
serverUrl + '/users/' + res.id,
|
||||||
{ authData: authData },
|
{ authData: authData },
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": APPID,
|
'X-Parse-Application-Id': APPID,
|
||||||
"X-Parse-Master-key": masterKEY,
|
'X-Parse-Master-key': masterKEY,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -40,22 +40,22 @@ export default async function GoogleSign(request) {
|
|||||||
if (SignIn.data) {
|
if (SignIn.data) {
|
||||||
// console.log("google Sign in", SignIn);
|
// console.log("google Sign in", SignIn);
|
||||||
const sessiontoken = SignIn.data.sessionToken;
|
const sessiontoken = SignIn.data.sessionToken;
|
||||||
console.log("Google sessiontoken", sessiontoken);
|
console.log('Google sessiontoken', sessiontoken);
|
||||||
return {
|
return {
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
message: "User Sign In",
|
message: 'User Sign In',
|
||||||
sessiontoken: sessiontoken,
|
sessiontoken: sessiontoken,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in user google sign in", err);
|
console.log('err in user google sign in', err);
|
||||||
return { message: "Internal server error" };
|
return { message: 'Internal server error' };
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// console.log("in sign up condition");
|
// console.log("in sign up condition");
|
||||||
try {
|
try {
|
||||||
const SignUp = await axios.post(
|
const SignUp = await axios.post(
|
||||||
serverUrl + "/users",
|
serverUrl + '/users',
|
||||||
{
|
{
|
||||||
authData: authData,
|
authData: authData,
|
||||||
username: userEmail,
|
username: userEmail,
|
||||||
@@ -65,8 +65,8 @@ export default async function GoogleSign(request) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": APPID,
|
'X-Parse-Application-Id': APPID,
|
||||||
"X-Parse-Revocable-Session": "1",
|
'X-Parse-Revocable-Session': '1',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -77,14 +77,14 @@ export default async function GoogleSign(request) {
|
|||||||
const sessiontoken = SignUp.data.sessionToken;
|
const sessiontoken = SignUp.data.sessionToken;
|
||||||
const payload = {
|
const payload = {
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
message: "User Sign Up",
|
message: 'User Sign Up',
|
||||||
sessiontoken: sessiontoken,
|
sessiontoken: sessiontoken,
|
||||||
};
|
};
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in user google sign up", err);
|
console.log('err in user google sign up', err);
|
||||||
return { message: "Internal server err" };
|
return { message: 'Internal server err' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ async function sendMailOTPv1(request) {
|
|||||||
code +
|
code +
|
||||||
'</p></div> </div> </div></body></html>',
|
'</p></div> </div> </div></body></html>',
|
||||||
});
|
});
|
||||||
console.log('OTP sent');
|
console.log('OTP sent', code);
|
||||||
if (request.params?.docId) {
|
if (request.params?.docId) {
|
||||||
const extUserId = await getDocument(request.params?.docId);
|
const extUserId = await getDocument(request.params?.docId);
|
||||||
if (extUserId) {
|
if (extUserId) {
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
import getPresignedUrl from './getSignedUrl.js';
|
import getPresignedUrl from './getSignedUrl.js';
|
||||||
|
|
||||||
async function SignatureAfterFind(request) {
|
async function SignatureAfterFind(request) {
|
||||||
if (request.objects.length === 1) {
|
if (useLocal !== 'true') {
|
||||||
if (request.objects) {
|
if (request.objects.length === 1) {
|
||||||
const obj = request.objects[0];
|
if (request.objects) {
|
||||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
const obj = request.objects[0];
|
||||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||||
|
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||||
if (ImageURL) {
|
if (ImageURL) {
|
||||||
obj.set('ImageURL', getPresignedUrl(ImageURL));
|
obj.set('ImageURL', getPresignedUrl(ImageURL));
|
||||||
|
}
|
||||||
|
if (Initials) {
|
||||||
|
obj.set('Initials', getPresignedUrl(Initials));
|
||||||
|
}
|
||||||
|
return [obj];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Initials) {
|
|
||||||
obj.set('Initials', getPresignedUrl(Initials));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [obj];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
import getPresignedUrl from './getSignedUrl.js';
|
import getPresignedUrl from './getSignedUrl.js';
|
||||||
|
|
||||||
async function TemplateAfterFind(request) {
|
async function TemplateAfterFind(request) {
|
||||||
if (request.objects.length === 1) {
|
if (useLocal !== 'true') {
|
||||||
if (request.objects) {
|
if (request.objects.length === 1) {
|
||||||
const obj = request.objects[0];
|
if (request.objects) {
|
||||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
const obj = request.objects[0];
|
||||||
const Url = obj?.get('URL') && obj?.get('URL');
|
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
const Url = obj?.get('URL') && obj?.get('URL');
|
||||||
if (SignedUrl) {
|
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
if (SignedUrl) {
|
||||||
}
|
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||||
if (Url) {
|
}
|
||||||
obj.set('URL', getPresignedUrl(Url));
|
if (Url) {
|
||||||
}
|
obj.set('URL', getPresignedUrl(Url));
|
||||||
if (certificateUrl) {
|
}
|
||||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
if (certificateUrl) {
|
||||||
}
|
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||||
|
}
|
||||||
|
|
||||||
return [obj];
|
return [obj];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,14 @@ export default async function TemplateAfterSave(request) {
|
|||||||
console.log('new entry is insert in contracts_Template');
|
console.log('new entry is insert in contracts_Template');
|
||||||
// update acl of New Document If There are signers present in array
|
// update acl of New Document If There are signers present in array
|
||||||
const signers = request.object.get('Signers');
|
const signers = request.object.get('Signers');
|
||||||
|
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
|
||||||
|
if (AutoReminder) {
|
||||||
|
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
|
||||||
|
const ReminderDate = new Date(request?.object?.get('createdAt'));
|
||||||
|
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
|
||||||
|
request.object.set('NextReminderDate', ReminderDate);
|
||||||
|
await request.object.save(null, { useMasterKey: true });
|
||||||
|
}
|
||||||
if (signers && signers.length > 0) {
|
if (signers && signers.length > 0) {
|
||||||
await updateAclDoc(request.object.id);
|
await updateAclDoc(request.object.id);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
import getPresignedUrl from './getSignedUrl.js';
|
import getPresignedUrl from './getSignedUrl.js';
|
||||||
|
|
||||||
async function TenantAterFind(request) {
|
async function TenantAterFind(request) {
|
||||||
if (request.objects.length === 1) {
|
if (useLocal !== 'true') {
|
||||||
if (request.objects) {
|
if (request.objects.length === 1) {
|
||||||
const obj = request.objects[0];
|
if (request.objects) {
|
||||||
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
const obj = request.objects[0];
|
||||||
if (Logo) {
|
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
||||||
obj.set('Logo', getPresignedUrl(Logo));
|
if (Logo) {
|
||||||
|
obj.set('Logo', getPresignedUrl(Logo));
|
||||||
|
}
|
||||||
|
return [obj];
|
||||||
}
|
}
|
||||||
return [obj];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
|
import { useLocal } from '../../Utils.js';
|
||||||
import getPresignedUrl from './getSignedUrl.js';
|
import getPresignedUrl from './getSignedUrl.js';
|
||||||
|
|
||||||
async function UserAfterFind(request) {
|
async function UserAfterFind(request) {
|
||||||
if (request.objects.length === 1) {
|
if (useLocal !== 'true') {
|
||||||
if (request.objects) {
|
if (request.objects.length === 1) {
|
||||||
const obj = request.objects[0];
|
if (request.objects) {
|
||||||
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
const obj = request.objects[0];
|
||||||
|
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
||||||
if (ProfilePic) {
|
if (ProfilePic) {
|
||||||
obj.set('ProfilePic', getPresignedUrl(ProfilePic));
|
obj.set('ProfilePic', getPresignedUrl(ProfilePic));
|
||||||
|
}
|
||||||
|
return [obj];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [obj];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,41 @@
|
|||||||
|
|
||||||
//-- Export Modules
|
//-- Export Modules
|
||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
dotenv.config()
|
dotenv.config();
|
||||||
import axios from "axios";
|
import axios from 'axios';
|
||||||
|
|
||||||
export async function getUserGroups(request) {
|
export async function getUserGroups(request) {
|
||||||
try {
|
try {
|
||||||
var appname = request.params.appname;
|
var appname = request.params.appname;
|
||||||
if (appname == "") {
|
if (appname == '') {
|
||||||
return Promise.reject("Error:please provide appname");
|
return Promise.reject('Error:please provide appname');
|
||||||
}
|
}
|
||||||
var response = {};
|
var response = {};
|
||||||
var rolelist = {};
|
var rolelist = {};
|
||||||
appname = appname + "_";
|
appname = appname + '_';
|
||||||
//--function to get the userid from session token
|
//--function to get the userid from session token
|
||||||
function getuserid(request) {
|
function getuserid(request) {
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
const options = {
|
const options = {
|
||||||
url: process.env.SERVER_URL + "/users/me",
|
url: process.env.SERVER_URL + '/users/me',
|
||||||
method: "get",
|
method: 'get',
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": process.env.APP_ID,
|
'X-Parse-Application-Id': process.env.APP_ID,
|
||||||
"X-Parse-Session-Token": request.headers["sessiontoken"],
|
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
axios(options)
|
axios(options)
|
||||||
.then((x) => {
|
.then(x => {
|
||||||
const body = x.data;
|
const body = x.data;
|
||||||
var error = body == "" ? true : false;
|
var error = body == '' ? true : false;
|
||||||
if (error) {
|
if (error) {
|
||||||
reject("result not found!");
|
reject('result not found!');
|
||||||
} else {
|
} else {
|
||||||
resolve(body);
|
resolve(body);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return;
|
return;
|
||||||
@@ -46,7 +46,7 @@ export async function getUserGroups(request) {
|
|||||||
}
|
}
|
||||||
var userData = await getuserid(request);
|
var userData = await getuserid(request);
|
||||||
var userid = userData.objectId;
|
var userid = userData.objectId;
|
||||||
console.log("userid " + userid);
|
// console.log("userid " + userid);
|
||||||
var url =
|
var url =
|
||||||
process.env.SERVER_URL +
|
process.env.SERVER_URL +
|
||||||
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
|
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
|
||||||
@@ -60,29 +60,29 @@ export async function getUserGroups(request) {
|
|||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
const options = {
|
const options = {
|
||||||
url: url,
|
url: url,
|
||||||
method: "get",
|
method: 'get',
|
||||||
headers: {
|
headers: {
|
||||||
"X-Parse-Application-Id": process.env.APP_ID,
|
'X-Parse-Application-Id': process.env.APP_ID,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
axios(options)
|
axios(options)
|
||||||
.then((x) => {
|
.then(x => {
|
||||||
const body = x.data;
|
const body = x.data;
|
||||||
var roleres = [];
|
var roleres = [];
|
||||||
for (var i = 0; i < body["results"].length; i++) {
|
for (var i = 0; i < body['results'].length; i++) {
|
||||||
var rolename = body["results"][i]["name"];
|
var rolename = body['results'][i]['name'];
|
||||||
//var roleprefix = rolename.split("_")[0];
|
//var roleprefix = rolename.split("_")[0];
|
||||||
roleres.push(rolename);
|
roleres.push(rolename);
|
||||||
}
|
}
|
||||||
var error = roleres == "" ? true : false;
|
var error = roleres == '' ? true : false;
|
||||||
if (error) {
|
if (error) {
|
||||||
reject("result not found!");
|
reject('result not found!');
|
||||||
} else {
|
} else {
|
||||||
resolve(roleres);
|
resolve(roleres);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return;
|
return;
|
||||||
@@ -92,7 +92,7 @@ export async function getUserGroups(request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rolelist = await getRoleList(request);
|
rolelist = await getRoleList(request);
|
||||||
console.log(rolelist);
|
// console.log(rolelist);
|
||||||
//--check user roles according to appId
|
//--check user roles according to appId
|
||||||
var rolesInapp = [];
|
var rolesInapp = [];
|
||||||
for (let i = 0; i < rolelist.length; i++) {
|
for (let i = 0; i < rolelist.length; i++) {
|
||||||
@@ -102,11 +102,11 @@ export async function getUserGroups(request) {
|
|||||||
rolesInapp.push(rolelist[i]);
|
rolesInapp.push(rolelist[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(rolesInapp);
|
// console.log(rolesInapp);
|
||||||
return rolesInapp;
|
return rolesInapp;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in usergroup");
|
console.log('err in usergroup');
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return Promise.reject("Error:Result not found");
|
return Promise.reject('Error:Result not found');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
export default async function VerifyEmail(request) {
|
||||||
|
try {
|
||||||
|
if (!request?.user) {
|
||||||
|
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||||
|
} else {
|
||||||
|
let otpN = request.params.otp;
|
||||||
|
let otp = parseInt(otpN);
|
||||||
|
let email = request.params.email;
|
||||||
|
|
||||||
|
//checking otp is correct or not which already save in defaultdata_Otp class
|
||||||
|
const checkOtp = new Parse.Query('defaultdata_Otp');
|
||||||
|
checkOtp.equalTo('Email', email);
|
||||||
|
checkOtp.equalTo('OTP', otp);
|
||||||
|
|
||||||
|
const res = await checkOtp.first({ useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
// Fetch the user by their objectId
|
||||||
|
const isEmailVerified = request?.user?.get('emailVerified');
|
||||||
|
if (isEmailVerified) {
|
||||||
|
return { message: 'Email is already verified.' };
|
||||||
|
} else {
|
||||||
|
const userQuery = new Parse.Query(Parse.User);
|
||||||
|
const user = await userQuery.get(request?.user.id, {
|
||||||
|
sessionToken: request?.user.getSessionToken(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the emailVerified field to true
|
||||||
|
user.set('emailVerified', true);
|
||||||
|
// Save the user object
|
||||||
|
const res = await user.save(null, { useMasterKey: true });
|
||||||
|
if (res) {
|
||||||
|
return { message: 'Email is verified.' };
|
||||||
|
} else {
|
||||||
|
const error = new Error('Something went wrong, please try again later!');
|
||||||
|
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const error = new Error('OTP is invalid.');
|
||||||
|
error.code = 400; // Set the error code (e.g., 400 for bad request)
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('err ', err.code + ' ' + err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user