mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-04 08:48:55 +02:00
Compare commits
196
Commits
v1.4.0-beta
...
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebeeedc96c | ||
|
|
5cecf3390b | ||
|
|
9890d568fd | ||
|
|
06bcae64be | ||
|
|
fb017c4bcd | ||
|
|
01d3edd262 | ||
|
|
41a025715e | ||
|
|
306768fbd2 | ||
|
|
d82da53258 | ||
|
|
673245df02 | ||
|
|
42549e5792 | ||
|
|
cc8b4b7157 | ||
|
|
4cbd7e74f7 | ||
|
|
b300a5bb39 | ||
|
|
5422fefc54 | ||
|
|
248b0cac0b | ||
|
|
a388a7c345 | ||
|
|
fc564ca37b | ||
|
|
37b2e4c97a | ||
|
|
a3bc9817e7 | ||
|
|
8a216e6c0d | ||
|
|
613b1cfe07 | ||
|
|
ee77534e35 | ||
|
|
737b33db1e | ||
|
|
ac214d0467 | ||
|
|
f8b21e7a93 | ||
|
|
7e6df13b71 | ||
|
|
3e584cec1a | ||
|
|
bc75dd109f | ||
|
|
6b3abed782 | ||
|
|
f163958cd0 | ||
|
|
ecd461b96c | ||
|
|
1371c2c09a | ||
|
|
3121f35158 | ||
|
|
3c0db9473c | ||
|
|
4f2b72dd4d | ||
|
|
7d9af1db95 | ||
|
|
0e38783560 | ||
|
|
6880ae35a8 | ||
|
|
9a07c67b69 | ||
|
|
33bfdeef15 | ||
|
|
a404247604 | ||
|
|
c48579daa7 | ||
|
|
509f8f9a07 | ||
|
|
5b85d5b277 | ||
|
|
bbf3170398 | ||
|
|
c172a7c55c | ||
|
|
90f3e0066b | ||
|
|
b6e028a2c0 | ||
|
|
11384192ba | ||
|
|
9df63265fb | ||
|
|
ff6a8ce755 | ||
|
|
60c1aa253a | ||
|
|
ad2fec0d66 | ||
|
|
0451a6cae0 | ||
|
|
0d5f8981fe | ||
|
|
cfd8be0eee | ||
|
|
86e5ef6ef5 | ||
|
|
1af47c8425 | ||
|
|
f0395cb2c7 | ||
|
|
598aa2975f | ||
|
|
0742291216 | ||
|
|
94eeceeaf9 | ||
|
|
970fbecb7b | ||
|
|
361e082930 | ||
|
|
3f9b6eb5a9 | ||
|
|
2265bee61f | ||
|
|
6390706d8d | ||
|
|
37e03c1026 | ||
|
|
c6fa6c710d | ||
|
|
2e921e30b6 | ||
|
|
686a773d34 | ||
|
|
b03e1b8f9d | ||
|
|
f1ceae794f | ||
|
|
5a8bcb8f9a | ||
|
|
ce70da7d8e | ||
|
|
db03dfa641 | ||
|
|
7e70a5e111 | ||
|
|
f5f18f1630 | ||
|
|
fa6856c649 | ||
|
|
3b4946e47b | ||
|
|
05fe68d458 | ||
|
|
2f3cdca821 | ||
|
|
4d8a54a6c5 | ||
|
|
4fe5bdaad8 | ||
|
|
b95ede5273 | ||
|
|
5b2684a9f4 | ||
|
|
d779720f26 | ||
|
|
3cf8029978 | ||
|
|
0bb857a942 | ||
|
|
103d32edcb | ||
|
|
eca6ad374b | ||
|
|
d2efe88266 | ||
|
|
450e5ec7f7 | ||
|
|
3bbbda91b8 | ||
|
|
a48db306bb | ||
|
|
2a60b30c75 | ||
|
|
19cfa9d27d | ||
|
|
0fc12c957e | ||
|
|
6fd332f8b4 | ||
|
|
39f7ccecaa | ||
|
|
3bd306809b | ||
|
|
04e9d13206 | ||
|
|
b24739d61b | ||
|
|
9dda211605 | ||
|
|
93708a6875 | ||
|
|
e78f6f6df4 | ||
|
|
ccaaca75ba | ||
|
|
dedff36b35 | ||
|
|
4d561ca19e | ||
|
|
f70e9e2a3f | ||
|
|
e4a3ce405e | ||
|
|
c3be2a1fef | ||
|
|
8b2f7f1e95 | ||
|
|
55465a035f | ||
|
|
d30105299c | ||
|
|
78b038da13 | ||
|
|
d230ad2655 | ||
|
|
367c824918 | ||
|
|
5f8affb1bb | ||
|
|
3bd23f83cf | ||
|
|
ab1dbecab2 | ||
|
|
44c6aa9b0b | ||
|
|
ad922c16c7 | ||
|
|
87ee2e881e | ||
|
|
3659a1de55 | ||
|
|
cc49a2fd4e | ||
|
|
babd43e710 | ||
|
|
8dde38edf1 | ||
|
|
6c16d00031 | ||
|
|
99f444a8a1 | ||
|
|
f0149f6fd8 | ||
|
|
bdb7d9be8f | ||
|
|
27dcd7fefd | ||
|
|
a2467ef1a6 | ||
|
|
824b0a5407 | ||
|
|
ac8195a52d | ||
|
|
e0920bf5d8 | ||
|
|
4a5f030d87 | ||
|
|
ba839b0fc8 | ||
|
|
27130fd223 | ||
|
|
19ba9e3091 | ||
|
|
8c64a89f08 | ||
|
|
1c3d2d5c9a | ||
|
|
b36df32b19 | ||
|
|
155a3eee14 | ||
|
|
7bb46782a3 | ||
|
|
40961936d0 | ||
|
|
db310b0d73 | ||
|
|
f0e9c6b3e4 | ||
|
|
97bd375de3 | ||
|
|
e2d450f9aa | ||
|
|
add01cb02a | ||
|
|
cea1653805 | ||
|
|
9d81fce956 | ||
|
|
79e3762b36 | ||
|
|
aa04a957ed | ||
|
|
d31910aefa | ||
|
|
96a4a2e7fa | ||
|
|
e8aaab99f9 | ||
|
|
c61be7c35b | ||
|
|
0ee85f53da | ||
|
|
748e2196da | ||
|
|
58199a934a | ||
|
|
7eec822a65 | ||
|
|
6c21028523 | ||
|
|
099a3cc46d | ||
|
|
5adaffc684 | ||
|
|
d588caf6d4 | ||
|
|
5930fd940d | ||
|
|
741f66b019 | ||
|
|
0bf07c2703 | ||
|
|
de26c2d719 | ||
|
|
881ac12b03 | ||
|
|
1932dda6d6 | ||
|
|
46159b7214 | ||
|
|
c57c15a4cb | ||
|
|
724fa7c27e | ||
|
|
2762c3e170 | ||
|
|
10a0c60056 | ||
|
|
036c9b8d4a | ||
|
|
1f50368259 | ||
|
|
6d5e07dac7 | ||
|
|
3656175f11 | ||
|
|
febb50c3cd | ||
|
|
f8ba0d357b | ||
|
|
74bfd39abc | ||
|
|
5a25b2d874 | ||
|
|
3115ff3e2a | ||
|
|
d1e7b9a2ab | ||
|
|
adbf1fe1e0 | ||
|
|
60140885e0 | ||
|
|
fe2f3c5f7c | ||
|
|
a06723ff3f | ||
|
|
8a01efee4c | ||
|
|
eafb26d462 |
@@ -42,24 +42,24 @@ Please star ⭐ the repo to support us! 😀
|
||||
|
||||
### Introduction
|
||||
|
||||
Welcome to OpenSign, the premier open-source docusign alternative - document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
||||
Welcome to OpenSign, the premier open source docusign alternative - document e-signing solution designed to provide a secure, reliable and free alternative to commercial esign platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
|
||||
|
||||
---
|
||||
|
||||
### Features
|
||||
|
||||
- **Secure PDF E-Signing:** With the help of Robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
|
||||
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that comes with hand drawn signatures support as well as uploaded images & saved signatures for the simplest signing experience.
|
||||
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "One click signatures" and "OpenSign Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
|
||||
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players.
|
||||
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility.
|
||||
- **Annotate Documents:** OpenSign™ allows you to annotate PDF documents with an advanced signing pad that allows hand drawn signatures, uploaded images, typed signatures & saved signatures for the simplest signing experience ever.
|
||||
- **User-Friendly Interface:** OpenSign™ was built while keeping Intuitive design in mind for ease of use. Features like "Sign yourself", "Templates", "One click signatures" and "OpenSign™ Drive" makes it stand out of the crowd and even makes it better than a lot of so-called industry leaders.
|
||||
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite witnesses & being able to enforce signing in a sequence makes it the only open source solution that is fully loaded and allows it to compete head-to-head with established players in e-signature space.
|
||||
- **Email Unique Code(OTP) verification support for guest signers:** With OpenSign™, your documents are fully secure even when being signed by guest users. Guest signers can only sign the document after entering a unique code sent to their email address.
|
||||
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign it. Not just this, OpenSign also allows signers to reject signing a document.
|
||||
- **"Expiring Docs" & "Rejection":** You can set documents to expire after certain number of days after which nobody will be able to sign. Not just this, OpenSign™ also allows signers to reject signing a document.
|
||||
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates.
|
||||
- **PDF Template Creation(coming soon):** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time.
|
||||
- **OpenSign™ Drive:** It is a centralised secure vault for your signed documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
|
||||
- **PDF Template Creation:** OpenSign™ allows you to create and store PDF document templates for repeated use thereby saving you a lot of time & collect e-signatures seamlessly.
|
||||
- **OpenSign™ Drive:** It is a centralised secure vault for your digital documents that makes storing, signing, organizing, sharing & achieving your docs a breeze.
|
||||
- **Audit Trails & completion certificate:** Being a security focused solution, OpenSign™ makes it a top priority to save detailed logs for tracking document activities along with time-stamps, IP addresses, email IDs & phone numbers. A completion certificate is generated as soon as document is completed which contains all the document related logs for added safety.
|
||||
- **API Support(coming soon):** OpenSign™ API allows seamless integration into existing systems and software. APIs will soon be available as a cloud hosted solution.
|
||||
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is coming soon.
|
||||
- **API Support:** OpenSign™ API allows seamless integration into existing systems and software. You can generate an API key from the app and refer the [docs](https://docs.opensignlabs.com) to start integrating it in your existing applications.
|
||||
- **Integrations:** Seamless integrations with various Cloud storage systems, CRMs & enterprise platforms is available. We also have a Zapier integration that allows you to integrate it with virtually any application.
|
||||
|
||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/b37ff443-7099-4273-9aeb-21c46d7154cf height='200'>
|
||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/86db91b7-6c2f-4885-a33c-58f4fd35ec89 height='200'>
|
||||
@@ -76,7 +76,7 @@ Please refer to the [Installation Guide](INSTALLATION.md) for detailed instructi
|
||||
|
||||
### Usage
|
||||
|
||||
For comprehensive guidelines on how to use OpenSign, please consult our [User Manual](USAGE.md).
|
||||
For comprehensive guidelines on how to use OpenSign™, please consult our [User Manual](USAGE.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Generated
+175
-5
@@ -22,6 +22,7 @@
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-cookie": "^7.1.4",
|
||||
"react-datepicker": "^6.4.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
@@ -33,6 +34,7 @@
|
||||
"react-konva": "^18.2.10",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
@@ -41,6 +43,7 @@
|
||||
"react-select": "^5.8.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.3",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.2",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
@@ -6054,6 +6057,11 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.44.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.4.tgz",
|
||||
@@ -6108,11 +6116,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.3.tgz",
|
||||
"integrity": "sha512-Wny3a2UXn5FEA1l7gc6BbpoV5mD1XijZqgkp4TRgDCDL5r3B5ieOFGUX5h3n78Tr1MEG7BfvoM8qeztdvNU0fw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
|
||||
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
@@ -6205,6 +6211,14 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz",
|
||||
"integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg=="
|
||||
},
|
||||
"node_modules/@types/quill": {
|
||||
"version": "1.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
|
||||
"integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
|
||||
"dependencies": {
|
||||
"parchment": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz",
|
||||
@@ -9428,6 +9442,25 @@
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
|
||||
"integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA=="
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz",
|
||||
"integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==",
|
||||
"dependencies": {
|
||||
"is-arguments": "^1.1.1",
|
||||
"is-date-object": "^1.0.5",
|
||||
"is-regex": "^1.1.4",
|
||||
"object-is": "^1.1.5",
|
||||
"object-keys": "^1.1.1",
|
||||
"regexp.prototype.flags": "^1.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/deep-extend": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||
@@ -10986,6 +11019,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"node_modules/external-editor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
|
||||
@@ -12612,6 +12650,21 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"has-tostringtag": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
|
||||
@@ -16841,6 +16894,21 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-is": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
|
||||
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/object-keys": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||
@@ -17194,6 +17262,11 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/parchment": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
|
||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -19134,6 +19207,50 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/quill": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
|
||||
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
|
||||
"dependencies": {
|
||||
"clone": "^2.1.1",
|
||||
"deep-equal": "^1.0.1",
|
||||
"eventemitter3": "^2.0.3",
|
||||
"extend": "^3.0.2",
|
||||
"parchment": "^1.1.4",
|
||||
"quill-delta": "^3.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
|
||||
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
|
||||
"dependencies": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"extend": "^3.0.2",
|
||||
"fast-diff": "1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/quill-delta/node_modules/fast-diff": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
|
||||
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
|
||||
},
|
||||
"node_modules/quill/node_modules/clone": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/quill/node_modules/eventemitter3": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
|
||||
"integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg=="
|
||||
},
|
||||
"node_modules/radix-ui": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.0.1.tgz",
|
||||
@@ -19354,6 +19471,19 @@
|
||||
"react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-cookie": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz",
|
||||
"integrity": "sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw==",
|
||||
"dependencies": {
|
||||
"@types/hoist-non-react-statics": "^3.3.5",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"universal-cookie": "^7.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-datepicker": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-6.4.0.tgz",
|
||||
@@ -19732,6 +19862,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-quill": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz",
|
||||
"integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==",
|
||||
"dependencies": {
|
||||
"@types/quill": "^1.3.10",
|
||||
"lodash": "^4.17.4",
|
||||
"quill": "^1.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16 || ^17 || ^18",
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-reconciler": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
|
||||
@@ -20082,6 +20226,15 @@
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-web-share": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-web-share/-/react-web-share-2.0.2.tgz",
|
||||
"integrity": "sha512-bGm1TJc6xtC0MhAnYFsrzT4En7l2oAdLTgCK7nk7b4xGTg3L5gOldiPrmYQ8a/dEXNydgekvOj/tfjduIlIRVA==",
|
||||
"peerDependencies": {
|
||||
"react": "^16 || ^17 || ^18",
|
||||
"react-dom": "^16 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/reactour": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.2.tgz",
|
||||
@@ -22701,6 +22854,23 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-cookie": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz",
|
||||
"integrity": "sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g==",
|
||||
"dependencies": {
|
||||
"@types/cookie": "^0.6.0",
|
||||
"cookie": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-cookie/node_modules/cookie": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-cookie": "^7.1.4",
|
||||
"react-datepicker": "^6.4.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
@@ -28,6 +29,7 @@
|
||||
"react-konva": "^18.2.10",
|
||||
"react-modal": "^3.16.1",
|
||||
"react-pdf": "^7.7.1",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-router-dom": "^6.22.3",
|
||||
@@ -36,6 +38,7 @@
|
||||
"react-select": "^5.8.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.3",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.2",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<!-- <link rel="icon" type="image/png"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
|
||||
sizes="40x40" /> -->
|
||||
@@ -15,7 +15,7 @@
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
|
||||
@@ -15,6 +15,7 @@ import DraftDocument from "./components/pdf/DraftDocument";
|
||||
import PlaceHolderSign from "./pages/PlaceHolderSign";
|
||||
import PdfRequestFiles from "./pages/PdfRequestFiles";
|
||||
import LazyPage from "./primitives/LazyPage";
|
||||
import { isEnableSubscription } from "./constant/const";
|
||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||
@@ -113,7 +114,7 @@ function App() {
|
||||
path="/forgetpassword"
|
||||
element={<LazyPage Page={ForgetPassword} />}
|
||||
/>
|
||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
||||
{isEnableSubscription && (
|
||||
<>
|
||||
<Route
|
||||
path="/pgsignup"
|
||||
|
||||
@@ -56,11 +56,11 @@ const AddSigner = (props) => {
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -48,11 +48,11 @@ const AddUser = (props) => {
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_User");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -4,19 +4,43 @@ import FullScreenButton from "./FullScreenButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
|
||||
const Header = ({ showSidebar }) => {
|
||||
const navigation = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
const { width } = useWindowSize();
|
||||
let applogo = localStorage.getItem("appLogo") || "";
|
||||
let username = localStorage.getItem("username");
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||
const [isPro, setIsPro] = useState(false);
|
||||
const [applogo, setAppLogo] = useState(
|
||||
localStorage.getItem("appLogo") || " "
|
||||
);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
useEffect(() => {
|
||||
checkSubscription();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
async function checkSubscription() {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
if (getIsSubscribe) {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo) {
|
||||
setAppLogo(applogo);
|
||||
} else {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
setIsPro(getIsSubscribe);
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
}
|
||||
|
||||
const closeDropdown = () => {
|
||||
setIsOpen(false);
|
||||
@@ -43,7 +67,7 @@ const Header = ({ showSidebar }) => {
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
navigation("/");
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
//handle to close profile drop down menu onclick screen
|
||||
@@ -83,6 +107,21 @@ const Header = ({ showSidebar }) => {
|
||||
id="profile-menu"
|
||||
className="flex justify-between items-center gap-x-3"
|
||||
>
|
||||
{!isSubscribe && (
|
||||
<div>
|
||||
<button
|
||||
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
|
||||
onClick={() => navigate("/subscription")}
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{isPro && (
|
||||
<div className="w-[35px] h-[35px] rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||
PRO
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
@@ -123,7 +162,7 @@ const Header = ({ showSidebar }) => {
|
||||
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigation("/profile");
|
||||
navigate("/profile");
|
||||
}}
|
||||
>
|
||||
<i className="fa-regular fa-user"></i> Profile
|
||||
@@ -132,11 +171,19 @@ const Header = ({ showSidebar }) => {
|
||||
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
navigation("/changepassword");
|
||||
navigate("/changepassword");
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-lock"></i> Change Password
|
||||
</li>
|
||||
<li
|
||||
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={() => {
|
||||
window.open("https://console.opensignlabs.com/");
|
||||
}}
|
||||
>
|
||||
<i className="fa-regular fa-id-card"></i> Console
|
||||
</li>
|
||||
<li
|
||||
className="hover:bg-gray-100 rounded-b-lg py-1 px-2 cursor-pointer font-normal"
|
||||
onClick={closeDropdown}
|
||||
|
||||
@@ -149,7 +149,7 @@ const DashboardCard = (props) => {
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
const listData = res.data?.result.filter(
|
||||
const listData = res.data?.result?.filter(
|
||||
(x) => x.Signers.length > 0
|
||||
);
|
||||
let arr = [];
|
||||
@@ -353,7 +353,7 @@ const DashboardCard = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs absolute top-1 right-1">
|
||||
<div className="text-xs absolute top-2 right-1">
|
||||
<Tooltip id={props.Label} iconColor={"white"} message={props?.Data?.tourMessage} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,11 +97,13 @@ function DashboardReport(props) {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setIsNextRecord(false);
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
if (!res.data.result.error) {
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
@@ -110,6 +112,7 @@ function DashboardReport(props) {
|
||||
console.log("err ", err);
|
||||
setIsLoader(false);
|
||||
}
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
|
||||
@@ -70,6 +70,7 @@ function DriveBody(props) {
|
||||
return item;
|
||||
});
|
||||
props.setPdfData(updatedData);
|
||||
props.sortingData(null, null, updatedData);
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
@@ -214,8 +215,8 @@ function DriveBody(props) {
|
||||
? true
|
||||
: false
|
||||
: selecFolderId
|
||||
? false
|
||||
: true;
|
||||
? false
|
||||
: true;
|
||||
if (!checkExist) {
|
||||
if (moveFolderId) {
|
||||
updateData = {
|
||||
@@ -384,6 +385,7 @@ function DriveBody(props) {
|
||||
flexDirection: "column",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
data-tut={props.dataTutSeventh}
|
||||
>
|
||||
<img
|
||||
alt="no img"
|
||||
@@ -451,6 +453,7 @@ function DriveBody(props) {
|
||||
className="pdf-icon"
|
||||
src={pdfLogo}
|
||||
onClick={() => checkPdfStatus(data)}
|
||||
data-tut={props.dataTutSixth}
|
||||
/>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
|
||||
@@ -30,7 +30,7 @@ const AddRoleModal = (props) => {
|
||||
margin: "10px 0 10px 5px"
|
||||
}}
|
||||
>
|
||||
e.g: Hr, Director, Manager, New joinee, Accountant, etc...
|
||||
e.g: Customer, Hr, Director, Manager, Student, etc...
|
||||
</p>
|
||||
<div>
|
||||
<div
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import { isEnableSubscription, themeColor } from "../../constant/const";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { radioButtonWidget } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
function DropdownWidgetOption(props) {
|
||||
const [dropdownOptionList, setDropdownOptionList] = useState([
|
||||
"option-1",
|
||||
@@ -241,36 +242,59 @@ function DropdownWidgetOption(props) {
|
||||
}}
|
||||
className="fa-solid fa-square-plus"
|
||||
></i>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Minimun check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={minCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMinCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
<label style={{ fontSize: "13px", fontWeight: "600" }}>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className="drodown-input"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
color: !props.isSubscribe && "gray"
|
||||
}}
|
||||
>
|
||||
Minimun check
|
||||
</label>
|
||||
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={minCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMinCount(count);
|
||||
}}
|
||||
className={
|
||||
props.isSubscribe || !isEnableSubscription
|
||||
? "drodown-input"
|
||||
: "disabled drodown-input"
|
||||
}
|
||||
/>
|
||||
<label
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
color: !props.isSubscribe && "gray"
|
||||
}}
|
||||
>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className={
|
||||
props.isSubscribe || !isEnableSubscription
|
||||
? "drodown-input"
|
||||
: "disabled drodown-input"
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||
<>
|
||||
@@ -378,13 +402,15 @@ function DropdownWidgetOption(props) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{props.type === "checkbox" &&
|
||||
!props.isSignYourself &&
|
||||
!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
props.type === "checkbox" && !props.isSignYourself
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import "../../styles/AddUser.css";
|
||||
import { getFileName } from "../../constant/Utils";
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
|
||||
const EditTemplate = ({ template, onSuccess }) => {
|
||||
@@ -45,7 +46,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
fontWeight: "700"
|
||||
}}
|
||||
>
|
||||
{template.URL?.split("/")?.pop()?.split("_")[1]}
|
||||
{getFileName(template.URL)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import React from "react";
|
||||
import { Quill } from "react-quill";
|
||||
|
||||
// Custom Undo button icon component for Quill editor. You can import it directly
|
||||
// from 'quill/assets/icons/undo.svg' but I found that a number of loaders do not
|
||||
// handle them correctly
|
||||
const CustomUndo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="6 10 4 12 2 10 6 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M8.09,13.91A4.6,4.6,0,0,0,9,14,5,5,0,1,0,4,9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Redo button icon component for Quill editor
|
||||
const CustomRedo = () => (
|
||||
<svg viewBox="0 0 18 18">
|
||||
<polygon className="ql-fill ql-stroke" points="12 10 14 12 16 10 12 10" />
|
||||
<path
|
||||
className="ql-stroke"
|
||||
d="M9.91,13.91A4.6,4.6,0,0,1,9,14a5,5,0,1,1,5-5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Undo and redo functions for Custom Toolbar
|
||||
function undoChange() {
|
||||
this.quill.history.undo();
|
||||
}
|
||||
function redoChange() {
|
||||
this.quill.history.redo();
|
||||
}
|
||||
|
||||
// Add sizes to whitelist and register them
|
||||
const Size = Quill.import("formats/size");
|
||||
Size.whitelist = ["extra-small", "small", "medium", "large"];
|
||||
Quill.register(Size, true);
|
||||
|
||||
// Add fonts to whitelist and register them
|
||||
const Font = Quill.import("formats/font");
|
||||
Font.whitelist = [
|
||||
"arial",
|
||||
"comic-sans",
|
||||
"courier-new",
|
||||
"georgia",
|
||||
"helvetica",
|
||||
"lucida"
|
||||
];
|
||||
Quill.register(Font, true);
|
||||
|
||||
// Modules object for setting up the Quill editor
|
||||
export const module1 = {
|
||||
toolbar: {
|
||||
container: "#toolbar1",
|
||||
handlers: {
|
||||
undo: undoChange,
|
||||
redo: redoChange
|
||||
}
|
||||
},
|
||||
history: {
|
||||
delay: 500,
|
||||
maxStack: 100,
|
||||
userOnly: true
|
||||
}
|
||||
};
|
||||
// Formats objects for setting up the Quill editor
|
||||
export const formats = [
|
||||
"header",
|
||||
"font",
|
||||
"size",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
"align",
|
||||
"strike",
|
||||
"script",
|
||||
"blockquote",
|
||||
"background",
|
||||
"list",
|
||||
"bullet",
|
||||
"indent",
|
||||
"link",
|
||||
"image",
|
||||
"color",
|
||||
"code-block"
|
||||
];
|
||||
// Quill Toolbar component
|
||||
export const QuillToolbar = ({ containerId }) => (
|
||||
<div id={containerId}>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-font" defaultValue="arial">
|
||||
<option value="arial">Arial</option>
|
||||
<option value="comic-sans">Comic Sans</option>
|
||||
<option value="courier-new">Courier New</option>
|
||||
<option value="georgia">Georgia</option>
|
||||
<option value="helvetica">Helvetica</option>
|
||||
<option value="lucida">Lucida</option>
|
||||
</select>
|
||||
<select className="ql-size" defaultValue="medium">
|
||||
<option value="extra-small">Size 1</option>
|
||||
<option value="small">Size 2</option>
|
||||
<option value="medium">Size 3</option>
|
||||
<option value="large">Size 4</option>
|
||||
</select>
|
||||
<select className="ql-header" defaultValue="3">
|
||||
<option value="1">Heading</option>
|
||||
<option value="2">Subheading</option>
|
||||
<option value="3">Normal</option>
|
||||
</select>
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-bold" />
|
||||
<button className="ql-italic" />
|
||||
<button className="ql-underline" />
|
||||
<button className="ql-strike" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-list" value="ordered" />
|
||||
<button className="ql-list" value="bullet" />
|
||||
<button className="ql-indent" value="-1" />
|
||||
<button className="ql-indent" value="+1" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-script" value="super" />
|
||||
<button className="ql-script" value="sub" />
|
||||
<button className="ql-blockquote" />
|
||||
<button className="ql-direction" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<select className="ql-align" />
|
||||
<select className="ql-color" />
|
||||
<select className="ql-background" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-link" />
|
||||
<button className="ql-image" />
|
||||
<button className="ql-video" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-formula" />
|
||||
<button className="ql-code-block" />
|
||||
<button className="ql-clean" />
|
||||
</span>
|
||||
<span className="ql-formats">
|
||||
<button className="ql-undo">
|
||||
<CustomUndo />
|
||||
</button>
|
||||
<button className="ql-redo">
|
||||
<CustomRedo />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default QuillToolbar;
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.snow.css";
|
||||
import EditorToolbar, { module1, formats } from "./EditorToolbar";
|
||||
|
||||
export function EmailBody(props) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={`flex justify-center py-1 p-2`}>
|
||||
<div className=" flex flex-col">
|
||||
<form>
|
||||
<div className="text-lg font-normal text-black">
|
||||
<div className=" m-2 md:m-10 p-3 md:p-10 shadow-md hover:shadow-lg border-[1px] border-indigo-800 rounded-md">
|
||||
<label className="text-sm ml-2">
|
||||
Subject <Tooltip message={"email subject"} />
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
value={props.requestSubject}
|
||||
onChange={(e) => props.setRequestSubject(e.target.value)}
|
||||
placeholder="${senderName} has requested you to sign ${documentName}"
|
||||
className="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">
|
||||
Body <Tooltip message={"email body"} />
|
||||
</label>
|
||||
|
||||
<div className="px-3 py-2 w-full focus:outline-none text-xs">
|
||||
<EditorToolbar containerId="toolbar1" />
|
||||
<ReactQuill
|
||||
theme="snow"
|
||||
value={props.requestBody}
|
||||
// value={typedText}
|
||||
placeholder="add body of email "
|
||||
// readOnly={loading}
|
||||
// onChangeSelection={handleTextSelection} // Listen for text selection
|
||||
ref={props.editorRef}
|
||||
modules={module1}
|
||||
formats={formats}
|
||||
onChange={props.handleOnchangeRequest}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -15,7 +15,8 @@ function EmailComponent({
|
||||
setSuccessEmail,
|
||||
pdfName,
|
||||
sender,
|
||||
setIsAlert
|
||||
setIsAlert,
|
||||
extUserId
|
||||
}) {
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState();
|
||||
@@ -38,6 +39,7 @@ function EmailComponent({
|
||||
const openSignUrl = "https://www.opensignlabs.com/contact-us";
|
||||
const themeBGcolor = themeColor;
|
||||
let params = {
|
||||
extUserId: extUserId,
|
||||
pdfName: pdfName,
|
||||
url: pdfUrl,
|
||||
recipient: emailList[i],
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
import Certificate from "./Certificate";
|
||||
import printModule from "print-js";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { saveAs } from "file-saver";
|
||||
@@ -9,18 +7,17 @@ import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import Certificate from "./Certificate";
|
||||
import { PDFDownloadLink } from "@react-pdf/renderer";
|
||||
|
||||
function Header({
|
||||
isPdfRequestFiles,
|
||||
isPlaceholder,
|
||||
recipient,
|
||||
setIsDecline,
|
||||
pageNumber,
|
||||
isAlreadySign,
|
||||
allPages,
|
||||
changePage,
|
||||
pdfUrl,
|
||||
documentStatus,
|
||||
embedWidgetsData,
|
||||
pdfDetails,
|
||||
signerPos,
|
||||
@@ -94,8 +91,21 @@ function Header({
|
||||
// Replace spaces with underscore
|
||||
return pdfName.replace(/ /g, "_");
|
||||
};
|
||||
//certificate generate and download component in mobile view
|
||||
const CertificateDropDown = () => {
|
||||
|
||||
//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) {
|
||||
@@ -122,76 +132,20 @@ function Header({
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
>
|
||||
{({ blob, loading }) => (
|
||||
<>
|
||||
{loading ? (
|
||||
<div
|
||||
style={{
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{ border: "none", backgroundColor: "#fff" }}
|
||||
onClick={() =>
|
||||
handleDownload(
|
||||
blob,
|
||||
`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`
|
||||
)
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
);
|
||||
};
|
||||
const CertificateComponent = () => {
|
||||
return (
|
||||
<PDFDownloadLink
|
||||
style={{ textDecoration: "none" }}
|
||||
document={<Certificate pdfData={pdfDetails} />}
|
||||
fileName={`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`}
|
||||
>
|
||||
{({ loading }) => (
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn certificateBtn"
|
||||
onClick={() =>
|
||||
handleDownload(
|
||||
blob,
|
||||
`completion certificate-${
|
||||
pdfDetails[0] && pdfDetails[0].Name
|
||||
}.pdf`
|
||||
)
|
||||
}
|
||||
disabled={loading}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
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"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
<i className="fa-solid fa-award py-[3px]" aria-hidden="true"></i>
|
||||
<span className="md:hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
</PDFDownloadLink>
|
||||
@@ -210,11 +164,7 @@ function Header({
|
||||
}}
|
||||
>
|
||||
<div className="preBtn2">
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
>
|
||||
<div onClick={() => navigate(-1)}>
|
||||
<i
|
||||
className="fa fa-arrow-left"
|
||||
aria-hidden="true"
|
||||
@@ -266,7 +216,6 @@ function Header({
|
||||
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="DropdownMenuContent"
|
||||
@@ -290,42 +239,53 @@ function Header({
|
||||
Download
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
{recipient && pdfDetails[0] && pdfDetails.length > 0 ? (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
) : isPdfRequestFiles &&
|
||||
alreadySign &&
|
||||
isCompleted.isCertificate ? (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
) : (
|
||||
isSignYourself && (
|
||||
<>
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<CertificateDropDown />
|
||||
</DropdownMenu.Item>
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsEmail(true)}
|
||||
onClick={() => handleDownloadCertificate()}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
border: "none",
|
||||
backgroundColor: "#fff"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-envelope"
|
||||
className="fa-solid fa-award"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
Certificate
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<DropdownMenu.Item className="DropdownMenuItem">
|
||||
<GenerateCertificate />
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isSignYourself && (
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa fa-envelope"
|
||||
style={{ marginRight: "2px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
)}
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
@@ -337,7 +297,6 @@ function Header({
|
||||
flexDirection: "row"
|
||||
}}
|
||||
>
|
||||
{" "}
|
||||
<i
|
||||
className="fa fa-print"
|
||||
aria-hidden="true"
|
||||
@@ -416,217 +375,126 @@ function Header({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="signatureHeader headerBtn">
|
||||
<div className="flex flex-wrap justify-between items-center mt-[5px]">
|
||||
<PrevNext
|
||||
pageNumber={pageNumber}
|
||||
allPages={allPages}
|
||||
changePage={changePage}
|
||||
/>
|
||||
|
||||
{recipient ? (
|
||||
pdfUrl || isAlreadySign.mssg ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{pdfDetails && pdfDetails.length > 0 && (
|
||||
<CertificateComponent />
|
||||
)}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="defaultBtn printBtn"
|
||||
>
|
||||
<i
|
||||
className="fa fa-print"
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
marginRight: "3px",
|
||||
color: "white"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="defaultBtn downloadBtn"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<button
|
||||
style={{
|
||||
background: "#de4337"
|
||||
}}
|
||||
className="finishBtn hoverCss"
|
||||
onClick={() => {
|
||||
handleDeclinePdfAlert();
|
||||
}}
|
||||
>
|
||||
Decline
|
||||
</button>
|
||||
|
||||
<button
|
||||
data-tut="reactourThird"
|
||||
style={{
|
||||
background: !pdfUrl ? "#188ae2" : "#d3edeb"
|
||||
}}
|
||||
className="finishBtn sendHover"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
) : isPlaceholder ? (
|
||||
{isPlaceholder ? (
|
||||
<>
|
||||
{!isMailSend &&
|
||||
signersdata.length > 0 &&
|
||||
signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length}{" "}
|
||||
recipients signature
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length} more
|
||||
recipients signature
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="flex mx-[100px] lg:mx-0 order-last lg:order-none">
|
||||
{!isMailSend &&
|
||||
signersdata.length > 0 &&
|
||||
signersdata.length !== filterPrefill.length && (
|
||||
<div>
|
||||
{filterPrefill.length === 0 ? (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length}{" "}
|
||||
recipients signature
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ fontSize: "13px", color: "#f5405e" }}>
|
||||
Add {signersdata.length - filterPrefill.length} more
|
||||
recipients signature
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
{setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => setIsEditTemplate(true)}
|
||||
style={{
|
||||
border: "none",
|
||||
outline: "none",
|
||||
textAlign: "center"
|
||||
}}
|
||||
className="outline-none border-none text-center mr-[5px]"
|
||||
>
|
||||
<i className="fa-solid fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
onClick={() => navigate(-1)}
|
||||
type="button"
|
||||
className="defaultBtn backBtn"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
|
||||
<button
|
||||
disabled={isMailSend && true}
|
||||
data-tut="reactourFour"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] font-[500] text-sm mr-[5px]"
|
||||
style={{
|
||||
background: isMailSend ? "rgb(203, 203, 203)" : "#188ae2",
|
||||
color: isMailSend && "rgb(77, 75, 75)"
|
||||
color: isMailSend ? "rgb(77, 75, 75)" : "white"
|
||||
}}
|
||||
onClick={() => {
|
||||
alertSendEmail();
|
||||
}}
|
||||
className={isMailSend ? "sendMail" : "sendMail sendHover"}
|
||||
>
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
{completeBtnTitle
|
||||
? completeBtnTitle
|
||||
: isMailSend
|
||||
? "Sent"
|
||||
: "Send"}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : isPdfRequestFiles ? (
|
||||
alreadySign ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
{isCompleted.isCertificate && <CertificateComponent />}
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<button
|
||||
type="button"
|
||||
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]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">
|
||||
Certificate
|
||||
</span>
|
||||
</button>
|
||||
) : (
|
||||
<GenerateCertificate />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
type="button"
|
||||
className="defaultBtn printBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||
>
|
||||
<i
|
||||
className="fa fa-print"
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
marginRight: "3px",
|
||||
color: "white"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Print</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn downloadBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
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()}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
onClick={() => navigate(-1)}
|
||||
type="button"
|
||||
className="defaultBtn backBtn"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
{currentSigner && (
|
||||
<>
|
||||
<button
|
||||
style={{
|
||||
background: "#de4337"
|
||||
}}
|
||||
className="finishBtn hoverCss"
|
||||
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-[#f14343]"
|
||||
onClick={() => {
|
||||
handleDeclinePdfAlert();
|
||||
}}
|
||||
@@ -638,7 +506,7 @@ function Header({
|
||||
background: "#188ae2"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn sendHover"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||
onClick={() => {
|
||||
embedWidgetsData();
|
||||
}}
|
||||
@@ -649,93 +517,69 @@ function Header({
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
) : pdfUrl || (documentStatus && documentStatus.isCompleted) ? (
|
||||
) : isCompleted ? (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<CertificateComponent />
|
||||
{isCompleted && (
|
||||
<>
|
||||
{pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? (
|
||||
<button
|
||||
type="button"
|
||||
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]"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
) : (
|
||||
<GenerateCertificate />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
type="button"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center"
|
||||
}}
|
||||
className="defaultBtn printBtn"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||
>
|
||||
<i
|
||||
className="fa fa-print"
|
||||
style={{
|
||||
fontSize: "15px",
|
||||
marginRight: "3px",
|
||||
color: "white"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Print</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn downloadBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
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()}
|
||||
>
|
||||
<i
|
||||
className="fa fa-download"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Download</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="defaultBtn mailBtn"
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginLeft: "10px"
|
||||
}}
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#3ba7e5]"
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<i
|
||||
className="fa fa-envelope"
|
||||
style={{
|
||||
color: "white",
|
||||
fontSize: "15px",
|
||||
marginRight: "3px"
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
<i className="fa fa-envelope py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Mail</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
}}
|
||||
type="button"
|
||||
className="defaultBtn backBtn"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-black font-[500] text-sm mr-[5px] bg-white"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
|
||||
<button
|
||||
style={{
|
||||
background: "#188ae2"
|
||||
}}
|
||||
type="button"
|
||||
className="finishBtn sendHover"
|
||||
className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[18px] text-white font-[500] text-[13px] mr-[5px] bg-[#188ae2]"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
|
||||
@@ -275,6 +275,7 @@ function Placeholder(props) {
|
||||
}
|
||||
};
|
||||
const handleOnClickPlaceholder = () => {
|
||||
props.setCurrWidgetsDetails && props.setCurrWidgetsDetails(props.pos);
|
||||
if (!props.isNeedSign) {
|
||||
props.setWidgetType(props.pos.type);
|
||||
}
|
||||
@@ -415,7 +416,7 @@ function Placeholder(props) {
|
||||
|
||||
{props.pos.type !== textWidget && !props.isSignYourself && (
|
||||
<i
|
||||
data-tut="reactourLinkUser"
|
||||
data-tut="assignSigner"
|
||||
className="fa-regular fa-user signUserIcon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -541,7 +542,15 @@ function Placeholder(props) {
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
props.pos.type !== textWidget &&
|
||||
// props.pos.type !== textWidget
|
||||
![
|
||||
textWidget,
|
||||
"email",
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
textInputWidget
|
||||
].includes(props.pos.type) &&
|
||||
(props.pos.Width
|
||||
? props.pos.Width / props.pos.Height
|
||||
: defaultWidthHeight(props.pos.type).width /
|
||||
|
||||
@@ -38,15 +38,17 @@ function PlaceholderType(props) {
|
||||
"December"
|
||||
];
|
||||
const validateExpression = (regexValidation) => {
|
||||
let regexObject = regexValidation;
|
||||
if (props.pos?.options.validation.type === "regex") {
|
||||
regexObject = RegexParser(regexValidation);
|
||||
}
|
||||
// new RegExp(regexValidation);
|
||||
let isValidate = regexObject.test(textValue);
|
||||
if (!isValidate) {
|
||||
props?.setValidateAlert(true);
|
||||
inputRef.current.focus();
|
||||
if (textValue) {
|
||||
let regexObject = regexValidation;
|
||||
if (props.pos?.options.validation.type === "regex") {
|
||||
regexObject = RegexParser(regexValidation);
|
||||
}
|
||||
// new RegExp(regexValidation);
|
||||
let isValidate = regexObject.test(textValue);
|
||||
if (!isValidate) {
|
||||
props?.setValidateAlert(true);
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -61,7 +63,7 @@ function PlaceholderType(props) {
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "number":
|
||||
regexValidation = /^\d+$/;
|
||||
regexValidation = /^[0-9\s]*$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "text":
|
||||
@@ -135,7 +137,7 @@ function PlaceholderType(props) {
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.pos]);
|
||||
}, [props.pos?.options?.defaultValue]);
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
@@ -288,7 +290,7 @@ function PlaceholderType(props) {
|
||||
};
|
||||
//handle height on enter press in text area
|
||||
const handleEnterPress = (e) => {
|
||||
const height = 15;
|
||||
const height = 18;
|
||||
if (e.key === "Enter") {
|
||||
//function to save height of text area
|
||||
onChangeHeightOfTextArea(
|
||||
@@ -307,6 +309,7 @@ function PlaceholderType(props) {
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
@@ -328,6 +331,7 @@ function PlaceholderType(props) {
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
@@ -401,19 +405,12 @@ function PlaceholderType(props) {
|
||||
case textInputWidget:
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={validatePlaceholder}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
type="text"
|
||||
tabIndex="0"
|
||||
disabled={
|
||||
props.isNeedSign && props.data?.signerObjId !== props.signerObjId
|
||||
? true
|
||||
: props.isPlaceholder
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
setTextValue(e.target.value);
|
||||
@@ -427,6 +424,13 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -483,6 +487,7 @@ function PlaceholderType(props) {
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
@@ -503,15 +508,12 @@ function PlaceholderType(props) {
|
||||
case "name":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
tabIndex="0"
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={"name"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
const isDefault = false;
|
||||
handleTextValid(e);
|
||||
@@ -525,6 +527,13 @@ function PlaceholderType(props) {
|
||||
isDefault
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -539,14 +548,12 @@ function PlaceholderType(props) {
|
||||
case "company":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={"company"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
placeholder="Enter text"
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
@@ -559,6 +566,13 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -573,14 +587,12 @@ function PlaceholderType(props) {
|
||||
case "job title":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={"job title"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
handleTextValid(e);
|
||||
onChangeInput(
|
||||
@@ -593,6 +605,13 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -668,6 +687,7 @@ function PlaceholderType(props) {
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
@@ -688,12 +708,11 @@ function PlaceholderType(props) {
|
||||
case "email":
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<input
|
||||
className="inputPlaceholder"
|
||||
type="text"
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
placeholder={"email"}
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
rows={1}
|
||||
onKeyDown={handleEnterPress}
|
||||
value={textValue}
|
||||
onBlur={handleInputBlur}
|
||||
onChange={(e) => {
|
||||
@@ -708,6 +727,13 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
@@ -778,7 +804,7 @@ function PlaceholderType(props) {
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap" }}
|
||||
style={{ whiteSpace: "pre-wrap", overflow: "hidden" }}
|
||||
cols="50"
|
||||
/>
|
||||
);
|
||||
@@ -787,6 +813,7 @@ function PlaceholderType(props) {
|
||||
<div style={{ pointerEvents: "none" }}>
|
||||
<img
|
||||
alt="signimg"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
//for go to previous page
|
||||
@@ -14,41 +15,35 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
<div>
|
||||
<div className="preBtn1">
|
||||
<button
|
||||
style={{
|
||||
padding: "3px 20px ",
|
||||
fontSize: "10px",
|
||||
background: "#d3edeb",
|
||||
fontWeight: "600",
|
||||
|
||||
border: "0px",
|
||||
marginRight: "5px"
|
||||
}}
|
||||
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] mr-[5px]"
|
||||
disabled={pageNumber <= 1}
|
||||
onClick={previousPage}
|
||||
>
|
||||
Prev
|
||||
<span className="block lg:hidden">
|
||||
<i
|
||||
className="fa fa-backward"
|
||||
aria-hidden="true"
|
||||
style={{ color: themeColor, cursor: "pointer" }}
|
||||
></i>
|
||||
</span>
|
||||
<span className="lg:block hidden">Prev</span>
|
||||
</button>
|
||||
<span
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontWeight: "600"
|
||||
}}
|
||||
>
|
||||
<span className="text-xs font-[500]">
|
||||
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
||||
</span>
|
||||
<button
|
||||
style={{
|
||||
padding: "3px 20px ",
|
||||
fontSize: "10px",
|
||||
background: "#d3edeb",
|
||||
fontWeight: "600",
|
||||
marginLeft: "5px",
|
||||
border: "0px"
|
||||
}}
|
||||
className="py-[3px] px-[10px] text-xs bg-[#d3edeb] font-[600] ml-[5px]"
|
||||
disabled={pageNumber >= allPages}
|
||||
onClick={nextPage}
|
||||
>
|
||||
Next
|
||||
<span className="block lg:hidden">
|
||||
<i
|
||||
className="fa fa-forward"
|
||||
aria-hidden="true"
|
||||
style={{ color: themeColor, cursor: "pointer" }}
|
||||
></i>
|
||||
</span>
|
||||
<span className="lg:block hidden">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -116,7 +116,13 @@ const RecipientList = (props) => {
|
||||
data-tut="reactourFirst"
|
||||
onMouseEnter={() => setIsHover(ind)}
|
||||
onMouseLeave={() => setIsHover(null)}
|
||||
className={props.sendInOrder && "dragCursor"}
|
||||
className={
|
||||
props.sendInOrder
|
||||
? props.isMailSend
|
||||
? "disabled"
|
||||
: "dragCursor"
|
||||
: props.isMailSend && "disabled"
|
||||
}
|
||||
style={
|
||||
(!isMobile && isHover === ind) || props.isSelectListId === ind
|
||||
? onHoverStyle(ind, obj?.blockColor)
|
||||
@@ -224,7 +230,11 @@ const RecipientList = (props) => {
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{obj.Name && <span className={"useEmail"}>{obj.Role}</span>}
|
||||
{obj.Name && (
|
||||
<span className={"useEmail"}>
|
||||
{obj?.Role || obj?.Email}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && props.sendInOrder && (
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Document, Page } from "react-pdf";
|
||||
import { themeColor } from "../../constant/const";
|
||||
|
||||
function RenderAllPdfPage({
|
||||
pdfUrl,
|
||||
signPdfUrl,
|
||||
allPages,
|
||||
setAllPages,
|
||||
@@ -50,8 +49,7 @@ function RenderAllPdfPage({
|
||||
<Document
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={onDocumentLoad}
|
||||
file={pdfUrl ? pdfUrl : signPdfUrl}
|
||||
// file="https://api.printnode.com/static/test/pdf/multipage.pdf"
|
||||
file={signPdfUrl}
|
||||
>
|
||||
{Array.from(new Array(allPages), (el, index) => (
|
||||
<div
|
||||
|
||||
@@ -272,6 +272,7 @@ function RenderPdf({
|
||||
unSignedWidgetId={unSignedWidgetId}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -295,9 +296,11 @@ function RenderPdf({
|
||||
{type && (
|
||||
<div style={{ fontWeight: "700", fontSize: 11 }}>{type}</div>
|
||||
)}
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
{Role && (
|
||||
<div style={{ color: "black", fontSize: 8, fontWeight: "500" }}>
|
||||
{`(${Role})`}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -20,7 +20,9 @@ function SignPad({
|
||||
isInitial,
|
||||
setIsInitial,
|
||||
setIsStamp,
|
||||
widgetType
|
||||
widgetType,
|
||||
currWidgetsDetails,
|
||||
setCurrWidgetsDetails
|
||||
}) {
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const allColor = ["blue", "red", "black"];
|
||||
@@ -31,6 +33,7 @@ function SignPad({
|
||||
const [signValue, setSignValue] = useState("");
|
||||
const [textWidth, setTextWidth] = useState(null);
|
||||
const [textHeight, setTextHeight] = useState(null);
|
||||
const [signatureType, setSignatureType] = useState("draw");
|
||||
const fontOptions = [
|
||||
{ value: "Fasthand" },
|
||||
{ value: "Dancing Script" },
|
||||
@@ -48,19 +51,11 @@ function SignPad({
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
);
|
||||
const jsonSender = JSON.parse(senderUser);
|
||||
|
||||
const currentUserName = jsonSender && jsonSender.name;
|
||||
|
||||
useEffect(() => {
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setFontSelect("Fasthand");
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//function for clear signature image
|
||||
const handleClear = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
if (isTab === "draw") {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.clear();
|
||||
@@ -72,7 +67,7 @@ function SignPad({
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
}
|
||||
setIsInitial(false);
|
||||
// setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = () => {
|
||||
@@ -100,27 +95,28 @@ function SignPad({
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
setCurrWidgetsDetails({});
|
||||
if (!image) {
|
||||
if (isTab === "mysignature") {
|
||||
setIsSignImg("");
|
||||
if (isInitial) {
|
||||
onSaveSign("initials");
|
||||
onSaveSign(signatureType, "initials");
|
||||
} else {
|
||||
onSaveSign("default");
|
||||
onSaveSign(null, "default");
|
||||
}
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(false, textWidth, textHeight);
|
||||
onSaveSign(null, false, textWidth, textHeight);
|
||||
} else {
|
||||
onSaveSign();
|
||||
onSaveSign(signatureType);
|
||||
}
|
||||
}
|
||||
|
||||
setPenColor("blue");
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
onSaveImage();
|
||||
onSaveImage(signatureType);
|
||||
}
|
||||
setIsSignPad(false);
|
||||
setIsInitial(false);
|
||||
@@ -150,11 +146,27 @@ function SignPad({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
//useEffect for set already draw or save signature url/text url of signature text type and draw type for initial type and signature type widgets
|
||||
useEffect(() => {
|
||||
if (canvasRef.current && isSignImg) {
|
||||
canvasRef.current.fromDataURL(isSignImg);
|
||||
if (currWidgetsDetails && canvasRef.current) {
|
||||
const isWidgetType = currWidgetsDetails?.type;
|
||||
const signatureType = currWidgetsDetails?.signatureType;
|
||||
const url = currWidgetsDetails?.SignUrl;
|
||||
|
||||
//checking widget type and draw type signature url
|
||||
if (isInitial) {
|
||||
if (isWidgetType === "initials" && signatureType === "draw" && url) {
|
||||
canvasRef.current.fromDataURL(url);
|
||||
}
|
||||
} else if (
|
||||
isWidgetType === "signature" &&
|
||||
signatureType === "draw" &&
|
||||
url
|
||||
) {
|
||||
canvasRef.current.fromDataURL(url);
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
@@ -349,6 +361,7 @@ function SignPad({
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("uploadImage");
|
||||
setSignatureType("");
|
||||
}}
|
||||
style={{
|
||||
color:
|
||||
@@ -373,6 +386,7 @@ function SignPad({
|
||||
setIsDefaultSign(false);
|
||||
setIsImageSelect(false);
|
||||
setIsTab("type");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
@@ -400,6 +414,7 @@ function SignPad({
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
@@ -430,6 +445,7 @@ function SignPad({
|
||||
setIsDefaultSign(true);
|
||||
setIsImageSelect(true);
|
||||
setIsTab("mysignature");
|
||||
setSignatureType("");
|
||||
setImage();
|
||||
}}
|
||||
style={{
|
||||
@@ -469,6 +485,7 @@ function SignPad({
|
||||
setIsDefaultSign(false);
|
||||
setImage();
|
||||
setIsTab("draw");
|
||||
setSignatureType("draw");
|
||||
setSignValue("");
|
||||
setIsStamp(false);
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import RecipientList from "./RecipientList";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
function SignerListPlace(props) {
|
||||
return (
|
||||
@@ -13,20 +14,89 @@ function SignerListPlace(props) {
|
||||
>
|
||||
<span className="signedStyle">
|
||||
{props.title ? props.title : "Recipients"}
|
||||
<span className="absolute text-xs z-[30] mt-1 ml-0.5">
|
||||
{props?.title === "Roles" && (
|
||||
<>
|
||||
<a data-tooltip-id="my-tooltip">
|
||||
<sup>
|
||||
<i
|
||||
className="fa-solid fa-question rounded-full"
|
||||
style={{
|
||||
borderColor: "white",
|
||||
color: "white",
|
||||
fontSize: 11,
|
||||
borderWidth: 1.5,
|
||||
padding: "1px 3px"
|
||||
}}
|
||||
></i>
|
||||
</sup>
|
||||
</a>
|
||||
<Tooltip id="my-tooltip">
|
||||
<div className="max-w-[450px]">
|
||||
<p className="font-bold">What are template roles?</p>
|
||||
<p>
|
||||
Begin by specifying each role needed for the completion of
|
||||
the document. Think about the parties involved in the
|
||||
signing process and what their responsibilities are.
|
||||
Common roles include HR for internal documents, Customer
|
||||
for agreements or Vendor for business agreements.{" "}
|
||||
</p>
|
||||
<p className="font-bold">
|
||||
Why pre-attach users to some roles?
|
||||
</p>
|
||||
<p>
|
||||
For roles that consistently involve the same individual
|
||||
(e.g., the CEO's signature on employee offer
|
||||
letters), you can pre-attach a user to a role within the
|
||||
template. This step is optional but recommended for
|
||||
efficiency and consistency across documents.
|
||||
</p>
|
||||
<p className="font-bold">
|
||||
When do i specify the user attached to each role?
|
||||
</p>
|
||||
<p>
|
||||
When you create a document from your template, you'll
|
||||
be prompted to attach users to each defined role. If a
|
||||
role already has a user attached, this will be pre-filled,
|
||||
but you can modify it as needed before sending out the
|
||||
document.
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="signerList">
|
||||
<RecipientList {...props} />
|
||||
</div>
|
||||
{props.handleAddSigner && (
|
||||
{props.handleAddSigner ? (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
||||
onClick={() => props.handleAddSigner()}
|
||||
style={{
|
||||
opacity: props.isMailSend && "0.5",
|
||||
pointerEvents: props.isMailSend && "none"
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
||||
onClick={() => props.setIsAddSigner(true)}
|
||||
style={{
|
||||
opacity: props.isMailSend && "0.5",
|
||||
pointerEvents: props.isMailSend && "none"
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add recipients</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,8 @@ function WidgetComponent({
|
||||
sendInOrder,
|
||||
isTemplateFlow,
|
||||
setBlockColor,
|
||||
blockColor
|
||||
blockColor,
|
||||
setIsAddSigner
|
||||
}) {
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
|
||||
@@ -319,7 +320,7 @@ function WidgetComponent({
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{handleAddSigner && (
|
||||
{handleAddSigner ? (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
style={{
|
||||
@@ -333,6 +334,22 @@ function WidgetComponent({
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add role</span>
|
||||
</div>
|
||||
) : (
|
||||
setIsAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
style={{
|
||||
margin: "5px 0 5px 0",
|
||||
backgroundColor: themeColor,
|
||||
color: "white"
|
||||
}}
|
||||
className="p-[10px] my-[2px] flex flex-row items-center justify-center border-[1px] border-[#47a3ad] hover:bg-[#47a3ad] text-[#47a3ad] hover:text-white cursor-pointer"
|
||||
onClick={() => setIsAddSigner(true)}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span style={{ marginLeft: 2 }}>Add recipients</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
<div
|
||||
@@ -364,7 +381,12 @@ function WidgetComponent({
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div data-tut={dataTut} className="signerComponent">
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
className={
|
||||
isMailSend ? "disabled signerComponent " : "signerComponent"
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
background: themeColor,
|
||||
@@ -386,7 +408,7 @@ function WidgetComponent({
|
||||
)}
|
||||
{isSignersModal && (
|
||||
<ModalUi
|
||||
title={"Roles"}
|
||||
title={title ? title : "Recipients"}
|
||||
isOpen={isSignersModal}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
@@ -419,7 +441,7 @@ function WidgetComponent({
|
||||
textAlign: "center"
|
||||
}}
|
||||
>
|
||||
Please add a role
|
||||
Please add a {title ? title : "recipients"}
|
||||
</div>
|
||||
)}
|
||||
</ModalUi>
|
||||
|
||||
@@ -4,6 +4,8 @@ import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
import { textInputWidget } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const [formdata, setFormdata] = useState({
|
||||
@@ -100,13 +102,14 @@ const WidgetNameModal = (props) => {
|
||||
title={"Widget info"}
|
||||
>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget) && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
props.widgetName === textInputWidget) &&
|
||||
!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
@@ -133,10 +136,20 @@ const WidgetNameModal = (props) => {
|
||||
props.widgetName === textInputWidget) && (
|
||||
<>
|
||||
<div className="form-section">
|
||||
<label htmlFor="textvalidate" style={{ fontSize: 13 }}>
|
||||
<label
|
||||
htmlFor="textvalidate"
|
||||
className={
|
||||
!props.isSubscribe && isEnableSubscription && "disabled"
|
||||
}
|
||||
style={{ fontSize: 13 }}
|
||||
>
|
||||
Validation
|
||||
</label>
|
||||
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
<div
|
||||
className={
|
||||
!props.isSubscribe && isEnableSubscription && "disabled"
|
||||
}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
@@ -178,6 +191,7 @@ const WidgetNameModal = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-section">
|
||||
<label htmlFor="name" style={{ fontSize: 13 }}>
|
||||
Default value
|
||||
@@ -196,7 +210,10 @@ const WidgetNameModal = (props) => {
|
||||
}}
|
||||
/>
|
||||
{isValid === false && (
|
||||
<div className="warning defaultvalueWarning" style={{ fontSize: 12 }}>
|
||||
<div
|
||||
className="warning defaultvalueWarning"
|
||||
style={{ fontSize: 12 }}
|
||||
>
|
||||
<i
|
||||
className="fas fa-exclamation-circle"
|
||||
style={{ color: "#fab005", fontSize: 15 }}
|
||||
|
||||
@@ -196,7 +196,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
{selectFolder && selectFolder.Name ? `(${folderPath})` : ""}
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute top-1 right-1 cursor-pointer">
|
||||
<div className="absolute top-2 right-1 cursor-pointer">
|
||||
<Tooltip
|
||||
message={
|
||||
"If you do not select a folder, your signed document will be saved in the Main OpenSign drive folder."
|
||||
|
||||
@@ -53,9 +53,22 @@ const SelectSigners = (props) => {
|
||||
const contactRes = await contactbook.find();
|
||||
if (contactRes) {
|
||||
const res = JSON.parse(JSON.stringify(contactRes));
|
||||
// console.log("userList ", res);
|
||||
setUserList(res);
|
||||
return await res.map((item) => ({
|
||||
//compareArrays is a function where compare between two array (total signersList and dcument signers list)
|
||||
//and filter signers from total signer's list which already present in document's signers list
|
||||
const compareArrays = (res, signerObj) => {
|
||||
return res.filter(
|
||||
(item1) =>
|
||||
!signerObj.find((item2) => item2.objectId === item1.objectId)
|
||||
);
|
||||
};
|
||||
|
||||
//get update signer's List if signersdata is present
|
||||
const updateSignersList =
|
||||
props?.signersData && compareArrays(res, props?.signersData);
|
||||
|
||||
const result = updateSignersList ? updateSignersList : res;
|
||||
setUserList(result);
|
||||
return await result.map((item) => ({
|
||||
label: item.Email,
|
||||
value: item.objectId
|
||||
}));
|
||||
|
||||
@@ -105,7 +105,7 @@ const SignersInput = (props) => {
|
||||
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<label className="block">
|
||||
<label className="block relative">
|
||||
Signers
|
||||
{props.required && <span className="text-red-500 text-[13px]">*</span>}
|
||||
<span className="absolute ml-1 text-xs z-50">
|
||||
|
||||
@@ -41,7 +41,7 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
||||
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-cover"
|
||||
className="w-full h-full object-contain"
|
||||
src={image}
|
||||
alt="Profile"
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import { themeColor } from "./const";
|
||||
import { isEnableSubscription, themeColor } from "./const";
|
||||
import React from "react";
|
||||
import { rgb } from "pdf-lib";
|
||||
import Parse from "parse";
|
||||
|
||||
export const isMobile = window.innerWidth < 767;
|
||||
export const textInputWidget = "text input";
|
||||
@@ -12,6 +13,54 @@ export const openInNewTab = (url) => {
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
};
|
||||
|
||||
export async function fetchSubscription(extUserId) {
|
||||
try {
|
||||
const extClass = localStorage.getItem("Extand_Class");
|
||||
let extUser;
|
||||
if (extClass) {
|
||||
const jsonSender = JSON.parse(extClass);
|
||||
extUser = jsonSender[0].objectId;
|
||||
} else {
|
||||
extUser = extUserId;
|
||||
}
|
||||
const baseURL = localStorage.getItem("baseUrl");
|
||||
const url = `${baseURL}functions/getsubscriptions`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const params = { extUserId: extUser };
|
||||
const tenatRes = await axios.post(url, params, { headers: headers });
|
||||
const plan = tenatRes.data?.result?.result?.PlanCode;
|
||||
const billingDate = tenatRes.data?.result?.result?.Next_billing_date?.iso;
|
||||
return { plan, billingDate };
|
||||
} catch (err) {
|
||||
console.log("Err in fetch subscription", err);
|
||||
return { plan: "", billingDate: "" };
|
||||
}
|
||||
}
|
||||
//function to get subcripition details from Extand user class
|
||||
export async function checkIsSubscribed() {
|
||||
try {
|
||||
const res = await fetchSubscription();
|
||||
if (res.plan === "freeplan") {
|
||||
return false;
|
||||
} else if (res.billingDate) {
|
||||
if (new Date(res.billingDate) > new Date()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in fetch subscription", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
@@ -477,16 +526,33 @@ export const signPdfFun = async (
|
||||
base64Url,
|
||||
documentId,
|
||||
signerObjectId,
|
||||
setIsAlert
|
||||
setIsAlert,
|
||||
objectId,
|
||||
isSubscribed
|
||||
) => {
|
||||
let singleSign;
|
||||
let singleSign,
|
||||
isCustomCompletionMail = false;
|
||||
|
||||
//get tenant details
|
||||
const tenantDetails = await getTenantDetails(objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert("User does not exist");
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
(!isEnableSubscription || isSubscribed)
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
|
||||
singleSign = {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId,
|
||||
userId: signerObjectId
|
||||
userId: signerObjectId,
|
||||
isCustomCompletionMail: isCustomCompletionMail
|
||||
};
|
||||
|
||||
const response = await axios
|
||||
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
|
||||
headers: {
|
||||
@@ -935,6 +1001,7 @@ export const embedDocId = async (pdfDoc, documentId, allPages) => {
|
||||
|
||||
//function for save button to save signature or image url
|
||||
export function onSaveSign(
|
||||
type,
|
||||
xyPostion,
|
||||
index,
|
||||
signKey,
|
||||
@@ -975,6 +1042,7 @@ export function onSaveSign(
|
||||
Width: posWidth,
|
||||
Height: posHeight,
|
||||
SignUrl: signatureImg,
|
||||
signatureType: type && type,
|
||||
options: {
|
||||
...position.options,
|
||||
response: signatureImg
|
||||
@@ -1109,10 +1177,10 @@ export const multiSignEmbed = async (
|
||||
const newUrl = await convertPNGtoJPEG(signUrl);
|
||||
signUrl = newUrl;
|
||||
}
|
||||
const checkUrl = urlValidator(signUrl);
|
||||
if (checkUrl) {
|
||||
signUrl = signUrl + "?get";
|
||||
}
|
||||
// const checkUrl = urlValidator(signUrl);
|
||||
// if (checkUrl) {
|
||||
// signUrl = signUrl + "?get";
|
||||
// }
|
||||
const res = await fetch(signUrl);
|
||||
return res.arrayBuffer();
|
||||
}
|
||||
@@ -1247,6 +1315,7 @@ export const multiSignEmbed = async (
|
||||
}
|
||||
};
|
||||
const widgetTypeExist = [
|
||||
textWidget,
|
||||
textInputWidget,
|
||||
"name",
|
||||
"company",
|
||||
@@ -1302,7 +1371,7 @@ export const multiSignEmbed = async (
|
||||
checkbox.enableReadOnly();
|
||||
});
|
||||
}
|
||||
} else if (position.type === textWidget) {
|
||||
} else if (widgetTypeExist) {
|
||||
const font = await pdfDoc.embedFont("Helvetica");
|
||||
const fontSize = 12;
|
||||
let textContent;
|
||||
@@ -1378,24 +1447,8 @@ export const multiSignEmbed = async (
|
||||
color: rgb(0, 0, 0),
|
||||
size: fontSize
|
||||
});
|
||||
y -= 15; // Adjust the line height as needed
|
||||
y -= 18; // Adjust the line height as needed
|
||||
}
|
||||
} else if (widgetTypeExist) {
|
||||
const font = await pdfDoc.embedFont("Helvetica");
|
||||
const fontSize = 12;
|
||||
let textContent;
|
||||
if (position?.options?.response) {
|
||||
textContent = position.options?.response;
|
||||
} else if (position?.options?.defaultValue) {
|
||||
textContent = position?.options?.defaultValue;
|
||||
}
|
||||
page.drawText(textContent, {
|
||||
x: xPos(position),
|
||||
y: yPos(position) + 10,
|
||||
size: fontSize,
|
||||
font,
|
||||
color: rgb(0, 0, 0)
|
||||
});
|
||||
} else if (position.type === "dropdown") {
|
||||
const dropdownRandomId = "dropdown" + randomId();
|
||||
const dropdown = form.createDropdown(dropdownRandomId);
|
||||
@@ -1766,3 +1819,101 @@ export const handleCopyNextToWidget = (
|
||||
setXyPostion(updatePlaceholder);
|
||||
}
|
||||
};
|
||||
|
||||
export const getFileName = (fileUrl) => {
|
||||
if (fileUrl) {
|
||||
const url = new URL(fileUrl);
|
||||
const filename = url.pathname.substring(url.pathname.indexOf("_") + 1);
|
||||
return filename || "";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
//fetch tenant app logo from `partners_Tenant` class by domain name
|
||||
export const getAppLogo = async () => {
|
||||
const domainName = window.location.host;
|
||||
try {
|
||||
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
|
||||
tenantCreditsQuery.equalTo("Domain", domainName);
|
||||
const res = await tenantCreditsQuery.first();
|
||||
|
||||
if (res) {
|
||||
const updateRes = JSON.parse(JSON.stringify(res));
|
||||
return updateRes?.Logo;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const getTenantDetails = async (objectId) => {
|
||||
try {
|
||||
const tenantCreditsQuery = new Parse.Query("partners_Tenant");
|
||||
tenantCreditsQuery.equalTo("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: objectId
|
||||
});
|
||||
const res = await tenantCreditsQuery.first();
|
||||
if (res) {
|
||||
const updateRes = JSON.parse(JSON.stringify(res));
|
||||
return updateRes;
|
||||
}
|
||||
} catch (e) {
|
||||
return "user does not exist!";
|
||||
}
|
||||
};
|
||||
|
||||
//function to convert variable string name to variable value of email body and subject
|
||||
export function replaceMailVaribles(subject, body, variables) {
|
||||
let replacedSubject = subject;
|
||||
let replacedBody = body;
|
||||
|
||||
for (const variable in variables) {
|
||||
const regex = new RegExp(`{{${variable}}}`, "g");
|
||||
if (subject) {
|
||||
replacedSubject = replacedSubject.replace(regex, variables[variable]);
|
||||
}
|
||||
if (body) {
|
||||
replacedBody = replacedBody.replace(regex, variables[variable]);
|
||||
}
|
||||
}
|
||||
|
||||
const result = {
|
||||
subject: replacedSubject,
|
||||
body: replacedBody
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export const copytoData = (text) => {
|
||||
// navigator.clipboard.writeText(text);
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text);
|
||||
} else {
|
||||
// Fallback for browsers that don't support navigator.clipboard
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
};
|
||||
|
||||
export const convertPdfArrayBuffer = async (url) => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
// Check if the response was successful (status 200)
|
||||
if (!response.ok) {
|
||||
return "Error";
|
||||
}
|
||||
// Convert the response to ArrayBuffer
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return arrayBuffer;
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
return "Error";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,3 +9,4 @@ export const modalSubmitBtnColor = "#17a2b8";
|
||||
export const modalCancelBtnColor = "white";
|
||||
export const themeColor = "#47a3ad";
|
||||
export const iconColor = "#686968";
|
||||
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
|
||||
|
||||
@@ -2,26 +2,25 @@ import axios from "axios";
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
|
||||
export const SaveFileSize = async (size, imageUrl) => {
|
||||
export const SaveFileSize = async (size, imageUrl, tenantId) => {
|
||||
//checking server url and save file's size
|
||||
|
||||
const tenantPtr = {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: tenantId
|
||||
};
|
||||
const _tenantPtr = JSON.stringify(tenantPtr);
|
||||
try {
|
||||
const response = await axios
|
||||
.get(`${serverUrl}classes/partners_TenantCredits`, {
|
||||
const res = await axios.get(
|
||||
`${serverUrl}classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
})
|
||||
.then((result) => {
|
||||
const res = result.data;
|
||||
// console.log("res", res);
|
||||
|
||||
return res.results;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("axois err ", err);
|
||||
});
|
||||
}
|
||||
);
|
||||
const response = res.data.results;
|
||||
let data;
|
||||
// console.log("response", response);
|
||||
if (response && response.length > 0) {
|
||||
@@ -30,69 +29,49 @@ export const SaveFileSize = async (size, imageUrl) => {
|
||||
? response[0].usedStorage + size
|
||||
: size
|
||||
};
|
||||
await axios
|
||||
.put(
|
||||
`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
// const res = result.data;
|
||||
// console.log("save res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("axois err ", err);
|
||||
});
|
||||
} else {
|
||||
data = {
|
||||
usedStorage: size
|
||||
};
|
||||
await axios
|
||||
.post(`${serverUrl}classes/partners_TenantCredits`, data, {
|
||||
await axios.put(
|
||||
`${serverUrl}classes/partners_TenantCredits/${response[0].objectId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// const res = result.data;
|
||||
// console.log("res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("axois err ", err);
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
||||
await axios.post(`${serverUrl}classes/partners_TenantCredits`, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("org app error", e);
|
||||
} catch (err) {
|
||||
console.log("err in save usage", err);
|
||||
}
|
||||
saveDataFile(size, imageUrl);
|
||||
saveDataFile(size, imageUrl, tenantPtr);
|
||||
};
|
||||
|
||||
//function for save fileUrl and file size in particular client db class partners_DataFiles
|
||||
const saveDataFile = async (size, imageUrl) => {
|
||||
const saveDataFile = async (size, imageUrl, tenantPtr) => {
|
||||
const data = {
|
||||
FileUrl: imageUrl,
|
||||
FileSize: size
|
||||
FileSize: size,
|
||||
TenantPtr: tenantPtr
|
||||
};
|
||||
|
||||
// console.log("data save",file, data)
|
||||
await axios
|
||||
.post(`${serverUrl}classes/partners_DataFiles`, data, {
|
||||
try {
|
||||
await axios.post(`${serverUrl}classes/partners_DataFiles`, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
// const res = result.data;
|
||||
// console.log("res", res);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("axois err ", err);
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in save usage ", err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import "./index.css";
|
||||
import App from "./App";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./redux/store";
|
||||
import { CookiesProvider } from "react-cookie";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { TouchBackend } from "react-dnd-touch-backend";
|
||||
import {
|
||||
@@ -62,10 +63,12 @@ if (process.env.REACT_APP_GTM) {
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
<CookiesProvider defaultSetOptions={{ path: "/" }}>
|
||||
<Provider store={store}>
|
||||
<DndProvider options={HTML5toTouch}>
|
||||
<Preview>{generatePreview}</Preview>
|
||||
<App />
|
||||
</DndProvider>
|
||||
</Provider>
|
||||
</CookiesProvider>
|
||||
);
|
||||
|
||||
@@ -12,14 +12,26 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "sign",
|
||||
btnId: "1231",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "draftDocument"
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "2142",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#ff4848",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
helpMsg: "This are documents you have started but have not finalized for sending."
|
||||
helpMsg:
|
||||
"These are documents you have started but have not finalized for sending."
|
||||
};
|
||||
// Need your sign report
|
||||
case "4Hhwbp482K":
|
||||
@@ -28,11 +40,14 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnId: "4536",
|
||||
btnLabel: "sign",
|
||||
hoverLabel: "Sign",
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
btnIcon: "fa-solid fa-signature",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
action: "redirect"
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
@@ -45,11 +60,48 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnId: "8901",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa-solid fa-share",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
{
|
||||
btnId: "1588",
|
||||
hoverLabel: "View",
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "2234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "transparent",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "1688",
|
||||
btnLabel: "Revoke",
|
||||
hoverLabel: "Revoke",
|
||||
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||
redirectUrl: "",
|
||||
action: "revoke"
|
||||
},
|
||||
{
|
||||
btnId: "1488",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
@@ -62,14 +114,26 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnId: "1378",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "draftDocument"
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1278",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#ff4848",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
helpMsg: "This is a list of documents that have been signed by all parties."
|
||||
helpMsg:
|
||||
"This is a list of documents that have been signed by all parties."
|
||||
};
|
||||
// declined documents report
|
||||
case "UPr2Fm5WY3":
|
||||
@@ -78,14 +142,26 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnId: "1458",
|
||||
hoverLabel: "View",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "draftDocument"
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1358",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#ff4848",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
helpMsg: "This is a list of documents that have been declined by one of the signers."
|
||||
helpMsg:
|
||||
"This is a list of documents that have been declined by one of the signers."
|
||||
};
|
||||
// Expired Documents report
|
||||
case "zNqBHXHsYH":
|
||||
@@ -94,14 +170,26 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnId: "1898",
|
||||
hoverLabel: "View",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "draftDocument"
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1998",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#ff4848",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
helpMsg: "This is a list of documents that have reached their expiration date."
|
||||
helpMsg:
|
||||
"This is a list of documents that have reached their expiration date."
|
||||
};
|
||||
// Recently sent for signatures report show on dashboard
|
||||
case "d9k3UfYHBc":
|
||||
@@ -110,11 +198,48 @@ export default function reportJson(id) {
|
||||
heading: dashboardReportHead,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "View",
|
||||
btnId: "1999",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa-solid fa-share",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
{
|
||||
btnId: "1999",
|
||||
hoverLabel: "View",
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "2234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "transparent",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "6788",
|
||||
btnLabel: "Revoke",
|
||||
hoverLabel: "Revoke",
|
||||
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||
redirectUrl: "",
|
||||
action: "revoke"
|
||||
},
|
||||
{
|
||||
btnId: "2000",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -125,11 +250,14 @@ export default function reportJson(id) {
|
||||
heading: dashboardReportHead,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "Sign",
|
||||
btnColor: "#4bd396",
|
||||
btnId: "2001",
|
||||
btnLabel: "sign",
|
||||
hoverLabel: "Sign",
|
||||
btnColor: "#3ac9d6",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles"
|
||||
btnIcon: "fa-solid fa-signature",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
action: "redirect"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -140,11 +268,22 @@ export default function reportJson(id) {
|
||||
heading: ["Title", "Note", "Folder", "File", "Owner", "Signers"],
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "sign",
|
||||
btnId: "2003",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "draftDocument"
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "2004",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#ff4848",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -155,14 +294,17 @@ export default function reportJson(id) {
|
||||
heading: contactbook,
|
||||
actions: [
|
||||
{
|
||||
btnLabel: "",
|
||||
btnId: "2204",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "#f55a42",
|
||||
textColor: "white",
|
||||
btnIcon: "fa-solid fa-trash"
|
||||
btnIcon: "fa-solid fa-trash",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
form: "ContactBook",
|
||||
helpMsg: "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
|
||||
helpMsg:
|
||||
"This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
|
||||
};
|
||||
// template report
|
||||
case "6TeaPr321t":
|
||||
@@ -171,18 +313,46 @@ export default function reportJson(id) {
|
||||
heading: head,
|
||||
actions: [
|
||||
{
|
||||
btnId: "2234",
|
||||
btnLabel: "Use",
|
||||
hoverLabel: "Use",
|
||||
btnColor: "#4bd396",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "placeHolderSign"
|
||||
redirectUrl: "placeHolderSign",
|
||||
action: "redirect",
|
||||
selector: "reactourSecond",
|
||||
message:
|
||||
"Click the 'Use' button to create a new document from an existing template. "
|
||||
},
|
||||
{
|
||||
btnLabel: "Edit",
|
||||
btnColor: "#00c9d5",
|
||||
textColor: "white",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "template"
|
||||
btnId: "2234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "transparent",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
selector: "reactourThird",
|
||||
message:
|
||||
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won’t affect existing documents.Use the Delete button you can delete template. ",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Edit",
|
||||
hoverLabel: "Edit",
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
redirectUrl: "template",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1834",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
helpMsg:
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
const plans = [
|
||||
{
|
||||
planName: "OPENSIGN™ FREE",
|
||||
currency: "",
|
||||
monthlyPrice: "Free",
|
||||
yearlyPrice: "Free",
|
||||
subtitle: "Free Unlimited E-signatures, Forever.",
|
||||
btnText: "Subscribe",
|
||||
url: "",
|
||||
target: "_blank",
|
||||
benefits: [
|
||||
"Unlimited envelopes",
|
||||
"Sign documents yourself",
|
||||
"Request signatures from others",
|
||||
"14 field types",
|
||||
"Automatic e-signatures",
|
||||
"Completion certificates",
|
||||
"Send in order",
|
||||
"Document templates",
|
||||
"Import from Dropbox",
|
||||
"Contact book",
|
||||
"Document expiry support",
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"And much more"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ PROFESSIONAL",
|
||||
currency: "$",
|
||||
monthlyPrice: "29.99",
|
||||
yearlyPrice: "329.99",
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btnText: "Subscribe",
|
||||
url:
|
||||
window.location.origin === "https://staging-app.opensignlabs.com"
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
|
||||
yearlyUrl:
|
||||
window.location.origin === "https://staging-app.opensignlabs.com"
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"Field validations",
|
||||
"Regular expression validations",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Webhooks",
|
||||
"Zapier integration",
|
||||
"API Access",
|
||||
"100 API signatures included",
|
||||
"DocumentId removal from signed docs",
|
||||
"Custom email templates(coming soon)"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ ENTERPRISE",
|
||||
currency: "",
|
||||
monthlyPrice: "Request Price",
|
||||
yearlyPrice: "Request Price",
|
||||
subtitle: "Scalable Features with priority support.",
|
||||
btnText: "Contact us",
|
||||
url: "https://www.opensignlabs.com/contact-us",
|
||||
target: "_blank",
|
||||
benefits: [
|
||||
"All features",
|
||||
"Custom domain",
|
||||
"Custom branding",
|
||||
"Uptime SLA",
|
||||
"SSO support",
|
||||
"Priority support"
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export default plans;
|
||||
@@ -1,65 +0,0 @@
|
||||
[
|
||||
{
|
||||
"planName": "SELF HOSTED",
|
||||
"currency": "",
|
||||
"price": "Free",
|
||||
"subtitle": "Free for life One click install.",
|
||||
"btnText": "Visit github",
|
||||
"url": "https://github.com/opensignlabs/opensign",
|
||||
"target": "_blank",
|
||||
"benefits": [
|
||||
"Sign Unlimited Documents",
|
||||
"S3 Doc Storage",
|
||||
"Unlimited Guest Signers",
|
||||
"Unlimited completion certificates",
|
||||
"Unique Code(OTP) verification for guest signers",
|
||||
"Expiring docs & reject signature support",
|
||||
"PDF Template Creation(coming soon)",
|
||||
"Audit Trails",
|
||||
"API support",
|
||||
"Add-ons support(coming soon)",
|
||||
"Self hosted cloud infrastructure",
|
||||
"Free for lifetime",
|
||||
"Community support"
|
||||
]
|
||||
},
|
||||
{
|
||||
"planName": "THANKS PLAN",
|
||||
"currency": "$",
|
||||
"price": "29.99",
|
||||
"subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)",
|
||||
"btnText": "Subscribe",
|
||||
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
|
||||
"target": "_self",
|
||||
"benefits": [
|
||||
"Sign Unlimited Documents",
|
||||
"Unlimited Secure Doc Storage with OpenSign™ Drive",
|
||||
"Unlimited Guest Signers",
|
||||
"Unlimited completion certificates",
|
||||
"Unique Code(OTP) verification for guest signers",
|
||||
"Expiring docs & reject signature support",
|
||||
"PDF Template Creation(coming soon)",
|
||||
"Audit Trails",
|
||||
"API support",
|
||||
"Add-ons support(coming soon)",
|
||||
"Self hosted cloud infrastructure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"planName": "ENTERPRISE",
|
||||
"currency": "",
|
||||
"price": "Request Price",
|
||||
"subtitle": "Customization available Priority support.",
|
||||
"btnText": "Contact us",
|
||||
"url": "https://www.opensignlabs.com/contact-us",
|
||||
"target": "_blank",
|
||||
"benefits": [
|
||||
"All features",
|
||||
"Custom domain",
|
||||
"Whitelabeling",
|
||||
"Uptime SLA",
|
||||
"SSO support",
|
||||
"24/7 support"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -9,6 +9,9 @@ import { useSelector } from "react-redux";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useNavigate, useLocation, Outlet } from "react-router-dom";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
|
||||
const HomeLayout = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -18,6 +21,13 @@ const HomeLayout = () => {
|
||||
const arr = useSelector((state) => state.TourSteps);
|
||||
const [isUserValid, setIsUserValid] = useState(true);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
// reactour state
|
||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||
const [tourConfigs, setTourConfigs] = useState([]);
|
||||
const [, setCookie] = useCookies(["accesstoken", "main_Domain"]);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
@@ -27,8 +37,8 @@ const HomeLayout = () => {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
localStorage.setItem("profileImg", user.get("ProfilePic") || "");
|
||||
checkIsSubscribed();
|
||||
} else {
|
||||
setIsUserValid(false);
|
||||
}
|
||||
@@ -37,14 +47,49 @@ const HomeLayout = () => {
|
||||
setIsUserValid(false);
|
||||
}
|
||||
})();
|
||||
saveCookies();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
//function to use save data in cookies storage
|
||||
const saveCookies = () => {
|
||||
const main_Domain = window.location.origin;
|
||||
const domainName = window.location.hostname; //app.opensignlabs.com
|
||||
// Find the index of the first dot in the string
|
||||
const indexOfFirstDot = domainName.indexOf(".");
|
||||
// Remove the first dot and get the substring starting from the next character
|
||||
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
|
||||
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
});
|
||||
setCookie("main_Domain", main_Domain, {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
});
|
||||
};
|
||||
|
||||
// reactour state
|
||||
const [isCloseBtn, setIsCloseBtn] = useState(true);
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||
const [tourConfigs, setTourConfigs] = useState([]);
|
||||
|
||||
async function checkIsSubscribed() {
|
||||
if (isEnableSubscription) {
|
||||
const res = await fetchSubscription();
|
||||
if (res.plan === "freeplan") {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
} else if (res.billingDate) {
|
||||
if (new Date(res.billingDate) > new Date()) {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
setIsUserValid(true);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const showSidebar = () => {
|
||||
setIsOpen((value) => !value);
|
||||
};
|
||||
@@ -198,7 +243,7 @@ const HomeLayout = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-50">
|
||||
<Header showSidebar={showSidebar} />
|
||||
{!isLoader && <Header showSidebar={showSidebar} />}
|
||||
</div>
|
||||
{isUserValid ? (
|
||||
<>
|
||||
@@ -223,7 +268,6 @@ const HomeLayout = () => {
|
||||
<>
|
||||
<div className="flex md:flex-row flex-col z-50">
|
||||
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
|
||||
|
||||
<div
|
||||
id="renderList"
|
||||
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { saveTourSteps } from "../redux/reducers/TourStepsReducer";
|
||||
|
||||
const Dashboard = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -10,6 +10,8 @@ import SelectFolder from "../components/shared/fields/SelectFolder";
|
||||
import SignersInput from "../components/shared/fields/SignersInput";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import { getFileName } from "../constant/Utils";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
function Form() {
|
||||
@@ -82,6 +84,7 @@ const Forms = (props) => {
|
||||
const handleFileUpload = async (file) => {
|
||||
setfileload(true);
|
||||
const fileName = file.name;
|
||||
const size = file.size;
|
||||
const name = sanitizeFileName(fileName);
|
||||
const pdfFile = file;
|
||||
const parseFile = new Parse.File(name, pdfFile);
|
||||
@@ -101,6 +104,8 @@ const Forms = (props) => {
|
||||
setFileUpload(response.url());
|
||||
setfileload(false);
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -113,6 +118,7 @@ const Forms = (props) => {
|
||||
const dropboxSuccess = async (files) => {
|
||||
setfileload(true);
|
||||
const file = files[0];
|
||||
const size = file.bytes;
|
||||
const url = file.link;
|
||||
const mb = Math.round(file.bytes / Math.pow(1024, 2));
|
||||
|
||||
@@ -141,6 +147,8 @@ const Forms = (props) => {
|
||||
setfileload(false);
|
||||
|
||||
if (response.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -247,6 +255,7 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
setTimeout(() => setIsReset(false), 50);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<Title title={props?.title} />
|
||||
@@ -291,13 +300,10 @@ const Forms = (props) => {
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected :{" "}
|
||||
{fileupload?.split("/")?.pop()?.split("_")[1]}
|
||||
file selected : {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setFileUpload([]);
|
||||
}}
|
||||
onClick={() => setFileUpload([])}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import axios from "axios";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Alert from "../primitives/Alert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { rejectBtn, submitBtn } from "../constant/const";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
|
||||
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
|
||||
function GenerateToken() {
|
||||
const navigation = useNavigate();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [apiToken, SetApiToken] = useState("");
|
||||
@@ -17,6 +19,7 @@ function GenerateToken() {
|
||||
const [isGenerate, setIsGenerate] = useState(false);
|
||||
const [isErr, setIsErr] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchToken();
|
||||
@@ -25,6 +28,11 @@ function GenerateToken() {
|
||||
|
||||
const fetchToken = async () => {
|
||||
try {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
|
||||
const url = parseBaseUrl + "functions/getapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -35,6 +43,7 @@ function GenerateToken() {
|
||||
if (res) {
|
||||
SetApiToken(res.data.result.result);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
SetApiToken();
|
||||
@@ -83,13 +92,14 @@ function GenerateToken() {
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
copytoData(text);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleModal = () => setIsModal(!isModal);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"API Token"} />
|
||||
@@ -117,35 +127,47 @@ function GenerateToken() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-white flex flex-col justify-center shadow rounded">
|
||||
<PremiumAlertHeader />
|
||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
||||
API Token{" "}
|
||||
{!isEnableSubscription && <PremiumAlertHeader />}
|
||||
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||
OpenSign™ API{" "}
|
||||
<Tooltip
|
||||
url={"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"}
|
||||
url={
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</h1>
|
||||
<ul className="w-full flex flex-col p-2 text-sm">
|
||||
<ul
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "w-full flex flex-col p-2 text-sm "
|
||||
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||
}
|
||||
>
|
||||
<li
|
||||
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<span className="w-[40%]">Api Token:</span>{" "}
|
||||
<span
|
||||
id="token"
|
||||
className="w-[60%] md:text-end cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
|
||||
<span className="">Api Token:</span>{" "}
|
||||
<span
|
||||
id="token"
|
||||
className=" md:text-end cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
{apiToken ? apiToken : "_____"}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
{apiToken && apiToken}
|
||||
</span>
|
||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
</button>
|
||||
|
||||
<div className="flex items-center justify-center ">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
@@ -153,12 +175,41 @@ function GenerateToken() {
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
)
|
||||
}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
View Docs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<>
|
||||
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||
Upgrade to PRO Plan
|
||||
</h1>
|
||||
<ul className={"w-full flex flex-col p-2 text-sm "}>
|
||||
<li
|
||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
|
||||
<span className="">$29.99/month:</span>{" "}
|
||||
<span id="token" className=" md:text-end cursor-pointer">
|
||||
Sign 100 docs using API. Only 0.15/docs after that.
|
||||
</span>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigation("/subscription")}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={"Regenerate Token"}
|
||||
|
||||
@@ -3,8 +3,9 @@ import { useNavigate, useParams } from "react-router-dom";
|
||||
import "../styles/loginPage.css";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
import axios from "axios";
|
||||
import { themeColor } from "../constant/const";
|
||||
import { contractUsers } from "../constant/Utils";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import { contractUsers, getAppLogo } from "../constant/Utils";
|
||||
import logo from "../assets/images/logo.png";
|
||||
|
||||
function GuestLogin() {
|
||||
const { id, userMail, contactBookId, serverUrl } = useParams();
|
||||
@@ -14,14 +15,27 @@ function GuestLogin() {
|
||||
const [EnterOTP, setEnterOtp] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [appLogo, setAppLogo] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
handleServerUrl();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||
const handleServerUrl = () => {
|
||||
const handleServerUrl = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo) {
|
||||
setAppLogo(applogo);
|
||||
} else {
|
||||
setAppLogo(logo);
|
||||
}
|
||||
} else {
|
||||
setAppLogo(logo);
|
||||
}
|
||||
|
||||
//split url in array from '&'
|
||||
localStorage.clear();
|
||||
const checkSplit = serverUrl.split("&");
|
||||
@@ -59,7 +73,8 @@ function GuestLogin() {
|
||||
"X-Parse-Application-Id": parseId
|
||||
};
|
||||
let body = {
|
||||
email: email.toString()
|
||||
email: email.toString(),
|
||||
docId: id
|
||||
};
|
||||
let Otp = await axios.post(url, body, { headers: headers });
|
||||
|
||||
@@ -163,12 +178,14 @@ function GuestLogin() {
|
||||
}}
|
||||
>
|
||||
<div className="main_head">
|
||||
<div className="main-logo">
|
||||
<img
|
||||
alt="sign img"
|
||||
src="https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"
|
||||
width="100%"
|
||||
/>
|
||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||
{appLogo && (
|
||||
<img
|
||||
src={appLogo}
|
||||
className="object-contain h-full"
|
||||
alt="logo"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -10,11 +10,16 @@ import { NavLink, useNavigate, useLocation } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
|
||||
import {
|
||||
isEnableSubscription,
|
||||
modalCancelBtnColor,
|
||||
modalSubmitBtnColor
|
||||
} from "../constant/const";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import { fetchSubscription, getAppLogo } from "../constant/Utils";
|
||||
function Login() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -39,7 +44,7 @@ function Login() {
|
||||
Destination: ""
|
||||
});
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const image = appInfo?.applogo || undefined;
|
||||
const [image, setImage] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("accesstoken")) {
|
||||
@@ -47,8 +52,23 @@ function Login() {
|
||||
GetLoginData();
|
||||
}
|
||||
dispatch(fetchAppInfo());
|
||||
saveLogo();
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const saveLogo = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const logo = await getAppLogo();
|
||||
if (logo) {
|
||||
setImage(logo);
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
};
|
||||
const handleChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setState({ ...state, [name]: value });
|
||||
@@ -149,7 +169,7 @@ function Login() {
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
}).then(
|
||||
(result) => {
|
||||
async (result) => {
|
||||
let tenentInfo = [];
|
||||
const results = [result];
|
||||
if (results) {
|
||||
@@ -209,7 +229,7 @@ function Login() {
|
||||
x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
@@ -239,10 +259,7 @@ function Login() {
|
||||
element.pageType
|
||||
);
|
||||
setState({ ...state, loading: false });
|
||||
if (
|
||||
process.env
|
||||
.REACT_APP_ENABLE_SUBSCRIPTION
|
||||
) {
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
@@ -253,11 +270,15 @@ function Login() {
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const billingDate =
|
||||
results[0].get("Next_billing_date") &&
|
||||
results[0].get("Next_billing_date");
|
||||
if (billingDate) {
|
||||
if (billingDate > new Date()) {
|
||||
const res = await fetchSubscription();
|
||||
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"
|
||||
);
|
||||
@@ -292,9 +313,7 @@ function Login() {
|
||||
element.pageType
|
||||
);
|
||||
setState({ ...state, loading: false });
|
||||
if (
|
||||
process.env.REACT_APP_ENABLE_SUBSCRIPTION
|
||||
) {
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: _user.name,
|
||||
email: email,
|
||||
@@ -391,6 +410,18 @@ function Login() {
|
||||
const setThirdpartyLoader = (value) => {
|
||||
setState({ ...state, thirdpartyLoader: value });
|
||||
};
|
||||
const handleFreePlan = async (id) => {
|
||||
try {
|
||||
const params = { userId: id };
|
||||
const res = await Parse.Cloud.run("freesubscription", params);
|
||||
if (res.status === "error") {
|
||||
alert(res.result);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in free subscribe", err.message);
|
||||
alert("Somenthing went wrong, please try again later!");
|
||||
}
|
||||
};
|
||||
const thirdpartyLoginfn = async (sessionToken, billingDate) => {
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
@@ -400,6 +431,11 @@ function Login() {
|
||||
"X-Parse-Application-Id": parseAppId
|
||||
}
|
||||
});
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan = param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(res.data.objectId);
|
||||
}
|
||||
await Parse.User.become(sessionToken).then(() => {
|
||||
window.localStorage.setItem("accesstoken", sessionToken);
|
||||
});
|
||||
@@ -542,7 +578,7 @@ function Login() {
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
@@ -571,18 +607,28 @@ function Login() {
|
||||
);
|
||||
setThirdpartyLoader(false);
|
||||
setState({ ...state, loading: false });
|
||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
||||
if (isEnableSubscription) {
|
||||
if (billingDate) {
|
||||
if (billingDate > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(redirectUrl);
|
||||
@@ -597,17 +643,29 @@ function Login() {
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
setState({ ...state, loading: false });
|
||||
setThirdpartyLoader(false);
|
||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
||||
if (isEnableSubscription) {
|
||||
if (billingDate) {
|
||||
if (billingDate > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
if (isFreeplan) {
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
navigate(redirectUrl);
|
||||
@@ -745,10 +803,17 @@ function Login() {
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem("extended_class", item.extended_class);
|
||||
const currentUser = Parse.User.current();
|
||||
const userSettings = appInfo.settings;
|
||||
const setting = userSettings.find(
|
||||
(x) => x.role === _currentRole
|
||||
);
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${setting.pageType}/${setting.pageId}`;
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
}).then(
|
||||
(result) => {
|
||||
async (result) => {
|
||||
let tenentInfo = [];
|
||||
const results = [result];
|
||||
if (results) {
|
||||
@@ -777,22 +842,74 @@ function Login() {
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
localStorage.setItem("PageLanding", item.pageId);
|
||||
localStorage.setItem("defaultmenuid", item.menuId);
|
||||
localStorage.setItem("pageType", item.pageType);
|
||||
navigate(`/${item.pageType}/${item.pageId}`);
|
||||
localStorage.setItem("PageLanding", setting.pageId);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
setting.menuId
|
||||
);
|
||||
localStorage.setItem("pageType", setting.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0].get("Phone"),
|
||||
company: results[0].get("Company")
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const res = await fetchSubscription();
|
||||
const billingDate = res.billingDate;
|
||||
const freeplan = res.plan;
|
||||
if (freeplan === "freeplan") {
|
||||
navigate(redirectUrl);
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
localStorage.removeItem("userDetails");
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem("PageLanding", item.pageId);
|
||||
localStorage.setItem("defaultmenuid", item.menuId);
|
||||
localStorage.setItem("pageType", item.pageType);
|
||||
navigate(`/${item.pageType}/${item.pageId}`);
|
||||
localStorage.setItem("PageLanding", setting.pageId);
|
||||
localStorage.setItem("defaultmenuid", setting.menuId);
|
||||
localStorage.setItem("pageType", setting.pageType);
|
||||
setState({ ...state, loading: false });
|
||||
if (isEnableSubscription) {
|
||||
const LocalUserDetails = {
|
||||
name: results[0].get("Name"),
|
||||
email: results[0].get("Email"),
|
||||
phone: results[0].get("Phone")
|
||||
// company: results.get("Company"),
|
||||
};
|
||||
localStorage.setItem(
|
||||
"userDetails",
|
||||
JSON.stringify(LocalUserDetails)
|
||||
);
|
||||
const billingDate = "";
|
||||
if (billingDate) {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
@@ -949,12 +1066,14 @@ function Login() {
|
||||
<>
|
||||
<div aria-labelledby="loginHeading" role="region">
|
||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-4 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
||||
<div className="w-[250px] h-[66px] inline-block">
|
||||
<img
|
||||
src={image}
|
||||
width="100%"
|
||||
alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly."
|
||||
/>
|
||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||
{image && (
|
||||
<img
|
||||
src={image}
|
||||
className="object-contain h-full"
|
||||
alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly."
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
||||
<div>
|
||||
@@ -1045,7 +1164,11 @@ function Login() {
|
||||
</button>
|
||||
<NavLink
|
||||
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
|
||||
to="/signup"
|
||||
to={
|
||||
location.search
|
||||
? "/signup" + location.search
|
||||
: "/signup"
|
||||
}
|
||||
style={width < 768 ? { textAlign: "center" } : {}}
|
||||
>
|
||||
Create Account
|
||||
@@ -1054,25 +1177,12 @@ function Login() {
|
||||
</form>
|
||||
<br />
|
||||
{(appInfo.fbAppId || appInfo.googleClietId) && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
className="text-sm"
|
||||
>
|
||||
<hr
|
||||
className={"border-[1px] border-gray-300 w-full"}
|
||||
style={{ color: "grey" }}
|
||||
/>
|
||||
<span style={{ color: "grey" }} className="px-2 ">
|
||||
<div className="text-sm flex justify-center items-center">
|
||||
<hr className="border-[1px] border-gray-300 w-full" />
|
||||
<span className="px-2 text-gray-500 cursor-default">
|
||||
OR
|
||||
</span>
|
||||
<hr
|
||||
className={"border-[1px] border-gray-300 w-full"}
|
||||
style={{ color: "grey" }}
|
||||
/>
|
||||
<hr className="border-[1px] border-gray-300 w-full" />
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
|
||||
@@ -5,6 +5,7 @@ import "../styles/signature.css";
|
||||
import { toDataUrl } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
const ManageSign = () => {
|
||||
const appName = appInfo.appname;
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
@@ -158,7 +159,11 @@ const ManageSign = () => {
|
||||
try {
|
||||
const parseFile = new Parse.File(file.name, file);
|
||||
const response = await parseFile.save();
|
||||
return response?.url();
|
||||
if (response?.url()) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(file.size, response.url(), tenantId);
|
||||
return response?.url();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("sign upload err", err);
|
||||
setIsLoader(false);
|
||||
|
||||
@@ -7,6 +7,10 @@ import { useNavigate } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Tour from "reactour";
|
||||
import axios from "axios";
|
||||
|
||||
const DriveBody = React.lazy(
|
||||
() => import("../components/opensigndrive/DriveBody")
|
||||
);
|
||||
@@ -35,13 +39,15 @@ function Opensigndrive() {
|
||||
const scrollRef = useRef(null);
|
||||
const [isList, setIsList] = useState(false);
|
||||
const [selectedSort, setSelectedSort] = useState("Date");
|
||||
const [sortingOrder, setSortingOrder] = useState("Decending");
|
||||
const [sortingOrder, setSortingOrder] = useState("Descending");
|
||||
const [pdfData, setPdfData] = useState([]);
|
||||
const [isFolder, setIsFolder] = useState(false);
|
||||
const [newFolderName, setNewFolderName] = useState();
|
||||
const [error, setError] = useState();
|
||||
const [folderLoader, setIsFolderLoader] = useState(false);
|
||||
const [isShowSort, setIsShowSort] = useState(false);
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
const [tourStatusArr, setTourStatusArr] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
@@ -54,6 +60,16 @@ function Opensigndrive() {
|
||||
const [skip, setSkip] = useState(0);
|
||||
const limit = 100;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const sortOrder = ["Ascending", "Descending"];
|
||||
const sortingValue = ["Name", "Date"];
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [tourData, setTourData] = useState();
|
||||
const orderName = {
|
||||
Ascending: "Ascending",
|
||||
Descending: "Descending",
|
||||
Name: "Name",
|
||||
Date: "Date"
|
||||
};
|
||||
const currentUser =
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
@@ -65,12 +81,60 @@ function Opensigndrive() {
|
||||
|
||||
useEffect(() => {
|
||||
getPdfDocumentList();
|
||||
|
||||
// eslint-disable-next-line
|
||||
}, [docId]);
|
||||
|
||||
const tourConfigs = [
|
||||
{
|
||||
selector: '[data-tut="reactourFirst"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Click on the breadcrumb links to easily navigate through the folder hierarchy and view the documents within each folder.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSecond"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Click the add button to create a new folder or document.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourThird"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Sort your documents by Date or Name using this menu.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourForth"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Click on this menu to display the documents in list view.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
//function for get all pdf document list
|
||||
const getPdfDocumentList = async (disbaleLoading) => {
|
||||
setLoading(true);
|
||||
checkTourStatus();
|
||||
if (!disbaleLoading) {
|
||||
setIsLoading({ isLoad: true, message: "This might take some time" });
|
||||
}
|
||||
@@ -79,8 +143,61 @@ function Opensigndrive() {
|
||||
if (driveDetails && driveDetails === "Error: Something went wrong!") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
} else if (driveDetails && driveDetails.length > 0) {
|
||||
const addMoreTour = [
|
||||
{
|
||||
selector: '[data-tut="reactourFifth"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`The document list is displayed according to the selected sorting option. Icons next to each document indicate its current status.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSixth"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Right-click on a document to see options such as Download, Rename, Move, and Delete. Click on the document to open it.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
},
|
||||
{
|
||||
selector: '[data-tut="reactourSeventh"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Right-click on any folder to see options. Choose ‘Rename’ to change the folder’s name or click on the folder to navigate through its contents.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "bottom",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
let newTour = [...tourConfigs, ...addMoreTour];
|
||||
const isFolderExist = driveDetails.some(
|
||||
(data) => data.Type === "Folder"
|
||||
);
|
||||
if (!isFolderExist) {
|
||||
newTour = newTour.filter(
|
||||
(data) => data.selector !== '[data-tut="reactourSeventh"]'
|
||||
);
|
||||
}
|
||||
const isDocumentExist = driveDetails.some((data) => data.URL);
|
||||
if (!isDocumentExist) {
|
||||
newTour = newTour.filter(
|
||||
(data) => data.selector !== '[data-tut="reactourSixth"]'
|
||||
);
|
||||
}
|
||||
setTourData(newTour);
|
||||
setSkip((prevSkip) => prevSkip + limit);
|
||||
sortApps("Date", "Decending", driveDetails, true);
|
||||
sortingData(null, null, driveDetails, true);
|
||||
} else {
|
||||
setTourData(tourConfigs);
|
||||
}
|
||||
if (!docId) {
|
||||
setFolderName([{ name: "OpenSign™ Drive", objectId: "" }]);
|
||||
@@ -114,6 +231,7 @@ function Opensigndrive() {
|
||||
//disableLoading is used disable initial loader
|
||||
const disableLoading = true;
|
||||
// If the fetched data length is less than the limit, it means there's no more data to fetch
|
||||
|
||||
if (!loading && pdfData.length % 100 === 0) {
|
||||
getPdfDocumentList(disableLoading);
|
||||
}
|
||||
@@ -129,7 +247,7 @@ function Opensigndrive() {
|
||||
};
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [loading]); // 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
|
||||
const handleRoute = (index) => {
|
||||
@@ -194,6 +312,7 @@ function Opensigndrive() {
|
||||
const result = JSON.parse(JSON.stringify(res));
|
||||
|
||||
setPdfData((prev) => [...prev, result]);
|
||||
sortingData(null, null, [result], true);
|
||||
setNewFolderName();
|
||||
setIsFolderLoader(false);
|
||||
setIsFolder(false);
|
||||
@@ -210,35 +329,42 @@ function Opensigndrive() {
|
||||
}
|
||||
};
|
||||
|
||||
const sortingApp = (appInfo, type, order) => {
|
||||
if (type === "Name") {
|
||||
if (order === "Accending") {
|
||||
return appInfo.sort((a, b) => (a.Name > b.Name ? 1 : -1));
|
||||
} else if (order === "Decending") {
|
||||
return appInfo.sort((a, b) => (a.Name > b.Name ? -1 : 1));
|
||||
//function to use sorting document list according to type and order
|
||||
const sortedBy = (appInfo, type, order) => {
|
||||
if (type === orderName.Name) {
|
||||
if (order === orderName.Ascending) {
|
||||
return appInfo.sort((a, b) =>
|
||||
a.Name.toLowerCase() < b.Name.toLowerCase() ? -1 : 1
|
||||
);
|
||||
} else if (order === orderName.Descending) {
|
||||
return appInfo.sort((a, b) =>
|
||||
a.Name.toLowerCase() < b.Name.toLowerCase() ? 1 : -1
|
||||
);
|
||||
}
|
||||
} else if (type === "Date") {
|
||||
if (order === "Accending") {
|
||||
} else if (type === orderName.Date) {
|
||||
if (order === orderName.Ascending) {
|
||||
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1));
|
||||
} else if (order === "Decending") {
|
||||
} else if (order === orderName.Descending) {
|
||||
return appInfo.sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sortApps = (type, order, driveDetails, isInitial) => {
|
||||
const selectedSortType = type ? type : selectedSort ? selectedSort : "Date";
|
||||
const sortOrder = order ? order : sortingOrder ? sortingOrder : "Decending";
|
||||
if (selectedSortType === "Name") {
|
||||
sortingApp(driveDetails, "Name", sortOrder);
|
||||
} else if (selectedSortType === "Date") {
|
||||
sortingApp(driveDetails, "Date", sortOrder);
|
||||
}
|
||||
if (isInitial) {
|
||||
setPdfData([...pdfData, ...driveDetails]);
|
||||
} else {
|
||||
setPdfData(driveDetails);
|
||||
//function to use get sorting type, order and document list to sort
|
||||
const sortingData = (type, order, driveDetails, isInitial) => {
|
||||
const selectedSortType = type ? type : selectedSort;
|
||||
const sortOrder = order ? order : sortingOrder;
|
||||
|
||||
//check isInitial true it means sort previous 20 and get on scrolling 20 = 40 document list
|
||||
const allPdfData = isInitial ? [...pdfData, ...driveDetails] : driveDetails;
|
||||
//call sortedBy function according to selected Type and order
|
||||
if (selectedSortType === orderName.Name) {
|
||||
sortedBy(allPdfData, orderName.Name, sortOrder);
|
||||
} else if (selectedSortType === orderName.Date) {
|
||||
sortedBy(allPdfData, orderName.Date, sortOrder);
|
||||
}
|
||||
|
||||
setPdfData(allPdfData);
|
||||
};
|
||||
|
||||
//function for handle auto scroll on folder path
|
||||
@@ -341,9 +467,76 @@ function Opensigndrive() {
|
||||
setNewFolderName("");
|
||||
setError("");
|
||||
};
|
||||
|
||||
const handleDontShow = (isChecked) => {
|
||||
setIsDontShow(isChecked);
|
||||
};
|
||||
|
||||
const closeTour = async () => {
|
||||
setIsTour(false);
|
||||
if (isDontShow) {
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const appId = localStorage.getItem("parseAppId");
|
||||
const extUserClass = localStorage.getItem("extended_class");
|
||||
const json = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const extUserId = json && json.length > 0 && json[0].objectId;
|
||||
let updatedTourStatus = [];
|
||||
if (tourStatusArr.length > 0) {
|
||||
updatedTourStatus = [...tourStatusArr];
|
||||
const driveTourIndex = tourStatusArr.findIndex(
|
||||
(obj) => obj["driveTour"] === false || obj["driveTour"] === true
|
||||
);
|
||||
if (driveTourIndex !== -1) {
|
||||
updatedTourStatus[driveTourIndex] = { driveTour: true };
|
||||
} else {
|
||||
updatedTourStatus.push({ driveTour: true });
|
||||
}
|
||||
} else {
|
||||
updatedTourStatus = [{ driveTour: true }];
|
||||
}
|
||||
await axios.put(
|
||||
serverUrl + "classes/" + extUserClass + "/" + extUserId,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"X-Parse-Application-Id": appId
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
//function to use check tour status of open sign drive
|
||||
async function checkTourStatus() {
|
||||
const currentUser = Parse.User.current();
|
||||
const cloudRes = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
const res = { data: cloudRes.toJSON() };
|
||||
if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) {
|
||||
const tourStatus = res.data.TourStatus;
|
||||
setTourStatusArr(tourStatus);
|
||||
const filteredtourStatus = tourStatus.filter((obj) => obj["driveTour"]);
|
||||
if (filteredtourStatus.length > 0) {
|
||||
const driveTour = filteredtourStatus[0]["driveTour"];
|
||||
// console.log("loginTour", loginTour);
|
||||
if (driveTour) {
|
||||
setIsTour(false);
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
} else {
|
||||
setIsTour(true);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div style={{ backgroundColor: "white" }} className="folderComponent">
|
||||
<Title title={"OpenSign™ Drive"} drive={true} />
|
||||
|
||||
<div>
|
||||
<ModalUi
|
||||
headerColor={"#dc3545"}
|
||||
@@ -506,7 +699,19 @@ function Opensigndrive() {
|
||||
) : (
|
||||
<>
|
||||
<div className="folderContainer">
|
||||
{tourData && (
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourData}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
data-tut="reactourFirst"
|
||||
onMouseEnter={(e) => handleMouseEnter(e)}
|
||||
ref={scrollRef}
|
||||
style={{
|
||||
@@ -524,7 +729,7 @@ function Opensigndrive() {
|
||||
}
|
||||
onClick={() => setIsNewFol(!isNewFol)}
|
||||
>
|
||||
<div className="sort">
|
||||
<div className="sort" data-tut="reactourSecond">
|
||||
<i
|
||||
className="fa fa-plus-square"
|
||||
aria-hidden="true"
|
||||
@@ -585,7 +790,11 @@ function Opensigndrive() {
|
||||
className={isShowSort ? "dropdown show" : "dropdown"}
|
||||
onClick={() => setIsShowSort(!isShowSort)}
|
||||
>
|
||||
<div className=" sort " data-toggle="dropdown">
|
||||
<div
|
||||
data-tut="reactourThird"
|
||||
className=" sort "
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i
|
||||
className="fa fa-sort-amount-asc"
|
||||
aria-hidden="true"
|
||||
@@ -611,95 +820,56 @@ function Opensigndrive() {
|
||||
aria-labelledby="dropdownMenuButton"
|
||||
aria-expanded={isShowSort ? "true" : "false"}
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
setSelectedSort("Name");
|
||||
sortApps("Name", null, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
>
|
||||
{selectedSort !== "Name" ? (
|
||||
<i
|
||||
className="fa fa-arrow-up"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
) : (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
Name
|
||||
</span>
|
||||
<span
|
||||
onClick={() => {
|
||||
setSelectedSort("Date");
|
||||
sortApps("Date", null, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
>
|
||||
{selectedSort !== "Date" ? (
|
||||
<i
|
||||
className="fa fa-arrow-up"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
) : (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
Date
|
||||
</span>
|
||||
{sortingValue.map((value, ind) => {
|
||||
return (
|
||||
<span
|
||||
key={ind}
|
||||
onClick={() => {
|
||||
setSelectedSort(value);
|
||||
sortingData(value, null, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
style={{
|
||||
paddingLeft: selectedSort !== value && "33px"
|
||||
}}
|
||||
>
|
||||
{selectedSort === value && (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
{value}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
|
||||
<hr className="hrStyle" />
|
||||
<span
|
||||
onClick={() => {
|
||||
setSortingOrder("Accending");
|
||||
sortApps(null, "Accending", pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
>
|
||||
{sortingOrder !== "Accending" ? (
|
||||
<i
|
||||
className="fa fa-arrow-up"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
) : (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
Accending
|
||||
</span>
|
||||
<span
|
||||
onClick={() => {
|
||||
setSortingOrder("Decending");
|
||||
sortApps(null, "Decending", pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
>
|
||||
{sortingOrder !== "Decending" ? (
|
||||
<i
|
||||
className="fa fa-arrow-up"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
) : (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
Decending
|
||||
</span>
|
||||
{sortOrder.map((order, ind) => {
|
||||
return (
|
||||
<span
|
||||
key={ind}
|
||||
onClick={() => {
|
||||
setSortingOrder(order);
|
||||
sortingData(null, order, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
style={{
|
||||
paddingLeft: sortingOrder !== order && "33px"
|
||||
}}
|
||||
>
|
||||
{sortingOrder === order && (
|
||||
<i
|
||||
className="fa fa-check"
|
||||
aria-hidden="true"
|
||||
style={{ marginRight: "5px" }}
|
||||
></i>
|
||||
)}
|
||||
{order}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -714,7 +884,11 @@ function Opensigndrive() {
|
||||
></i>
|
||||
</div>
|
||||
) : (
|
||||
<div className="sort" onClick={() => setIsList(!isList)}>
|
||||
<div
|
||||
data-tut="reactourForth"
|
||||
className="sort"
|
||||
onClick={() => setIsList(!isList)}
|
||||
>
|
||||
<i
|
||||
className="fa fa-list"
|
||||
aria-hidden="true"
|
||||
@@ -739,23 +913,28 @@ function Opensigndrive() {
|
||||
<span style={{ fontWeight: "bold" }}>No Data Found!</span>
|
||||
</div>
|
||||
) : (
|
||||
<React.Suspense fallback={<Loader />}>
|
||||
<DriveBody
|
||||
pdfData={pdfData}
|
||||
setFolderName={setFolderName}
|
||||
setIsLoading={setIsLoading}
|
||||
setDocId={setDocId}
|
||||
getPdfDocumentList={getPdfDocumentList}
|
||||
isDocId={docId}
|
||||
setPdfData={setPdfData}
|
||||
isList={isList}
|
||||
setIsAlert={setIsAlert}
|
||||
setSkip={setSkip}
|
||||
/>
|
||||
{loading && (
|
||||
<div style={{ textAlign: "center" }}>Loading...</div>
|
||||
)}
|
||||
</React.Suspense>
|
||||
<div data-tut="reactourFifth">
|
||||
<React.Suspense fallback={<Loader />}>
|
||||
<DriveBody
|
||||
dataTutSixth="reactourSixth"
|
||||
dataTutSeventh="reactourSeventh"
|
||||
pdfData={pdfData}
|
||||
setFolderName={setFolderName}
|
||||
setIsLoading={setIsLoading}
|
||||
setDocId={setDocId}
|
||||
getPdfDocumentList={getPdfDocumentList}
|
||||
isDocId={docId}
|
||||
setPdfData={setPdfData}
|
||||
isList={isList}
|
||||
setIsAlert={setIsAlert}
|
||||
setSkip={setSkip}
|
||||
sortingData={sortingData}
|
||||
/>
|
||||
{loading && (
|
||||
<div style={{ textAlign: "center" }}>Loading...</div>
|
||||
)}
|
||||
</React.Suspense>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,12 +41,8 @@ const PgSignUp = () => {
|
||||
const url = window.location.href;
|
||||
let paramString = url.split("?")[1];
|
||||
let queryString = new URLSearchParams(paramString);
|
||||
// console.log("url ", queryString);
|
||||
|
||||
let obj = { ...formData };
|
||||
for (let pair of queryString.entries()) {
|
||||
// console.log("Key is: " + pair[0]);
|
||||
// console.log("Value is: " + pair[1]);
|
||||
obj = { ...obj, [pair[0]]: pair[1] };
|
||||
}
|
||||
const zohoRes = await axios.post(
|
||||
@@ -61,34 +57,27 @@ const PgSignUp = () => {
|
||||
}
|
||||
}
|
||||
);
|
||||
// console.log("zohoRes ", zohoRes);
|
||||
const userSettings = JSON.parse(localStorage.getItem("userSettings"));
|
||||
const extClass = userSettings[0].extended_class;
|
||||
// console.log("extClass ", extClass);
|
||||
const params = { email: userDetails.email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("res", res);
|
||||
if (res) {
|
||||
const updateQuery = new Parse.Object(extClass);
|
||||
updateQuery.id = res.id;
|
||||
updateQuery.set(
|
||||
"Next_billing_date",
|
||||
new Date(zohoRes.data.result.nextBillingDate)
|
||||
);
|
||||
updateQuery.set("Plan", zohoRes.data.result.plan);
|
||||
updateQuery.set("Customer_id", zohoRes.data.result.customer_id);
|
||||
updateQuery.set(
|
||||
"Subscription_id",
|
||||
zohoRes.data.result.subscription_id
|
||||
);
|
||||
const updateRes = await updateQuery.save();
|
||||
if (updateRes) {
|
||||
const params = { subscription: zohoRes.data.result.subscription };
|
||||
const baseurl = `${parseBaseUrl}functions/savesubscription`;
|
||||
try {
|
||||
const res = await axios.post(baseurl, params, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
}
|
||||
});
|
||||
if (res) {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(
|
||||
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId
|
||||
);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err.message);
|
||||
setIsLoader(false);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
@@ -108,12 +97,8 @@ const PgSignUp = () => {
|
||||
const url = window.location.href;
|
||||
let paramString = url.split("?")[1];
|
||||
let queryString = new URLSearchParams(paramString);
|
||||
// console.log("url ", queryString);
|
||||
|
||||
let obj = { ...formData };
|
||||
for (let pair of queryString.entries()) {
|
||||
// console.log("Key is: " + pair[0]);
|
||||
// console.log("Value is: " + pair[1]);
|
||||
obj = { ...obj, [pair[0]]: pair[1] };
|
||||
}
|
||||
saveUser(obj);
|
||||
@@ -157,19 +142,8 @@ const PgSignUp = () => {
|
||||
email: zohoRes.data.result.email,
|
||||
phone: zohoRes.data.result.phone,
|
||||
role: obj.role
|
||||
// "pincode": "",
|
||||
// "address": "",
|
||||
// "country": "",
|
||||
// "state": "",
|
||||
// "city": ""
|
||||
},
|
||||
|
||||
planDetails: {
|
||||
plan: zohoRes.data.result.plan || {},
|
||||
nextBillingDate: zohoRes.data.result.nextBillingDate || "",
|
||||
customer_id: zohoRes.data.result.customer_id || "",
|
||||
subscription_id: zohoRes.data.result.subscription_id || ""
|
||||
}
|
||||
subscription: zohoRes.data.result.subscription
|
||||
};
|
||||
const usersignup = await Parse.Cloud.run("usersignup", params);
|
||||
// console.log("usersignup ", usersignup);
|
||||
@@ -353,7 +327,7 @@ const PgSignUp = () => {
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import checkmark from "../assets/images/checkmark.png";
|
||||
import plansArr from "../json/plansArr.json";
|
||||
import plansArr from "../json/plansArr";
|
||||
import Title from "../components/Title";
|
||||
import Parse from "parse";
|
||||
const listItemStyle = {
|
||||
paddingLeft: "20px", // Add padding to create space for the image
|
||||
backgroundImage: `url(${checkmark})`, // Set your image as the list style image
|
||||
@@ -19,15 +19,22 @@ const PlanSubscriptions = () => {
|
||||
|
||||
const userDetails = JSON.parse(localStorage.getItem("userDetails"));
|
||||
const name =
|
||||
userDetails && userDetails.name ? "first_name=" + userDetails.name : "";
|
||||
userDetails && userDetails.name
|
||||
? "first_name=" + encodeURIComponent(userDetails.name)
|
||||
: "";
|
||||
const email =
|
||||
userDetails && userDetails.email ? "&email=" + userDetails.email : "";
|
||||
userDetails && userDetails.email
|
||||
? "&email=" + encodeURIComponent(userDetails.email)
|
||||
: "";
|
||||
const company =
|
||||
userDetails && userDetails.company
|
||||
? "&company_name=" + userDetails.company
|
||||
? "&company_name=" + encodeURIComponent(userDetails.company)
|
||||
: "";
|
||||
const phone =
|
||||
userDetails && userDetails.phone ? "&mobile=" + userDetails.phone : "";
|
||||
userDetails && userDetails.phone
|
||||
? "&mobile=" + encodeURIComponent(userDetails.phone)
|
||||
: "";
|
||||
|
||||
const details =
|
||||
"?shipping_country_code=US&billing_country_code=US&billing_state_code=CA&" +
|
||||
name +
|
||||
@@ -35,15 +42,35 @@ const PlanSubscriptions = () => {
|
||||
company +
|
||||
phone;
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("accesstoken")) {
|
||||
setIsLoader(false);
|
||||
setYearlyVisible(false);
|
||||
} else {
|
||||
navigate("/", { replace: true });
|
||||
}
|
||||
// if (localStorage.getItem("accesstoken")) {
|
||||
setIsLoader(false);
|
||||
// } else {
|
||||
// navigate("/", { replace: true });
|
||||
// }
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const handleFreePlan = async () => {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const params = { userId: Parse.User.current().id };
|
||||
const res = await Parse.Cloud.run("freesubscription", params);
|
||||
if (res.status === "success" && res.result === "already subscribed!") {
|
||||
setIsLoader(false);
|
||||
alert("You have already subscribed to plan!");
|
||||
} else if (res.status === "success") {
|
||||
setIsLoader(false);
|
||||
navigate("/");
|
||||
} else if (res.status === "error") {
|
||||
setIsLoader(false);
|
||||
alert(res.result);
|
||||
}
|
||||
} catch (err) {
|
||||
setIsLoader(false);
|
||||
console.log("err in free subscribe", err.message);
|
||||
alert("Somenthing went wrong, please try again later!");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Title title={"Subscriptions"} />
|
||||
@@ -69,23 +96,40 @@ const PlanSubscriptions = () => {
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
overflowY: "auto",
|
||||
maxHeight: "100%",
|
||||
"--theme-color": "#7952b3",
|
||||
"--plan-width": 30
|
||||
maxHeight: "100%"
|
||||
}}
|
||||
>
|
||||
<div
|
||||
id="monthlyPlans"
|
||||
className={`${yearlyVisible ? "none" : "block my-2"}`}
|
||||
>
|
||||
<div className="flex justify-center w-full my-2">
|
||||
<ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300">
|
||||
<div className="block my-2">
|
||||
<div className="flex flex-col justify-center items-center w-full">
|
||||
<div className="mb-6 mt-2 flex flex-row border-[1px] p-2 border-gray-300 rounded text-sm">
|
||||
<span
|
||||
onClick={() => setYearlyVisible(false)}
|
||||
className={`${
|
||||
!yearlyVisible
|
||||
? "bg-[#002862] text-white"
|
||||
: "bg-white text-black"
|
||||
} px-4 py-1 rounded cursor-pointer`}
|
||||
>
|
||||
Monthly
|
||||
</span>
|
||||
<span
|
||||
onClick={() => setYearlyVisible(true)}
|
||||
className={`${
|
||||
yearlyVisible
|
||||
? "bg-[#002862] text-white"
|
||||
: "bg-white text-black"
|
||||
} px-4 py-1 rounded cursor-pointer`}
|
||||
>
|
||||
Yearly (10% off)
|
||||
</span>
|
||||
</div>
|
||||
<ul className="flex flex-col md:flex-row h-full bg-white justify-center">
|
||||
{plansArr.map((item) => (
|
||||
<li
|
||||
className="flex flex-col md:my-0 text-center border-[1px] border-gray-300 w-[260px]"
|
||||
className="flex flex-col text-center border-collapse border-[1px] border-gray-300 max-w-[250px]"
|
||||
key={item.planName}
|
||||
>
|
||||
<div className="p-2 flex flex-col justify-center items-center">
|
||||
<div className="p-2 flex flex-col justify-center items-center max-h-[310px]">
|
||||
<h3 className="text-[#002862] uppercase">
|
||||
{item.planName}
|
||||
</h3>
|
||||
@@ -96,20 +140,20 @@ const PlanSubscriptions = () => {
|
||||
alt="freeimg"
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<div>
|
||||
<span className="text-3xl">
|
||||
{item.currency && <small>{item.currency}</small>}
|
||||
{item.price}
|
||||
{yearlyVisible
|
||||
? item?.yearlyPrice
|
||||
: item.monthlyPrice}
|
||||
</span>
|
||||
<div
|
||||
className={`${
|
||||
item.subtitle.length <= 32
|
||||
? "w-[150px] text-center"
|
||||
: ""
|
||||
} text-sm text-center my-2`}
|
||||
>
|
||||
<p
|
||||
<p className="font-semibold pt-2 text-sm">
|
||||
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
|
||||
</p>
|
||||
<div className="max-w-[250px] h-[40px] text-center text-sm my-2">
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
backgroundColor: item.subtitlecolor
|
||||
? item.subtitlecolor
|
||||
: "white"
|
||||
@@ -124,23 +168,33 @@ const PlanSubscriptions = () => {
|
||||
) : (
|
||||
<span>{item.subtitle}</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NavLink
|
||||
to={
|
||||
item.btnText === "Subscribe"
|
||||
? item.url + details
|
||||
: item.url
|
||||
}
|
||||
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white"
|
||||
target={item.target}
|
||||
>
|
||||
{item.btnText}
|
||||
</NavLink>
|
||||
{item.url ? (
|
||||
<NavLink
|
||||
to={
|
||||
item.btnText === "Subscribe"
|
||||
? yearlyVisible
|
||||
? item.yearlyUrl + details
|
||||
: item.url + details
|
||||
: item.url
|
||||
}
|
||||
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
|
||||
target={item.target}
|
||||
>
|
||||
{item.btnText}
|
||||
</NavLink>
|
||||
) : (
|
||||
<button
|
||||
className="bg-[#002862] w-full text-white py-2 rounded uppercase hover:text-white cursor-pointer"
|
||||
onClick={() => handleFreePlan()}
|
||||
>
|
||||
{item.btnText}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<hr className="w-full bg-gray-300 p-[.5px]" />
|
||||
<hr className="w-full bg-gray-300 h-[0.5px]" />
|
||||
<ul className="mx-1 p-3 text-left break-words text-sm list-none">
|
||||
{item.benefits.map((subitem, index) => (
|
||||
<li style={listItemStyle} key={index} className="m-1">
|
||||
@@ -154,6 +208,23 @@ const PlanSubscriptions = () => {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="text-sm flex justify-center items-center">
|
||||
<hr className="border-[1px] border-gray-300 w-[20%]" />
|
||||
<span className="px-2 text-gray-500 cursor-default">or</span>
|
||||
<hr className="border-[1px] border-gray-300 w-[20%]" />
|
||||
</div>
|
||||
<div className="flex flex-col justify-center w-full items-center">
|
||||
<h3 className="text-[#002862] mt-1 mb-2">
|
||||
Host it yourself for free
|
||||
</h3>
|
||||
<NavLink
|
||||
to={"https://github.com/OpenSignLabs/OpenSign"}
|
||||
className="bg-[#002862] w-[200px] text-center text-white py-2 rounded uppercase hover:no-underline hover:text-white cursor-pointer"
|
||||
target={"_blank"}
|
||||
>
|
||||
Visit Github
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -6,6 +6,7 @@ import reportJson from "../json/ReportJson";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
import PageNotFound from "./PageNotFound";
|
||||
import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
|
||||
const Report = () => {
|
||||
const { id } = useParams();
|
||||
@@ -18,6 +19,8 @@ const Report = () => {
|
||||
const [isNextRecord, setIsNextRecord] = useState(false);
|
||||
const [isMoreDocs, setIsMoreDocs] = useState(true);
|
||||
const [form, setForm] = useState("");
|
||||
const [tourData, setTourData] = useState([]);
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const abortController = new AbortController();
|
||||
const docPerPage = 10;
|
||||
|
||||
@@ -46,6 +49,9 @@ const Report = () => {
|
||||
// eslint-disable-next-line
|
||||
}, [isNextRecord]);
|
||||
|
||||
const handleDontShow = (isChecked) => {
|
||||
setIsDontShow(isChecked);
|
||||
};
|
||||
const getReportData = async (skipUserRecord = 0, limit = 200) => {
|
||||
// setIsLoader(true);
|
||||
const json = reportJson(id);
|
||||
@@ -54,7 +60,7 @@ const Report = () => {
|
||||
setHeading(json.heading);
|
||||
setReportName(json.reportName);
|
||||
setForm(json.form);
|
||||
setReportHelp(json?.helpMsg)
|
||||
setReportHelp(json?.helpMsg);
|
||||
const currentUser = Parse.User.current().id;
|
||||
|
||||
const headers = {
|
||||
@@ -69,6 +75,42 @@ const Report = () => {
|
||||
headers: headers,
|
||||
signal: abortController.signal // is used to cancel fetch query
|
||||
});
|
||||
if (id === "6TeaPr321t") {
|
||||
const tourConfig = [
|
||||
{
|
||||
selector: "[data-tut=reactourFirst]",
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={
|
||||
"Click the 'Add' button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like ‘HR Manager’ and ‘New Employee’. Each time you use the template, you can assign the ‘New Employee’ role to different incoming staff members, while the ‘HR Manager’ role remains constant, facilitating a seamless onboarding process for each recruit. "
|
||||
}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
if (res.data.result && res.data.result?.length > 0) {
|
||||
json.actions.map((data) => {
|
||||
const newConfig = {
|
||||
selector: `[data-tut="${data?.selector}"]`,
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={data?.message}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
};
|
||||
tourConfig.push(newConfig);
|
||||
});
|
||||
}
|
||||
|
||||
setTourData(tourConfig);
|
||||
}
|
||||
if (id === "4Hhwbp482K") {
|
||||
const listData = res.data?.result.filter((x) => x.Signers.length > 0);
|
||||
let arr = [];
|
||||
@@ -106,12 +148,14 @@ const Report = () => {
|
||||
} else {
|
||||
setIsMoreDocs(false);
|
||||
}
|
||||
setIsNextRecord(false);
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
if (!res.data.result.error) {
|
||||
setIsNextRecord(false);
|
||||
setList((prevRecord) =>
|
||||
prevRecord.length > 0
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
@@ -159,6 +203,8 @@ const Report = () => {
|
||||
docPerPage={docPerPage}
|
||||
form={form}
|
||||
report_help={reporthelp}
|
||||
tourData={tourData}
|
||||
isDontShow={isDontShow}
|
||||
/>
|
||||
) : (
|
||||
<PageNotFound prefix={"Report"} />
|
||||
|
||||
@@ -2,17 +2,20 @@ import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import Title from "../components/Title";
|
||||
import { useNavigate, NavLink } from "react-router-dom";
|
||||
import { useNavigate, NavLink, useLocation } from "react-router-dom";
|
||||
import login_img from "../assets/images/login_img.svg";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import Alert from "../primitives/Alert";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { fetchAppInfo } from "../redux/reducers/infoReducer";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { getAppLogo } from "../constant/Utils";
|
||||
const Signup = () => {
|
||||
const { width } = useWindowSize();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
@@ -20,6 +23,7 @@ const Signup = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [company, setCompany] = useState("");
|
||||
const [jobTitle, setJobTitle] = useState("");
|
||||
const [image, setImage] = useState();
|
||||
const [state, setState] = useState({
|
||||
loading: false,
|
||||
alertType: "success",
|
||||
@@ -31,7 +35,6 @@ const Signup = () => {
|
||||
const [specialCharValid, setSpecialCharValid] = useState(false);
|
||||
|
||||
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
||||
const image = appInfo.applogo;
|
||||
|
||||
const clearStorage = async () => {
|
||||
if (Parse.User.current()) {
|
||||
@@ -67,6 +70,19 @@ const Signup = () => {
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
};
|
||||
|
||||
const handleFreePlan = async (id) => {
|
||||
try {
|
||||
const params = { userId: id };
|
||||
const res = await Parse.Cloud.run("freesubscription", params);
|
||||
if (res.status === "error") {
|
||||
alert(res.result);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in free subscribe", err.message);
|
||||
alert("Somenthing went wrong, please try again later!");
|
||||
}
|
||||
};
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
@@ -110,7 +126,13 @@ const Signup = () => {
|
||||
params
|
||||
);
|
||||
if (usersignup) {
|
||||
handleNavigation(r.getSessionToken());
|
||||
const param = new URLSearchParams(location.search);
|
||||
const isFreeplan =
|
||||
param?.get("subscription") === "freeplan";
|
||||
if (isFreeplan) {
|
||||
await handleFreePlan(r.id);
|
||||
}
|
||||
handleNavigation(r.getSessionToken(), isFreeplan);
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
@@ -155,7 +177,7 @@ const Signup = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleNavigation = async (sessionToken) => {
|
||||
const handleNavigation = async (sessionToken, isFreeplan = false) => {
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const res = await axios.get(baseUrl + "users/me", {
|
||||
@@ -259,9 +281,7 @@ const Signup = () => {
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
dispatch(
|
||||
showTenant(
|
||||
tenentInfo[0].tenentName || ""
|
||||
)
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
@@ -297,7 +317,7 @@ const Signup = () => {
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenetId",
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
@@ -325,8 +345,14 @@ const Signup = () => {
|
||||
element.pageType
|
||||
);
|
||||
setState({ loading: false });
|
||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
if (isEnableSubscription) {
|
||||
if (isFreeplan) {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
alert("Registered user successfully");
|
||||
navigate(
|
||||
@@ -343,8 +369,14 @@ const Signup = () => {
|
||||
);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
setState({ loading: false });
|
||||
if (process.env.REACT_APP_ENABLE_SUBSCRIPTION) {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
if (isEnableSubscription) {
|
||||
if (isFreeplan) {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
@@ -438,9 +470,23 @@ const Signup = () => {
|
||||
};
|
||||
useEffect(() => {
|
||||
dispatch(fetchAppInfo());
|
||||
saveLogo();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const saveLogo = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const logo = await getAppLogo();
|
||||
if (logo) {
|
||||
setImage(logo);
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordChange = (e) => {
|
||||
const newPassword = e.target.value;
|
||||
setPassword(newPassword);
|
||||
@@ -486,8 +532,10 @@ const Signup = () => {
|
||||
{appInfo && appInfo.applogo ? (
|
||||
<div>
|
||||
<div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 ">
|
||||
<div className="w-[250px] h-[66px] inline-block">
|
||||
<img src={image} width="100%" alt="" />
|
||||
<div className="w-[250px] h-[66px] inline-block overflow-hidden">
|
||||
{image && (
|
||||
<img src={image} className="object-contain h-full" alt="logo" />
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
|
||||
<div className="">
|
||||
@@ -626,7 +674,7 @@ const Signup = () => {
|
||||
</button>
|
||||
<NavLink
|
||||
className="rounded-sm cursor-pointer bg-white border-[1px] border-[#15b4e9] text-[#15b4e9] w-full py-3 shadow uppercase"
|
||||
to="/"
|
||||
to={location.search ? "/" + location.search : "/"}
|
||||
style={width < 768 ? { textAlign: "center" } : {}}
|
||||
>
|
||||
Login
|
||||
@@ -661,4 +709,4 @@ const Signup = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Signup
|
||||
export default Signup;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import { themeColor } from "../constant/const";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import axios from "axios";
|
||||
import Loader from "../primitives/LoaderWithMsg";
|
||||
import loader from "../assets/images/loader2.gif";
|
||||
@@ -26,7 +26,10 @@ import {
|
||||
contactBook,
|
||||
randomId,
|
||||
getDate,
|
||||
textWidget
|
||||
textWidget,
|
||||
getTenantDetails,
|
||||
checkIsSubscribed,
|
||||
convertPdfArrayBuffer
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
@@ -67,7 +70,6 @@ function SignYourSelf() {
|
||||
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
||||
const [successEmail, setSuccessEmail] = useState(false);
|
||||
const imageRef = useRef(null);
|
||||
const [documentStatus, setDocumentStatus] = useState({ isCompleted: false });
|
||||
const [myInitial, setMyInitial] = useState("");
|
||||
const [isInitial, setIsInitial] = useState(false);
|
||||
const [isUiLoading, setIsUiLoading] = useState(false);
|
||||
@@ -90,7 +92,7 @@ function SignYourSelf() {
|
||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||
status: false
|
||||
});
|
||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
|
||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||
const [widgetType, setWidgetType] = useState("");
|
||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||
@@ -99,6 +101,9 @@ function SignYourSelf() {
|
||||
});
|
||||
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [extUserId, setExtUserId] = useState("");
|
||||
const [isCompleted, setIsCompleted] = useState(false);
|
||||
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -180,19 +185,24 @@ function SignYourSelf() {
|
||||
|
||||
//function for get document details for perticular signer with signer'object id
|
||||
const getDocumentDetails = async (showComplete) => {
|
||||
let isCompleted;
|
||||
//getting document details
|
||||
const documentData = await contractDocument(documentId);
|
||||
|
||||
if (documentData && documentData.length > 0) {
|
||||
setPdfDetails(documentData);
|
||||
|
||||
const isCompleted =
|
||||
documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
|
||||
const url = documentData[0] && documentData[0]?.URL;
|
||||
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
|
||||
const arrayBuffer = await convertPdfArrayBuffer(url);
|
||||
if (arrayBuffer === "Error") {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
} else {
|
||||
setPdfArrayBuffer(arrayBuffer);
|
||||
}
|
||||
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||
if (isCompleted) {
|
||||
const docStatus = {
|
||||
isCompleted: isCompleted
|
||||
};
|
||||
setDocumentStatus(docStatus);
|
||||
setIsCompleted(true);
|
||||
setPdfUrl(documentData[0].SignedUrl);
|
||||
const alreadySign = {
|
||||
status: true,
|
||||
@@ -255,16 +265,13 @@ function SignYourSelf() {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
setIsLoading(loadObj);
|
||||
});
|
||||
|
||||
const contractUsersRes = await contractUsers(jsonSender.email);
|
||||
|
||||
if (contractUsersRes[0] && contractUsersRes.length > 0) {
|
||||
setContractName("_Users");
|
||||
setSignerUserId(contractUsersRes[0].objectId);
|
||||
const tourstatuss =
|
||||
contractUsersRes[0].TourStatus && contractUsersRes[0].TourStatus;
|
||||
|
||||
if (tourstatuss && tourstatuss.length > 0) {
|
||||
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
|
||||
setTourStatus(tourstatuss);
|
||||
const checkTourRecipients = tourstatuss.filter(
|
||||
(data) => data.signyourself
|
||||
@@ -272,6 +279,8 @@ function SignYourSelf() {
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
||||
}
|
||||
} else {
|
||||
setCheckTourStatus(true);
|
||||
}
|
||||
const loadObj = {
|
||||
isLoad: false
|
||||
@@ -291,7 +300,8 @@ function SignYourSelf() {
|
||||
const tourstatuss =
|
||||
contractContactBook[0].TourStatus &&
|
||||
contractContactBook[0].TourStatus;
|
||||
if (tourstatuss && tourstatuss.length > 0) {
|
||||
|
||||
if (tourstatuss && tourstatuss.length > 0 && !isCompleted) {
|
||||
setTourStatus(tourstatuss);
|
||||
const checkTourRecipients = tourstatuss.filter(
|
||||
(data) => data.signyourself
|
||||
@@ -299,6 +309,8 @@ function SignYourSelf() {
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].signyourself);
|
||||
}
|
||||
} else {
|
||||
setCheckTourStatus(true);
|
||||
}
|
||||
} else {
|
||||
setHandleError("No Data Found!");
|
||||
@@ -557,22 +569,14 @@ function SignYourSelf() {
|
||||
setIsCeleb(false);
|
||||
}, 3000);
|
||||
setIsUiLoading(true);
|
||||
const url = pdfDetails[0] && pdfDetails[0].URL;
|
||||
|
||||
const existingPdfBytes = await fetch(url).then((res) =>
|
||||
res.arrayBuffer()
|
||||
);
|
||||
|
||||
const existingPdfBytes = pdfArrayBuffer;
|
||||
// Load a PDFDocument from the existing PDF bytes
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
|
||||
const flag = 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);
|
||||
@@ -582,7 +586,7 @@ function SignYourSelf() {
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
flag,
|
||||
isSignYourSelfFlow,
|
||||
containerWH
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
@@ -593,9 +597,25 @@ function SignYourSelf() {
|
||||
// console.log("signyourself", xyPostion);
|
||||
//function for get digital signature
|
||||
const signPdfFun = async (base64Url, documentId) => {
|
||||
let isCustomCompletionMail = false;
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
const tenantDetails = await getTenantDetails(jsonSender.objectId);
|
||||
if (tenantDetails && tenantDetails === "user does not exist!") {
|
||||
alert("User does not exist");
|
||||
} else {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
(!isEnableSubscription || getIsSubscribe)
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
}
|
||||
|
||||
let singleSign = {
|
||||
pdfFile: base64Url,
|
||||
docId: documentId
|
||||
docId: documentId,
|
||||
isCustomCompletionMail: isCustomCompletionMail
|
||||
};
|
||||
|
||||
await axios
|
||||
@@ -704,7 +724,7 @@ function SignYourSelf() {
|
||||
};
|
||||
|
||||
//function for save button to save signature or image url
|
||||
const saveSign = (isDefaultSign, width, height) => {
|
||||
const saveSign = (type, isDefaultSign, width, height) => {
|
||||
const isTypeText = width && height ? true : false;
|
||||
const signatureImg = isDefaultSign
|
||||
? isDefaultSign === "initials"
|
||||
@@ -727,6 +747,7 @@ function SignYourSelf() {
|
||||
}
|
||||
}
|
||||
const getUpdatePosition = onSaveSign(
|
||||
type,
|
||||
xyPostion,
|
||||
index,
|
||||
signKey,
|
||||
@@ -763,6 +784,7 @@ function SignYourSelf() {
|
||||
|
||||
//function for delete signature block
|
||||
const handleDeleteSign = (key) => {
|
||||
setCurrWidgetsDetails({});
|
||||
const updateResizeData = [];
|
||||
|
||||
let filterData = xyPostion[index].pos.filter((data) => data.key !== key);
|
||||
@@ -979,8 +1001,9 @@ function SignYourSelf() {
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
|
||||
<RenderAllPdfPage
|
||||
pdfUrl={pdfUrl}
|
||||
signPdfUrl={pdfDetails[0] && pdfDetails[0].URL}
|
||||
signPdfUrl={
|
||||
pdfDetails[0] && (pdfDetails[0].SignedUrl || pdfDetails[0].URL)
|
||||
}
|
||||
allPages={allPages}
|
||||
setAllPages={setAllPages}
|
||||
setPageNumber={setPageNumber}
|
||||
@@ -1101,6 +1124,8 @@ function SignYourSelf() {
|
||||
setIsInitial={setIsInitial}
|
||||
setIsStamp={setIsStamp}
|
||||
widgetType={widgetType}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
{/*render email component to send email after finish signature on document */}
|
||||
<EmailComponent
|
||||
@@ -1112,6 +1137,7 @@ function SignYourSelf() {
|
||||
setSuccessEmail={setSuccessEmail}
|
||||
sender={jsonSender}
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
@@ -1119,7 +1145,6 @@ function SignYourSelf() {
|
||||
allPages={allPages}
|
||||
changePage={changePage}
|
||||
pdfUrl={pdfUrl}
|
||||
documentStatus={documentStatus}
|
||||
embedWidgetsData={embedWidgetsData}
|
||||
pdfDetails={pdfDetails}
|
||||
isShowHeader={true}
|
||||
@@ -1127,6 +1152,7 @@ function SignYourSelf() {
|
||||
alreadySign={pdfUrl ? true : false}
|
||||
isSignYourself={true}
|
||||
setIsEmail={setIsEmail}
|
||||
isCompleted={isCompleted}
|
||||
/>
|
||||
|
||||
<div data-tut="reactourSecond">
|
||||
@@ -1179,7 +1205,7 @@ function SignYourSelf() {
|
||||
}}
|
||||
className="autoSignScroll"
|
||||
>
|
||||
{!documentStatus.isCompleted ? (
|
||||
{!isCompleted ? (
|
||||
<div>
|
||||
<WidgetComponent
|
||||
dataTut="reactourFirst"
|
||||
|
||||
@@ -3,7 +3,7 @@ import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import { useParams, useNavigate } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import "../styles/signature.css";
|
||||
import { themeColor } from "../constant/const";
|
||||
import { isEnableSubscription, themeColor } from "../constant/const";
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { useDrag, useDrop } from "react-dnd";
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
defaultWidthHeight,
|
||||
addWidgetOptions,
|
||||
textInputWidget,
|
||||
radioButtonWidget
|
||||
radioButtonWidget,
|
||||
fetchSubscription
|
||||
} from "../constant/Utils";
|
||||
import RenderPdf from "../components/pdf/RenderPdf";
|
||||
import "../styles/AddUser.css";
|
||||
@@ -52,6 +53,7 @@ const TemplatePlaceholder = () => {
|
||||
const [isSelectListId, setIsSelectId] = useState();
|
||||
const [isSendAlert, setIsSendAlert] = useState(false);
|
||||
const [isCreateDocModal, setIsCreateDocModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoad: true,
|
||||
message: "This might take some time"
|
||||
@@ -143,6 +145,7 @@ const TemplatePlaceholder = () => {
|
||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState([]);
|
||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||
const [widgetName, setWidgetName] = useState(false);
|
||||
const [isAddRole, setIsAddRole] = useState(false);
|
||||
const senderUser =
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
@@ -168,7 +171,23 @@ const TemplatePlaceholder = () => {
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current]);
|
||||
|
||||
async function checkIsSubscribed() {
|
||||
const res = await fetchSubscription();
|
||||
const freeplan = res.plan;
|
||||
const billingDate = res.billingDate;
|
||||
if (freeplan === "freeplan") {
|
||||
return true;
|
||||
} else if (billingDate) {
|
||||
if (new Date(billingDate) > new Date()) {
|
||||
setIsSubscribe(true);
|
||||
return true;
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`);
|
||||
}
|
||||
}
|
||||
// `fetchTemplate` function in used to get Template from server and setPlaceholder ,setSigner if present
|
||||
const fetchTemplate = async () => {
|
||||
try {
|
||||
@@ -190,6 +209,9 @@ const TemplatePlaceholder = () => {
|
||||
: [];
|
||||
|
||||
if (documentData && documentData.length > 0) {
|
||||
if (isEnableSubscription) {
|
||||
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
|
||||
}
|
||||
setPdfDetails(documentData);
|
||||
setIsSigners(true);
|
||||
if (documentData[0].Signers && documentData[0].Signers.length > 0) {
|
||||
@@ -287,10 +309,10 @@ const TemplatePlaceholder = () => {
|
||||
if (tourstatus && tourstatus.length > 0) {
|
||||
setTourStatus(tourstatus);
|
||||
const checkTourRecipients = tourstatus.filter(
|
||||
(data) => data.templatetour
|
||||
(data) => data.templateTour
|
||||
);
|
||||
if (checkTourRecipients && checkTourRecipients.length > 0) {
|
||||
setCheckTourStatus(checkTourRecipients[0].templatetour);
|
||||
setCheckTourStatus(checkTourRecipients[0].templateTour);
|
||||
}
|
||||
}
|
||||
const loadObj = {
|
||||
@@ -460,9 +482,20 @@ const TemplatePlaceholder = () => {
|
||||
} else {
|
||||
setIsReceipent(false);
|
||||
}
|
||||
} else {
|
||||
setIsAddRole(true);
|
||||
}
|
||||
};
|
||||
|
||||
const tourAddRole = [
|
||||
{
|
||||
selector: '[data-tut="reactourAddbtn"]',
|
||||
content: "You need to add a role before you can add fields for it. ",
|
||||
position: "top",
|
||||
style: { fontSize: "13px" }
|
||||
}
|
||||
];
|
||||
|
||||
//function for get pdf page details
|
||||
const pageDetails = async (pdf) => {
|
||||
const load = {
|
||||
@@ -741,7 +774,7 @@ const TemplatePlaceholder = () => {
|
||||
selector: '[data-tut="reactourSecond"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Drag a widget placeholder onto the PDF to choose your desired signing location.`}
|
||||
message={`Drag or click on a field to add it to the document.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
@@ -763,7 +796,7 @@ const TemplatePlaceholder = () => {
|
||||
selector: '[data-tut="reactourFour"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Clicking "Save" button will save the template and will ask you if you want to create a new document using this template.`}
|
||||
message={`Clicking ‘Save’ will store the current template. After saving, you’ll be prompted to create a new document from this template if you wish.`}
|
||||
isChecked={handleDontShow}
|
||||
/>
|
||||
),
|
||||
@@ -781,15 +814,15 @@ const TemplatePlaceholder = () => {
|
||||
if (tourStatus.length > 0) {
|
||||
updatedTourStatus = [...tourStatus];
|
||||
const templatetourIndex = tourStatus.findIndex(
|
||||
(obj) => obj["templatetour"] === false || obj["templatetour"] === true
|
||||
(obj) => obj["templateTour"] === false || obj["templateTour"] === true
|
||||
);
|
||||
if (templatetourIndex !== -1) {
|
||||
updatedTourStatus[templatetourIndex] = { templatetour: true };
|
||||
updatedTourStatus[templatetourIndex] = { templateTour: true };
|
||||
} else {
|
||||
updatedTourStatus.push({ templatetour: true });
|
||||
updatedTourStatus.push({ templateTour: true });
|
||||
}
|
||||
} else {
|
||||
updatedTourStatus = [{ templatetour: true }];
|
||||
updatedTourStatus = [{ templateTour: true }];
|
||||
}
|
||||
await axios
|
||||
.put(
|
||||
@@ -1197,6 +1230,15 @@ const TemplatePlaceholder = () => {
|
||||
closeWithMask={false}
|
||||
/>
|
||||
)}
|
||||
{isAddRole && (
|
||||
<Tour
|
||||
onRequestClose={() => setIsAddRole(false)}
|
||||
steps={tourAddRole}
|
||||
isOpen={isAddRole}
|
||||
rounded={5}
|
||||
closeWithMask={false}
|
||||
/>
|
||||
)}
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdfDetails[0].URL}
|
||||
@@ -1222,7 +1264,7 @@ const TemplatePlaceholder = () => {
|
||||
handleClose={() => setIsSendAlert(false)}
|
||||
>
|
||||
<div style={{ height: "100%", padding: 20 }}>
|
||||
<p>Please add field for all recipients.</p>
|
||||
<p>Please add at least one signature field for all roles.</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<ModalUi
|
||||
@@ -1323,6 +1365,7 @@ const TemplatePlaceholder = () => {
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
/>
|
||||
<DropdownWidgetOption
|
||||
type="checkbox"
|
||||
@@ -1333,6 +1376,7 @@ const TemplatePlaceholder = () => {
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
/>
|
||||
<DropdownWidgetOption
|
||||
type="dropdown"
|
||||
@@ -1343,6 +1387,7 @@ const TemplatePlaceholder = () => {
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleClose={handleNameModal}
|
||||
isSubscribe={isSubscribe}
|
||||
/>
|
||||
<PlaceholderCopy
|
||||
isPageCopy={isPageCopy}
|
||||
@@ -1464,6 +1509,7 @@ const TemplatePlaceholder = () => {
|
||||
className="autoSignScroll"
|
||||
>
|
||||
<SignerListPlace
|
||||
isMailSend={isMailSend}
|
||||
signerPos={signerPos}
|
||||
signersdata={signersdata}
|
||||
isSelectListId={isSelectListId}
|
||||
@@ -1484,7 +1530,7 @@ const TemplatePlaceholder = () => {
|
||||
/>
|
||||
<div data-tut="reactourSecond">
|
||||
<WidgetComponent
|
||||
pdfUrl={isMailSend}
|
||||
isMailSend={isMailSend}
|
||||
dragSignature={dragSignature}
|
||||
signRef={signRef}
|
||||
handleDivClick={handleDivClick}
|
||||
@@ -1523,6 +1569,7 @@ const TemplatePlaceholder = () => {
|
||||
isAddUser={isAddUser}
|
||||
uniqueId={uniqueId}
|
||||
closePopup={closePopup}
|
||||
signersData={signersdata}
|
||||
/>
|
||||
</div>
|
||||
<ModalUi
|
||||
@@ -1541,6 +1588,7 @@ const TemplatePlaceholder = () => {
|
||||
isOpen={isNameModal}
|
||||
handleClose={handleNameModal}
|
||||
handleData={handleWidgetdefaultdata}
|
||||
isSubscribe={isSubscribe}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,10 +8,14 @@ import sanitizeFileName from "../primitives/sanitizeFileName";
|
||||
import axios from "axios";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed } from "../constant/Utils";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
|
||||
function UserProfile() {
|
||||
const navigate = useNavigate();
|
||||
let UserProfile = JSON.parse(localStorage.getItem("UserInformation"));
|
||||
let extendUser = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [editmode, setEditMode] = useState(false);
|
||||
@@ -21,15 +25,30 @@ function UserProfile() {
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [percentage, setpercentage] = useState(0);
|
||||
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [company, setCompany] = useState(
|
||||
extendUser && extendUser?.[0]?.Company
|
||||
);
|
||||
const [jobTitle, setJobTitle] = useState(
|
||||
extendUser && extendUser?.[0]?.JobTitle
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
getUserDetail();
|
||||
}, []);
|
||||
|
||||
const getUserDetail = async () => {
|
||||
const extClass = localStorage.getItem("Extand_Class");
|
||||
const jsonSender = JSON.parse(extClass);
|
||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
if (HeaderDocId) {
|
||||
setIsDisableDocId(HeaderDocId);
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoader(true);
|
||||
@@ -54,6 +73,7 @@ function UserProfile() {
|
||||
SetPhone(res.phone);
|
||||
setImage(res.ProfilePic);
|
||||
localStorage.setItem("username", res.name);
|
||||
localStorage.setItem("profileImg", res.ProfilePic);
|
||||
await updateExtUser({ Name: res.name, Phone: res.phone });
|
||||
alert("Profile updated successfully.");
|
||||
setEditMode(false);
|
||||
@@ -81,7 +101,9 @@ function UserProfile() {
|
||||
const body = {
|
||||
Phone: obj.Phone,
|
||||
Name: obj.Name,
|
||||
HeaderDocId: isDisableDocId
|
||||
HeaderDocId: isDisableDocId,
|
||||
JobTitle: jobTitle,
|
||||
Company: company
|
||||
};
|
||||
await axios.put(
|
||||
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
||||
@@ -135,9 +157,9 @@ function UserProfile() {
|
||||
// console.log("File URL:", response.url());
|
||||
if (response.url()) {
|
||||
setImage(response.url());
|
||||
localStorage.setItem("profileImg", response.url());
|
||||
setpercentage(0);
|
||||
SaveFileSize(size, response.url());
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(size, response.url(), tenantId);
|
||||
return response.url();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -155,7 +177,6 @@ function UserProfile() {
|
||||
const handleDisableDocId = () => {
|
||||
setIsDisableDocId((prevChecked) => !prevChecked);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"Profile"} />
|
||||
@@ -252,6 +273,40 @@ function UserProfile() {
|
||||
<span className="font-semibold">Email:</span>{" "}
|
||||
<span>{UserProfile && UserProfile.email}</span>
|
||||
</li>
|
||||
<li
|
||||
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||
editmode ? "py-1" : "py-2"
|
||||
}`}
|
||||
>
|
||||
<span className="font-semibold">Company:</span>{" "}
|
||||
{editmode ? (
|
||||
<input
|
||||
type="text"
|
||||
value={company}
|
||||
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<span>{extendUser?.[0].Company}</span>
|
||||
)}
|
||||
</li>
|
||||
<li
|
||||
className={`flex justify-between items-center border-t-[1px] border-gray-300 break-all ${
|
||||
editmode ? "py-1" : "py-2"
|
||||
}`}
|
||||
>
|
||||
<span className="font-semibold">Job title:</span>{" "}
|
||||
{editmode ? (
|
||||
<input
|
||||
type="text"
|
||||
value={jobTitle}
|
||||
className="py-1 px-2 text-sm border-[1px] border-[#15b4e9] text-black rounded"
|
||||
onChange={(e) => setJobTitle(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<span>{extendUser?.[0]?.JobTitle}</span>
|
||||
)}
|
||||
</li>
|
||||
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold">Is Email verified:</span>{" "}
|
||||
<span>
|
||||
@@ -262,14 +317,30 @@ function UserProfile() {
|
||||
</li>
|
||||
<li className="border-y-[1px] border-gray-300 break-all">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span className="font-semibold">
|
||||
<span
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
Disable DocumentId :{" "}
|
||||
<Tooltip url={"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"} />{" "}
|
||||
<Tooltip
|
||||
url={
|
||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||
}
|
||||
isSubscribe={isSubscribe}
|
||||
/>{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>{" "}
|
||||
<label
|
||||
className={`${
|
||||
editmode ? "cursor-pointer" : ""
|
||||
} relative inline-flex items-center mb-0`}
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? `${
|
||||
editmode ? "cursor-pointer" : ""
|
||||
} relative inline-flex items-center mb-0`
|
||||
: "relative inline-flex items-center mb-0 pointer-events-none opacity-50"
|
||||
}
|
||||
>
|
||||
<input
|
||||
disabled={editmode ? false : true}
|
||||
@@ -282,11 +353,13 @@ function UserProfile() {
|
||||
<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"></div>
|
||||
</label>
|
||||
</div>
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Disable documentId is free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
{!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Disable documentId is free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Title from "../components/Title";
|
||||
import axios from "axios";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Alert from "../primitives/Alert";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { rejectBtn, submitBtn } from "../constant/const";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
|
||||
import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
|
||||
function Webhook() {
|
||||
const navigation = useNavigate();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [webhook, setWebhook] = useState();
|
||||
@@ -17,6 +19,8 @@ function Webhook() {
|
||||
const [isGenerate, setIsGenerate] = useState(false);
|
||||
const [isErr, setIsErr] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetchWebhook();
|
||||
@@ -25,6 +29,10 @@ function Webhook() {
|
||||
|
||||
const fetchWebhook = async () => {
|
||||
const email = Parse.User.current().getEmail();
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
const params = { email: email };
|
||||
try {
|
||||
const extRes = await Parse.Cloud.run("getUserDetails", params);
|
||||
@@ -40,41 +48,48 @@ function Webhook() {
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
try {
|
||||
const params = { url: webhook };
|
||||
const url = parseBaseUrl + "functions/savewebhook";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.post(url, params, { headers: headers }).then((res) => {
|
||||
if (res.data && res.data.result && res.data.result.Webhook) {
|
||||
setWebhook(res.data.result.Webhook);
|
||||
setIsGenerate(true);
|
||||
setTimeout(() => {
|
||||
setIsGenerate(false);
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
console.error("Error while generating webhook");
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
if (webhook && webhook.startsWith("https://")) {
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
try {
|
||||
const params = { url: webhook };
|
||||
const url = parseBaseUrl + "functions/savewebhook";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.post(url, params, { headers: headers }).then((res) => {
|
||||
if (res.data && res.data.result && res.data.result.Webhook) {
|
||||
setWebhook(res.data.result.Webhook);
|
||||
setIsGenerate(true);
|
||||
setTimeout(() => {
|
||||
setIsGenerate(false);
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
} else {
|
||||
console.error("Error while generating webhook");
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
|
||||
console.log("err", error);
|
||||
console.log("err", error);
|
||||
}
|
||||
} else {
|
||||
setError("The webhook url should always start with https://");
|
||||
setTimeout(() => {
|
||||
setError("");
|
||||
}, 1500);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,6 +99,7 @@ function Webhook() {
|
||||
<Title title={"Webhook"} />
|
||||
{isGenerate && <Alert type="success">Webhook added successfully!</Alert>}
|
||||
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
||||
|
||||
{isLoader ? (
|
||||
<div
|
||||
style={{
|
||||
@@ -103,31 +119,41 @@ function Webhook() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-white flex flex-col justify-center shadow rounded">
|
||||
<PremiumAlertHeader />
|
||||
<h1 className="ml-4 mt-3 mb-2 font-semibold">
|
||||
Webhook{" "}
|
||||
{!isEnableSubscription && <PremiumAlertHeader />}
|
||||
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||
OpenSign™ Webhook{" "}
|
||||
<Tooltip
|
||||
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</h1>
|
||||
<ul className="w-full flex flex-col p-2 text-sm">
|
||||
<ul
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "w-full flex flex-col p-2 text-sm "
|
||||
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||
}
|
||||
>
|
||||
<li
|
||||
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<span className="w-[40%]">Webhook:</span>{" "}
|
||||
<span id="token" className="w-[60%] md:text-end cursor-pointer">
|
||||
{webhook && webhook}
|
||||
</span>
|
||||
<div className="w-[70%] flex-col md:flex-row flex items-center gap-5 ">
|
||||
<span className="">Webhook:</span>{" "}
|
||||
<span id="token" className=" md:text-end cursor-pointer">
|
||||
{webhook ? webhook : "_____"}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleModal}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
{webhook ? "Update Webhook" : "Add Webhook"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleModal}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
{webhook ? "Update Webhook" : "Add Webhook"}
|
||||
</button>
|
||||
|
||||
<div className="flex items-center justify-center ">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
@@ -135,17 +161,47 @@ function Webhook() {
|
||||
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
|
||||
)
|
||||
}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] my-2 border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
View Docs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<>
|
||||
<h1 className={"ml-4 mt-3 mb-2 font-semibold"}>
|
||||
Upgrade to PRO Plan
|
||||
</h1>
|
||||
<ul className={"w-full flex flex-col p-2 text-sm "}>
|
||||
<li
|
||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<div className="w-[70%] flex-col md:flex-row flex items-center gap-3 ">
|
||||
<span className="">$29.99/month:</span>{" "}
|
||||
<span id="token" className=" md:text-end cursor-pointer">
|
||||
First 100 documents included, then just $0.15 per
|
||||
document.
|
||||
</span>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigation("/subscription")}
|
||||
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={"Regenerate Token"}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
{error && <Alert type="danger">{error}</Alert>}
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
<label className="text-sm ml-2">Webhook</label>
|
||||
|
||||
@@ -54,11 +54,11 @@ const AddUser = (props) => {
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_Guest");
|
||||
|
||||
if (localStorage.getItem("TenetId")) {
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenetId")
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,12 +7,16 @@ const LinkUserModal = (props) => {
|
||||
return (
|
||||
<ModalUi
|
||||
title={"Add/Choose Signer"}
|
||||
isOpen={props.isAddUser[props.uniqueId]}
|
||||
isOpen={
|
||||
props?.isAddSigner ||
|
||||
(props?.isAddUser && props?.isAddUser[props?.uniqueId])
|
||||
}
|
||||
handleClose={props.closePopup}
|
||||
>
|
||||
<SelectSigners
|
||||
details={props.handleAddUser}
|
||||
closePopup={props.closePopup}
|
||||
signersData={props?.signersData}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from "react";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import PremiumAlertHeader from "./PremiumAlertHeader";
|
||||
|
||||
const ModalUi = ({
|
||||
children,
|
||||
@@ -8,12 +10,13 @@ const ModalUi = ({
|
||||
handleClose,
|
||||
showHeader = true,
|
||||
showClose = true,
|
||||
styleClass
|
||||
styleClass,
|
||||
showHeaderMessage
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{isOpen && (
|
||||
<div className="fixed z-[999] top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
||||
<div className="fixed z-[999] 2 top-0 left-0 w-[100%] h-[100%] bg-black bg-opacity-[75%]">
|
||||
<div
|
||||
className={
|
||||
styleClass
|
||||
@@ -37,6 +40,13 @@ const ModalUi = ({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isEnableSubscription && showHeaderMessage && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Cutomize Email is free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,19 +8,11 @@ function CustomModal({
|
||||
bodyMssg,
|
||||
footerMessage,
|
||||
declineDoc,
|
||||
setIsDecline,
|
||||
containerWH
|
||||
setIsDecline
|
||||
}) {
|
||||
const isMobile = window.innerWidth < 767;
|
||||
return (
|
||||
show && (
|
||||
<div
|
||||
className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center"
|
||||
style={{
|
||||
width: containerWH && containerWH.width,
|
||||
height: isMobile ? "100%" : containerWH && containerWH.height
|
||||
}}
|
||||
>
|
||||
<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 className="bg-[#de4337] text-white p-[10px] rounded-t">
|
||||
{headMsg && headMsg}
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
import React from "react";
|
||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
const Tooltip = ({ children, id, message, url, iconColor }) =>
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
const Tooltip = ({ id, message, url, iconColor, isSubscribe }) =>
|
||||
url ? (
|
||||
<button onClick={() => openInNewTab(url)} className="text-center">
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<i className="fa-solid fa-question text-xs border-[1.5px] px-1 rounded-full border-[#33bbff] text-[#33bbff]"></i>
|
||||
)}
|
||||
<button
|
||||
onClick={() => openInNewTab(url)}
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "text-center"
|
||||
: "text-center opacity-20 pointer-events-none"
|
||||
}
|
||||
>
|
||||
<sup>
|
||||
<i
|
||||
className="fa-solid fa-question rounded-full"
|
||||
style={{
|
||||
borderColor: iconColor ? iconColor : "#33bbff",
|
||||
color: iconColor ? iconColor : "#33bbff",
|
||||
fontSize: 13,
|
||||
borderWidth: 1.5,
|
||||
padding: "1.5px 4px"
|
||||
}}
|
||||
></i>
|
||||
</sup>
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
@@ -17,17 +32,18 @@ const Tooltip = ({ children, id, message, url, iconColor }) =>
|
||||
data-tooltip-content={message}
|
||||
className="z-50"
|
||||
>
|
||||
{children ? (
|
||||
children
|
||||
) : (
|
||||
<sup>
|
||||
<i
|
||||
className="fa-solid fa-question text-xs border-[1.5px] px-1 rounded-full border-[#33bbff] text-[#33bbff]"
|
||||
className="fa-solid fa-question rounded-full"
|
||||
style={{
|
||||
borderColor: iconColor ? iconColor : "#33bbff",
|
||||
color: iconColor ? iconColor : "#33bbff"
|
||||
color: iconColor ? iconColor : "#33bbff",
|
||||
fontSize: 13,
|
||||
borderWidth: 1.5,
|
||||
padding: "1.5px 4px"
|
||||
}}
|
||||
></i>
|
||||
)}
|
||||
</sup>
|
||||
</a>
|
||||
<ReactTooltip id={id ? id : "my-tooltip"} className="max-w-[200px]" />
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { openInNewTab } from "../constant/Utils";
|
||||
|
||||
function Upgrade({ message, newWindow }) {
|
||||
const navigation = useNavigate();
|
||||
|
||||
return (
|
||||
<sup>
|
||||
<span
|
||||
onClick={() => {
|
||||
if (newWindow) {
|
||||
const url = window.location.origin + "/subscription";
|
||||
openInNewTab(url);
|
||||
} else {
|
||||
navigation("/subscription");
|
||||
}
|
||||
}}
|
||||
className="text-blue-800 text-sm cursor-pointer underline"
|
||||
>
|
||||
{message ? message : "Upgrade now"}
|
||||
</span>
|
||||
</sup>
|
||||
);
|
||||
}
|
||||
|
||||
export default Upgrade;
|
||||
@@ -9,9 +9,15 @@ const Validate = () => {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const userDetails = JSON.parse(
|
||||
localStorage.getItem(
|
||||
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
|
||||
)
|
||||
);
|
||||
|
||||
// Use the session token to validate the user
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(Parse.User.current().id, {
|
||||
const user = await userQuery.get(userDetails?.objectId, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
@@ -24,7 +30,10 @@ const Validate = () => {
|
||||
setIsUserValid(false);
|
||||
}
|
||||
})();
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
try {
|
||||
Parse.User.logOut();
|
||||
|
||||
@@ -395,14 +395,6 @@
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.backBtn {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.backBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.defaultBtn {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
||||
padding: 3px 26px;
|
||||
@@ -420,42 +412,6 @@
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.downloadBtn {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
||||
padding: 3px 15px !important;
|
||||
background-color: rgb(241 79 79);
|
||||
border: none !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.downloadBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(154, 36, 36, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.printBtn {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
||||
padding: 3px 15px !important;
|
||||
background: #188ae2;
|
||||
border: none !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.printBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(23, 48, 137, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.certificateBtn {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.18);
|
||||
padding: 3px 15px !important;
|
||||
background: #08bc66;
|
||||
border: none !important;
|
||||
color: white !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.certificateBtn:hover {
|
||||
box-shadow: 0 2px 4px rgba(15, 150, 87, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.signerList {
|
||||
overflow-y: scroll;
|
||||
@@ -497,6 +453,14 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
/* Example: reduce opacity to visually indicate disabled state */
|
||||
pointer-events: none;
|
||||
/* Prevents mouse clicks and other events */
|
||||
/* background-color: #888; */
|
||||
}
|
||||
|
||||
.useEmail {
|
||||
font-size: 10px;
|
||||
color: #424242;
|
||||
@@ -553,13 +517,6 @@
|
||||
position: "relative";
|
||||
}
|
||||
|
||||
.signatureHeader {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.signerComponent {
|
||||
/* box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px; */
|
||||
@@ -847,6 +804,7 @@ option {
|
||||
z-index: 999;
|
||||
border-radius: 2px;
|
||||
resize: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.labelTextArea:focus {
|
||||
@@ -1010,6 +968,9 @@ option {
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@media screen and (max-width: 350px) and (min-width: 311px) {
|
||||
.signatureCanvas {
|
||||
width: 280px;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const appId = process.env.APP_ID;
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
export function customAPIurl() {
|
||||
const url = new URL(process.env.SERVER_URL);
|
||||
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
|
||||
@@ -38,3 +42,204 @@ export function replaceMailVaribles(subject, body, variables) {
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
export const saveFileUsage = async (size, imageUrl, userId) => {
|
||||
//checking server url and save file's size
|
||||
|
||||
try {
|
||||
const tenantQuery = new Parse.Query('partners_Tenant');
|
||||
tenantQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userId,
|
||||
});
|
||||
const tenant = await tenantQuery.first();
|
||||
if (tenant) {
|
||||
const tenantPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: tenant.id,
|
||||
};
|
||||
const _tenantPtr = JSON.stringify(tenantPtr);
|
||||
try {
|
||||
const res = await axios.get(
|
||||
`${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
},
|
||||
}
|
||||
);
|
||||
const response = res.data.results;
|
||||
|
||||
let data;
|
||||
// console.log("response", response);
|
||||
if (response && response.length > 0) {
|
||||
data = {
|
||||
usedStorage: response[0].usedStorage ? response[0].usedStorage + size : size,
|
||||
};
|
||||
await axios.put(
|
||||
`${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
data = { usedStorage: size, PartnersTenant: tenantPtr };
|
||||
await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in save usage', err);
|
||||
}
|
||||
saveDataFile(size, imageUrl, tenantPtr);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in fetch tenant Id', err);
|
||||
}
|
||||
};
|
||||
|
||||
//function for save fileUrl and file size in particular client db class partners_DataFiles
|
||||
const saveDataFile = async (size, imageUrl, tenantPtr) => {
|
||||
const data = {
|
||||
FileUrl: imageUrl,
|
||||
FileSize: size,
|
||||
TenantPtr: tenantPtr,
|
||||
};
|
||||
|
||||
// console.log("data save",file, data)
|
||||
try {
|
||||
await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': appId,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log('error in save usage ', err);
|
||||
}
|
||||
};
|
||||
|
||||
export const updateMailCount = async extUserId => {
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', extUserId);
|
||||
|
||||
try {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('EmailCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('EmailCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating EmailCount in contracts_users: ' + error.message);
|
||||
}
|
||||
};
|
||||
|
||||
export function formatWidgetOptions(type, options) {
|
||||
const status = options?.required === true ? 'required' : 'optional' || 'required';
|
||||
const defaultValue = options?.default || '';
|
||||
const values = options?.values || [];
|
||||
switch (type) {
|
||||
case 'signature':
|
||||
return { name: 'signature', status: 'required' };
|
||||
case 'stamp':
|
||||
return { status: status, name: 'stamp' };
|
||||
case 'initials':
|
||||
return { status: status, name: options.name || 'initials' };
|
||||
case 'image':
|
||||
return { status: status, name: options.name || 'image' };
|
||||
case 'email':
|
||||
return { status: status, name: options.name || 'email', validation: { type: 'email' } };
|
||||
case 'name':
|
||||
return { status: status, name: options.name || 'name' };
|
||||
case 'job title':
|
||||
return { status: status, name: options.name || 'job title' };
|
||||
case 'company':
|
||||
return { status: status, name: options.name || 'company' };
|
||||
case 'date': {
|
||||
let today = new Date();
|
||||
let dd = String(today.getDate()).padStart(2, '0');
|
||||
let mm = String(today.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||
let yyyy = today.getFullYear();
|
||||
today = dd + '-' + mm + '-' + yyyy;
|
||||
let dateFormat = options?.format;
|
||||
dateFormat = dateFormat.replace(/m/g, 'M');
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'date',
|
||||
response: defaultValue || today,
|
||||
validation: { format: dateFormat || 'dd-MM-yyyy', type: 'date-format' },
|
||||
};
|
||||
}
|
||||
case 'textbox':
|
||||
return {
|
||||
status: status,
|
||||
name: 'textbox',
|
||||
defaultValue: defaultValue,
|
||||
hint: options.hint,
|
||||
validation: { type: 'regex', pattern: options?.regularexpression || '/^[a-zA-Z0-9s]+$/' },
|
||||
};
|
||||
case 'checkbox': {
|
||||
const arr = options?.values;
|
||||
let selectedvalues = [];
|
||||
for (const obj of options.selectedvalues) {
|
||||
const index = arr.indexOf(obj);
|
||||
selectedvalues.push(index);
|
||||
}
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'checkbox',
|
||||
values: values,
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
validation: {
|
||||
minRequiredCount: options?.validation?.minselections || 0,
|
||||
maxRequiredCount: options?.validation?.maxselections || 0,
|
||||
},
|
||||
defaultValue: selectedvalues || [],
|
||||
};
|
||||
}
|
||||
case 'radio button': {
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'radio',
|
||||
values: values,
|
||||
isReadOnly: options?.readonly || false,
|
||||
isHideLabel: options?.hidelabel || false,
|
||||
defaultValue: defaultValue,
|
||||
};
|
||||
}
|
||||
case 'dropdown':
|
||||
return {
|
||||
status: status,
|
||||
name: options.name || 'dropdown',
|
||||
values: values,
|
||||
defaultValue: defaultValue,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function sanitizeFileName(fileName) {
|
||||
// Remove spaces and invalid characters
|
||||
const file = fileName.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||
const removedot = file.replace(/\.(?=.*\.)/g, '');
|
||||
return removedot.replace(/[^a-zA-Z0-9._-]/g, '');
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
//--npm modules
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
export const app = express();
|
||||
import dotenv from 'dotenv';
|
||||
import uploadFile from './uploadFile.js';
|
||||
import saveSubscription from './saveSubscription.js';
|
||||
import saveInvoice from './saveInvoice.js';
|
||||
import savePayments from './savePayments.js';
|
||||
export const app = express();
|
||||
|
||||
dotenv.config();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
import uploadFile from './uploadFile.js';
|
||||
app.use(express.json({ limit: '50mb' }));
|
||||
app.use(express.urlencoded({ limit: '50mb', extended: true }));
|
||||
|
||||
app.post('/file_upload', uploadFile);
|
||||
|
||||
app.post('/savesubscription', saveSubscription)
|
||||
app.post('/saveinvoice', saveInvoice)
|
||||
app.post('/savepayment', savePayments)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
export default async function saveInvoice(request, response) {
|
||||
const InvoiceId = request.body.data.invoice.invoice_id;
|
||||
const body = request.body;
|
||||
const Email = request.body.data.invoice.email;
|
||||
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('Email', Email);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const invoiceCls = new Parse.Query('contracts_Invoices');
|
||||
invoiceCls.equalTo('InvoiceId', InvoiceId);
|
||||
const invoice = await invoiceCls.first({ useMasterKey: true });
|
||||
if (invoice) {
|
||||
const updateInvoice = new Parse.Object('contracts_Invoices');
|
||||
updateInvoice.id = invoice.id;
|
||||
updateInvoice.set('InvoiceDetails', body);
|
||||
await updateInvoice.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'update invoice!' });
|
||||
} else {
|
||||
const createInvoice = new Parse.Object('contracts_Invoices');
|
||||
createInvoice.set('InvoiceId', InvoiceId);
|
||||
createInvoice.set('InvoiceDetails', body);
|
||||
createInvoice.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
});
|
||||
createInvoice.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: extUser.get('UserId').id,
|
||||
});
|
||||
if (extUser?.get('TenantId')?.id) {
|
||||
createInvoice.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
}
|
||||
await createInvoice.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'create invoice!' });
|
||||
}
|
||||
} else {
|
||||
return response.status(404).json({ status: 'user not found!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in save invoice', err);
|
||||
return response.status(400).json({ status: 'error:' + err.message });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
export default async function savePayments(request, response) {
|
||||
const PaymentId = request.body.data.payment.payment_id;
|
||||
const body = request.body;
|
||||
const Email = request.body.data.payment.email;
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('Email', Email);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const paymentCls = new Parse.Query('contracts_Payments');
|
||||
paymentCls.equalTo('PaymentId', PaymentId);
|
||||
const payment = await paymentCls.first({ useMasterKey: true });
|
||||
if (payment) {
|
||||
const updatePayment = new Parse.Object('contracts_Payments');
|
||||
updatePayment.id = payment.id;
|
||||
updatePayment.set('PaymentDetails', body);
|
||||
await updatePayment.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'update Payment!' });
|
||||
} else {
|
||||
const createPayment = new Parse.Object('contracts_Payments');
|
||||
createPayment.set('PaymentId', PaymentId);
|
||||
createPayment.set('PaymentDetails', body);
|
||||
createPayment.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
});
|
||||
createPayment.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: extUser.get('UserId').id,
|
||||
});
|
||||
if (extUser?.get('TenantId')?.id) {
|
||||
createPayment.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
}
|
||||
await createPayment.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'create payments!' });
|
||||
}
|
||||
} else {
|
||||
return response.status(404).json({ status: 'user not found!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in save payment', err);
|
||||
return response.status(400).json({ status: 'error:' + err.message });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
export default async function saveSubscription(request, response) {
|
||||
const SubscriptionId = request.body.data.subscription.subscription_id;
|
||||
const body = request.body;
|
||||
const Email = request.body.data.subscription.customer.email;
|
||||
const Next_billing_date = request.body.data.subscription.next_billing_at;
|
||||
const planCode = request.body.data.subscription.plan.plan_code
|
||||
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('Email', Email);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const subcriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subcriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
const subscription = await subcriptionCls.first({ useMasterKey: true });
|
||||
if (subscription) {
|
||||
const updateSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
updateSubscription.id = subscription.id;
|
||||
updateSubscription.set('SubscriptionId', SubscriptionId);
|
||||
updateSubscription.set('SubscriptionDetails', body);
|
||||
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
|
||||
updateSubscription.set('PlanCode', planCode);
|
||||
await updateSubscription.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'update subscription!' });
|
||||
} else {
|
||||
const createSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
createSubscription.set('SubscriptionId', SubscriptionId);
|
||||
createSubscription.set('SubscriptionDetails', body);
|
||||
createSubscription.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
});
|
||||
createSubscription.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: extUser.get('UserId').id,
|
||||
});
|
||||
createSubscription.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
|
||||
createSubscription.set('PlanCode', planCode);
|
||||
await createSubscription.save(null, { useMasterKey: true });
|
||||
return response.status(200).json({ status: 'create subscription!' });
|
||||
}
|
||||
} else {
|
||||
return response.status(404).json({ status: 'user not found!' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in save subscription', err);
|
||||
return response.status(400).json({ status: 'error:' + err.message });
|
||||
}
|
||||
}
|
||||
@@ -284,6 +284,7 @@ export default async function createDocumentWithTemplate(request, response) {
|
||||
subject: subject,
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: template.ExtUserPtr.objectId,
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import axios from 'axios';
|
||||
import { color, customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
|
||||
import {
|
||||
color,
|
||||
customAPIurl,
|
||||
replaceMailVaribles,
|
||||
saveFileUsage,
|
||||
formatWidgetOptions,
|
||||
sanitizeFileName,
|
||||
} from '../../../../Utils.js';
|
||||
|
||||
// `sendDoctoWebhook` is used to send res data of document on webhook
|
||||
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
|
||||
@@ -8,6 +15,7 @@ async function sendDoctoWebhook(doc, WebhookUrl, userId) {
|
||||
event: 'created',
|
||||
...doc,
|
||||
};
|
||||
|
||||
await axios
|
||||
.post(WebhookUrl, params, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -65,6 +73,7 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
if (!reqToken) {
|
||||
return response.status(400).json({ error: 'Please Provide API Token' });
|
||||
}
|
||||
|
||||
try {
|
||||
const tokenQuery = new Parse.Query('appToken');
|
||||
tokenQuery.equalTo('token', reqToken);
|
||||
@@ -81,17 +90,21 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
if (signers && signers.length > 0) {
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: fileData?.toString('base64'),
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const file = new Parse.File(`${name}.pdf`, {
|
||||
base64: base64File,
|
||||
});
|
||||
const filename = sanitizeFileName(`${name}.pdf`);
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
@@ -170,17 +183,17 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
|
||||
for (const widget of signer.widgets) {
|
||||
const pageNumber = widget.page;
|
||||
const options = formatWidgetOptions(widget.type, widget.options);
|
||||
const page = placeHolder.find(page => page.pageNumber === pageNumber);
|
||||
const signOpt = { name: 'signature', status: 'required' };
|
||||
const widgetData = {
|
||||
isStamp: widget.type === 'stamp',
|
||||
isStamp: widget.type === 'stamp' || widget.type === 'image',
|
||||
key: randomId(),
|
||||
isDrag: false,
|
||||
scale: 1,
|
||||
isMobile: false,
|
||||
zIndex: 1,
|
||||
type: widget.type,
|
||||
options: widget.type === 'signature' ? signOpt : widget.options,
|
||||
type: widget.type === 'textbox' ? 'text input' : widget.type,
|
||||
options: options,
|
||||
Width: widget.w,
|
||||
Height: widget.h,
|
||||
xPosition: widget.x,
|
||||
@@ -242,7 +255,6 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
year: 'numeric',
|
||||
});
|
||||
let sender = parseExtUser.Email;
|
||||
let sendMail;
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const newServer = serverUrl.replaceAll('/', '%2F');
|
||||
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
|
||||
@@ -330,9 +342,10 @@ export default async function createDocumentwithCoordinate(request, response) {
|
||||
subject: subject,
|
||||
from: sender,
|
||||
html: html,
|
||||
extUserId: extUser.id,
|
||||
};
|
||||
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { customAPIurl } from '../../../../Utils.js';
|
||||
|
||||
const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
import { saveFileUsage } from '../../../../Utils.js';
|
||||
export default async function createTemplate(request, response) {
|
||||
const name = request.body?.title;
|
||||
const note = request.body?.note;
|
||||
@@ -36,12 +34,17 @@ export default async function createTemplate(request, response) {
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: fileData?.toString('base64'),
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import axios from 'axios';
|
||||
import { color, customAPIurl } from '../../../../Utils.js';
|
||||
import {
|
||||
color,
|
||||
customAPIurl,
|
||||
saveFileUsage,
|
||||
formatWidgetOptions,
|
||||
sanitizeFileName,
|
||||
} from '../../../../Utils.js';
|
||||
|
||||
const randomId = () => Math.floor(1000 + Math.random() * 9000);
|
||||
export default async function createTemplatewithCoordinate(request, response) {
|
||||
@@ -34,17 +40,21 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
if (signers && signers.length > 0) {
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: fileData?.toString('base64'),
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const file = new Parse.File(`${name}.pdf`, {
|
||||
base64: base64File,
|
||||
});
|
||||
const filename = sanitizeFileName(`${name}.pdf`);
|
||||
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
@@ -129,16 +139,16 @@ export default async function createTemplatewithCoordinate(request, response) {
|
||||
for (const widget of signer.widgets) {
|
||||
const pageNumber = widget.page;
|
||||
const page = placeHolder.find(page => page.pageNumber === pageNumber);
|
||||
const signOpt = { name: 'signature', status: 'required' };
|
||||
const options = formatWidgetOptions(widget.type, widget.options);
|
||||
const widgetData = {
|
||||
isStamp: widget.type === 'stamp',
|
||||
isStamp: widget.type === 'stamp' || widget.type === 'image',
|
||||
key: randomId(),
|
||||
isDrag: false,
|
||||
scale: 1,
|
||||
isMobile: false,
|
||||
zIndex: 1,
|
||||
type: widget.type,
|
||||
options: widget.type === 'signature' ? signOpt : widget.options,
|
||||
type: widget.type === 'textbox' ? 'text input' : widget.type,
|
||||
options: options,
|
||||
Width: widget.w,
|
||||
Height: widget.h,
|
||||
xPosition: widget.x,
|
||||
|
||||
@@ -36,17 +36,22 @@ export default async function draftDocument(request, response) {
|
||||
if (signers && signers.length > 0) {
|
||||
let fileUrl;
|
||||
if (request.files?.[0]) {
|
||||
const base64 = fileData?.toString('base64');
|
||||
const file = new Parse.File(request.files?.[0]?.originalname, {
|
||||
base64: fileData?.toString('base64'),
|
||||
base64: base64,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
} else {
|
||||
const file = new Parse.File(`${name}.pdf`, {
|
||||
base64: base64File,
|
||||
});
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
const buffer = Buffer.from(base64File, 'base64');
|
||||
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
|
||||
}
|
||||
const contractsUser = new Parse.Query('contracts_Users');
|
||||
contractsUser.equalTo('UserId', userPtr);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import PDF from './parsefunction/pdf/PDF.min.js';
|
||||
import sendmail from './parsefunction/sendMail.js';
|
||||
import sendmailv3 from './parsefunction/sendMailv3.js';
|
||||
import GoogleSign from './parsefunction/GoogleSign.js';
|
||||
import ZohoDetails from './parsefunction/ZohoDetails.js';
|
||||
import usersignup from './parsefunction/usersignup.js';
|
||||
@@ -10,7 +10,6 @@ import DocumentAftersave from './parsefunction/DocumentAftersave.js';
|
||||
import ContactbookAftersave from './parsefunction/ContactBookAftersave.js';
|
||||
import ContractUsersAftersave from './parsefunction/ContractUsersAftersave.js';
|
||||
import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js';
|
||||
import SendMailv1 from './parsefunction/SendMailv1.js';
|
||||
import AuthLoginAsMail from './parsefunction/AuthLoginAsMail.js';
|
||||
import getUserId from './parsefunction/getUserId.js';
|
||||
import getUserDetails from './parsefunction/getUserDetails.js';
|
||||
@@ -23,17 +22,28 @@ import TemplateAfterSave from './parsefunction/TemplateAfterSave.js';
|
||||
import GetTemplate from './parsefunction/GetTemplate.js';
|
||||
import savewebhook from './parsefunction/saveWebhook.js';
|
||||
import callWebhook from './parsefunction/callWebhook.js';
|
||||
import SubscribeFree from './parsefunction/SubscribeFree.js';
|
||||
import DocumentBeforesave from './parsefunction/DocumentBeforesave.js';
|
||||
import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js';
|
||||
import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js';
|
||||
import TemplateAfterFind from './parsefunction/TemplateAfterFind.js';
|
||||
import UserAfterFind from './parsefunction/UserAfterFInd.js';
|
||||
import SignatureAfterFind from './parsefunction/SignatureAfterFind.js';
|
||||
import getInvoices from './parsefunction/getInvoices.js';
|
||||
import getPayments from './parsefunction/getPayments.js';
|
||||
import getSubscriptions from './parsefunction/getSubscriptions.js';
|
||||
import TenantAterFind from './parsefunction/TenantAfterFind.js';
|
||||
import saveSubscriptio from './parsefunction/saveSubscription.js';
|
||||
|
||||
Parse.Cloud.define('AddUserToRole', addUserToGroups);
|
||||
Parse.Cloud.define('UserGroups', getUserGroups);
|
||||
Parse.Cloud.define('signPdf', PDF);
|
||||
Parse.Cloud.define('sendmailv3', sendmail);
|
||||
Parse.Cloud.define('sendmailv3', sendmailv3);
|
||||
Parse.Cloud.define('googlesign', GoogleSign);
|
||||
Parse.Cloud.define('zohodetails', ZohoDetails);
|
||||
Parse.Cloud.define('usersignup', usersignup);
|
||||
Parse.Cloud.define('facebooksign', FacebookSign);
|
||||
Parse.Cloud.define('SendOTPMailV1', sendMailOTPv1);
|
||||
Parse.Cloud.define('sendmail', SendMailv1);
|
||||
Parse.Cloud.define('AuthLoginAsMail', AuthLoginAsMail);
|
||||
Parse.Cloud.define('getUserId', getUserId);
|
||||
Parse.Cloud.define('getUserDetails', getUserDetails);
|
||||
@@ -45,7 +55,19 @@ Parse.Cloud.define('getapitoken', getapitoken);
|
||||
Parse.Cloud.define('getTemplate', GetTemplate);
|
||||
Parse.Cloud.define('savewebhook', savewebhook);
|
||||
Parse.Cloud.define('callwebhook', callWebhook);
|
||||
Parse.Cloud.define('freesubscription', SubscribeFree);
|
||||
Parse.Cloud.define('getinvoices', getInvoices);
|
||||
Parse.Cloud.define('getpayments', getPayments);
|
||||
Parse.Cloud.define('getsubscriptions', getSubscriptions);
|
||||
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
|
||||
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
|
||||
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
|
||||
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
|
||||
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
|
||||
Parse.Cloud.beforeSave('contracts_Template', TemplateBeforeSave);
|
||||
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);
|
||||
Parse.Cloud.define('savesubscription', saveSubscriptio);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function DocumentAfterFind(request) {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
export default DocumentAfterFind;
|
||||
@@ -0,0 +1,33 @@
|
||||
async function DocumentBeforesave(request) {
|
||||
try {
|
||||
// below code is used to update document when user sent document or self signed
|
||||
const document = request.object;
|
||||
const oldDocument = request.original;
|
||||
|
||||
// Check if SignedUrl field has been added (transition from undefined to defined)
|
||||
if (!oldDocument?.get('SignedUrl') && document?.get('SignedUrl')) {
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', oldDocument.get('ExtUserPtr').id);
|
||||
|
||||
try {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('DocumentCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('DocumentCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating document count in contracts_users: ' + error.message);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in document beforesave', err.message);
|
||||
}
|
||||
}
|
||||
export default DocumentBeforesave;
|
||||
@@ -1,47 +1,65 @@
|
||||
import axios from 'axios'
|
||||
|
||||
import { updateMailCount } from '../../Utils.js';
|
||||
async function getDocument(docId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
query.include('ExtUserPtr');
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
query.include('Placeholders');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
const _res = res.toJSON();
|
||||
return _res?.ExtUserPtr?.objectId;
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
}
|
||||
}
|
||||
async function sendMailOTPv1(request) {
|
||||
try {
|
||||
//--for elearning app side
|
||||
let code = Math.floor(1000 + Math.random() * 9000);
|
||||
let email = request.params.email;
|
||||
var TenantId = request.params.TenantId ? request.params.TenantId : undefined;
|
||||
// console.log("In tempSendOTPv2");
|
||||
|
||||
// console.log(JSON.stringify(request));
|
||||
|
||||
if (email) {
|
||||
axios({
|
||||
method: 'POST',
|
||||
url: process.env.SERVER_URL + '/functions/sendmail',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
},
|
||||
params: {
|
||||
otp: code,
|
||||
email: email,
|
||||
TenantId: TenantId,
|
||||
},
|
||||
}).then(
|
||||
function (httpResponse) {},
|
||||
function (httpResponse) {
|
||||
console.error('sms Request failed with response code ' + httpResponse.status);
|
||||
return Promise.reject('sms Request failed with response code ' + httpResponse.status);
|
||||
const recipient = request.params.email;
|
||||
const mailsender = process.env.SMTP_ENABLE
|
||||
? process.env.SMTP_USER_EMAIL
|
||||
: process.env.MAILGUN_SENDER;
|
||||
try {
|
||||
await Parse.Cloud.sendEmail({
|
||||
from: 'Opensign™' + ' <' + mailsender + '>',
|
||||
recipient: recipient,
|
||||
subject: 'Your OpenSign™ OTP',
|
||||
text: 'This email is a test.',
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" +
|
||||
code +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
});
|
||||
console.log('OTP sent');
|
||||
if (request.params?.docId) {
|
||||
const extUserId = await getDocument(request.params?.docId);
|
||||
if (extUserId) {
|
||||
updateMailCount(extUserId);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
console.log('error in send OTP mail', err);
|
||||
}
|
||||
const tempOtp = new Parse.Query('defaultdata_Otp');
|
||||
tempOtp.equalTo('Email', email);
|
||||
const resultOTP = await tempOtp.first({ useMasterKey: true });
|
||||
console.log('resultOTP', resultOTP);
|
||||
// console.log('resultOTP', resultOTP);
|
||||
if (resultOTP !== undefined) {
|
||||
const updateOtpQuery = new Parse.Query('defaultdata_Otp');
|
||||
const updateOtp = await updateOtpQuery.get(resultOTP.id, {
|
||||
useMasterKey: true,
|
||||
});
|
||||
updateOtp.set('OTP', code);
|
||||
const updateRes = updateOtp.save(null, { useMasterKey: true });
|
||||
updateOtp.save(null, { useMasterKey: true });
|
||||
// console.log("update otp Res in tempSendOtp ", updateRes);
|
||||
} else {
|
||||
const otpClass = Parse.Object.extend('defaultdata_Otp');
|
||||
@@ -49,17 +67,17 @@ async function sendMailOTPv1(request) {
|
||||
newOtpQuery.set('OTP', code);
|
||||
newOtpQuery.set('Email', email);
|
||||
newOtpQuery.set('TenantId', TenantId);
|
||||
const newRes = await newOtpQuery.save(null, { useMasterKey: true });
|
||||
await newOtpQuery.save(null, { useMasterKey: true });
|
||||
// console.log("new otp Res in tempSendOtp ", newRes);
|
||||
}
|
||||
return 'Otp send';
|
||||
} else {
|
||||
return Promise.reject('Please Enter valid email');
|
||||
return 'Please Enter valid email';
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in sendMailOTPv1');
|
||||
console.log(err);
|
||||
return Promise.reject(err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
export default sendMailOTPv1;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
async function SendMailv1(request) {
|
||||
console.log('in SendMailv1');
|
||||
|
||||
try {
|
||||
const recipient = request.params.email;
|
||||
const otp = request.params.otp;
|
||||
const res = await Parse.Cloud.sendEmail({
|
||||
from: 'Test user' + ' <' + process.env.SMTP_ENABLE ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER + '>',
|
||||
recipient: recipient,
|
||||
subject: 'Your OpenSign™ OTP',
|
||||
text: 'This email is a test.',
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for OpenSign™ verification is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" +
|
||||
otp +
|
||||
'</p></div> </div> </div></body></html>',
|
||||
// "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body style='text-align: center;'><div style='display:flex;flex-direction:column;justify-content:center;align-item:center;margin:40px'> <p style='font-weight: bolder; font-size: large;'>Hello,</p> <span>Your OTP for LegaDaft verification . </span> <p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px'>76984</p><span>Thank You!</span> </div> </body></html>"
|
||||
});
|
||||
console.log('Res');
|
||||
console.log(res);
|
||||
return otp;
|
||||
} catch (err) {
|
||||
console.log('err in SendMailv1');
|
||||
console.log(err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
export default SendMailv1;
|
||||
@@ -0,0 +1,22 @@
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function SignatureAfterFind(request) {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
|
||||
if (ImageURL) {
|
||||
obj.set('ImageURL', getPresignedUrl(ImageURL));
|
||||
}
|
||||
|
||||
if (Initials) {
|
||||
obj.set('Initials', getPresignedUrl(Initials));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
export default SignatureAfterFind;
|
||||
@@ -0,0 +1,67 @@
|
||||
export default async function SubscribeFree(request) {
|
||||
const userId = request.params.userId;
|
||||
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
|
||||
try {
|
||||
const extQuery = new Parse.Query('contracts_Users');
|
||||
extQuery.equalTo('UserId', userPtr);
|
||||
const extUser = await extQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subscriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
subscriptionCls.descending('createdAt');
|
||||
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
|
||||
if (subcripitions?.get('PlanCode') === 'freeplan') {
|
||||
return { status: 'success', result: 'already subscribed!' };
|
||||
} else if (subcripitions?.get('Next_billing_date') < new Date()) {
|
||||
try {
|
||||
const updateSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
updateSubscription.id = subcripitions.id;
|
||||
updateSubscription.set('PlanCode', 'freeplan');
|
||||
await updateSubscription.save(null, { useMasterKey: true });
|
||||
return { status: 'success', result: 'subscribed!' };
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
} else if (subcripitions?.get('Next_billing_date') > new Date()) {
|
||||
return { status: 'success', result: 'already subscribed!' };
|
||||
} else {
|
||||
try {
|
||||
const createSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
createSubscription.set('PlanCode', 'freeplan');
|
||||
createSubscription.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
});
|
||||
createSubscription.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: extUser.get('UserId').id,
|
||||
});
|
||||
if (extUser?.get('TenantId')) {
|
||||
createSubscription.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
}
|
||||
await createSubscription.save(null, { useMasterKey: true });
|
||||
return { status: 'success', result: 'subscribed!' };
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'User not found!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function TemplateAfterFind(request) {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
export default TemplateAfterFind;
|
||||
@@ -0,0 +1,31 @@
|
||||
async function TemplateBeforeSave(request) {
|
||||
try {
|
||||
if (!request.original) {
|
||||
// below code is used to update template when user sent template or self signed
|
||||
const template = request.object;
|
||||
|
||||
// Update count in contracts_Users class
|
||||
const query = new Parse.Query('contracts_Users');
|
||||
query.equalTo('objectId', template.get('ExtUserPtr').id);
|
||||
|
||||
try {
|
||||
const contractUser = await query.first({ useMasterKey: true });
|
||||
if (contractUser) {
|
||||
contractUser.increment('TemplateCount', 1);
|
||||
await contractUser.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// Create new entry if not found
|
||||
const ContractsUsers = Parse.Object.extend('contracts_users');
|
||||
const newContractUser = new ContractsUsers();
|
||||
newContractUser.set('TemplateCount', 1);
|
||||
await newContractUser.save(null, { useMasterKey: true });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error updating template count in contracts_users: ' + error.message);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in template beforesave', err.message);
|
||||
}
|
||||
}
|
||||
export default TemplateBeforeSave;
|
||||
@@ -0,0 +1,15 @@
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function TenantAterFind(request) {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
||||
if (Logo) {
|
||||
obj.set('Logo', getPresignedUrl(Logo));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
export default TenantAterFind;
|
||||
@@ -0,0 +1,17 @@
|
||||
import getPresignedUrl from './getSignedUrl.js';
|
||||
|
||||
async function UserAfterFind(request) {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
||||
|
||||
if (ProfilePic) {
|
||||
obj.set('ProfilePic', getPresignedUrl(ProfilePic));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
export default UserAfterFind;
|
||||
@@ -23,23 +23,16 @@ export default async function ZohoDetails(request) {
|
||||
};
|
||||
// Make the POST request using Axios
|
||||
const res = await axios.post(url, formData, { headers });
|
||||
|
||||
// console.log("Access Token:", res.data);
|
||||
|
||||
if (res.data.access_token) {
|
||||
const hostedpages = request.params.hostedpagesId;
|
||||
const userData = await axios.get('https://billing.zoho.in/api/v1/hostedpages/' + hostedpages, {
|
||||
headers: {
|
||||
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
|
||||
'X-com-zoho-billing-organizationid': process.env.ZOHO_BILLING_ORG_ID,
|
||||
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
|
||||
},
|
||||
});
|
||||
// console.log(
|
||||
// "userData.data.data.subscription.contactpersons ",
|
||||
// userData.data.data.subscription.contactpersons
|
||||
// );
|
||||
|
||||
// console.log("userData.data.expiring_time ", userData.data.expiring_time);
|
||||
const first_name = userData.data.data.subscription.contactpersons[0].first_name || '';
|
||||
const last_name = userData.data.data.subscription.contactpersons[0].last_name || '';
|
||||
const company_name =
|
||||
@@ -60,6 +53,7 @@ export default async function ZohoDetails(request) {
|
||||
customer_id: userData.data.data.subscription.customer_id,
|
||||
subscription_id: userData.data.data.subscription.subscription_id,
|
||||
jobTitle: jobTitle,
|
||||
subscription: userData.data,
|
||||
};
|
||||
return resData;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function getDocument(request) {
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
query.include('Placeholders');
|
||||
query.notEqualTo('IsArchive', true)
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const acl = res.getACL();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import axios from 'axios';
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
export default async function getInvoices(request) {
|
||||
const limit = request.params.limit || 100;
|
||||
const skip = request.params.skip || 0;
|
||||
const extUserId = request.params.extUserId;
|
||||
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) {
|
||||
if (!extUserId) {
|
||||
return { status: 'error', result: 'Please provide parameter!' };
|
||||
}
|
||||
const invoiceCls = new Parse.Query('contracts_Invoices');
|
||||
invoiceCls.equalTo('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUserId,
|
||||
});
|
||||
invoiceCls.limit(limit);
|
||||
invoiceCls.skip(skip);
|
||||
invoiceCls.descending('createdAt');
|
||||
const invoices = await invoiceCls.find({ useMasterKey: true });
|
||||
if (invoices?.length > 0) {
|
||||
const _invoices = JSON.parse(JSON.stringify(invoices));
|
||||
return { status: 'success', result: _invoices };
|
||||
} else {
|
||||
return { status: 'success', result: [] };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid session token!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in get invoices', err.message);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import axios from 'axios';
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
export default async function getPayments(request) {
|
||||
const limit = request.params.limit || 100;
|
||||
const skip = request.params.skip || 0;
|
||||
const extUserId = request.params.extUserId;
|
||||
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 paymentsCls = new Parse.Query('contracts_Payments');
|
||||
paymentsCls.equalTo('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_User',
|
||||
objectId: extUserId,
|
||||
});
|
||||
paymentsCls.limit(limit);
|
||||
paymentsCls.skip(skip);
|
||||
paymentsCls.descending('createdAt');
|
||||
const payments = await paymentsCls.find({ useMasterKey: true });
|
||||
if (payments?.length > 0) {
|
||||
const _payments = JSON.parse(JSON.stringify(payments));
|
||||
return { status: 'success', result: _payments };
|
||||
} else {
|
||||
return { status: 'success', result: [] };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid session token!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in get Payments', err.message);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export default async function getReport(request) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err', err);
|
||||
console.log('err', err.message);
|
||||
if (err.code == 209) {
|
||||
return { error: 'Invalid session token' };
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import AWS from 'aws-sdk';
|
||||
const credentials = {
|
||||
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
||||
};
|
||||
AWS.config.update({ credentials: credentials, region: process.env.DO_REGION });
|
||||
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint });
|
||||
|
||||
export default function getPresignedUrl(url) {
|
||||
// Create a new URL object
|
||||
const parsedUrl = new URL(url);
|
||||
// Get the pathname of the URL
|
||||
const pathname = parsedUrl.pathname;
|
||||
// Extract the filename from the pathname
|
||||
const filename = pathname.substring(pathname.lastIndexOf('/') + 1);
|
||||
|
||||
// presignedGETURL return presignedUrl with expires time
|
||||
const presignedGETURL = s3.getSignedUrl('getObject', {
|
||||
Bucket: process.env.DO_SPACE,
|
||||
Key: filename, //filename
|
||||
Expires: 160, //time to expire in seconds
|
||||
});
|
||||
return presignedGETURL;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import axios from 'axios';
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
export default async function getSubscription(request) {
|
||||
const extUserId = request.params.extUserId;
|
||||
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 subcriptionCls = new Parse.Query('contracts_Users');
|
||||
const exUser = await subcriptionCls.get(extUserId, { useMasterKey: true });
|
||||
if (exUser) {
|
||||
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subscriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: exUser.get('TenantId').id,
|
||||
});
|
||||
subscriptionCls.descending('createdAt');
|
||||
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
|
||||
if (subcripitions) {
|
||||
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
|
||||
return { status: 'success', result: _subcripitions };
|
||||
} else {
|
||||
return { status: 'success', result: {} };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'User not found!' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'error', result: 'Invalid session token!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in get subscription', err.message);
|
||||
return { status: 'error', result: err.message };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
|
||||
import fs from 'node:fs';
|
||||
export default async function GenerateCertificate(docDetails) {
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);
|
||||
const pngUrl = fs.readFileSync('./logo.png').buffer;
|
||||
const pngImage = await pdfDoc.embedPng(pngUrl);
|
||||
const page = pdfDoc.addPage();
|
||||
const { width, height } = page.getSize();
|
||||
const startX = 15;
|
||||
const startY = 15;
|
||||
const borderColor = rgb(0.12, 0.12, 0.12);
|
||||
const titleColor = rgb(0, 0.2, 0.4); //rgb(0, 0.53, 0.71);
|
||||
const titleUnderline = rgb(0, 0.2, 0.4); // rgb(0.12, 0.12, 0.12);
|
||||
const title = 25;
|
||||
const subtitle = 20;
|
||||
const text = 14;
|
||||
const textKeyColor = rgb(0.12, 0.12, 0.12);
|
||||
const textValueColor = rgb(0.3, 0.3, 0.3);
|
||||
const completedAt = new Date(docDetails.updatedAt);
|
||||
const completedUTCtime = completedAt.toUTCString();
|
||||
const signersCount = docDetails?.Signers?.length || 1;
|
||||
const createdAt = new Date();
|
||||
const createdUTCTime = createdAt.toUTCString();
|
||||
const createDate = 'Generated On ' + createdUTCTime;
|
||||
const company = docDetails?.ExtUserPtr?.Company || '';
|
||||
const auditTrail =
|
||||
docDetails.AuditTrail?.length > 1
|
||||
? docDetails.AuditTrail.map(x => {
|
||||
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
|
||||
return { ...data, ipAddress: x.ipAddress };
|
||||
})
|
||||
: [{ ...docDetails.ExtUserPtr, ipAddress: docDetails?.AuditTrail[0].ipAddress }];
|
||||
|
||||
// Draw a border
|
||||
page.drawRectangle({
|
||||
x: startX,
|
||||
y: startY,
|
||||
width: width - 2 * startX,
|
||||
height: height - 2 * startY,
|
||||
borderColor: borderColor,
|
||||
borderWidth: 1,
|
||||
});
|
||||
page.drawImage(pngImage, {
|
||||
x: 30,
|
||||
y: 790,
|
||||
width: 100,
|
||||
height: 25,
|
||||
});
|
||||
|
||||
page.drawText(createDate, {
|
||||
x: 320,
|
||||
y: 810,
|
||||
size: 12,
|
||||
font: timesRomanFont,
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
});
|
||||
|
||||
page.drawText('Certificate of Completion', {
|
||||
x: 160,
|
||||
y: 750,
|
||||
size: title,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
|
||||
const underlineY = 740;
|
||||
page.drawLine({
|
||||
start: { x: 30, y: underlineY },
|
||||
end: { x: width - 30, y: underlineY },
|
||||
color: titleUnderline,
|
||||
thickness: 1,
|
||||
});
|
||||
|
||||
page.drawText('Summary', {
|
||||
x: 30,
|
||||
y: 710,
|
||||
size: subtitle,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
|
||||
page.drawText('Document Id :', {
|
||||
x: 30,
|
||||
y: 685,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(docDetails.objectId, {
|
||||
x: 115,
|
||||
y: 685,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Document Name :', {
|
||||
x: 30,
|
||||
y: 665,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(docDetails.Name, {
|
||||
x: 140,
|
||||
y: 665,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Organization :', {
|
||||
x: 30,
|
||||
y: 645,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(company, {
|
||||
x: 115,
|
||||
y: 645,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Completed on :', {
|
||||
x: 30,
|
||||
y: 625,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${completedUTCtime}`, {
|
||||
x: 120,
|
||||
y: 625,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Signers :', {
|
||||
x: 30,
|
||||
y: 605,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(`${signersCount}`, {
|
||||
x: 80,
|
||||
y: 605,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawLine({
|
||||
start: { x: 30, y: 565 },
|
||||
end: { x: width - 30, y: 565 },
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
thickness: 0.5,
|
||||
});
|
||||
page.drawText('Recipients', {
|
||||
x: 30,
|
||||
y: 575,
|
||||
size: subtitle,
|
||||
font: timesRomanFont,
|
||||
color: titleColor,
|
||||
});
|
||||
let yPosition1 = 550;
|
||||
let yPosition2 = 530;
|
||||
let yPosition3 = 510;
|
||||
let yPosition4 = 500;
|
||||
auditTrail.forEach(x => {
|
||||
page.drawText('Name :', {
|
||||
x: 30,
|
||||
y: yPosition1,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(x?.Name, {
|
||||
x: 75,
|
||||
y: yPosition1,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Email :', {
|
||||
x: 30,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(x?.Email, {
|
||||
x: 75,
|
||||
y: yPosition2,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawText('Accessed from :', {
|
||||
x: 30,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textKeyColor,
|
||||
});
|
||||
|
||||
page.drawText(x?.ipAddress, {
|
||||
x: 125,
|
||||
y: yPosition3,
|
||||
size: text,
|
||||
font: timesRomanFont,
|
||||
color: textValueColor,
|
||||
});
|
||||
|
||||
page.drawLine({
|
||||
start: { x: 30, y: yPosition4 },
|
||||
end: { x: width - 30, y: yPosition4 },
|
||||
color: rgb(0.12, 0.12, 0.12),
|
||||
thickness: 0.5,
|
||||
});
|
||||
|
||||
yPosition1 = yPosition4 - 20;
|
||||
yPosition2 = yPosition1 - 20;
|
||||
yPosition3 = yPosition2 - 20;
|
||||
yPosition4 = yPosition4 - 70;
|
||||
});
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
return pdfBytes;
|
||||
}
|
||||
+197
-156
@@ -3,13 +3,15 @@ import fs from 'node:fs';
|
||||
import axios from 'axios';
|
||||
import { pdflibAddPlaceholder } from './customSignPdf/pdflibplaceholder.min.js';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
|
||||
import GenerateCertificate from './GenerateCertificate.js';
|
||||
const serverUrl = process.env.SERVER_URL,
|
||||
APPID = process.env.APP_ID,
|
||||
masterKEY = process.env.MASTER_KEY;
|
||||
async function uploadFile(e, a) {
|
||||
try {
|
||||
var t = fs.readFileSync(a),
|
||||
s = new Parse.File(e, [...t]),
|
||||
s = new Parse.File(e, [...t], 'application/pdf'),
|
||||
r = (await s.save({ useMasterKey: !0 }), s.url());
|
||||
return { imageUrl: r };
|
||||
} catch (e) {
|
||||
@@ -18,18 +20,18 @@ async function uploadFile(e, a) {
|
||||
}
|
||||
async function updateDoc(t, s, r, i, o, n) {
|
||||
try {
|
||||
var d = {
|
||||
var l = {
|
||||
UserPtr: { __type: 'Pointer', className: n, objectId: r },
|
||||
SignedUrl: s,
|
||||
Activity: 'Signed',
|
||||
ipAddress: i,
|
||||
};
|
||||
let e;
|
||||
var l = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, d] : [d]).filter(
|
||||
var d = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, l] : [l]).filter(
|
||||
e => 'Signed' === e.Activity
|
||||
);
|
||||
let a = !1;
|
||||
!((o.Signers && 0 < o.Signers.length && l.length !== o.Signers.length) || !(a = !0));
|
||||
!((o.Signers && 0 < o.Signers.length && d.length !== o.Signers.length) || !(a = !0));
|
||||
var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, {
|
||||
headers: {
|
||||
@@ -38,54 +40,70 @@ async function updateDoc(t, s, r, i, o, n) {
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
return { isCompleted: a, message: 'success' };
|
||||
return { isCompleted: a, message: 'success', AuditTrail: e };
|
||||
} catch (e) {
|
||||
return console.log('update doc err ', e), 'err';
|
||||
}
|
||||
}
|
||||
async function sendMail(e) {
|
||||
var a = e.url,
|
||||
t = e.sender,
|
||||
s = e.pdfName,
|
||||
a = {
|
||||
url: a,
|
||||
from: 'OpenSign™',
|
||||
recipient: e.receiver,
|
||||
subject: 'You have signed the doc - ' + s,
|
||||
pdfName: s,
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " +
|
||||
s +
|
||||
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
|
||||
t.Mail +
|
||||
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
|
||||
};
|
||||
await axios.post(serverUrl + '/functions/sendmailv3', a, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
});
|
||||
}
|
||||
async function sendCompletedMail(e) {
|
||||
var a = e.url,
|
||||
t = e.sender,
|
||||
s = e.pdfName,
|
||||
a = {
|
||||
url: a,
|
||||
from: 'OpenSign™',
|
||||
recipient: e.receiver,
|
||||
subject: `Document ${s} has been signed by all parties`,
|
||||
pdfName: s,
|
||||
html:
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document sign successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document" +
|
||||
s +
|
||||
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
|
||||
t.Mail +
|
||||
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
|
||||
};
|
||||
await axios.post(serverUrl + '/functions/sendmailv3', a, {
|
||||
t = e.doc,
|
||||
s = e.doc.ExtUserPtr,
|
||||
r = t.Name,
|
||||
i = s.Email;
|
||||
let o = `Document "${r}" has been signed by all parties`,
|
||||
n =
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document signed successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document " +
|
||||
`<b>"${r}"</b>` +
|
||||
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
|
||||
s.Email +
|
||||
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>';
|
||||
if (e?.isCustomMail)
|
||||
try {
|
||||
var l,
|
||||
d,
|
||||
c,
|
||||
p,
|
||||
m,
|
||||
g = new Parse.Query('partners_Tenant');
|
||||
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: s.UserId.objectId });
|
||||
const u = await g.first();
|
||||
u &&
|
||||
((l = JSON.parse(JSON.stringify(u)))?.CompletionSubject && (o = l?.CompletionSubject),
|
||||
l?.CompletionBody && (n = l?.CompletionBody),
|
||||
(d = t.ExpiryDate.iso),
|
||||
(c = new Date(d).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
})),
|
||||
(p = {
|
||||
document_title: r,
|
||||
sender_name: s.Name,
|
||||
sender_mail: s.Email,
|
||||
sender_phone: s.Phone,
|
||||
receiver_name: s.Name,
|
||||
receiver_email: s.Email,
|
||||
receiver_phone: s.Phone,
|
||||
expiry_date: c,
|
||||
company_name: s.Company,
|
||||
}),
|
||||
(m = replaceMailVaribles(o, n, p)),
|
||||
(o = m.subject),
|
||||
(n = m.body));
|
||||
} catch (e) {
|
||||
console.log('error in fetch tenant in signpdf', e.message);
|
||||
}
|
||||
e = {
|
||||
extUserId: s.objectId,
|
||||
url: a,
|
||||
from: 'OpenSign™',
|
||||
recipient: i,
|
||||
subject: o,
|
||||
pdfName: r,
|
||||
html: n,
|
||||
};
|
||||
await axios.post(serverUrl + '/functions/sendmailv3', e, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
@@ -132,11 +150,11 @@ async function sendDoctoWebhook(t, e, a, s) {
|
||||
}),
|
||||
a.save(null, { useMasterKey: !0 });
|
||||
} catch (e) {
|
||||
console.log('err save in contracts_Webhook', e);
|
||||
console.log('err save in contracts_Webhook', e.message);
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log('Err send data to webhook', e);
|
||||
console.log('Err send data to webhook', e.message);
|
||||
try {
|
||||
var a = new Parse.Object('contracts_Webhook');
|
||||
a.set('Log', e?.status),
|
||||
@@ -147,16 +165,17 @@ async function sendDoctoWebhook(t, e, a, s) {
|
||||
}),
|
||||
a.save(null, { useMasterKey: !0 });
|
||||
} catch (e) {
|
||||
console.log('err save in contracts_Webhook', e);
|
||||
console.log('err save in contracts_Webhook', e.message);
|
||||
}
|
||||
}));
|
||||
}
|
||||
async function PDF(i) {
|
||||
async function PDF(o) {
|
||||
try {
|
||||
var e = i.params.docId,
|
||||
a = i.params.userId,
|
||||
o = await axios.get(
|
||||
serverUrl + '/classes/contracts_Document/' + e + '?include=ExtUserPtr,Signers',
|
||||
var n = o.params.docId,
|
||||
e = o.params.userId,
|
||||
l = o.params.isCustomCompletionMail,
|
||||
d = await axios.get(
|
||||
serverUrl + '/classes/contracts_Document/' + n + '?include=ExtUserPtr,Signers',
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -165,143 +184,165 @@ async function PDF(i) {
|
||||
},
|
||||
}
|
||||
),
|
||||
t = await axios.get(serverUrl + '/users/me', {
|
||||
c = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': i.headers.sessiontoken,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
});
|
||||
if (!t.data || !t.data.objectId) return { status: 'error', message: 'this user not allowed!' };
|
||||
if (!c.data || !c.data.objectId) return { status: 'error', message: 'This user not allowed!' };
|
||||
{
|
||||
var n,
|
||||
d,
|
||||
l,
|
||||
c = JSON.stringify({ objectId: a });
|
||||
let s, r;
|
||||
r = a
|
||||
? (n = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, {
|
||||
var a,
|
||||
t,
|
||||
s,
|
||||
p = JSON.stringify({ objectId: e });
|
||||
let r, i;
|
||||
i = e
|
||||
? (a = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + p, {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': i.headers.sessiontoken,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
})).data && 0 < n.data.results.length
|
||||
? ((s = n), 'contracts_Contactbook')
|
||||
: ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, {
|
||||
})).data && 0 < a.data.results.length
|
||||
? ((r = a), 'contracts_Contactbook')
|
||||
: ((r = await axios.get(serverUrl + '/classes/contracts_Users?where=' + p, {
|
||||
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
|
||||
})),
|
||||
'contracts_Users')
|
||||
: ((d = JSON.stringify({
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId },
|
||||
: ((t = JSON.stringify({
|
||||
UserId: { __type: 'Pointer', className: '_User', objectId: c.data.objectId },
|
||||
})),
|
||||
(l = await axios.get(serverUrl + '/classes/contracts_Users?where=' + d, {
|
||||
(s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + t, {
|
||||
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
|
||||
})).data && 0 < l.data.results.length
|
||||
? ((s = l), 'contracts_Users')
|
||||
: ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + d, {
|
||||
})).data && 0 < s.data.results.length
|
||||
? ((r = s), 'contracts_Users')
|
||||
: ((r = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + t, {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Session-Token': i.headers.sessiontoken,
|
||||
'X-Parse-Session-Token': o.headers.sessiontoken,
|
||||
},
|
||||
})),
|
||||
'contracts_Contactbook'));
|
||||
var p = s.data.results[0].Name,
|
||||
m = s.data.results[0].Email;
|
||||
if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' };
|
||||
var m = r.data.results[0].Name,
|
||||
g = r.data.results[0].Email;
|
||||
if (!o.params.pdfFile) return { status: 'error', message: 'Pdf file not present!' };
|
||||
{
|
||||
let e = Buffer.from(i.params.pdfFile, 'base64');
|
||||
var g = process.env.PFX_BASE64,
|
||||
h = Buffer.from(g, 'base64'),
|
||||
u = {
|
||||
UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId },
|
||||
let e = Buffer.from(o.params.pdfFile, 'base64');
|
||||
var u = process.env.PFX_BASE64,
|
||||
h = Buffer.from(u, 'base64'),
|
||||
f = {
|
||||
UserPtr: { __type: 'Pointer', className: i, objectId: r.data.results[0].objectId },
|
||||
SignedUrl: '',
|
||||
Activity: 'Signed',
|
||||
ipAddress: i.headers['x-real-ip'],
|
||||
ipAddress: o.headers['x-real-ip'],
|
||||
};
|
||||
let a;
|
||||
var f = (a =
|
||||
o.data.AuditTrail && 0 < o.data.AuditTrail.length
|
||||
? [...o.data.AuditTrail, u]
|
||||
: [u]).filter(e => 'Signed' === e.Activity);
|
||||
var P = (a =
|
||||
d.data.AuditTrail && 0 < d.data.AuditTrail.length
|
||||
? [...d.data.AuditTrail, f]
|
||||
: [f]).filter(e => 'Signed' === e.Activity);
|
||||
let t = !1;
|
||||
!(
|
||||
(o.data.Signers && 0 < o.data.Signers.length && f.length !== o.data.Signers.length) ||
|
||||
(d.data.Signers && 0 < d.data.Signers.length && P.length !== d.data.Signers.length) ||
|
||||
!(t = !0)
|
||||
);
|
||||
var y,
|
||||
P,
|
||||
v,
|
||||
b,
|
||||
x,
|
||||
w,
|
||||
v,
|
||||
U,
|
||||
I,
|
||||
S,
|
||||
A = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
|
||||
D = './exports/' + A,
|
||||
E =
|
||||
(t
|
||||
? ((y = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
|
||||
(e =
|
||||
y && 0 < y.length
|
||||
? ((P = await PDFDocument.load(e)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: P,
|
||||
reason: 'Digitally signed by OpenSign for ' + y?.join(', '),
|
||||
location: 'location',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(v = await P.save()),
|
||||
Buffer.from(v))
|
||||
: ((b = await PDFDocument.load(e)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: b,
|
||||
reason: 'Digitally signed by OpenSign for ' + p + ' <' + m + '>',
|
||||
location: 'location',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(x = await b.save()),
|
||||
Buffer.from(x))),
|
||||
(w = await new SignPDF(e, h).signPDF()),
|
||||
fs.writeFileSync(D, w))
|
||||
: fs.writeFileSync(D, e),
|
||||
await uploadFile(A, D));
|
||||
if (E && E.imageUrl)
|
||||
w,
|
||||
D = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
|
||||
S = './exports/' + D;
|
||||
let s = e.length;
|
||||
s = (
|
||||
t
|
||||
? ((y = d.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
|
||||
(e =
|
||||
y && 0 < y.length
|
||||
? ((b = await PDFDocument.load(e)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: b,
|
||||
reason: 'Digitally signed by OpenSign for ' + y?.join(', '),
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(v = await b.save()),
|
||||
Buffer.from(v))
|
||||
: ((U = await PDFDocument.load(e)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: U,
|
||||
reason: 'Digitally signed by OpenSign for ' + m + ' <' + g + '>',
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
(I = await U.save()),
|
||||
Buffer.from(I))),
|
||||
(w = await new SignPDF(e, h).signPDF()),
|
||||
fs.writeFileSync(S, w),
|
||||
w)
|
||||
: (fs.writeFileSync(S, e), e)
|
||||
).length;
|
||||
var _,
|
||||
A,
|
||||
x,
|
||||
E,
|
||||
j,
|
||||
k,
|
||||
F,
|
||||
C,
|
||||
T,
|
||||
N = await uploadFile(D, S);
|
||||
if (N && N.imageUrl)
|
||||
return (
|
||||
(U = await updateDoc(
|
||||
i.params.docId,
|
||||
E.imageUrl,
|
||||
s.data.results[0].objectId,
|
||||
i.headers['x-real-ip'],
|
||||
o.data,
|
||||
r
|
||||
(_ = await updateDoc(
|
||||
o.params.docId,
|
||||
N.imageUrl,
|
||||
r.data.results[0].objectId,
|
||||
o.headers['x-real-ip'],
|
||||
d.data,
|
||||
i
|
||||
)),
|
||||
(I = {
|
||||
url: E.imageUrl,
|
||||
sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name },
|
||||
pdfName: o.data.Name,
|
||||
receiver: m,
|
||||
}),
|
||||
o.data.IsSendMail && !1 === o.data.IsSendMail
|
||||
? console.log("don't send mail")
|
||||
: sendMail(I),
|
||||
sendDoctoWebhook(o, E.imageUrl, 'signed', s?.data.results?.[0]),
|
||||
U &&
|
||||
U.isCompleted &&
|
||||
((S = {
|
||||
url: E.imageUrl,
|
||||
sender: { Mail: o.data.ExtUserPtr.Email, Name: 'OpenSign™' },
|
||||
pdfName: o.data.Name,
|
||||
receiver: o.data.ExtUserPtr.Email,
|
||||
sendDoctoWebhook(d, N.imageUrl, 'signed', r?.data.results?.[0]),
|
||||
saveFileUsage(s, N.imageUrl, c.data.objectId),
|
||||
_ &&
|
||||
_.isCompleted &&
|
||||
((A = { ...d.data, AuditTrail: _.AuditTrail }),
|
||||
(x = await GenerateCertificate(A)),
|
||||
(E = await PDFDocument.load(x)),
|
||||
pdflibAddPlaceholder({
|
||||
pdfDoc: E,
|
||||
reason: 'Digitally signed by OpenSign.',
|
||||
location: 'n/a',
|
||||
signatureLength: 15e3,
|
||||
}),
|
||||
o.data.IsSendMail && !1 === o.data.IsSendMail
|
||||
(j = await E.save()),
|
||||
(k = Buffer.from(j)),
|
||||
(F = await new SignPDF(k, h).signPDF()),
|
||||
fs.writeFileSync('./exports/certificate.pdf', F),
|
||||
(T = {
|
||||
CertificateUrl: (C = await uploadFile(
|
||||
'certificate.pdf',
|
||||
'./exports/certificate.pdf'
|
||||
)).imageUrl,
|
||||
}),
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + n, T, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
}),
|
||||
d.data.IsSendMail && !1 === d.data.IsSendMail
|
||||
? console.log("don't send mail")
|
||||
: sendCompletedMail(S),
|
||||
sendDoctoWebhook(o, E.imageUrl, 'completed')),
|
||||
fs.unlinkSync(D),
|
||||
console.log('New Signed PDF created called: ' + D),
|
||||
'success' === U.message
|
||||
? { status: 'success', data: E.imageUrl }
|
||||
: { status: 'error', message: 'please provide required parameters!' }
|
||||
: sendCompletedMail({ url: N.imageUrl, isCustomMail: l, doc: d.data }),
|
||||
saveFileUsage(k.length, C.imageUrl, c.data.objectId),
|
||||
sendDoctoWebhook(d, N.imageUrl, 'completed')),
|
||||
fs.unlinkSync(S),
|
||||
console.log('New Signed PDF created called: ' + S),
|
||||
'success' === _.message
|
||||
? { status: 'success', data: N.imageUrl }
|
||||
: { status: 'error', message: 'Please provide required parameters!' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,19 +164,10 @@ export default function reportJson(id, userId) {
|
||||
IsDeclined: { $ne: true },
|
||||
IsArchive: { $ne: true },
|
||||
Type: { $ne: 'Folder' },
|
||||
$and: [
|
||||
{
|
||||
$or: [
|
||||
{ Signers: { $ne: null }, SignedUrl: { $ne: null } },
|
||||
{ Placeholders: { $ne: null } },
|
||||
],
|
||||
},
|
||||
{
|
||||
ExpiryDate: {
|
||||
$lt: { __type: 'Date', iso: new Date().toISOString() },
|
||||
},
|
||||
},
|
||||
],
|
||||
SignedUrl: { $ne: null },
|
||||
ExpiryDate: {
|
||||
$lt: { __type: 'Date', iso: new Date().toISOString() },
|
||||
},
|
||||
CreatedBy: {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import axios from 'axios';
|
||||
export default async function saveSubscriptio(request) {
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const subscription = request.params.subscription;
|
||||
const SubscriptionId = subscription.data.subscription.subscription_id;
|
||||
const body = subscription;
|
||||
const Next_billing_date = subscription.data.subscription.next_billing_at;
|
||||
const planCode = subscription.data.subscription.plan.plan_code;
|
||||
|
||||
try {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': appId,
|
||||
'X-Parse-Session-Token': request.headers['sessiontoken'],
|
||||
},
|
||||
});
|
||||
if (userRes.data && userRes.data.objectId) {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: userRes.data.objectId,
|
||||
});
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const subcriptionCls = new Parse.Query('contracts_Subscriptions');
|
||||
subcriptionCls.equalTo('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
const subscription = await subcriptionCls.first({ useMasterKey: true });
|
||||
if (subscription) {
|
||||
const updateSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
updateSubscription.id = subscription.id;
|
||||
updateSubscription.set('SubscriptionId', SubscriptionId);
|
||||
updateSubscription.set('SubscriptionDetails', body);
|
||||
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
|
||||
updateSubscription.set('PlanCode', planCode);
|
||||
await updateSubscription.save(null, { useMasterKey: true });
|
||||
return { status: 'update subscription!' };
|
||||
} else {
|
||||
const createSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
createSubscription.set('SubscriptionId', SubscriptionId);
|
||||
createSubscription.set('SubscriptionDetails', body);
|
||||
createSubscription.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUser.id,
|
||||
});
|
||||
createSubscription.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: extUser.get('UserId').id,
|
||||
});
|
||||
createSubscription.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: extUser.get('TenantId').id,
|
||||
});
|
||||
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
|
||||
createSubscription.set('PlanCode', planCode);
|
||||
await createSubscription.save(null, { useMasterKey: true });
|
||||
return { status: 'create subscription!' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'user not found!' };
|
||||
}
|
||||
} else {
|
||||
return { status: 'user not found!' };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in save subscription', err);
|
||||
throw new Error(err.message);
|
||||
}
|
||||
}
|
||||
+42
-7
@@ -3,8 +3,9 @@ import https from 'https';
|
||||
import formData from 'form-data';
|
||||
import Mailgun from 'mailgun.js';
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { updateMailCount } from '../../Utils.js';
|
||||
|
||||
async function sendmail(req) {
|
||||
async function sendmailv3(req) {
|
||||
try {
|
||||
let transporterSMTP;
|
||||
let mailgunClient;
|
||||
@@ -57,8 +58,20 @@ async function sendmail(req) {
|
||||
data: process.env.SMTP_ENABLE ? undefined : PdfBuffer,
|
||||
};
|
||||
|
||||
// const html = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body style='text-align: center;'> <p style='font-weight: bolder; font-size: large;'>Hello!</p> <p>This is a html checking mail</p><p><button style='background-color: lightskyblue; cursor: pointer; border-radius: 5px; padding: 10px; border-style: solid; border-width: 2px; text-decoration: none; font-weight: bolder; color:blue'>Verify email</button></p></body></html>"
|
||||
|
||||
let attachment;
|
||||
// `certificateBuffer` used to create buffer from pdf file
|
||||
try {
|
||||
const certificateBuffer = fs.readFileSync('./exports/certificate.pdf');
|
||||
const certificate = {
|
||||
filename: 'certificate.pdf',
|
||||
content: process.env.SMTP_ENABLE ? certificateBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'),
|
||||
data: process.env.SMTP_ENABLE ? undefined : certificateBuffer,
|
||||
};
|
||||
attachment = [file, certificate];
|
||||
} catch (err) {
|
||||
attachment = [file];
|
||||
console.log('Err in read certificate sendmailv3', err);
|
||||
}
|
||||
const from = req.params.from || '';
|
||||
const mailsender = process.env.SMTP_ENABLE
|
||||
? process.env.SMTP_USER_EMAIL
|
||||
@@ -70,13 +83,21 @@ async function sendmail(req) {
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params.html || '',
|
||||
attachments: process.env.SMTP_ENABLE ? [file] : undefined,
|
||||
attachment: process.env.SMTP_ENABLE ? undefined : file,
|
||||
attachments: process.env.SMTP_ENABLE ? attachment : undefined,
|
||||
attachment: process.env.SMTP_ENABLE ? undefined : attachment,
|
||||
};
|
||||
if (transporterSMTP) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('Res ', res);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
}
|
||||
try {
|
||||
fs.unlinkSync('./exports/certificate.pdf');
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
@@ -85,6 +106,14 @@ async function sendmail(req) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('Res ', res);
|
||||
if (res.status === 200) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
}
|
||||
try {
|
||||
fs.unlinkSync('./exports/certificate.pdf');
|
||||
} catch (err) {
|
||||
console.log('Err in unlink certificate sendmailv3');
|
||||
}
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
@@ -109,6 +138,9 @@ async function sendmail(req) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('Res ', res);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
}
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
@@ -117,6 +149,9 @@ async function sendmail(req) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('Res ', res);
|
||||
if (res.status === 200) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId);
|
||||
}
|
||||
return {
|
||||
status: 'success',
|
||||
};
|
||||
@@ -124,11 +159,11 @@ async function sendmail(req) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err ', err);
|
||||
console.log('err in sendmailv3', err);
|
||||
if (err) {
|
||||
return { status: 'error' };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default sendmail;
|
||||
export default sendmailv3;
|
||||
@@ -1,22 +1,22 @@
|
||||
import axios from "axios";
|
||||
import axios from 'axios';
|
||||
const serverUrl = process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
|
||||
async function saveUser(userDetails) {
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
userQuery.equalTo("username", userDetails.email);
|
||||
userQuery.equalTo('username', userDetails.email);
|
||||
const userRes = await userQuery.first({ useMasterKey: true });
|
||||
|
||||
if (userRes) {
|
||||
const url = `${serverUrl}/loginAs`;
|
||||
const axiosRes = await axios({
|
||||
method: "POST",
|
||||
method: 'POST',
|
||||
url: url,
|
||||
headers: {
|
||||
"Content-Type": "application/json;charset=utf-8",
|
||||
"X-Parse-Application-Id": APPID,
|
||||
"X-Parse-Master-Key": masterKEY,
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
'X-Parse-Master-Key': masterKEY,
|
||||
},
|
||||
params: {
|
||||
userId: userRes.id,
|
||||
@@ -27,39 +27,39 @@ async function saveUser(userDetails) {
|
||||
return { id: login.objectId, sessionToken: login.sessionToken };
|
||||
} else {
|
||||
const user = new Parse.User();
|
||||
user.set("username", userDetails.email);
|
||||
user.set("password", userDetails.password);
|
||||
user.set("email", userDetails.email);
|
||||
user.set("phone", userDetails.phone);
|
||||
user.set("name", userDetails.name);
|
||||
user.set('username', userDetails.email);
|
||||
user.set('password', userDetails.password);
|
||||
user.set('email', userDetails.email);
|
||||
user.set('phone', userDetails.phone);
|
||||
user.set('name', userDetails.name);
|
||||
|
||||
const res = await user.signUp();
|
||||
// console.log("res ", res);
|
||||
return { id: res.id, sessionToken: res.getSessionToken() };
|
||||
}
|
||||
}
|
||||
export default async function (request) {
|
||||
export default async function usersignup(request) {
|
||||
const userDetails = request.params.userDetails;
|
||||
const planDetails = request.params.planDetails;
|
||||
const subscription = request.params.subscription;
|
||||
const user = await saveUser(userDetails);
|
||||
|
||||
try {
|
||||
const extClass = userDetails.role.split("_")[0];
|
||||
const extClass = userDetails.role.split('_')[0];
|
||||
|
||||
const extQuery = new Parse.Query(extClass + "_Users");
|
||||
extQuery.equalTo("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
const extQuery = new Parse.Query(extClass + '_Users');
|
||||
extQuery.equalTo('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user.id,
|
||||
});
|
||||
const extUser = await extQuery.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
return { message: "User already exist" };
|
||||
return { message: 'User already exist' };
|
||||
} else {
|
||||
const roleurl = `${serverUrl}/functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": APPID,
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': APPID,
|
||||
sessionToken: user.sessionToken, //localStorage.getItem("accesstoken"),
|
||||
};
|
||||
let body = {
|
||||
@@ -69,74 +69,98 @@ export default async function (request) {
|
||||
};
|
||||
let role = await axios.post(roleurl, body, { headers: headers });
|
||||
// console.log("role ", role);
|
||||
const partnerCls = Parse.Object.extend("partners_Tenant");
|
||||
const partnerCls = Parse.Object.extend('partners_Tenant');
|
||||
const partnerQuery = new partnerCls();
|
||||
partnerQuery.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
partnerQuery.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user.id,
|
||||
});
|
||||
|
||||
partnerQuery.set("ContactNumber", userDetails.phone);
|
||||
partnerQuery.set("TenantName", userDetails.name);
|
||||
partnerQuery.set("EmailAddress", userDetails.email);
|
||||
partnerQuery.set("IsActive", true);
|
||||
partnerQuery.set("CreatedBy", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
partnerQuery.set('ContactNumber', userDetails.phone);
|
||||
partnerQuery.set('TenantName', userDetails.name);
|
||||
partnerQuery.set('EmailAddress', userDetails.email);
|
||||
partnerQuery.set('IsActive', true);
|
||||
partnerQuery.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user.id,
|
||||
});
|
||||
if (userDetails && userDetails.pincode) {
|
||||
partnerQuery.set("PinCode", userDetails.pincode);
|
||||
partnerQuery.set('PinCode', userDetails.pincode);
|
||||
}
|
||||
if (userDetails && userDetails.country) {
|
||||
partnerQuery.set("Country", userDetails.country);
|
||||
partnerQuery.set('Country', userDetails.country);
|
||||
}
|
||||
if (userDetails && userDetails.state) {
|
||||
partnerQuery.set("State", userDetails.state);
|
||||
partnerQuery.set('State', userDetails.state);
|
||||
}
|
||||
if (userDetails && userDetails.city) {
|
||||
partnerQuery.set("City", userDetails.city);
|
||||
partnerQuery.set('City', userDetails.city);
|
||||
}
|
||||
if (userDetails && userDetails.address) {
|
||||
partnerQuery.set("Address", userDetails.address);
|
||||
partnerQuery.set('Address', userDetails.address);
|
||||
}
|
||||
const tenantRes = await partnerQuery.save(null, { useMasterKey: true });
|
||||
// console.log("tenantRes ", tenantRes);
|
||||
const extCls = Parse.Object.extend(extClass + "_Users");
|
||||
const extCls = Parse.Object.extend(extClass + '_Users');
|
||||
const newObj = new extCls();
|
||||
newObj.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
newObj.set('UserId', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: user.id,
|
||||
});
|
||||
newObj.set("UserRole", userDetails.role);
|
||||
newObj.set("Email", userDetails.email);
|
||||
newObj.set("Name", userDetails.name);
|
||||
newObj.set("Phone", userDetails.phone);
|
||||
newObj.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
newObj.set('UserRole', userDetails.role);
|
||||
newObj.set('Email', userDetails.email);
|
||||
newObj.set('Name', userDetails.name);
|
||||
newObj.set('Phone', userDetails.phone);
|
||||
newObj.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: tenantRes.id,
|
||||
});
|
||||
if (userDetails && userDetails.company) {
|
||||
newObj.set("Company", userDetails.company);
|
||||
newObj.set('Company', userDetails.company);
|
||||
}
|
||||
if (userDetails && userDetails.jobTitle) {
|
||||
newObj.set("JobTitle", userDetails.jobTitle);
|
||||
newObj.set('JobTitle', userDetails.jobTitle);
|
||||
}
|
||||
if (planDetails && planDetails.customer_id) {
|
||||
newObj.set("Next_billing_date", new Date(planDetails.nextBillingDate));
|
||||
newObj.set("Plan", planDetails.plan);
|
||||
newObj.set("Customer_id", planDetails.customer_id);
|
||||
newObj.set("Subscription_id", planDetails.subscription_id);
|
||||
}
|
||||
|
||||
const extRes = await newObj.save(null, { useMasterKey: true });
|
||||
|
||||
return { message: "User sign up", sessionToken: user.sessionToken };
|
||||
await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
|
||||
return { message: 'User sign up', sessionToken: user.sessionToken };
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err ", err);
|
||||
console.log('Err ', err);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSubscription(extUserId, UserId, tenantId, subscription) {
|
||||
const SubscriptionId = subscription?.data?.subscription?.subscription_id || '';
|
||||
const Next_billing_date = subscription?.data?.subscription?.next_billing_at || '';
|
||||
const planCode = subscription?.data?.subscription?.plan?.plan_code || '';
|
||||
try {
|
||||
const createSubscription = new Parse.Object('contracts_Subscriptions');
|
||||
createSubscription.set('SubscriptionId', SubscriptionId);
|
||||
createSubscription.set('SubscriptionDetails', subscription);
|
||||
createSubscription.set('ExtUserPtr', {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Users',
|
||||
objectId: extUserId,
|
||||
});
|
||||
createSubscription.set('CreatedBy', {
|
||||
__type: 'Pointer',
|
||||
className: '_User',
|
||||
objectId: UserId,
|
||||
});
|
||||
createSubscription.set('TenantId', {
|
||||
__type: 'Pointer',
|
||||
className: 'partners_Tenant',
|
||||
objectId: tenantId,
|
||||
});
|
||||
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
|
||||
createSubscription.set('PlanCode', planCode);
|
||||
await createSubscription.save(null, { useMasterKey: true });
|
||||
} catch (err) {
|
||||
console.log('err in save subscription pgsignup', err);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user