Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
375dc86f0a | ||
|
|
d2f187f6c0 | ||
|
|
5393ee9ba9 | ||
|
|
d4d2948455 | ||
|
|
37617c04ad | ||
|
|
10e0153018 | ||
|
|
42cbe94af8 | ||
|
|
8f95e53d9d | ||
|
|
0dae21dcf1 | ||
|
|
912efeb5fd | ||
|
|
3d9307f96d | ||
|
|
873f4b080a | ||
|
|
e337a79e2e | ||
|
|
980ce62bf6 | ||
|
|
dccf757f56 | ||
|
|
4a6b88328e | ||
|
|
779a84cf0b | ||
|
|
aff409ba63 | ||
|
|
275167c0c4 | ||
|
|
19b2af7f57 | ||
|
|
5050b9ee97 | ||
|
|
23da4be6b1 | ||
|
|
299648b8e0 | ||
|
|
86fa0c87ae | ||
|
|
9619eec40f | ||
|
|
886161d65f | ||
|
|
d5d646e6e0 | ||
|
|
494103163c | ||
|
|
72e9e4848b | ||
|
|
1181ba6f90 | ||
|
|
b8d6ec5fe0 | ||
|
|
7335d7df0a | ||
|
|
44754095fb | ||
|
|
0f515bae01 | ||
|
|
76a11f6ee6 | ||
|
|
5fb28feb65 | ||
|
|
e5f1524b10 | ||
|
|
8a875691fb | ||
|
|
0b2710b97d | ||
|
|
57552a32b0 | ||
|
|
572ec3658e | ||
|
|
6d5c791f61 | ||
|
|
168e479c0c | ||
|
|
927e1b7ef9 | ||
|
|
76dccf804e | ||
|
|
4262e0e6eb | ||
|
|
7d487420aa | ||
|
|
c20611b50b | ||
|
|
15d73ee62d | ||
|
|
a334cda9aa | ||
|
|
69d4254b44 | ||
|
|
c422b11e3a | ||
|
|
95d2794754 | ||
|
|
65ce0d430c | ||
|
|
e7a98d55f0 | ||
|
|
d247c6d0e7 | ||
|
|
20e0277113 | ||
|
|
2ed3609c46 | ||
|
|
2e41dc20c1 | ||
|
|
50a8f93014 | ||
|
|
3803b29835 | ||
|
|
e18586887e | ||
|
|
d137c081ee | ||
|
|
62dfe32d32 | ||
|
|
f801192a8c | ||
|
|
13d161e3c8 | ||
|
|
a249baea66 | ||
|
|
f6f5b08d43 | ||
|
|
a1f4e9ea74 | ||
|
|
5eed145f4b | ||
|
|
546a0f8e87 | ||
|
|
975c4b079a | ||
|
|
09e0ac2415 | ||
|
|
6140aea31a | ||
|
|
c59c921a8b | ||
|
|
4b1e13d39a | ||
|
|
e33e0be735 | ||
|
|
2ff4e372d6 | ||
|
|
9cfffe3fbd | ||
|
|
82ef278a0e | ||
|
|
574d2d05a4 | ||
|
|
38f72c7415 | ||
|
|
e00704a43c | ||
|
|
778e28fae0 | ||
|
|
1574f86654 | ||
|
|
df17bdcd68 | ||
|
|
f290fd875f | ||
|
|
caeb055fc2 | ||
|
|
1eafc113e6 | ||
|
|
4e46795e0f | ||
|
|
f7087dfb29 | ||
|
|
e6a7cda3b6 | ||
|
|
db120ecda7 | ||
|
|
9bfe8b0e26 | ||
|
|
c1438be9e0 | ||
|
|
f4d7e33f97 | ||
|
|
4ec7fdb078 | ||
|
|
3413d0914d | ||
|
|
793b6a5550 | ||
|
|
77894f6754 | ||
|
|
812928a6a3 | ||
|
|
bb398d13a2 | ||
|
|
2d7a893424 | ||
|
|
62e943161a | ||
|
|
2604156c7f | ||
|
|
1e04bb4dbe | ||
|
|
0ac3a70a83 | ||
|
|
2f1ad45f92 | ||
|
|
8263e3247e | ||
|
|
9666e41880 | ||
|
|
e50f062a91 | ||
|
|
1c8f401060 | ||
|
|
a8e764a62c | ||
|
|
43bc1f3e17 | ||
|
|
eebed949cd | ||
|
|
c7cd9e56f8 | ||
|
|
1c9fb5ebcb | ||
|
|
1339259c3b | ||
|
|
27a069e1a2 | ||
|
|
7a69537255 | ||
|
|
9569b78e66 | ||
|
|
266b0f27f5 | ||
|
|
00e5c48828 | ||
|
|
c9e010e993 | ||
|
|
5b52825ea6 | ||
|
|
100b97cf1a | ||
|
|
7a3216a81c | ||
|
|
c6412be70d | ||
|
|
954dbb3375 | ||
|
|
c996825665 | ||
|
|
5fc1a58bdb | ||
|
|
5c947181a2 | ||
|
|
2e8b2fa525 | ||
|
|
1e6e5f9dba | ||
|
|
fa2e169df4 | ||
|
|
174023c62f | ||
|
|
1198460c97 | ||
|
|
07954c8bb6 | ||
|
|
d4d9521c31 | ||
|
|
d77477f3d1 | ||
|
|
ff36faa96c | ||
|
|
450de195a7 | ||
|
|
57b23d7f97 | ||
|
|
a246f74e11 | ||
|
|
89c687ed06 | ||
|
|
f7e2fa8aec | ||
|
|
8a2960511c | ||
|
|
832e5c2181 | ||
|
|
00d900c2b2 | ||
|
|
47ef685bcc | ||
|
|
9f4aff65f2 | ||
|
|
2eddb49b8e | ||
|
|
63f1bb9fc7 | ||
|
|
e73b055cc2 | ||
|
|
abd7cf9b71 | ||
|
|
2dbd41ce65 | ||
|
|
25ef7c9f39 | ||
|
|
81a4daa5e8 | ||
|
|
3f9e6d1f81 | ||
|
|
1507f113f4 | ||
|
|
d46ba8f307 | ||
|
|
2426e7484c | ||
|
|
17b9fc772e | ||
|
|
7b2b71fea5 | ||
|
|
70a3adebd7 | ||
|
|
9e012ef6c7 | ||
|
|
948e0d3270 | ||
|
|
6e4f9ba0e9 | ||
|
|
a816ceb04b | ||
|
|
f864f74f14 | ||
|
|
2c715a77a2 | ||
|
|
9b4cdde80a | ||
|
|
de2154e6fe | ||
|
|
76f4ccfcf3 | ||
|
|
af52bb30f5 | ||
|
|
cc398d68e4 | ||
|
|
cd4844863e | ||
|
|
6d4ebf7be5 | ||
|
|
18992e0334 | ||
|
|
0580a18b0d | ||
|
|
5a4114b786 | ||
|
|
4fdf2299f8 | ||
|
|
f15aa39588 | ||
|
|
694312812a | ||
|
|
2606b58434 | ||
|
|
943f9a0673 | ||
|
|
77622ebea9 | ||
|
|
6fb714d565 | ||
|
|
f6170e01f6 | ||
|
|
1e857b615f | ||
|
|
b982ca5218 | ||
|
|
c83817800e | ||
|
|
d1992839a8 | ||
|
|
bfb7136c89 | ||
|
|
52b2cc6af4 | ||
|
|
94d386541d | ||
|
|
d85d76c8aa | ||
|
|
ade97b972f | ||
|
|
532780629b | ||
|
|
1f2298f192 | ||
|
|
ae5d527e4f | ||
|
|
e59a7e0028 | ||
|
|
1d6655eafe | ||
|
|
09b39a467b | ||
|
|
e0e7dd5298 | ||
|
|
56d5091b96 | ||
|
|
0b28e64111 | ||
|
|
482b75c6a7 | ||
|
|
cdff7e252d | ||
|
|
a18aee0be1 | ||
|
|
89f67bfeeb | ||
|
|
e4f0e9cef3 | ||
|
|
12599c1e5a | ||
|
|
6bb3203417 | ||
|
|
0eb2c5ecc4 | ||
|
|
01ab19b235 | ||
|
|
ba6b0ce7e5 | ||
|
|
b99d3f643b | ||
|
|
fea174c48d | ||
|
|
1aa1a602b3 | ||
|
|
37b7e50b4c | ||
|
|
e9fab26ef3 | ||
|
|
3216a1d4ae | ||
|
|
eb0941f087 | ||
|
|
0a315ef9c7 | ||
|
|
134babd070 | ||
|
|
954ba13f22 | ||
|
|
66718fd416 | ||
|
|
f4358c5004 | ||
|
|
63f888b3ec | ||
|
|
e066d1d273 | ||
|
|
daddb10030 | ||
|
|
6d9090d414 | ||
|
|
795e8a8ec9 | ||
|
|
e8b0b333a1 | ||
|
|
93d9a761ca | ||
|
|
3642d4f3a3 | ||
|
|
08387fc406 | ||
|
|
d1d0f40b00 | ||
|
|
ad4a9e7141 | ||
|
|
d46cb7dc10 | ||
|
|
50b9717cb9 | ||
|
|
4e83734c5c | ||
|
|
6eeaea137a | ||
|
|
2c91499232 | ||
|
|
e6085eb2c0 | ||
|
|
6fa4c4759b | ||
|
|
f8d6547c24 | ||
|
|
ffdd952440 | ||
|
|
9594e9438e |
@@ -1,12 +0,0 @@
|
||||
# Clone the repo and rename this file to .env if you want to run the frontend react app locally for some quick contributions to the project.
|
||||
# When used as it is, this env config will connect to our hosted UAT backend
|
||||
# Note that any data in staging will get cleared from time to time and you may have to sign up again and again
|
||||
|
||||
# Set it to the URL form where the app home page will be accessed
|
||||
PUBLIC_URL=http://localhost:3000
|
||||
# Set it to true if you want to generate the Sourcemap for debugging
|
||||
GENERATE_SOURCEMAP=false
|
||||
# Set it to the URL from where APIs will be accessible, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
REACT_APP_SERVERURL=https://staging-app.opensignlabs.com/api/app
|
||||
# A 12 character long random app identifier. The value of this should be same as APP_ID which is a variable used by backend API.
|
||||
REACT_APP_APPID=opensign
|
||||
@@ -20,7 +20,7 @@ appName=open_sign_server
|
||||
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
|
||||
MASTER_KEY=XnAadwKxxByMr
|
||||
# Mongodb URI to connect to
|
||||
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
|
||||
MONGODB_URI=mongodb://mongo-container:27017/OpenSignDB
|
||||
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
|
||||
PARSE_MOUNT=/app
|
||||
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
|
||||
|
||||
@@ -2,7 +2,7 @@ name: ci
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'staging'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
@@ -10,9 +10,9 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- image: amolshejole/OpenSign
|
||||
- image: opensign/opensign
|
||||
dockerfile: apps/OpenSign/Dockerhubfile
|
||||
- image: amolshejole/OpenSignServer
|
||||
- image: opensign/opensignserver
|
||||
dockerfile: apps/OpenSignServer/Dockerhubfile
|
||||
steps:
|
||||
-
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
<a href="https://www.opensignlabs.com/">Website</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.opensignlabs.com">Docs</a>
|
||||
<a href="https://docs.opensignlabs.com">Help Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1">API Docs</a>
|
||||
<span> • </span>
|
||||
<a href="https://www.opensignlabs.com/blog">Blog</a>
|
||||
<span> • </span>
|
||||
@@ -22,7 +24,7 @@
|
||||
<a href="https://www.linkedin.com/company/opensign%E2%84%A2/about/">LinkedIn</a>
|
||||
|
||||
|
||||
## The open-source document e-signing solution
|
||||
## The premier open source document signing solution(DocuSign alternative)
|
||||
|
||||
---
|
||||
</div>
|
||||
@@ -42,29 +44,32 @@ 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 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.
|
||||
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 document 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 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.
|
||||
- **Secure PDF E-Signing:** With the help of robust encryption algorithms, OpenSign™ ensures maximum security, privacy & compatibility. Now sign unlimited documents even on the [cloud hosted free version of OpenSign](https://www.opensignlabs.com/).
|
||||
- **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 open source document 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. OpenSign intends to provide the best document signing experience in the open source ecosystem.
|
||||
- **Multi-signer Support:** OpenSign's ability to invite multiple signers for signing along with the ability to invite by sharing signing links & 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. 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.
|
||||
- **"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 with a reason that will be promptly shared with the sender.
|
||||
- **Beautiful email templates:** All document signing invitations, completion notifications & reminders are formatted using great looking email templates. Not just this, you are even allowed to customise the email templates making your free document signing invitations look the way you always wanted them to be.
|
||||
- **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.
|
||||
- **OpenSign™ Drive:** It is a centralised secure vault for your digital documents that makes storing, signing, organizing, sharing & archieving 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:** 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'>
|
||||
<img src=https://github.com/OpenSignLabs/OpenSign/assets/5486116/56835efd-c40e-42de-a206-20202c293876 height='200'>
|
||||
|
||||
- **API Support:** OpenSign™ API allows seamless integration into existing systems and software. You can generate an API key from the app and refer the [official API docs](https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1) to start integrating it in your existing applications.
|
||||
- **Integrations:** The open source document signing experience becomes even more seamless because of integrations with various Cloud storage systems, CRMs & enterprise platforms. We also have a Zapier integration that allows you to integrate it with virtually any application.
|
||||
<img alt="Login page" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/8016edb6-7d6e-4185-ab72-82d2c4a16032" height='200' >
|
||||
<img alt="Dash_board" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/6e7b96f7-3154-4c25-bc1e-caf33925cc47" height='200'>
|
||||
<img alt="Widgets" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/7edb65bd-c265-4b3d-9758-38e79766c5c2" height='200'>
|
||||
<img alt="Request Signature" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/1697ae46-dcac-41c9-a081-aded25693846" height='200'>
|
||||
<img alt="Create template" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/c2060fda-50f1-4116-a532-51b94b48463a" height='200'>
|
||||
<img alt="Manage_Template" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/90310c74-8271-49bd-8c5f-89daa5a623c4" height='200'>
|
||||
<img alt="Folders" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/a52207e7-3b0e-497c-b5f6-747bc4e67918" height='200'>
|
||||
<img alt="OpenSign_Confetti" src="https://github.com/OpenSignLabs/OpenSign/assets/171803164/8f4c6a6e-4a73-439c-9f34-f426044d0a7f" height='200'>
|
||||
|
||||
---
|
||||
|
||||
@@ -88,13 +93,13 @@ We welcome contributions from the open-source community. For more information on
|
||||
|
||||
### License
|
||||
|
||||
OpenSign is licensed under the AGPL License. For more details, see the [LICENSE](LICENSE) file.
|
||||
OpenSign is licensed under the AGPL-3 License. For more details, see the [LICENSE](LICENSE) file.
|
||||
|
||||
---
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
We would like to thank all our contributors and users for their support and feedback. Special thanks to [OpenSignLabs](https://github.com/OpenSignLabs) for spearheading this initiative.
|
||||
We would like to thank all our contributors and users for their support and feedback. Special thanks to [OpenSignLabs](https://www.opensignlabs.com) for spearheading this initiative.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
The community is working hard to put together a great usage guide.
|
||||
You will soon see one of the best usage guides here.
|
||||
|
||||
Till then you can view the quick promo video by clicking the link below -
|
||||
|
||||
[<img src="https://i.ytimg.com/vi/szYaBZhXZNo/maxresdefault.jpg" width="50%">](https://www.youtube.com/watch?v=szYaBZhXZNo "OpenSign promo")
|
||||
Please visit the official documentation [here](https://docs.opensignlabs.com).
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: ["@babel/preset-env", "@babel/preset-react"]
|
||||
};
|
||||
@@ -4,23 +4,24 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^0.8.2",
|
||||
"@radix-ui/themes": "^3.0.5",
|
||||
"@lottiefiles/dotlottie-react": "^0.8.5",
|
||||
"@radix-ui/themes": "^3.1.1",
|
||||
"@react-pdf/renderer": "^3.4.4",
|
||||
"@reduxjs/toolkit": "^2.2.5",
|
||||
"@reduxjs/toolkit": "^2.2.7",
|
||||
"axios": "^1.7.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"nth-check": "^2.1.1",
|
||||
"parse": "^5.1.0",
|
||||
"parse": "^5.3.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"print-js": "^1.6.0",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.2",
|
||||
"react-bootstrap": "^2.10.4",
|
||||
"react-confetti": "^6.1.0",
|
||||
"react-cookie": "^7.1.4",
|
||||
"react-datepicker": "^6.9.0",
|
||||
"react-cookie": "^7.2.0",
|
||||
"react-datepicker": "^7.3.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dnd-multi-backend": "^8.0.3",
|
||||
@@ -29,32 +30,35 @@
|
||||
"react-gtm-module": "^2.0.11",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-konva": "^18.2.10",
|
||||
"react-pdf": "^9.0.0",
|
||||
"react-pdf": "^9.1.0",
|
||||
"react-quill": "^2.0.0",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-rnd": "^10.4.11",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"react-router-dom": "^6.25.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-scrollbars-custom": "^4.1.1",
|
||||
"react-select": "^5.8.0",
|
||||
"react-signature-canvas": "^1.0.6",
|
||||
"react-tooltip": "^5.26.4",
|
||||
"react-tooltip": "^5.27.1",
|
||||
"react-web-share": "^2.0.2",
|
||||
"reactour": "^1.19.4",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"regex-parser": "^2.3.0",
|
||||
"serve": "^14.2.3",
|
||||
"styled-components": "^4.4.1",
|
||||
"web-vitals": "^4.0.1"
|
||||
"styled-components": "^5.3.0",
|
||||
"web-vitals": "^4.2.2",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build-webpack": "webpack --config webpack.config.js",
|
||||
"build": "npm run version && npm run build-webpack && react-scripts build",
|
||||
"build-watch": "webpack --config webpack.config.js --watch",
|
||||
"start-dev": "concurrently \"npm run build-watch\" \"react-scripts start\"",
|
||||
"start": "serve -s build",
|
||||
"start-dev": "react-scripts start",
|
||||
"version": "curl -s https://api.github.com/repos/opensignlabs/opensign/releases/latest | grep '\"tag_name\":' | awk -F '\"' '{print $4}' > ./public/version.txt",
|
||||
"version-win": "powershell -Command \"Invoke-RestMethod -Uri 'https://api.github.com/repos/opensignlabs/opensign/releases/latest' | Select-Object -ExpandProperty tag_name | Out-File -FilePath ./public/version.txt\"",
|
||||
"build": "npm run version && react-scripts build",
|
||||
"build-win": "npm run version-win && react-scripts build",
|
||||
"build-win": "npm run version-win && npm run build-webpack && react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"release": "standard-version",
|
||||
@@ -84,20 +88,33 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime-corejs2": "^7.24.7",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/preset-react": "^7.24.7",
|
||||
"@babel/runtime-corejs2": "^7.25.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"babel-loader": "^9.1.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"daisyui": "^4.12.2",
|
||||
"concurrently": "^8.2.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"daisyui": "^4.12.10",
|
||||
"dotenv": "^16.4.5",
|
||||
"dotenv-webpack": "^8.1.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"lint-staged": "^15.2.5",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^2.8.8",
|
||||
"pretty-quick": "^3.3.1",
|
||||
"tailwindcss": "^3.4.4"
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"lint-staged": "^15.2.7",
|
||||
"mini-css-extract-plugin": "^2.9.0",
|
||||
"postcss": "^8.4.40",
|
||||
"prettier": "^3.3.3",
|
||||
"pretty-quick": "^4.0.0",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"overrides": {
|
||||
"nth-check": "$nth-check"
|
||||
"nth-check": "$nth-check",
|
||||
"ws": "$ws"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="opensigncss">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<!-- <link rel="icon" type="image/png"
|
||||
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC"
|
||||
sizes="40x40" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="The fastest way to sign PDFs & request signatures from others" />
|
||||
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> -->
|
||||
<!--
|
||||
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="/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.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
|
||||
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
|
||||
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.opensignlabs.com/fonts.css" />
|
||||
<title>OpenSign™</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 86 KiB |
@@ -18,6 +18,8 @@ import LazyPage from "./primitives/LazyPage";
|
||||
import { isEnableSubscription } from "./constant/const";
|
||||
import SSOVerify from "./pages/SSOVerify";
|
||||
import Loader from "./primitives/Loader";
|
||||
import TeamList from "./pages/TeamList";
|
||||
import UserList from "./pages/UserList";
|
||||
const DebugPdf = lazy(() => import("./pages/DebugPdf"));
|
||||
const ForgetPassword = lazy(() => import("./pages/ForgetPassword"));
|
||||
const GuestLogin = lazy(() => import("./pages/GuestLogin"));
|
||||
@@ -30,11 +32,10 @@ const Opensigndrive = lazy(() => import("./pages/Opensigndrive"));
|
||||
const ManageSign = lazy(() => import("./pages/Managesign"));
|
||||
const GenerateToken = lazy(() => import("./pages/GenerateToken"));
|
||||
const Webhook = lazy(() => import("./pages/Webhook"));
|
||||
const AddAdmin = lazy(() => import("./pages/AddAdmin"));
|
||||
const UpdateExistUserAdmin = lazy(() => import("./pages/UpdateExistUserAdmin"));
|
||||
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
|
||||
"pdfjs-dist/legacy/build/pdf.worker.min.mjs",
|
||||
import.meta.url
|
||||
).toString();
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.mjs`;
|
||||
const AppLoader = () => {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-[100vh]">
|
||||
@@ -55,11 +56,9 @@ function App() {
|
||||
const baseurl = process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
: window.location.origin + "/api/app";
|
||||
const appName = "contracts";
|
||||
try {
|
||||
localStorage.setItem("baseUrl", `${baseurl}/`);
|
||||
localStorage.setItem("parseAppId", appId);
|
||||
localStorage.setItem("domain", appName);
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
@@ -75,7 +74,21 @@ function App() {
|
||||
<Routes>
|
||||
<Route element={<ValidateRoute />}>
|
||||
<Route exact path="/" element={<Login />} />
|
||||
<Route path="/signup" element={<LazyPage Page={Signup} />} />
|
||||
{isEnableSubscription && (
|
||||
<Route path="/signup" element={<LazyPage Page={Signup} />} />
|
||||
)}
|
||||
{!isEnableSubscription && (
|
||||
<>
|
||||
<Route
|
||||
path="/addadmin"
|
||||
element={<LazyPage Page={AddAdmin} />}
|
||||
/>
|
||||
<Route
|
||||
path="/upgrade-2.1"
|
||||
element={<LazyPage Page={UpdateExistUserAdmin} />}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Route>
|
||||
<Route element={<Validate />}>
|
||||
<Route
|
||||
@@ -142,11 +155,6 @@ function App() {
|
||||
path="/managesign"
|
||||
element={<LazyPage Page={ManageSign} />}
|
||||
/>
|
||||
<Route
|
||||
path="/generatetoken"
|
||||
element={<LazyPage Page={GenerateToken} />}
|
||||
/>
|
||||
<Route path="/webhook" element={<LazyPage Page={Webhook} />} />
|
||||
<Route
|
||||
path="/template/:templateId"
|
||||
element={<TemplatePlaceholder />}
|
||||
@@ -167,11 +175,30 @@ function App() {
|
||||
path="/pdfRequestFiles/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
|
||||
{/* recipient signature route with no rowlevel data using docId from url */}
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId/:contactBookId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
<Route
|
||||
path="/recipientSignPdf/:docId"
|
||||
element={<PdfRequestFiles />}
|
||||
/>
|
||||
{isEnableSubscription && (
|
||||
<>
|
||||
<Route path="/teams" element={<TeamList />} />
|
||||
<Route
|
||||
path="/generatetoken"
|
||||
element={<LazyPage Page={GenerateToken} />}
|
||||
/>
|
||||
<Route
|
||||
path="/webhook"
|
||||
element={<LazyPage Page={Webhook} />}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Route path="/users" element={<UserList />} />
|
||||
</Route>
|
||||
<Route path="/sso" element={<SSOVerify />} />
|
||||
<Route path="*" element={<PageNotFound />} />
|
||||
|
||||
|
Before Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 23 KiB |
@@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
const AddSigner = (props) => {
|
||||
@@ -10,8 +9,6 @@ const AddSigner = (props) => {
|
||||
const [addYourself, setAddYourself] = useState(false);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
@@ -85,18 +82,6 @@ const AddSigner = (props) => {
|
||||
}
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_Guest",
|
||||
userId: user.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
@@ -140,18 +125,6 @@ const AddSigner = (props) => {
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_Guest",
|
||||
userId: userRes.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
"CreatedBy",
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Title from "./Title";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
const AddTeam = (props) => {
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
team: ""
|
||||
});
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [teamList, setTeamList] = useState([]);
|
||||
useEffect(() => {
|
||||
getTeamList();
|
||||
}, []);
|
||||
|
||||
const getTeamList = async () => {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
const teams = await Parse.Cloud.run("getteams", { active: true });
|
||||
const teamRes = JSON.parse(JSON.stringify(teams));
|
||||
if (teamRes.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
||||
const allUsersteam = _teamRes.find((x) => x.Name === "All Users");
|
||||
if (allUsersteam) {
|
||||
setFormdata({ team: allUsersteam.objectId });
|
||||
}
|
||||
setTeamList(_teamRes);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in fetch top level teamList", err);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDropdown = (e) => {
|
||||
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
};
|
||||
// Define a function to handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// Extracting values except for the 'name' key
|
||||
let updatedAncestors = [];
|
||||
if (formdata.team) {
|
||||
const Ancestors = teamList.find(
|
||||
(x) => x.objectId === formdata.team
|
||||
)?.Ancestors;
|
||||
if (Ancestors && Ancestors.length > 0) {
|
||||
updatedAncestors = Ancestors.map((x) => ({
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: x.objectId
|
||||
}));
|
||||
} else {
|
||||
const AllUser = teamList.find((x) => x.objectId === "All Users");
|
||||
updatedAncestors = [
|
||||
AllUser,
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
try {
|
||||
setIsLoader(true);
|
||||
let data = { Name: formdata.name };
|
||||
if (updatedAncestors.length > 0) {
|
||||
const ParentId = updatedAncestors[updatedAncestors.length - 1];
|
||||
data["ParentId"] = ParentId?.objectId;
|
||||
data["Ancestors"] = updatedAncestors;
|
||||
}
|
||||
const newTeamRes = await Parse.Cloud.run("addteam", data);
|
||||
// console.log("teamRes ", newTeamRes);
|
||||
if (updatedAncestors.length > 0) {
|
||||
const ParentId = teamList.find((x) => x.objectId === formdata.team);
|
||||
props.handleTeamInfo({
|
||||
objectId: newTeamRes.id,
|
||||
Name: formdata.name,
|
||||
ParentId: ParentId,
|
||||
Ancestors: updatedAncestors,
|
||||
IsActive: true
|
||||
});
|
||||
} else {
|
||||
props.handleTeamInfo({
|
||||
objectId: newTeamRes.id,
|
||||
Name: formdata.name,
|
||||
ParentId: "",
|
||||
Ancestors: "",
|
||||
IsActive: true
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
setFormdata({ name: "", team: { name: "", objectId: "" } });
|
||||
props.setIsAlert({ type: "success", msg: "Team created successfully." });
|
||||
} catch (err) {
|
||||
console.log("err in save team", err);
|
||||
if (err.code === 137) {
|
||||
props.setIsAlert({ type: "danger", msg: "Teams already exists." });
|
||||
} else if (err.code === 102) {
|
||||
props.setIsAlert({ type: "warning", msg: "Provide team name." });
|
||||
} else {
|
||||
props.setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
}
|
||||
} finally {
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
setFormdata({
|
||||
name: "",
|
||||
team: { name: "", objectId: "" }
|
||||
});
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
|
||||
<Title title="Add Team" />
|
||||
{isLoader && (
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Name
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Parent Team
|
||||
</label>
|
||||
<select
|
||||
value={formdata.team}
|
||||
onChange={(e) => handleDropdown(e)}
|
||||
name="team"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
>
|
||||
{teamList.length > 0 &&
|
||||
teamList.map((x) => (
|
||||
<option key={x.objectId} value={x.objectId}>
|
||||
{x.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
Submit
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
Reset
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddTeam;
|
||||
@@ -1,24 +1,55 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import Title from "./Title";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { copytoData } from "../constant/Utils";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
function generatePassword(length) {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
const charactersLength = characters.length;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const AddUser = (props) => {
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [formdata, setFormdata] = useState({
|
||||
name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
team: "",
|
||||
password: "",
|
||||
role: ""
|
||||
});
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isUserExist, setIsUserExist] = useState(false);
|
||||
const parseBaseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
const [teamList, setTeamList] = useState([]);
|
||||
const role = ["OrgAdmin", "Editor", "User"];
|
||||
|
||||
useEffect(() => {
|
||||
getTeamList();
|
||||
}, []);
|
||||
|
||||
const getTeamList = async () => {
|
||||
setFormdata((prev) => ({ ...prev, password: generatePassword(12) }));
|
||||
const teamRes = await Parse.Cloud.run("getteams", { active: true });
|
||||
if (teamRes.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teamRes));
|
||||
setTeamList(_teamRes);
|
||||
if (!isEnableSubscription) {
|
||||
const allUserId =
|
||||
_teamRes.find((x) => x.Name === "All Users")?.objectId || "";
|
||||
setFormdata((prev) => ({ ...prev, team: allUserId }));
|
||||
}
|
||||
}
|
||||
};
|
||||
const checkUserExist = async () => {
|
||||
try {
|
||||
const res = await Parse.Cloud.run("getUserDetails", {
|
||||
email: email,
|
||||
userId: Parse.User.current().id
|
||||
email: formdata.email
|
||||
});
|
||||
if (res) {
|
||||
return true;
|
||||
@@ -33,24 +64,46 @@ const AddUser = (props) => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const localUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
setIsLoader(true);
|
||||
const res = await checkUserExist();
|
||||
if (res) {
|
||||
setIsUserExist(true);
|
||||
props.setIsAlert({ type: "danger", msg: "User already exist." });
|
||||
setIsLoader(false);
|
||||
setTimeout(() => {
|
||||
setIsUserExist(false);
|
||||
props.setIsAlert({ type: "success", msg: "" });
|
||||
}, 1000);
|
||||
} else {
|
||||
try {
|
||||
const contactQuery = new Parse.Object("contracts_Users");
|
||||
contactQuery.set("Name", name);
|
||||
contactQuery.set("Phone", phone);
|
||||
contactQuery.set("Email", email);
|
||||
contactQuery.set("UserRole", "contracts_User");
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.set("Name", formdata.name);
|
||||
if (formdata.phone) {
|
||||
extUser.set("Phone", formdata.phone);
|
||||
}
|
||||
extUser.set("Email", formdata.email);
|
||||
extUser.set("UserRole", `contracts_${formdata.role}`);
|
||||
if (formdata?.team) {
|
||||
extUser.set("TeamIds", [
|
||||
{
|
||||
__type: "Pointer",
|
||||
className: "contracts_Teams",
|
||||
objectId: formdata.team
|
||||
}
|
||||
]);
|
||||
}
|
||||
if (localUser && localUser.OrganizationId) {
|
||||
extUser.set("OrganizationId", {
|
||||
__type: "Pointer",
|
||||
className: "contracts_Organizations",
|
||||
objectId: localUser.OrganizationId.objectId
|
||||
});
|
||||
}
|
||||
if (localUser && localUser.Company) {
|
||||
extUser.set("Company", localUser.Company);
|
||||
}
|
||||
|
||||
if (localStorage.getItem("TenantId")) {
|
||||
contactQuery.set("TenantId", {
|
||||
extUser.set("TenantId", {
|
||||
__type: "Pointer",
|
||||
className: "partners_Tenant",
|
||||
objectId: localStorage.getItem("TenantId")
|
||||
@@ -60,89 +113,69 @@ const AddUser = (props) => {
|
||||
try {
|
||||
const _users = Parse.Object.extend("User");
|
||||
const _user = new _users();
|
||||
_user.set("name", name);
|
||||
_user.set("username", email);
|
||||
_user.set("email", email);
|
||||
_user.set("password", email);
|
||||
if (phone) {
|
||||
_user.set("phone", phone);
|
||||
_user.set("name", formdata.name);
|
||||
_user.set("username", formdata.email);
|
||||
_user.set("email", formdata.email);
|
||||
_user.set("password", formdata.password);
|
||||
if (formdata.phone) {
|
||||
_user.set("phone", formdata.phone);
|
||||
}
|
||||
|
||||
const user = await _user.save();
|
||||
if (user) {
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_User",
|
||||
userId: user.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", user);
|
||||
extUser.set("UserId", user);
|
||||
const acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
acl.setPublicWriteAccess(true);
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
extUser.setACL(acl);
|
||||
|
||||
const res = await contactQuery.save();
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
if (err.code === 202) {
|
||||
const params = { email: email };
|
||||
const params = { email: formdata.email };
|
||||
const userRes = await Parse.Cloud.run("getUserId", params);
|
||||
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const body = {
|
||||
appName: localStorage.getItem("_appName"),
|
||||
roleName: "contracts_User",
|
||||
userId: userRes.id
|
||||
};
|
||||
await axios.post(roleurl, body, { headers: headers });
|
||||
const currentUser = Parse.User.current();
|
||||
contactQuery.set(
|
||||
extUser.set(
|
||||
"CreatedBy",
|
||||
Parse.User.createWithoutData(currentUser.id)
|
||||
);
|
||||
|
||||
contactQuery.set("UserId", {
|
||||
extUser.set("UserId", {
|
||||
__type: "Pointer",
|
||||
className: "_User",
|
||||
objectId: userRes.id
|
||||
@@ -153,55 +186,75 @@ const AddUser = (props) => {
|
||||
acl.setReadAccess(currentUser.id, true);
|
||||
acl.setWriteAccess(currentUser.id, true);
|
||||
|
||||
contactQuery.setACL(acl);
|
||||
const res = await contactQuery.save();
|
||||
extUser.setACL(acl);
|
||||
const res = await extUser.save();
|
||||
|
||||
const parseData = JSON.parse(JSON.stringify(res));
|
||||
if (props.details) {
|
||||
props.details({
|
||||
value: parseData[props.valueKey],
|
||||
label: parseData[props.displayKey]
|
||||
});
|
||||
}
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
if (props.handleUserData) {
|
||||
if (formdata?.team) {
|
||||
const team = teamList.find((x) => x.objectId === formdata.team);
|
||||
parseData.TeamIds = parseData.TeamIds.map((y) =>
|
||||
y.objectId === team.objectId ? team : y
|
||||
);
|
||||
}
|
||||
props.handleUserData(parseData);
|
||||
}
|
||||
setIsLoader(false);
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log("err", err);
|
||||
console.log("err", err);
|
||||
setIsLoader(false);
|
||||
alert("something went wrong!");
|
||||
props.setIsAlert({ type: "danger", msg: "something went wrong." });
|
||||
} finally {
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define a function to handle the "add yourself" checkbox
|
||||
const handleReset = () => {
|
||||
setName("");
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
setFormdata({
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
team: "",
|
||||
role: ""
|
||||
});
|
||||
if (props.closePopup) {
|
||||
props.closePopup();
|
||||
}
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
setFormdata((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
props.setIsAlert({ type: "success", msg: "Copied" });
|
||||
setTimeout(() => props.setIsAlert({ type: "success", msg: "" }), 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
|
||||
<div className="shadow-md rounded-box my-[1px] p-3 bg-[#ffffff]">
|
||||
<Title title={"Add User"} />
|
||||
{isUserExist && <Alert type="danger">User already exists!</Alert>}
|
||||
{isLoader && (
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
|
||||
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full mx-auto">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<h1 className="text-[20px] font-semibold mb-4">Add User</h1>
|
||||
{/* <h1 className="text-[20px] font-semibold mb-4">Add User</h1> */}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="name"
|
||||
@@ -212,9 +265,9 @@ const AddUser = (props) => {
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
name="name"
|
||||
value={formdata.name}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
@@ -229,44 +282,111 @@ const AddUser = (props) => {
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
name="email"
|
||||
value={formdata.email}
|
||||
onChange={(e) => handleChange(e)}
|
||||
required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm text-base-content w-full h-full text-[13px]">
|
||||
<div className="break-all">{formdata?.password}</div>
|
||||
<i
|
||||
onClick={() => copytoclipboard(formdata?.password)}
|
||||
className="fa-light fa-copy rounded-full hover:bg-base-300 p-[8px] cursor-pointer "
|
||||
></i>
|
||||
</div>
|
||||
<div className="text-[12px] ml-2 mb-0 text-[red] select-none">
|
||||
Password will only be generated once; make sure to copy it.
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Phone
|
||||
<span className="text-[red] text-[13px]"> *</span>
|
||||
{/* <span className="text-[red] text-[13px]"> *</span> */}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
name="phone"
|
||||
placeholder="optional"
|
||||
value={formdata.phone}
|
||||
onChange={(e) => handleChange(e)}
|
||||
// required
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
|
||||
{isEnableSubscription && (
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Team<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.team}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="team"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
select
|
||||
</option>
|
||||
{teamList.length > 0 &&
|
||||
teamList.map((x) => (
|
||||
<option key={x.objectId} value={x.objectId}>
|
||||
{x.Name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)}
|
||||
<div className="mb-3">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
>
|
||||
Role<span className="text-[red] text-[13px]"> *</span>
|
||||
</label>
|
||||
<select
|
||||
value={formdata.role}
|
||||
onChange={(e) => handleChange(e)}
|
||||
name="role"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
required
|
||||
>
|
||||
<option defaultValue={""} value={""}>
|
||||
select
|
||||
</option>
|
||||
{role.length > 0 &&
|
||||
role.map((x) => (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
Submit
|
||||
</button>
|
||||
<div
|
||||
type="button"
|
||||
onClick={() => handleReset()}
|
||||
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
Reset
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -113,9 +113,15 @@ const BulkSendUi = (props) => {
|
||||
let Placeholders = [...props.item.Placeholders];
|
||||
// Initialize an empty array to store updated documents
|
||||
let Documents = [];
|
||||
|
||||
// Loop through each form
|
||||
forms.forEach((form) => {
|
||||
//checking if user enter email which already exist as a signer then add user in a signers array
|
||||
let existSigner = [];
|
||||
form.fields.map((data) => {
|
||||
if (data.signer) {
|
||||
existSigner.push(data.signer);
|
||||
}
|
||||
});
|
||||
// Map through the copied Placeholders array to update email values
|
||||
const updatedPlaceholders = Placeholders.map((placeholder) => {
|
||||
// Find the field in the current form that matches the placeholder Id
|
||||
@@ -123,23 +129,44 @@ const BulkSendUi = (props) => {
|
||||
(element) => parseInt(element.fieldId) === placeholder.Id
|
||||
);
|
||||
// If a matching field is found, update the email value in the placeholder
|
||||
const signer = field?.signer?.objectId ? field.signer : {};
|
||||
const signer = field?.signer?.objectId ? field.signer : "";
|
||||
if (field) {
|
||||
return {
|
||||
...placeholder,
|
||||
email: field.email,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
if (signer) {
|
||||
return {
|
||||
...placeholder,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...placeholder,
|
||||
email: field.email,
|
||||
signerObjId: field?.signer?.objectId || "",
|
||||
signerPtr: signer
|
||||
};
|
||||
}
|
||||
}
|
||||
// If no matching field is found, keep the placeholder as is
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
// Push a new document object with updated Placeholders into the Documents array
|
||||
Documents.push({ ...props.item, Placeholders: updatedPlaceholders });
|
||||
if (existSigner?.length > 0) {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders,
|
||||
Signers: props.item.Signers
|
||||
? [...props.item.Signers, ...existSigner]
|
||||
: [...existSigner]
|
||||
});
|
||||
} else {
|
||||
Documents.push({
|
||||
...props.item,
|
||||
Placeholders: updatedPlaceholders
|
||||
});
|
||||
}
|
||||
});
|
||||
// console.log("Documents ", Documents);
|
||||
//console.log("Documents ", Documents);
|
||||
await batchQuery(Documents);
|
||||
};
|
||||
|
||||
@@ -205,7 +232,7 @@ const BulkSendUi = (props) => {
|
||||
onClick={() => handleRemoveForm(index)}
|
||||
className="absolute right-3 top-1 text-[red] border-[1px] border-[red] rounded-lg w-[1.7rem] h-[1.7rem]"
|
||||
>
|
||||
<i className="fa-solid fa-trash"></i>
|
||||
<i className="fa-light fa-trash"></i>
|
||||
</button>
|
||||
)}
|
||||
<div ref={formRef}></div>
|
||||
@@ -217,13 +244,13 @@ const BulkSendUi = (props) => {
|
||||
onClick={handleAddForm}
|
||||
className="op-btn op-btn-primary focus:outline-none"
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i> <span>Add new</span>
|
||||
<i className="fa-light fa-plus"></i> <span>Add new</span>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-secondary focus:outline-none"
|
||||
>
|
||||
<i className="fa-solid fa-paper-plane"></i>{" "}
|
||||
<i className="fa-light fa-paper-plane"></i>{" "}
|
||||
<span>Send</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -58,10 +58,10 @@ const Footer = () => {
|
||||
<button
|
||||
className={`${
|
||||
showButton ? "block" : "hidden"
|
||||
} fixed bottom-4 right-4 px-3 p-2 text-xl bg-blue-500 text-white rounded focus:outline-none`}
|
||||
} fixed bottom-4 right-4 px-3 p-2 text-xl op-bg-secondary text-white rounded focus:outline-none`}
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
<i className="fa-solid fa-angle-up"></i>
|
||||
<i className="fa-light fa-angle-up"></i>
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -70,9 +70,9 @@ const FullScreenButton = () => {
|
||||
className="text-base-content p-2 text-sm focus:outline-none"
|
||||
>
|
||||
{isFullScreen ? (
|
||||
<i className="fa-solid fa-compress"></i>
|
||||
<i className="fa-light fa-compress"></i>
|
||||
) : (
|
||||
<i className="fa fa-maximize"></i>
|
||||
<i className="fa-light fa-maximize"></i>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -4,8 +4,13 @@ import FullScreenButton from "./FullScreenButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import { checkIsSubscribed, getAppLogo, openInNewTab } from "../constant/Utils";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
checkIsSubscribedTeam,
|
||||
getAppLogo,
|
||||
openInNewTab
|
||||
} from "../constant/Utils";
|
||||
import { isEnableSubscription, isStaging } from "../constant/const";
|
||||
|
||||
const Header = ({ showSidebar }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -15,6 +20,7 @@ const Header = ({ showSidebar }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(true);
|
||||
const [isPro, setIsPro] = useState(false);
|
||||
const [isTeam, setIsTeam] = useState(false);
|
||||
const [applogo, setAppLogo] = useState(
|
||||
localStorage.getItem("appLogo") || " "
|
||||
);
|
||||
@@ -29,15 +35,17 @@ const Header = ({ showSidebar }) => {
|
||||
async function checkSubscription() {
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
const getIsTeam = await checkIsSubscribedTeam();
|
||||
if (getIsSubscribe) {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo) {
|
||||
setAppLogo(applogo);
|
||||
if (applogo?.logo) {
|
||||
setAppLogo(applogo?.logo);
|
||||
} else {
|
||||
setAppLogo(localStorage.getItem("appLogo") || "");
|
||||
}
|
||||
}
|
||||
setIsPro(getIsSubscribe);
|
||||
setIsTeam(getIsTeam);
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
}
|
||||
@@ -53,22 +61,16 @@ const Header = ({ showSidebar }) => {
|
||||
}
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
@@ -91,15 +93,21 @@ const Header = ({ showSidebar }) => {
|
||||
document.removeEventListener("click", closeMenuOnOutsideClick);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
const handleConsoleRedirect = () => {
|
||||
if (isStaging) {
|
||||
window.open(" https://staging-console.opensignlabs.com/");
|
||||
} else {
|
||||
window.open("https://console.opensignlabs.com/");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="op-navbar bg-base-100 shadow">
|
||||
<div className="flex-none">
|
||||
<button
|
||||
className="op-btn op-btn-square op-btn-ghost focus:outline-none"
|
||||
className="op-btn op-btn-square op-btn-ghost focus:outline-none hover:bg-transparent op-btn-sm no-animation"
|
||||
onClick={showSidebar}
|
||||
>
|
||||
<i className="fa-solid fa-bars text-xl text-base-content"></i>
|
||||
<i className="fa-light fa-bars text-xl text-base-content"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex-1 ml-2">
|
||||
@@ -115,18 +123,23 @@ const Header = ({ showSidebar }) => {
|
||||
{!isSubscribe && (
|
||||
<div>
|
||||
<button
|
||||
className="text-xs bg-[#002864] p-2 text-white rounded shadow"
|
||||
className="text-xs md:text-sm shadow op-btn op-btn-outline op-btn-sm md:op-btn-md op-btn-accent"
|
||||
onClick={() => navigate("/subscription")}
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{isPro && (
|
||||
{!isTeam && isPro && (
|
||||
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||
PRO
|
||||
</div>
|
||||
)}
|
||||
{isTeam && (
|
||||
<div className="w-[35px] h-[35px] bg-white rounded-full ring-[1px] text-[13px] ring-offset-2 ring-[#002862] text-[#002862] overflow-hidden font-semibold flex items-center justify-center">
|
||||
TEAM
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<FullScreenButton />
|
||||
</div>
|
||||
@@ -151,12 +164,16 @@ const Header = ({ showSidebar }) => {
|
||||
</div>
|
||||
)}
|
||||
<div className="op-dropdown op-dropdown-end" id="profile-menu">
|
||||
<div tabIndex={0} role="button" className="op-btn op-btn-ghost">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="op-btn op-btn-ghost op-btn-xs w-[10px] h-[20px] hover:bg-transparent"
|
||||
>
|
||||
<i
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
onClick={toggleDropdown}
|
||||
className="fa-solid fa-angle-down text-base-content"
|
||||
className="fa-light fa-angle-down text-base-content"
|
||||
></i>
|
||||
</div>
|
||||
<ul
|
||||
@@ -165,7 +182,7 @@ const Header = ({ showSidebar }) => {
|
||||
>
|
||||
<li onClick={() => openInNewTab("https://docs.opensignlabs.com")}>
|
||||
<span>
|
||||
<i className="fa-solid fa-book"></i> Docs
|
||||
<i className="fa-light fa-book"></i> Docs
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
@@ -175,7 +192,7 @@ const Header = ({ showSidebar }) => {
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-regular fa-user"></i> Profile
|
||||
<i className="fa-light fa-user"></i> Profile
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
@@ -185,21 +202,21 @@ const Header = ({ showSidebar }) => {
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-solid fa-lock"></i> Change Password
|
||||
<i className="fa-light fa-lock"></i> Change Password
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
onClick={() => {
|
||||
window.open("https://console.opensignlabs.com/");
|
||||
handleConsoleRedirect();
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<i className="fa-regular fa-id-card"></i> Console
|
||||
<i className="fa-light fa-id-card"></i> Console
|
||||
</span>
|
||||
</li>
|
||||
<li onClick={closeDropdown}>
|
||||
<span>
|
||||
<i className="fa-solid fa-arrow-right-from-bracket"></i> Log Out
|
||||
<i className="fa-light fa-arrow-right-from-bracket"></i> Log Out
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -51,14 +51,8 @@ const GoogleSignInBtn = ({
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let appversion = localStorage.getItem("appVersion");
|
||||
let appTitle = localStorage.getItem("appTitle");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let _app_objectId = localStorage.getItem("_app_objectId");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let userSettings = localStorage.getItem("userSettings");
|
||||
|
||||
localStorage.clear();
|
||||
@@ -66,14 +60,8 @@ const GoogleSignInBtn = ({
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("appversion", appversion);
|
||||
localStorage.setItem("appTitle", appTitle);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("_app_objectId", _app_objectId);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("userSettings", userSettings);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
@@ -186,22 +174,16 @@ const GoogleSignInBtn = ({
|
||||
|
||||
let appdata = localStorage.getItem("userSettings");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
|
||||
localStorage.clear();
|
||||
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("userSettings", appdata);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { Stage, Layer, Rect } from "react-konva";
|
||||
import { Stage, Layer, Rect, Text } from "react-konva";
|
||||
const RenderDebugPdf = (props) => {
|
||||
return (
|
||||
<div>
|
||||
@@ -13,11 +13,7 @@ const RenderDebugPdf = (props) => {
|
||||
>
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
props.setPdfLoadFail(load);
|
||||
props.setPdfLoadFail(false);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={props.pageDetails}
|
||||
@@ -47,16 +43,30 @@ const RenderDebugPdf = (props) => {
|
||||
{props.annotations
|
||||
.filter((value) => value.page === props.pageNumber)
|
||||
.map((value) => {
|
||||
const textX = value.x + value.width / 3;
|
||||
const textY = value.y + value.height / 2.5;
|
||||
return (
|
||||
<Rect
|
||||
key={value.key}
|
||||
x={value.x}
|
||||
y={value.y}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
fill="transparent"
|
||||
stroke="black"
|
||||
/>
|
||||
<React.Fragment key={value.key}>
|
||||
<Rect
|
||||
x={value.x}
|
||||
y={value.y}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
fill="#cbe9ed"
|
||||
stroke="black"
|
||||
/>
|
||||
<Text
|
||||
x={textX}
|
||||
y={textY}
|
||||
text={
|
||||
value?.key > 0 ? "box " + value.key.toString() : "box"
|
||||
}
|
||||
fontSize={16}
|
||||
fill="black"
|
||||
align="center"
|
||||
verticalAlign="middle"
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</Layer>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import "../../styles/loader.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { openInNewTab } from "../../constant/Utils";
|
||||
|
||||
@@ -32,13 +31,11 @@ const DashboardButton = (props) => {
|
||||
<span className="rounded-full bg-base-content bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||
<i
|
||||
className={`${
|
||||
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||
props.Icon ? props.Icon : "fa-light fa-info"
|
||||
} text-[25px] lg:text-[30px]`}
|
||||
></i>
|
||||
</span>
|
||||
<div className="op-card-title text-lg ml-3 text-base-content">
|
||||
{props.Label}
|
||||
</div>
|
||||
<div className=" text-lg ml-3 text-base-content">{props.Label}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import Parse from "parse";
|
||||
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
|
||||
import "../../styles/loader.css";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
|
||||
@@ -262,21 +261,14 @@ const DashboardCard = (props) => {
|
||||
}
|
||||
}
|
||||
body = str;
|
||||
await axios
|
||||
.post(url, body, { headers: headers })
|
||||
.then((response) => {
|
||||
try {
|
||||
if (response.data.result.length > 0) {
|
||||
setresponse(response.data.result[0][props.FilterData.key]);
|
||||
setLoading(false);
|
||||
} else {
|
||||
setresponse("0");
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
const response = await axios.post(url, body, { headers: headers });
|
||||
if (response.data.result.length > 0) {
|
||||
setresponse(response.data.result[0][props.FilterData.key]);
|
||||
setLoading(false);
|
||||
} else {
|
||||
setresponse("0");
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -339,10 +331,10 @@ const DashboardCard = (props) => {
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-start gap-5 text-white">
|
||||
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||
<span className="rounded-full bg-base-300 bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
|
||||
<i
|
||||
className={`${
|
||||
props.Icon ? props.Icon : "fa fa-solid fa-info"
|
||||
props.Icon ? props.Icon : "fa-light fa-info"
|
||||
} text-[25px] lg:text-[30px]`}
|
||||
></i>
|
||||
</span>
|
||||
|
||||
@@ -7,13 +7,13 @@ const buttonList = [
|
||||
label: "Sign yourself",
|
||||
redirectId: "sHAnZphf69",
|
||||
redirectType: "Form",
|
||||
icon: "fas fa-pen-nib"
|
||||
icon: "fa-light fa-pen-nib"
|
||||
},
|
||||
{
|
||||
label: "Request signature",
|
||||
redirectId: "8mZzFxbG1z",
|
||||
redirectType: "Form",
|
||||
icon: "fa-solid fa-paper-plane"
|
||||
icon: "fa-light fa-paper-plane"
|
||||
}
|
||||
];
|
||||
const GetDashboard = (props) => {
|
||||
@@ -31,7 +31,7 @@ const GetDashboard = (props) => {
|
||||
<div
|
||||
className={`${
|
||||
col?.widget?.bgColor ? col.widget.bgColor : "bg-[#2ed8b6]"
|
||||
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md"`}
|
||||
} op-card w-full h-[140px] px-3 pt-4 mb-3 shadow-md`}
|
||||
data-tut={col.widget.data.tourSection}
|
||||
>
|
||||
<Suspense
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import folder from "../../assets/images/folder.png";
|
||||
import "../../styles/opensigndrive.css";
|
||||
import pdfLogo from "../../assets/images/pdf3.png";
|
||||
import axios from "axios";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { saveAs } from "file-saver";
|
||||
@@ -19,10 +17,10 @@ function DriveBody(props) {
|
||||
const [selectDoc, setSelectDoc] = useState();
|
||||
const [isDeleteDoc, setIsDeleteDoc] = useState(false);
|
||||
const contextMenu = [
|
||||
{ type: "Download", icon: "fa-solid fa-arrow-down" },
|
||||
{ type: "Rename", icon: "fa-solid fa-font" },
|
||||
{ type: "Move", icon: "fa-solid fa-file-export" },
|
||||
{ type: "Delete", icon: "fa-solid fa-trash" }
|
||||
{ type: "Download", icon: "fa-light fa-arrow-down" },
|
||||
{ type: "Rename", icon: "fa-light fa-font" },
|
||||
{ type: "Move", icon: "fa-light fa-file-export" },
|
||||
{ type: "Delete", icon: "fa-light fa-trash" }
|
||||
];
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -72,9 +70,9 @@ function DriveBody(props) {
|
||||
props.sortingData(null, null, updatedData);
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${docId}`,
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${docId}`,
|
||||
updateName,
|
||||
{
|
||||
headers: {
|
||||
@@ -106,7 +104,7 @@ function DriveBody(props) {
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
@@ -114,7 +112,7 @@ function DriveBody(props) {
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
@@ -124,8 +122,8 @@ function DriveBody(props) {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
else if (isPlaceholder?.length > 0 && signedUrl) {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
(signerExist?.length > 0 &&
|
||||
@@ -172,9 +170,7 @@ function DriveBody(props) {
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${docId}`,
|
||||
`${localStorage.getItem("baseUrl")}classes/contracts_Document/${docId}`,
|
||||
data,
|
||||
{
|
||||
headers: {
|
||||
@@ -221,7 +217,7 @@ function DriveBody(props) {
|
||||
updateData = {
|
||||
Folder: {
|
||||
__type: "Pointer",
|
||||
className: `${localStorage.getItem("_appName")}_Document`,
|
||||
className: "contracts_Document",
|
||||
objectId: moveFolderId
|
||||
}
|
||||
};
|
||||
@@ -233,9 +229,9 @@ function DriveBody(props) {
|
||||
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Document/${updateDocId}`,
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Document/${updateDocId}`,
|
||||
updateData,
|
||||
{
|
||||
headers: {
|
||||
@@ -315,17 +311,22 @@ function DriveBody(props) {
|
||||
const signerName = getSignersName.join(", ");
|
||||
|
||||
return (
|
||||
<span className="statusSpan w-[90%] break-words">{signerName}</span>
|
||||
<span className="text-[12px] font-medium w-[90%] break-words">
|
||||
{signerName}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
return listType === "table" ? (
|
||||
data.Type === "Folder" ? (
|
||||
<tr onClick={() => handleOnclikFolder(data)}>
|
||||
<td className="cursor-pointer flex items-center">
|
||||
<i
|
||||
className="fa fa-folder mr-[8px] text-[26px] text-[#f0ad26]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<td className="cursor-pointer flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[26px] h-[26px] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||
</td>
|
||||
<td>_</td>
|
||||
@@ -335,8 +336,14 @@ function DriveBody(props) {
|
||||
</tr>
|
||||
) : (
|
||||
<tr onClick={() => checkPdfStatus(data)}>
|
||||
<td className="cursor-pointer flex items-center">
|
||||
<i className="fa fa-file-pdf mr-[8px] text-[26px] text-[#ed4d0e]"></i>
|
||||
<td className="cursor-pointer flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[26px] h-[26px] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
<span className="text-[12px] font-medium">{data.Name}</span>
|
||||
</td>
|
||||
<td>{createddate}</td>
|
||||
@@ -348,23 +355,29 @@ function DriveBody(props) {
|
||||
e.stopPropagation();
|
||||
handleMenuItemClick("Download", data);
|
||||
}}
|
||||
className="fa fa-download mr-[8px] text-[#ed280e]"
|
||||
className="fa-light fa-download mr-[8px] op-text-primary"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
) : listType === "list" && data.Type === "Folder" ? (
|
||||
<div key={ind} className="icon-container">
|
||||
<div key={ind} className="relative w-[100px] h-[100px] mx-2 my-3">
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||
<div data-tut={props.dataTutSeventh}>
|
||||
<img
|
||||
alt="folder"
|
||||
onClick={() => handleOnclikFolder(data)}
|
||||
src={folder}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
<ContextMenu.Trigger className="flex flex-col justify-center items-center">
|
||||
{/* folder */}
|
||||
<div
|
||||
data-tut={props.dataTutSeventh}
|
||||
onClick={() => handleOnclikFolder(data)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[100px] h-[100px] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
onFocus={() => {
|
||||
@@ -385,7 +398,7 @@ function DriveBody(props) {
|
||||
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="foldName">{data.Name}</span>
|
||||
<span className="fileName">{data.Name}</span>
|
||||
)}
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
@@ -400,7 +413,7 @@ function DriveBody(props) {
|
||||
onClick={() => handleMenuItemClick("Rename", data)}
|
||||
className="ContextMenuItem"
|
||||
>
|
||||
<i className="fa-solid fa-font mr-[8px]"></i>
|
||||
<i className="fa-light fa-font mr-[8px]"></i>
|
||||
<span>Rename</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
@@ -412,58 +425,65 @@ function DriveBody(props) {
|
||||
<HoverCard.Trigger asChild>
|
||||
<div>
|
||||
<ContextMenu.Root>
|
||||
<div className="icon-container">
|
||||
<ContextMenu.Trigger className="ContextMenuTrigger">
|
||||
<img
|
||||
alt="PDF"
|
||||
className="w-full h-full"
|
||||
src={pdfLogo}
|
||||
onClick={() => checkPdfStatus(data)}
|
||||
<div className="relative w-[100px] h-[100px] mx-2 my-3">
|
||||
<ContextMenu.Trigger className="flex flex-col justify-center items-center">
|
||||
{/* pdf */}
|
||||
<div
|
||||
data-tut={props.dataTutSixth}
|
||||
/>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
onFocus={() => {
|
||||
inputRef.current.setSelectionRange(0, 0);
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
// value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName">{data.Name}</span>
|
||||
)}
|
||||
onClick={() => checkPdfStatus(data)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[100px] h-[100px] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
{rename === data.objectId ? (
|
||||
<input
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
onFocus={() => {
|
||||
inputRef.current.setSelectionRange(0, 0);
|
||||
const input = inputRef.current;
|
||||
if (input) {
|
||||
input.select();
|
||||
}
|
||||
}}
|
||||
onBlur={() => handledRenameDoc(data)}
|
||||
onKeyDown={(e) => handleEnterPress(e, data)}
|
||||
ref={inputRef}
|
||||
defaultValue={renameValue}
|
||||
// value={renameValue}
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
className="w-[100px] border-[1.5px] border-black rounded-sm text-[10px]"
|
||||
/>
|
||||
) : (
|
||||
<span className="fileName">{data.Name}</span>
|
||||
)}
|
||||
</div>
|
||||
</ContextMenu.Trigger>
|
||||
{status === "Completed" ? (
|
||||
<div className="status-badge completed">
|
||||
<i className="fas fa-check-circle"></i>
|
||||
<i className="fa-light fa-check-circle"></i>
|
||||
</div>
|
||||
) : status === "Declined" ? (
|
||||
<div className="status-badge declined">
|
||||
<i className="fa fa-thumbs-down"></i>
|
||||
<i className="fa-light fa-thumbs-down"></i>
|
||||
</div>
|
||||
) : status === "Expired" ? (
|
||||
<div className="status-badge expired">
|
||||
<i className="fa fa-hourglass-end"></i>
|
||||
<i className="fa-light fa-hourglass-end"></i>
|
||||
</div>
|
||||
) : status === "Draft" ? (
|
||||
<div className="status-badge draft">
|
||||
<i className="fa fa-file"></i>
|
||||
<i className="fa-light fa-file"></i>
|
||||
</div>
|
||||
) : (
|
||||
status === "In Progress" && (
|
||||
<div className="status-badge in-progress">
|
||||
<i className="fa fa-paper-plane"></i>
|
||||
<i className="fa-light fa-paper-plane"></i>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
@@ -495,21 +515,17 @@ function DriveBody(props) {
|
||||
<HoverCard.Portal>
|
||||
<HoverCard.Content className="HoverCardContent" sideOffset={5}>
|
||||
<strong className="text-[13px]">Title: </strong>
|
||||
<span className="statusSpan" style={{ marginBottom: "0px" }}>
|
||||
{" "}
|
||||
{data.Name}
|
||||
</span>
|
||||
<span className="text-[12px] font-medium mb-0"> {data.Name}</span>
|
||||
<br />
|
||||
<strong className="text-[13px]">Status: </strong>
|
||||
<span className="statusSpan"> {status}</span>
|
||||
<span className="text-[12px] font-medium"> {status}</span>
|
||||
<br />
|
||||
<strong className="text-[13px]">Created Date: </strong>
|
||||
<span className="statusSpan">{createddate}</span>
|
||||
<span className="text-[12px] font-medium">{createddate}</span>
|
||||
<br />
|
||||
{signerExist && (
|
||||
<>
|
||||
<strong className="text-[13px]">Signers: </strong>
|
||||
{/* <span className="statusSpan">kjefjjnejkfnkbjs bbfjkdsbjbfjkbjk kscbjkbjkb</span> */}
|
||||
{signersName()}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -3,13 +3,9 @@ import React from "react";
|
||||
function BorderResize({ right, top }) {
|
||||
return (
|
||||
<div
|
||||
className="borderResize"
|
||||
style={{
|
||||
right: right ? right + "px" : "-1px",
|
||||
bottom: top ? top + "px" : "-1px",
|
||||
borderRight: "3px solid #188ae2",
|
||||
borderBottom: "3px solid #188ae2"
|
||||
}}
|
||||
className={`${right ? `-right-[12px]` : "-right-[1px]"} ${
|
||||
top ? `-bottom-[11px]` : "-bottom-[1px]"
|
||||
} absolute inline-block w-[14px] h-[14px] hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import LoaderWithMsg from "../../primitives/LoaderWithMsg";
|
||||
import { contractDocument } from "../../constant/Utils";
|
||||
import HandleError from "../../primitives/HandleError";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
function useQuery() {
|
||||
return new URLSearchParams(useLocation().search);
|
||||
}
|
||||
function DraftDocument() {
|
||||
const navigate = useNavigate();
|
||||
const query = useQuery();
|
||||
const docId = query.get("docId");
|
||||
const [isLoading, setIsLoading] = useState({
|
||||
isLoader: true,
|
||||
message: "This might take some time"
|
||||
});
|
||||
|
||||
const rowLevel =
|
||||
localStorage.getItem("rowlevel") &&
|
||||
JSON.parse(localStorage.getItem("rowlevel"));
|
||||
const docId =
|
||||
rowLevel && rowLevel?.id
|
||||
? rowLevel.id
|
||||
: rowLevel?.objectId && rowLevel.objectId;
|
||||
|
||||
useEffect(() => {
|
||||
getDocumentDetails();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -53,7 +49,7 @@ function DraftDocument() {
|
||||
const signedUrl = data.SignedUrl;
|
||||
//checking if document has completed and request signature flow
|
||||
if (data?.IsCompleted && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
}
|
||||
//checking if document has completed and signyour-self flow
|
||||
else if (!signerExist && !isPlaceholder) {
|
||||
@@ -61,7 +57,7 @@ function DraftDocument() {
|
||||
}
|
||||
//checking if document has declined by someone
|
||||
else if (isDecline) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
//checking draft type document
|
||||
} else if (
|
||||
signerExist?.length > 0 &&
|
||||
@@ -71,8 +67,8 @@ function DraftDocument() {
|
||||
navigate(`/placeHolderSign/${data.objectId}`);
|
||||
}
|
||||
//Inprogress document
|
||||
else if (isPlaceholder?.length > 0 && signerExist?.length > 0) {
|
||||
navigate(`/pdfRequestFiles/${data.objectId}`);
|
||||
else if (isPlaceholder?.length > 0 && signedUrl) {
|
||||
navigate(`/recipientSignPdf/${data.objectId}`);
|
||||
} //placeholder draft document
|
||||
else if (
|
||||
(signerExist?.length > 0 &&
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react";
|
||||
import { isEnableSubscription } 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([
|
||||
@@ -205,64 +204,66 @@ function DropdownWidgetOption(props) {
|
||||
/>
|
||||
|
||||
<i
|
||||
className="fa-solid fa-rectangle-xmark text-[25px] ml-[10px] text-error"
|
||||
className="fa-light fa-rectangle-xmark text-[25px] ml-[10px] text-accent cursor-pointer"
|
||||
onClick={() => handleDeleteInput(index)}
|
||||
></i>
|
||||
</div>
|
||||
))}
|
||||
<i
|
||||
onClick={handleAddInput}
|
||||
className="fa-solid fa-square-plus text-[25px] ml-[10px] text-primary"
|
||||
className="fa-light fa-square-plus text-[25px] ml-[10px] op-text-primary cursor-pointer"
|
||||
></i>
|
||||
<div>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label
|
||||
className={`${
|
||||
!props.isSubscribe ? "text-[gray]" : ""
|
||||
} text-[13px] font-semibold`}
|
||||
>
|
||||
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
|
||||
? ""
|
||||
: "pointer-events-none bg-opacity-50"
|
||||
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||
/>
|
||||
<label
|
||||
className={`${
|
||||
!props.isSubscribe ? "text-[gray]" : ""
|
||||
} text-[13px] font-semibold`}
|
||||
>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className={`${
|
||||
props.isSubscribe || !isEnableSubscription
|
||||
? ""
|
||||
: "pointer-events-none bg-opacity-50"
|
||||
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isEnableSubscription && (
|
||||
<div>
|
||||
{props.type === "checkbox" && !props.isSignYourself && (
|
||||
<>
|
||||
<label
|
||||
className={`${
|
||||
!props.isSubscribe ? "text-[gray]" : ""
|
||||
} text-[13px] font-semibold`}
|
||||
>
|
||||
Minimun check
|
||||
</label>
|
||||
{!props.isSubscribe && isEnableSubscription && (
|
||||
<Upgrade />
|
||||
)}
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={minCount}
|
||||
disabled={props.isSubscribe ? false : true}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMinCount(count);
|
||||
}}
|
||||
className={`${
|
||||
props.isSubscribe ? "" : "pointer-events-none"
|
||||
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||
/>
|
||||
<label
|
||||
className={`${
|
||||
!props.isSubscribe ? "text-[gray]" : ""
|
||||
} text-[13px] font-semibold`}
|
||||
>
|
||||
Maximum check
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
defaultValue={0}
|
||||
value={maxCount}
|
||||
disabled={props.isSubscribe ? false : true}
|
||||
onChange={(e) => {
|
||||
const count = handleSetMinMax(e);
|
||||
setMaxCount(count);
|
||||
}}
|
||||
className={`${
|
||||
props.isSubscribe ? "" : "pointer-events-none"
|
||||
} op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{["dropdown", radioButtonWidget].includes(props.type) && (
|
||||
<>
|
||||
@@ -344,15 +345,6 @@ function DropdownWidgetOption(props) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{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,6 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { checkIsSubscribed, getFileName } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
// import SelectFolder from "../../premitives/SelectFolder";
|
||||
@@ -61,7 +60,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
<div className="max-h-[300px] md:max-h-[400px] overflow-y-scroll p-[10px]">
|
||||
<div className="text-base-content">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
File
|
||||
</label>
|
||||
@@ -69,7 +68,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
{getFileName(template.URL)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
Name
|
||||
<span className="text-[13px] text-[red]"> *</span>
|
||||
@@ -83,7 +82,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="Note" className="text-[13px]">
|
||||
Note
|
||||
</label>
|
||||
@@ -96,7 +95,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<div className="mb-[0.35rem]">
|
||||
<label htmlFor="Description" className="text-[13px]">
|
||||
Description
|
||||
</label>
|
||||
@@ -109,7 +108,7 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-section">
|
||||
<div className="mb-[0.35rem]">
|
||||
<label className="text-[13px]">Send In Order</label>
|
||||
<div className="flex items-center gap-[8px] ml-[8px] mb-[5px]">
|
||||
<input
|
||||
@@ -134,39 +133,32 @@ const EditTemplate = ({ template, onSuccess }) => {
|
||||
<div className="text-[12px]">No</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
{!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Disable Auto reminder is free in beta, this feature will incur a fee later."
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<span
|
||||
className={
|
||||
isSubscribe ? "font-semibold" : "font-semibold text-gray-300"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
Auto reminder{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
checked={formData.AutomaticReminders}
|
||||
onChange={handleAutoReminder}
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
>
|
||||
Auto reminder{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
checked={formData.AutomaticReminders}
|
||||
onChange={handleAutoReminder}
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white mt-2"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{isSubscribe && formData?.AutomaticReminders === true && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState } from "react";
|
||||
import { saveAs } from "file-saver";
|
||||
import celebration from "../../assets/images/newCeleb.gif";
|
||||
import axios from "axios";
|
||||
import { getBase64FromUrl } from "../../constant/Utils";
|
||||
import { themeColor } from "../../constant/const";
|
||||
import { themeColor, emailRegex } from "../../constant/const";
|
||||
import printModule from "print-js";
|
||||
import Loader from "../../primitives/Loader";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
|
||||
function EmailComponent({
|
||||
isEmail,
|
||||
@@ -19,9 +19,9 @@ function EmailComponent({
|
||||
activeMailAdapter
|
||||
}) {
|
||||
const [emailList, setEmailList] = useState([]);
|
||||
const [emailValue, setEmailValue] = useState();
|
||||
const [emailValue, setEmailValue] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isEmailCelebration, setIsEmailCelebration] = useState(false);
|
||||
const [emailErr, setEmailErr] = useState(false);
|
||||
//function for send email
|
||||
const sendEmail = async () => {
|
||||
setIsLoading(true);
|
||||
@@ -62,10 +62,6 @@ function EmailComponent({
|
||||
" target=_blank>here</a> </p></div></div></body></html>"
|
||||
};
|
||||
sendMail = await axios.post(url, params, { headers: headers });
|
||||
setIsEmailCelebration(true);
|
||||
setTimeout(() => {
|
||||
setIsEmailCelebration(false);
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
setIsLoading(false);
|
||||
@@ -116,17 +112,30 @@ function EmailComponent({
|
||||
//function for get email value
|
||||
const handleEmailValue = (e) => {
|
||||
const value = e.target.value;
|
||||
setEmailErr(false);
|
||||
setEmailValue(value);
|
||||
};
|
||||
|
||||
//function for save email in array after press enter
|
||||
const handleEnterPress = (e) => {
|
||||
const pattern = emailRegex;
|
||||
const validate = emailValue?.match(pattern);
|
||||
if (e.key === "Enter" && emailValue) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
if (validate) {
|
||||
const emailLowerCase = emailValue?.toLowerCase();
|
||||
setEmailList((prev) => [...prev, emailLowerCase]);
|
||||
setEmailValue("");
|
||||
} else {
|
||||
setEmailErr(true);
|
||||
}
|
||||
} else if (e === "add" && emailValue) {
|
||||
setEmailList((prev) => [...prev, emailValue]);
|
||||
setEmailValue("");
|
||||
if (validate) {
|
||||
const emailLowerCase = emailValue?.toLowerCase();
|
||||
setEmailList((prev) => [...prev, emailLowerCase]);
|
||||
setEmailValue("");
|
||||
} else {
|
||||
setEmailErr(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -170,146 +179,131 @@ function EmailComponent({
|
||||
<div>
|
||||
{/* isEmail */}
|
||||
{isEmail && (
|
||||
<div className="modaloverlay">
|
||||
<div className="modalcontainer">
|
||||
{isLoading && (
|
||||
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2] bg-opacity-80">
|
||||
<Loader />
|
||||
<span className="text-[12px] font-bold">
|
||||
This might take some time
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div style={{ background: "#32a3ac" }} className="modalheader">
|
||||
<span className="text-white">Successfully signed!</span>
|
||||
<div className="flex flex-row">
|
||||
<div></div>
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||
>
|
||||
<i className="fa fa-print" aria-hidden="true"></i>
|
||||
Print
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
>
|
||||
<i className="fa fa-download" aria-hidden="true"></i>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
<ModalUi isOpen showHeader={false}>
|
||||
{isLoading && (
|
||||
<div className="absolute w-full h-full flex flex-col justify-center items-center z-[20] bg-[#e6f2f2]/70">
|
||||
<Loader />
|
||||
<span className="text-[12px] text-base-content">
|
||||
This might take some time
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-full p-[20px]">
|
||||
{isEmailCelebration && (
|
||||
<div className="absolute w-[100%] flex justify-center items-center">
|
||||
<img alt="celeb" width={300} height={250} src={celebration} />
|
||||
</div>
|
||||
)}
|
||||
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
|
||||
Recipients added here will get a copy of the signed document.
|
||||
</p>
|
||||
{emailList.length > 0 ? (
|
||||
<>
|
||||
<div className="addEmail">
|
||||
<div className="flex flex-row flex-wrap">
|
||||
{emailList.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center bg-[#47a3ad] m-[4px] rounded-md py-[5px] px-[10px]"
|
||||
key={ind}
|
||||
>
|
||||
<span className="text-white text-[13px]">
|
||||
{data}
|
||||
</span>
|
||||
<span
|
||||
className="text-white text-[13px] font-semibold ml-[7px] cursor-pointer"
|
||||
onClick={() => removeChip(ind)}
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{emailList.length <= 9 && (
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-white outline-none"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
value={emailValue}
|
||||
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-white border-2 border-[#47a3ad]"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
placeholder="Add the email addresses"
|
||||
onBlur={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between items-center py-[10px] px-[20px] border-b-[1px] border-base-content">
|
||||
<span className="text-base-content font-semibold">
|
||||
Successfully signed!
|
||||
</span>
|
||||
<div className="flex flex-row">
|
||||
{!isAndroid && (
|
||||
<button
|
||||
onClick={handleToPrint}
|
||||
className="op-btn op-btn-neutral op-btn-sm text-[15px]"
|
||||
>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
Print
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className={`${
|
||||
emailValue ? "cursor-pointer" : "cursor-default"
|
||||
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
|
||||
onClick={() => {
|
||||
if (emailValue) {
|
||||
handleEnterPress("add");
|
||||
}
|
||||
}}
|
||||
className="op-btn op-btn-primary op-btn-sm text-[15px] ml-2"
|
||||
onClick={() => handleDownloadPdf()}
|
||||
>
|
||||
<i className="fa fa-plus" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
|
||||
<span className="font-bold">Note: </span>
|
||||
<span className="text-[15px]">
|
||||
You can only send to ten recipients at a time.
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={() => emailList.length > 0 && sendEmail()}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full p-[20px]">
|
||||
<p className="font-medium text-[15px] mb-[5px] text-base-content align-baseline">
|
||||
Recipients added here will get a copy of the signed document.
|
||||
</p>
|
||||
{emailList.length > 0 ? (
|
||||
<div className="p-0 border-[1.5px] op-border-primary rounded w-full text-[15px]">
|
||||
<div className="flex flex-row flex-wrap">
|
||||
{emailList.map((data, ind) => {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-row items-center op-bg-primary m-[4px] rounded-md py-[5px] px-[10px]"
|
||||
key={ind}
|
||||
>
|
||||
<span className="text-base-100 text-[13px]">
|
||||
{data}
|
||||
</span>
|
||||
<span
|
||||
className="text-base-100 text-[13px] font-semibold ml-[7px] cursor-pointer"
|
||||
onClick={() => removeChip(ind)}
|
||||
>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{emailList.length <= 9 && (
|
||||
<input
|
||||
type="email"
|
||||
value={emailValue}
|
||||
className="p-[10px] pb-[20px] rounded w-full text-[15px] bg-transparent outline-none"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
onBlur={() => emailValue && handleEnterPress("add")}
|
||||
required
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<input
|
||||
type="email"
|
||||
value={emailValue}
|
||||
className="p-[10px] pb-[20px] rounded w-full text-[15px] outline-none bg-transparent border-[1.5px] op-border-primary"
|
||||
onChange={handleEmailValue}
|
||||
onKeyDown={handleEnterPress}
|
||||
placeholder="Add an email address and hit enter"
|
||||
onBlur={() => emailValue && handleEnterPress("add")}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{emailErr && (
|
||||
<p className="text-xs text-[red] ml-1.5 mt-0.5">
|
||||
please provide correct email address
|
||||
</p>
|
||||
)}
|
||||
<button
|
||||
className={`${
|
||||
emailValue ? "cursor-pointer" : "cursor-default"
|
||||
} op-btn op-btn-primary op-btn-sm m-2 shadow-md`}
|
||||
onClick={() => emailValue && handleEnterPress("add")}
|
||||
>
|
||||
<i className="fa-light fa-plus" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<div className="bg-[#e3e2e1] mt-[10px] p-[5px] rounded">
|
||||
<span className="font-bold">Note: </span>
|
||||
<span className="text-[15px]">
|
||||
You can only send to ten recipients at a time.
|
||||
</span>
|
||||
</div>
|
||||
<hr className="w-full my-[15px] bg-base-content" />
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-secondary"
|
||||
onClick={() => emailList.length > 0 && sendEmail()}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost ml-2"
|
||||
onClick={() => {
|
||||
setIsEmail(false);
|
||||
setEmailValue("");
|
||||
setEmailList([]);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from "../../constant/Utils";
|
||||
import "../../styles/signature.css";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import Loader from "../../primitives/Loader";
|
||||
|
||||
@@ -29,31 +28,27 @@ function Header({
|
||||
isShowHeader,
|
||||
decline,
|
||||
currentSigner,
|
||||
dataTut4,
|
||||
alreadySign,
|
||||
isSignYourself,
|
||||
setIsEmail,
|
||||
completeBtnTitle,
|
||||
setIsEditTemplate
|
||||
setIsEditTemplate,
|
||||
isPublicTemplate
|
||||
}) {
|
||||
const filterPrefill =
|
||||
signerPos && signerPos?.filter((data) => data.Role !== "prefill");
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const navigate = useNavigate();
|
||||
const [isDownloading, setIsDownloading] = useState("");
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
//function for show decline alert
|
||||
const handleDeclinePdfAlert = async () => {
|
||||
const currentDecline = {
|
||||
currnt: "Sure",
|
||||
isDeclined: true
|
||||
};
|
||||
const currentDecline = { currnt: "Sure", isDeclined: true };
|
||||
setIsDecline(currentDecline);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead">
|
||||
<div className="flex py-[5px]">
|
||||
{isMobile && isShowHeader ? (
|
||||
<div
|
||||
id="navbar"
|
||||
@@ -65,9 +60,9 @@ function Header({
|
||||
}}
|
||||
>
|
||||
<div className="flex justify-between items-center py-[5px] px-[10px] ">
|
||||
<div onClick={() => navigate(-1)}>
|
||||
<div onClick={() => window.history.go(-2)}>
|
||||
<i
|
||||
className="fa fa-arrow-left text-base-content"
|
||||
className="fa-light fa-arrow-left text-base-content"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
@@ -80,7 +75,10 @@ function Header({
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="op-link op-link-primary no-underline text-[16px] font-semibold pr-[3px] pl-[5px]">
|
||||
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
<i
|
||||
className="fa-light fa-ellipsis-v"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
@@ -96,7 +94,7 @@ function Header({
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa fa-arrow-down mr-[2px]"
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Download
|
||||
@@ -114,7 +112,7 @@ function Header({
|
||||
>
|
||||
<div className="border-none bg-[#fff]">
|
||||
<i
|
||||
className="fa-solid fa-award mr-[2px]"
|
||||
className="fa-light fa-award mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Certificate
|
||||
@@ -128,7 +126,7 @@ function Header({
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa fa-envelope mr-[2px]"
|
||||
className="fa-light fa-envelope mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Mail
|
||||
@@ -143,7 +141,7 @@ function Header({
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i
|
||||
className="fa fa-print mr-[2px]"
|
||||
className="fa-light fa-print mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Print
|
||||
@@ -177,7 +175,7 @@ function Header({
|
||||
className={`${
|
||||
isMailSend ? "" : "op-link-primary"
|
||||
} op-link no-underline font-[650] text-[14px]`}
|
||||
data-tut={dataTut4}
|
||||
data-tut="headerArea"
|
||||
>
|
||||
{completeBtnTitle ? completeBtnTitle : "Send"}
|
||||
</div>
|
||||
@@ -198,16 +196,16 @@ function Header({
|
||||
)}
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<div className="font-[650] text-[18px] px-2 ml-[4px] op-link op-link-info no-underline">
|
||||
<div className="font-[650] text-[18px] px-2 ml-[4px] text-base-content no-underline">
|
||||
<i
|
||||
className="fa fa-ellipsis-v"
|
||||
className="fa-light fa-ellipsis-v"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content
|
||||
className="bg-white shadow-md rounded px-2 py-1"
|
||||
className="bg-white shadow-md rounded-full px-3 py-2"
|
||||
sideOffset={5}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
@@ -221,7 +219,7 @@ function Header({
|
||||
}
|
||||
>
|
||||
<i
|
||||
className="fa fa-arrow-down mr-[5px]"
|
||||
className="fa-light fa-arrow-down mr-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-[500]">Download</span>
|
||||
@@ -236,7 +234,7 @@ function Header({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-wrap justify-between items-center mt-[5px]">
|
||||
<div className="flex flex-wrap justify-between items-center w-full gap-y-1 ml-1">
|
||||
<PrevNext
|
||||
pageNumber={pageNumber}
|
||||
allPages={allPages}
|
||||
@@ -267,22 +265,22 @@ function Header({
|
||||
{setIsEditTemplate && (
|
||||
<button
|
||||
onClick={() => setIsEditTemplate(true)}
|
||||
className="outline-none border-none text-center mr-[5px]"
|
||||
className="outline-none border-none text-center mr-[3px]"
|
||||
>
|
||||
<i className="fa-solid fa-gear fa-lg"></i>
|
||||
<i className="fa-light fa-gear fa-lg"></i>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
disabled={isMailSend && true}
|
||||
data-tut="reactourFour"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||
data-tut="headerArea"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => alertSendEmail()}
|
||||
>
|
||||
{completeBtnTitle
|
||||
@@ -296,97 +294,80 @@ function Header({
|
||||
) : isPdfRequestFiles ? (
|
||||
alreadySign ? (
|
||||
<div className="flex flex-row">
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-light fa-print py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">Print</span>
|
||||
</button>
|
||||
{isCompleted && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-award py-[3px]"
|
||||
className="fa-light fa-award py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[5px] shadow"
|
||||
>
|
||||
<i className="fa fa-print py-[3px]" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block">Print</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa fa-download py-[3px]" aria-hidden="true"></i>
|
||||
<i
|
||||
className="fa-light fa-download py-[3px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="hidden lg:block">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex" data-tut="reactourFifth">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
{currentSigner && (
|
||||
<>
|
||||
<button
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[5px] shadow"
|
||||
className="op-btn op-btn-secondary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => handleDeclinePdfAlert()}
|
||||
>
|
||||
Decline
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[5px] shadow"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px] shadow"
|
||||
onClick={() => embedWidgetsData()}
|
||||
>
|
||||
Finish
|
||||
</button>
|
||||
<div className="op-dropdown op-dropdown-end">
|
||||
<div
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
className="op-btn op-btn-info op-btn-sm shadow"
|
||||
>
|
||||
<i className="fa fa-ellipsis-v" aria-hidden="true"></i>
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className="op-dropdown-content z-[1] op-menu op-menu-sm p-1 shadow bg-base-100 rounded-box mt-1"
|
||||
>
|
||||
<li
|
||||
onClick={() =>
|
||||
handleDownloadPdf(
|
||||
pdfDetails,
|
||||
pdfUrl,
|
||||
setIsDownloading
|
||||
)
|
||||
}
|
||||
>
|
||||
<span className="font-semibold text-[12px]">
|
||||
<i
|
||||
className="fa fa-arrow-down"
|
||||
aria-hidden="true"
|
||||
></i>{" "}
|
||||
Download
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa-light fa-arrow-down font-semibold lg:hidden"></i>
|
||||
<span className="hidden lg:block">Download</span>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@@ -399,51 +380,65 @@ function Header({
|
||||
onClick={() =>
|
||||
handleDownloadCertificate(pdfDetails, setIsDownloading)
|
||||
}
|
||||
className="op-btn op-btn-secondary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||
className="op-btn op-btn-secondary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa-solid fa-award" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block">Certificate</span>
|
||||
<i className="fa-light fa-award" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Certificate</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={(e) => handleToPrint(e, pdfUrl, setIsDownloading)}
|
||||
type="button"
|
||||
className="op-btn op-btn-neutral op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||
className="op-btn op-btn-neutral op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
>
|
||||
<i className="fa fa-print" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block">Print</span>
|
||||
<i className="fa-light fa-print" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Print</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||
className="op-btn op-btn-primary op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() =>
|
||||
handleDownloadPdf(pdfDetails, pdfUrl, setIsDownloading)
|
||||
}
|
||||
>
|
||||
<i className="fa fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block">Download</span>
|
||||
<i className="fa-light fa-download" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Download</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-info op-btn-sm font-medium text-[13px] mr-[5px] shadow"
|
||||
className="op-btn op-btn-info op-btn-sm gap-0 font-medium text-[12px] mr-[3px] shadow"
|
||||
onClick={() => setIsEmail(true)}
|
||||
>
|
||||
<i className="fa fa-envelope" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block">Mail</span>
|
||||
<i className="fa-light fa-envelope" aria-hidden="true"></i>
|
||||
<span className="hidden lg:block ml-1">Mail</span>
|
||||
</button>
|
||||
</div>
|
||||
) : isPublicTemplate ? (
|
||||
<>
|
||||
<div className="flex">
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm shadow"
|
||||
onClick={() => {
|
||||
embedWidgetsData();
|
||||
}}
|
||||
>
|
||||
Sign Now
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
onClick={() => window.history.go(-2)}
|
||||
type="button"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[5px]"
|
||||
className="op-btn op-btn-ghost op-btn-sm mr-[3px]"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[5px]"
|
||||
className="op-btn op-btn-primary op-btn-sm mr-[3px]"
|
||||
onClick={() => {
|
||||
if (!pdfUrl) {
|
||||
embedWidgetsData();
|
||||
@@ -474,10 +469,8 @@ function Header({
|
||||
{isDownloading === "certificate"}{" "}
|
||||
<p>
|
||||
Your completion certificate is being generated. Please wait
|
||||
momentarily.
|
||||
</p>
|
||||
<p>
|
||||
If the download doesn't start shortly, click the button again.
|
||||
momentarily. If the download doesn't start shortly, click the
|
||||
button again.
|
||||
</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
|
||||
function PdfZoom(props) {
|
||||
const onClickZoomIn = () => {
|
||||
props.setScale(props.scale + 0.1 * props.scale);
|
||||
props.setZoomPercent(props.zoomPercent + 10);
|
||||
};
|
||||
const onClickZoomOut = () => {
|
||||
if (props.zoomPercent > 0) {
|
||||
if (props.zoomPercent === 10) {
|
||||
props.setScale(1);
|
||||
} else {
|
||||
props.setScale(props.scale - 0.1 * props.scale);
|
||||
}
|
||||
props.setZoomPercent(props.zoomPercent - 10);
|
||||
}
|
||||
};
|
||||
const handleReset = () => {
|
||||
props.setScale(1);
|
||||
props.setZoomPercent(0);
|
||||
};
|
||||
return (
|
||||
// md:mt-[41px] xl:mt-[63px]mt=[]
|
||||
<span className="hidden md:flex flex-col gap-1 text-center md:w-[5%] mt-[50px] 2xl:mt-[63px]">
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={onClickZoomIn}
|
||||
title="Zoom in"
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
|
||||
onClick={handleReset}
|
||||
title="Reset"
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
<span
|
||||
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
|
||||
onClick={onClickZoomOut}
|
||||
style={{
|
||||
cursor: props.zoomPercent > 0 ? "pointer" : "default"
|
||||
}}
|
||||
title="Zoom out"
|
||||
>
|
||||
<i className="fa-light fa-magnifying-glass-minus text-gray-500 2xl:text-[30px]"></i>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default PdfZoom;
|
||||
@@ -4,8 +4,9 @@ import PlaceholderBorder from "./PlaceholderBorder";
|
||||
import { Rnd } from "react-rnd";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
getContainerScale,
|
||||
handleCopyNextToWidget,
|
||||
isMobile,
|
||||
isTabAndMobile,
|
||||
onChangeInput,
|
||||
radioButtonWidget,
|
||||
textInputWidget,
|
||||
@@ -88,8 +89,10 @@ const getDefaultdate = (selectedDate, format = "dd-MM-yyyy") => {
|
||||
const getDefaultFormat = (dateFormat) => dateFormat || "MM/dd/yyyy";
|
||||
|
||||
function Placeholder(props) {
|
||||
const [placeholderBorder, setPlaceholderBorder] = useState({ w: 0, h: 0 });
|
||||
const [isDraggingEnabled, setDraggingEnabled] = useState(true);
|
||||
const [isShowDateFormat, setIsShowDateFormat] = useState(false);
|
||||
const [containerScale, setContainerScale] = useState();
|
||||
const [selectDate, setSelectDate] = useState({
|
||||
date:
|
||||
props.pos.type === "date"
|
||||
@@ -119,6 +122,14 @@ function Placeholder(props) {
|
||||
width: null,
|
||||
height: null
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const getPdfPageWidth = props.pdfOriginalWH.find(
|
||||
(data) => data.pageNumber === props.pageNumber
|
||||
);
|
||||
setContainerScale(props.containerWH.width / getPdfPageWidth.width);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.containerWH.width, props.pdfOriginalWH]);
|
||||
const dateFormatArr = [
|
||||
"L",
|
||||
"DD-MM-YYYY",
|
||||
@@ -224,25 +235,29 @@ function Placeholder(props) {
|
||||
setDraggingEnabled(false);
|
||||
}
|
||||
if ((props.isNeedSign || props.isSignYourself) && !props.isDragging) {
|
||||
if (props.pos.type) {
|
||||
if (
|
||||
props.pos.type === "signature" ||
|
||||
props.pos.type === "stamp" ||
|
||||
props.pos.type === "image"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.pos.type === "initials") {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
props.setIsInitial(true);
|
||||
}
|
||||
if (props?.ispublicTemplate) {
|
||||
props.handleUserDetails();
|
||||
} else {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
|
||||
if (props.pos.type) {
|
||||
if (
|
||||
props.pos.type === "signature" ||
|
||||
props.pos.type === "stamp" ||
|
||||
props.pos.type === "image"
|
||||
) {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
} else if (props.pos.type === "initials") {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos.isStamp);
|
||||
props.setIsInitial(true);
|
||||
}
|
||||
} else {
|
||||
props.setIsSignPad(true);
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setIsStamp(props.pos?.isStamp ? props.pos.isStamp : false);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
props.isPlaceholder &&
|
||||
@@ -252,6 +267,10 @@ function Placeholder(props) {
|
||||
if (props.pos.key === props.selectWidgetId) {
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
} else if (!props.pos.type) {
|
||||
if (
|
||||
@@ -281,6 +300,12 @@ function Placeholder(props) {
|
||||
}
|
||||
if (props.isNeedSign && props.data?.signerObjId === props.signerObjId) {
|
||||
handleWidgetIdandPopup();
|
||||
} else if (
|
||||
props?.isNeedSign &&
|
||||
props?.uniqueId &&
|
||||
props.data?.Id === props?.uniqueId
|
||||
) {
|
||||
handleWidgetIdandPopup();
|
||||
} else if (props.isPlaceholder || props.isSignYourself) {
|
||||
handleWidgetIdandPopup();
|
||||
}
|
||||
@@ -293,12 +318,43 @@ function Placeholder(props) {
|
||||
props?.setShowDropdown(true);
|
||||
} else if (props.pos.type === "checkbox") {
|
||||
props?.setIsCheckbox(true);
|
||||
}
|
||||
//condition to handle setting icon for signyour-self flow for all type text widgets
|
||||
else if (
|
||||
[
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(props.pos.type) &&
|
||||
props.isSignYourself
|
||||
) {
|
||||
props.handleTextSettingModal(true);
|
||||
} else {
|
||||
props?.handleNameModal(true);
|
||||
props?.handleNameModal && props?.handleNameModal(true);
|
||||
}
|
||||
|
||||
if (props.isPlaceholder && props.type !== textWidget) {
|
||||
props.setUniqueId(props.data.Id);
|
||||
//condition for only placeholder and template flow
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
//condition to handle in placeholder and template flow for text widget signerId for text widgets i have to set uniqueId in tempSignerId because
|
||||
//it does not have any signer user and any signerobjId
|
||||
else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setWidgetType(props.pos.type);
|
||||
@@ -315,10 +371,19 @@ function Placeholder(props) {
|
||||
if (props.data && props?.pos?.type !== textWidget) {
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
} else if (props.data && props.pos.type === textWidget) {
|
||||
props.setTempSignerId(props.uniqueId);
|
||||
props.setSignerObjId(props?.data?.signerObjId);
|
||||
props.setUniqueId(props?.data?.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
|
||||
//checking widget's type and open widget copy modal for required widgets
|
||||
@@ -375,7 +440,18 @@ function Placeholder(props) {
|
||||
<>
|
||||
{(props.isPlaceholder || props.isSignYourself) && (
|
||||
<>
|
||||
{props.pos.type === "checkbox" && props.isSignYourself ? (
|
||||
{/* condition to add setting icon for signyour-self flow for particular text widgets
|
||||
and also it have diffrent position
|
||||
*/}
|
||||
{[
|
||||
"checkbox",
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(props.pos.type) && props.isSignYourself ? (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -385,7 +461,7 @@ function Placeholder(props) {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: "29px",
|
||||
@@ -393,11 +469,10 @@ function Placeholder(props) {
|
||||
}}
|
||||
></i>
|
||||
) : (
|
||||
/* condition to add setting icon for placeholder & template flow for all widgets except signature and date */
|
||||
((!props?.pos?.type && props.pos.isStamp) ||
|
||||
(props?.pos?.type &&
|
||||
!["date", textWidget, "signature"].includes(
|
||||
props.pos.type
|
||||
) &&
|
||||
!["date", "signature"].includes(props.pos.type) &&
|
||||
!props.isSignYourself)) && (
|
||||
<i
|
||||
onClick={(e) => {
|
||||
@@ -408,31 +483,44 @@ function Placeholder(props) {
|
||||
e.stopPropagation();
|
||||
handleOnClickSettingIcon();
|
||||
}}
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
style={{ color: "#188ae2", right: "47px", top: "-19px" }}
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
right: props?.pos?.type === textWidget ? "32px" : "47px",
|
||||
top: "-19px"
|
||||
}}
|
||||
></i>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* condition for usericon for all widgets except text widgets and signyour-self flow */}
|
||||
{props.pos.type !== textWidget && !props.isSignYourself && (
|
||||
<i
|
||||
data-tut="assignSigner"
|
||||
className="fa-regular fa-user signUserIcon"
|
||||
className="fa-light fa-user icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
props.handleLinkUser(props.data.Id);
|
||||
props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}}
|
||||
style={{ color: "#188ae2", right: "32px", top: "-18px" }}
|
||||
></i>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* setting icon only for date widgets */}
|
||||
{props.pos.type === "date" && selectDate && (
|
||||
<div
|
||||
id="menu-container"
|
||||
@@ -441,17 +529,17 @@ function Placeholder(props) {
|
||||
top: "-19px",
|
||||
right: props.isPlaceholder ? "60px" : "44px"
|
||||
}}
|
||||
className={
|
||||
isShowDateFormat
|
||||
? "dropdown signUserIcon show"
|
||||
: "dropdown signUserIcon"
|
||||
}
|
||||
className={`${isShowDateFormat ? "show" : ""} dropdown icon`}
|
||||
onClick={(e) => {
|
||||
setIsShowDateFormat(!isShowDateFormat);
|
||||
e.stopPropagation();
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
}}
|
||||
onTouchEnd={(e) => {
|
||||
@@ -460,11 +548,15 @@ function Placeholder(props) {
|
||||
if (props.data) {
|
||||
props.setSignKey(props.pos.key);
|
||||
props.setUniqueId(props.data.Id);
|
||||
const checkIndex = props.xyPostion.findIndex(
|
||||
(data) => data.Id === props.data.Id
|
||||
);
|
||||
props.setIsSelectId(checkIndex || 0);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className="fa-solid fa-gear settingIcon"
|
||||
className="fa-light fa-gear icon"
|
||||
style={{
|
||||
color: "#188ae2",
|
||||
fontSize: "14px"
|
||||
@@ -492,8 +584,7 @@ function Placeholder(props) {
|
||||
setSelectDate(data);
|
||||
handleSaveDate(data);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
style={{ fontSize: "12px" }}
|
||||
className="dropdown-item text-[13px]"
|
||||
>
|
||||
{data?.date ? data?.date : "nodata"}
|
||||
</span>
|
||||
@@ -502,29 +593,37 @@ function Placeholder(props) {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* copy icon for all widgets */}
|
||||
<i
|
||||
className="fa-regular fa-copy signCopy"
|
||||
className="fa-light fa-copy icon"
|
||||
onClick={(e) => handleCopyPlaceholder(e)}
|
||||
onTouchEnd={(e) => handleCopyPlaceholder(e)}
|
||||
style={{ color: "#188ae2", right: "12px", top: "-18px" }}
|
||||
></i>
|
||||
{/* delete icon for all widgets */}
|
||||
<i
|
||||
className="fa-regular fa-circle-xmark signCloseBtn"
|
||||
className="fa-light fa-circle-xmark icon"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
//condition for template and placeholder flow
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data.Id);
|
||||
} else {
|
||||
}
|
||||
//condition for signyour-self flow
|
||||
else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
}}
|
||||
//for mobile and tablet touch event
|
||||
onTouchEnd={(e) => {
|
||||
e.stopPropagation();
|
||||
//condition for template and placeholder flow
|
||||
if (props.data) {
|
||||
props.handleDeleteSign(props.pos.key, props.data?.Id);
|
||||
} else {
|
||||
}
|
||||
//condition for signyour-self flow
|
||||
else {
|
||||
props.handleDeleteSign(props.pos.key);
|
||||
props.setIsStamp(false);
|
||||
}
|
||||
@@ -535,14 +634,83 @@ function Placeholder(props) {
|
||||
)
|
||||
);
|
||||
};
|
||||
const xPos = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
const resizePos = pos.xPosition;
|
||||
if (signYourself) {
|
||||
return resizePos * containerScale * props.scale;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (pos.isMobile && pos.scale) {
|
||||
if (props.scale > 1) {
|
||||
return resizePos * pos.scale * containerScale * props.scale;
|
||||
} else {
|
||||
return resizePos * pos.scale * containerScale;
|
||||
}
|
||||
} else {
|
||||
// if (pos.scale === containerScale) {
|
||||
// return resizePos * pos.scale * props.scale;
|
||||
// } else {
|
||||
return resizePos * containerScale * props.scale;
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
||||
const yPos = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
props.pdfOriginalWH,
|
||||
props.pageNumber,
|
||||
props.containerWH
|
||||
);
|
||||
const resizePos = pos.yPosition;
|
||||
|
||||
if (signYourself) {
|
||||
return resizePos * containerScale * props.scale;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (pos.isMobile && pos.scale) {
|
||||
if (props.scale > 1) {
|
||||
return resizePos * pos.scale * containerScale * props.scale;
|
||||
} else {
|
||||
return resizePos * pos.scale * containerScale;
|
||||
}
|
||||
}
|
||||
// else if (pos.scale === containerScale) {
|
||||
// if (props.scale > 1) {
|
||||
// return resizePos * pos.scale * props.scale;
|
||||
// } else {
|
||||
// return resizePos * pos.scale;
|
||||
// }
|
||||
// }
|
||||
else {
|
||||
return resizePos * containerScale * props.scale;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const getCursor = () => {
|
||||
if (props.data && props.isNeedSign) {
|
||||
if (props?.signerObjId && props.data?.signerObjId === props.signerObjId) {
|
||||
return "pointer";
|
||||
} else if (props?.uniqueId && props.data?.Id === props.uniqueId) {
|
||||
return "pointer";
|
||||
} else {
|
||||
return "not-allowed";
|
||||
}
|
||||
} else {
|
||||
return "all-scroll";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Rnd
|
||||
id={props.pos.key}
|
||||
data-tut={props.pos.key === props.unSignedWidgetId ? "IsSigned" : ""}
|
||||
key={props.pos.key}
|
||||
lockAspectRatio={
|
||||
// props.pos.type !== textWidget
|
||||
![
|
||||
textWidget,
|
||||
"email",
|
||||
@@ -586,12 +754,7 @@ function Placeholder(props) {
|
||||
props.pos.type !== "company" &&
|
||||
props.pos.type !== "job title" &&
|
||||
"center",
|
||||
cursor:
|
||||
props.data && props.isNeedSign
|
||||
? props.data?.signerObjId === props.signerObjId
|
||||
? "pointer"
|
||||
: "not-allowed"
|
||||
: "all-scroll",
|
||||
cursor: getCursor(),
|
||||
zIndex:
|
||||
props.pos.type === "date"
|
||||
? props.pos.key === props.selectWidgetId
|
||||
@@ -620,8 +783,20 @@ function Placeholder(props) {
|
||||
setDraggingEnabled(true);
|
||||
props.setIsResize && props.setIsResize(true);
|
||||
}}
|
||||
onResizeStop={() => {
|
||||
onResizeStop={(e, direction, ref) => {
|
||||
props.setIsResize && props.setIsResize(false);
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
containerScale,
|
||||
props.scale,
|
||||
props.data && props.data.Id,
|
||||
props.isResize
|
||||
);
|
||||
}}
|
||||
disableDragging={
|
||||
props.isNeedSign
|
||||
@@ -634,21 +809,15 @@ function Placeholder(props) {
|
||||
props.handleStop &&
|
||||
props.handleStop(event, dragElement, props.data?.Id, props.pos?.key)
|
||||
}
|
||||
default={{
|
||||
x: props.xPos(props.pos, props.isSignYourself),
|
||||
y: props.yPos(props.pos, props.isSignYourself)
|
||||
position={{
|
||||
x: xPos(props.pos, props.isSignYourself),
|
||||
y: yPos(props.pos, props.isSignYourself)
|
||||
}}
|
||||
onResize={(e, direction, ref) => {
|
||||
props.handleSignYourselfImageResize &&
|
||||
props.handleSignYourselfImageResize(
|
||||
ref,
|
||||
props.pos.key,
|
||||
props.xyPostion,
|
||||
props.setXyPostion,
|
||||
props.index,
|
||||
props.data && props.data.Id,
|
||||
false
|
||||
);
|
||||
setPlaceholderBorder({
|
||||
w: ref.offsetWidth / (props.scale * containerScale),
|
||||
h: ref.offsetHeight / (props.scale * containerScale)
|
||||
});
|
||||
}}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
@@ -677,20 +846,22 @@ function Placeholder(props) {
|
||||
pos={props.pos}
|
||||
isPlaceholder={props.isPlaceholder}
|
||||
getCheckboxRenderWidth={getCheckboxRenderWidth}
|
||||
scale={props.scale}
|
||||
containerScale={containerScale}
|
||||
placeholderBorder={placeholderBorder}
|
||||
/>
|
||||
)}
|
||||
{isMobile ? (
|
||||
{isTabAndMobile ? (
|
||||
<div
|
||||
className="flex items-stretch"
|
||||
style={{
|
||||
left: props.xPos(props.pos, props.isSignYourself),
|
||||
top: props.yPos(props.pos, props.isSignYourself),
|
||||
left: xPos(props.pos, props.isSignYourself),
|
||||
top: yPos(props.pos, props.isSignYourself),
|
||||
width:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
? "auto"
|
||||
: props.posWidth(props.pos, props.isSignYourself),
|
||||
// "auto", //props.posWidth(props.pos, props.isSignYourself),
|
||||
// height: props.posHeight(props.pos, props.isSignYourself),
|
||||
height:
|
||||
props.pos.type === radioButtonWidget ||
|
||||
props.pos.type === "checkbox"
|
||||
@@ -699,6 +870,7 @@ function Placeholder(props) {
|
||||
zIndex: "10"
|
||||
}}
|
||||
onTouchEnd={() => handleOnClickPlaceholder()}
|
||||
onClick={() => handleOnClickPlaceholder()}
|
||||
>
|
||||
{props.pos.key === props.selectWidgetId && <PlaceholderIcon />}
|
||||
|
||||
@@ -723,6 +895,7 @@ function Placeholder(props) {
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -749,6 +922,7 @@ function Placeholder(props) {
|
||||
setStartDate={setStartDate}
|
||||
startDate={startDate}
|
||||
handleSaveDate={handleSaveDate}
|
||||
xPos={props.xPos}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -11,23 +11,34 @@ function PlaceholderBorder(props) {
|
||||
const getResizeBorderExtraWidth = resizeBorderExtraWidth();
|
||||
const defaultWidth = defaultWidthHeight(props.pos.type).width;
|
||||
const defaultHeight = defaultWidthHeight(props.pos.type).height;
|
||||
const width = () => {
|
||||
const getWidth =
|
||||
props.placeholderBorder.w || props.pos.Width || defaultWidth;
|
||||
return (
|
||||
getWidth * props.scale * props.containerScale + getResizeBorderExtraWidth
|
||||
);
|
||||
};
|
||||
const height = () => {
|
||||
const getHeight =
|
||||
props.placeholderBorder.h || props.pos.Height || defaultHeight;
|
||||
|
||||
return (
|
||||
getHeight * props.scale * props.containerScale + getResizeBorderExtraWidth
|
||||
);
|
||||
};
|
||||
|
||||
const handleMinWidth = () => {
|
||||
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||
return props.getCheckboxRenderWidth.width + getResizeBorderExtraWidth;
|
||||
} else {
|
||||
return props.pos.Width
|
||||
? props.pos.Width + getResizeBorderExtraWidth
|
||||
: defaultWidth + getResizeBorderExtraWidth;
|
||||
return width();
|
||||
}
|
||||
};
|
||||
const handleMinHeight = () => {
|
||||
if (props.pos.type === "checkbox" || props.pos.type === radioButtonWidget) {
|
||||
return props.getCheckboxRenderWidth.height + getResizeBorderExtraWidth;
|
||||
} else {
|
||||
return props.pos.Height
|
||||
? props.pos.Height + getResizeBorderExtraWidth
|
||||
: defaultHeight + getResizeBorderExtraWidth;
|
||||
return height();
|
||||
}
|
||||
};
|
||||
return (
|
||||
@@ -36,14 +47,11 @@ function PlaceholderBorder(props) {
|
||||
onTouchEnd={() =>
|
||||
props.pos.type === textWidget && props?.setDraggingEnabled(true)
|
||||
}
|
||||
className="borderResize"
|
||||
className="absolute inline-block w-[14px] h-[14px] border-[0.2px] overflow-hidden border-dashed"
|
||||
style={{
|
||||
borderColor: themeColor,
|
||||
borderStyle: "dashed",
|
||||
minWidth: handleMinWidth(),
|
||||
minHeight: handleMinHeight(),
|
||||
borderWidth: "0.2px",
|
||||
overflow: "hidden"
|
||||
minWidth: handleMinWidth() || 0,
|
||||
minHeight: handleMinHeight() || 0
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,6 @@ import { randomId, textWidget } from "../../constant/Utils";
|
||||
function PlaceholderCopy(props) {
|
||||
const copyType = ["All pages", "All pages but last", "All pages but first"];
|
||||
const [selectCopyType, setSelectCopyType] = useState("");
|
||||
|
||||
//function for get copy placeholder position
|
||||
const getCopyPlaceholderPosition = (
|
||||
type,
|
||||
@@ -190,9 +189,7 @@ function PlaceholderCopy(props) {
|
||||
<ModalUi
|
||||
isOpen={props.isPageCopy}
|
||||
title={"Copy to all pages"}
|
||||
handleClose={() => {
|
||||
props.setIsPageCopy(false);
|
||||
}}
|
||||
handleClose={() => handleUniqueId()}
|
||||
>
|
||||
<div className="h-full p-[20px] text-base-content">
|
||||
{copyType.map((data, key) => {
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||
import {
|
||||
getMonth,
|
||||
getYear,
|
||||
isMobile,
|
||||
onChangeHeightOfTextArea,
|
||||
onChangeInput,
|
||||
radioButtonWidget,
|
||||
@@ -14,7 +13,11 @@ import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import "../../styles/signature.css";
|
||||
import RegexParser from "regex-parser";
|
||||
|
||||
import { emailRegex } from "../../constant/const";
|
||||
const textWidgetCls =
|
||||
"w-full h-full md:min-w-full md:min-h-full z-[999] text-[12px] rounded-[2px] border-[1px] border-[#007bff] overflow-hidden resize-none outline-none text-base-content item-center whitespace-pre-wrap bg-white";
|
||||
const selectWidgetCls =
|
||||
"w-full h-full absolute left-0 top-0 border-[1px] border-[#007bff] rounded-[2px] focus:outline-none text-base-content";
|
||||
function PlaceholderType(props) {
|
||||
const type = props?.pos?.type;
|
||||
const [selectOption, setSelectOption] = useState("");
|
||||
@@ -59,7 +62,7 @@ function PlaceholderType(props) {
|
||||
if (validateType && validateType !== "text") {
|
||||
switch (validateType) {
|
||||
case "email":
|
||||
regexValidation = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
regexValidation = emailRegex;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "number":
|
||||
@@ -137,13 +140,12 @@ function PlaceholderType(props) {
|
||||
|
||||
const ExampleCustomInput = forwardRef(({ value, onClick }, ref) => (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ overflow: "hidden", fontSize: calculateFontSize() }}
|
||||
className={`${selectWidgetCls} text-[12px] overflow-hidden`}
|
||||
onClick={onClick}
|
||||
ref={ref}
|
||||
>
|
||||
{value}
|
||||
<i className="fa-regular fa-calendar" style={{ marginLeft: "5px" }}></i>
|
||||
<i className="fa-light fa-calendar ml-[5px]"></i>
|
||||
</div>
|
||||
));
|
||||
ExampleCustomInput.displayName = "ExampleCustomInput";
|
||||
@@ -169,12 +171,6 @@ function PlaceholderType(props) {
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [type]);
|
||||
|
||||
const calculateFontSize = () => {
|
||||
const fontSize = 10 + Math.min(props.pos.Width, props.pos.Height) * 0.1;
|
||||
const size = fontSize ? fontSize : 12;
|
||||
return size + "px";
|
||||
};
|
||||
//function for show checked checkbox
|
||||
const selectCheckbox = (ind) => {
|
||||
const res = props.pos.options?.response;
|
||||
@@ -304,17 +300,13 @@ function PlaceholderType(props) {
|
||||
case "signature":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
alt="signature"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
className="w-full h-full "
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
@@ -326,17 +318,13 @@ function PlaceholderType(props) {
|
||||
case "stamp":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
alt="stamp"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
@@ -417,19 +405,23 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
@@ -438,9 +430,8 @@ function PlaceholderType(props) {
|
||||
case "dropdown":
|
||||
return props.data?.signerObjId === props.signerObjId ? (
|
||||
<select
|
||||
className="inputPlaceholder"
|
||||
className={`${selectWidgetCls} text-[12px] bg-inherit`}
|
||||
id="myDropdown"
|
||||
style={{ fontSize: "12px", color: "initial" }}
|
||||
value={selectOption}
|
||||
onChange={(e) => {
|
||||
setSelectOption(e.target.value);
|
||||
@@ -460,7 +451,7 @@ function PlaceholderType(props) {
|
||||
{props?.pos?.options?.name}
|
||||
</option>
|
||||
|
||||
{props.pos?.options?.values.map((data, ind) => {
|
||||
{props.pos?.options?.values?.map((data, ind) => {
|
||||
return (
|
||||
<option key={ind} value={data}>
|
||||
{data}
|
||||
@@ -469,27 +460,20 @@ function PlaceholderType(props) {
|
||||
})}
|
||||
</select>
|
||||
) : (
|
||||
<div
|
||||
className="inputPlaceholder"
|
||||
style={{ fontSize: calculateFontSize() }}
|
||||
>
|
||||
<div className="text-[12px] overflow-hidden">
|
||||
{props.pos?.options?.name ? props.pos.options.name : type}
|
||||
</div>
|
||||
);
|
||||
case "initials":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
alt="initials"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
@@ -520,19 +504,24 @@ function PlaceholderType(props) {
|
||||
isDefault
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
@@ -559,19 +548,24 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
overflow: "hidden",
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
@@ -598,26 +592,32 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
</div>
|
||||
);
|
||||
case "date":
|
||||
return (
|
||||
return props.isSignYourself ||
|
||||
(props.isNeedSign && props.data?.signerObjId === props.signerObjId) ? (
|
||||
<div>
|
||||
<DatePicker
|
||||
renderCustomHeader={({ date, changeYear, changeMonth }) => (
|
||||
@@ -655,8 +655,7 @@ function PlaceholderType(props) {
|
||||
}
|
||||
onBlur={handleInputBlur}
|
||||
closeOnScroll={true}
|
||||
className="inputPlaceholder"
|
||||
style={{ outlineColor: "#007bff" }}
|
||||
className={`${selectWidgetCls} outline-[#007bff]`}
|
||||
selected={
|
||||
props?.startDate
|
||||
? props?.startDate
|
||||
@@ -675,21 +674,27 @@ function PlaceholderType(props) {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-[12px] text-black items-center overflow-hidden">
|
||||
<span>
|
||||
{props.selectDate
|
||||
? props.selectDate?.format
|
||||
: props.pos?.options?.validation?.format
|
||||
? props.pos?.options?.validation?.format
|
||||
: "MM/dd/yyyy"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
case "image":
|
||||
return props.pos.SignUrl ? (
|
||||
<img
|
||||
alt="signimg"
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
) : (
|
||||
<div style={{ fontSize: 11, color: "black", justifyContent: "center" }}>
|
||||
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(
|
||||
props?.data?.Id,
|
||||
@@ -720,19 +725,25 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
style={{ whiteSpace: "pre-wrap", color: "initial" }}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontFamily: "Arial, sans-serif"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
color: "black",
|
||||
fontSize: calculateFontSize()
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
overflow: "hidden"
|
||||
}}
|
||||
>
|
||||
<span>{type}</span>
|
||||
@@ -745,11 +756,9 @@ function PlaceholderType(props) {
|
||||
return (
|
||||
<div key={ind} className="flex items-center text-center gap-0.5">
|
||||
<input
|
||||
className={`${ind === 0 ? "mt-0" : "mt-[5px]"} ${
|
||||
props?.pos?.Width
|
||||
? `w-[${props?.pos?.Width}px] h-[${props?.pos?.Width}px]`
|
||||
: "w-[15px] h-[15px]"
|
||||
} flex justify-center op-radio`}
|
||||
className={`${
|
||||
ind === 0 ? "mt-0" : "mt-[5px]"
|
||||
} ${"w-[15px] h-[15px]"} flex justify-center op-radio`}
|
||||
type="radio"
|
||||
disabled={
|
||||
props.isNeedSign &&
|
||||
@@ -791,41 +800,29 @@ function PlaceholderType(props) {
|
||||
false
|
||||
);
|
||||
}}
|
||||
className={
|
||||
isMobile
|
||||
? "labelTextArea labelWidthMobile"
|
||||
: "labelTextArea labelWidthDesktop"
|
||||
}
|
||||
className={textWidgetCls}
|
||||
style={{
|
||||
whiteSpace: "pre-wrap",
|
||||
overflow: "hidden",
|
||||
color: "initial"
|
||||
fontFamily: "Arial, sans-serif",
|
||||
fontSize: props.pos.options?.fontSize
|
||||
? props.pos.options?.fontSize + "px"
|
||||
: "12px",
|
||||
color: props.pos.options?.fontColor || "black"
|
||||
}}
|
||||
cols="50"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return props.pos.SignUrl ? (
|
||||
<div style={{ pointerEvents: "none" }}>
|
||||
<div className="pointer-events-none">
|
||||
<img
|
||||
alt="signimg"
|
||||
alt="image"
|
||||
draggable="false"
|
||||
src={props.pos.SignUrl}
|
||||
style={{
|
||||
width: "99%",
|
||||
height: "100%",
|
||||
objectFit: "contain"
|
||||
}}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
fontSize: "10px",
|
||||
color: "black",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<div className="text-[11px] w-full h-full text-black flex flex-col justify-center items-center">
|
||||
{props.pos.isStamp ? <div>stamp</div> : <div>signature</div>}
|
||||
{props?.handleUserName &&
|
||||
props?.handleUserName(props?.data?.Id, props?.data?.Role)}
|
||||
|
||||
@@ -13,27 +13,27 @@ function PrevNext({ pageNumber, allPages, changePage }) {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||
className="op-btn op-btn-neutral op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||
disabled={pageNumber <= 1}
|
||||
onClick={previousPage}
|
||||
>
|
||||
<span className="block lg:hidden">
|
||||
<i className="fa fa-backward" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-backward" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span className="lg:block hidden">Prev</span>
|
||||
<span className="lg:block hidden 2xl:text-[30px]">Prev</span>
|
||||
</button>
|
||||
<span className="text-xs text-base-content font-medium mx-2">
|
||||
<span className="text-xs text-base-content font-medium mx-2 2xl:text-[30px]">
|
||||
{pageNumber || (allPages ? 1 : "--")} of {allPages || "--"}
|
||||
</span>
|
||||
<button
|
||||
className="op-btn op-btn-secondary op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||
className="op-btn op-btn-neutral op-btn-xs md:op-btn-sm font-semibold text-xs"
|
||||
disabled={pageNumber >= allPages}
|
||||
onClick={nextPage}
|
||||
>
|
||||
<span className="block lg:hidden">
|
||||
<i className="fa fa-forward" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-forward" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span className="lg:block hidden">Next</span>
|
||||
<span className="lg:block hidden 2xl:text-[30px]">Next</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@ const RecipientList = (props) => {
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
//function for onhover signer name change background color
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const isWidgetExist = (Id) => {
|
||||
return props.signerPos.some((x) => x.Id === Id);
|
||||
};
|
||||
@@ -62,17 +61,28 @@ const RecipientList = (props) => {
|
||||
);
|
||||
//splice method is used to replace or add new value in array at specific index
|
||||
remainingItems.splice(index, 0, ...draggedItem);
|
||||
|
||||
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
|
||||
props.setSignersData(remainingItems);
|
||||
props.setSignerObjId(remainingItems[index]?.objectId || "");
|
||||
props.setIsSelectId(index);
|
||||
props.setContractName(remainingItems[index]?.className || "");
|
||||
props.setUniqueId(remainingItems[index]?.Id);
|
||||
props.setRoleName(remainingItems[index]?.Role);
|
||||
props.setBlockColor(remainingItems[index]?.blockColor);
|
||||
};
|
||||
props?.setSignersData(remainingItems);
|
||||
props?.setSignerObjId(remainingItems[index]?.objectId || "");
|
||||
props?.setIsSelectId(index);
|
||||
props?.setContractName(remainingItems[index]?.className || "");
|
||||
props?.setUniqueId(remainingItems[index]?.Id);
|
||||
props?.setRoleName(remainingItems[index]?.Role);
|
||||
props?.setBlockColor(remainingItems[index]?.blockColor);
|
||||
|
||||
//change order of placeholder's list using sorting method
|
||||
//`remainingItems` is correct order of signers after change order
|
||||
const changeOrderSignerList = props?.signerPos.sort((a, b) => {
|
||||
//`indexA` and `indexB` is to get element position using index in correct order array
|
||||
const indexA = remainingItems.findIndex((item) => item.Id === a.Id);
|
||||
const indexB = remainingItems.findIndex((item) => item.Id === b.Id);
|
||||
//and then compare `indexA - indexB` value
|
||||
//if positive it means indexB element comes before indexA then need to sorting
|
||||
//if negative it means indexA element is on correct position do not need to sorting
|
||||
return indexA - indexB;
|
||||
});
|
||||
props?.setSignerPos(changeOrderSignerList);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{props.signersdata.length > 0 &&
|
||||
@@ -91,7 +101,7 @@ const RecipientList = (props) => {
|
||||
onDrop={(e) =>
|
||||
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
|
||||
}
|
||||
data-tut="reactourFirst"
|
||||
data-tut="recipientArea"
|
||||
onMouseEnter={() => setIsHover(ind)}
|
||||
onMouseLeave={() => setIsHover(null)}
|
||||
className={`${
|
||||
@@ -103,7 +113,7 @@ const RecipientList = (props) => {
|
||||
} flex flex-row rounded-xl px-2 py-[10px] mt-1 mx-1 items-center last:mb-0.5`}
|
||||
style={{
|
||||
background:
|
||||
(!isMobile && isHover === ind) || props.isSelectListId === ind
|
||||
(!isMobile && isHover === ind) || props.uniqueId === obj.Id
|
||||
? obj?.blockColor
|
||||
? obj?.blockColor
|
||||
: color[ind % color.length]
|
||||
@@ -133,7 +143,7 @@ const RecipientList = (props) => {
|
||||
>
|
||||
<span className="text-white uppercase font-bold text-center text-[12px]">
|
||||
{isWidgetExist(obj.Id) ? (
|
||||
<i className="fa-solid fa-check"></i>
|
||||
<i className="fa-light fa-check"></i>
|
||||
) : (
|
||||
<>
|
||||
{obj.Name
|
||||
@@ -145,7 +155,7 @@ const RecipientList = (props) => {
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
obj?.Name ? "flex-col" : "flex-row"
|
||||
obj.Name ? "flex-col" : "flex-row"
|
||||
} flex items-center`}
|
||||
>
|
||||
{obj.Name ? (
|
||||
@@ -175,7 +185,7 @@ const RecipientList = (props) => {
|
||||
{isEdit?.[obj.Id] && props.handleRoleChange ? (
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="bg-transparent p-[3px"
|
||||
className="bg-transparent p-[3px]"
|
||||
value={obj.Role}
|
||||
onChange={(e) => props.handleRoleChange(e, obj.Id)}
|
||||
onBlur={() => {
|
||||
@@ -190,7 +200,9 @@ const RecipientList = (props) => {
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span className="p-[3px]">{obj.Role}</span>
|
||||
<span className="text-[13px] 2xl:text-[25px]">
|
||||
{obj.Role}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
@@ -209,7 +221,7 @@ const RecipientList = (props) => {
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && props.sendInOrder && (
|
||||
<div className="flex flex-row items-center gap-[5px]">
|
||||
<div className="flex flex-row items-center gap-[5px] mr-2">
|
||||
<div
|
||||
onClick={(e) => {
|
||||
if (ind !== 0) {
|
||||
@@ -249,9 +261,9 @@ const RecipientList = (props) => {
|
||||
props.isSelectListId === ind
|
||||
? "text-[#424242]"
|
||||
: "text-base-content"
|
||||
} cursor-pointer ml-[5px]`}
|
||||
} cursor-pointer`}
|
||||
>
|
||||
<i className="fa-regular fa-trash-can"></i>
|
||||
<i className="fa-light fa-trash-can 2xl:text-[30px]"></i>
|
||||
</div>
|
||||
)}
|
||||
<hr />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from "react";
|
||||
import RSC from "react-scrollbars-custom";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
function RenderAllPdfPage({
|
||||
signPdfUrl,
|
||||
@@ -10,7 +10,8 @@ function RenderAllPdfPage({
|
||||
setSignBtnPosition,
|
||||
pageNumber,
|
||||
signerPos,
|
||||
signerObjectId
|
||||
signerObjectId,
|
||||
containerWH
|
||||
}) {
|
||||
const [signPageNumber, setSignPageNumber] = useState([]);
|
||||
const [bookmarkColor, setBookmarkColor] = useState("");
|
||||
@@ -33,7 +34,22 @@ function RenderAllPdfPage({
|
||||
}
|
||||
}
|
||||
}
|
||||
const pageContainer = useRef();
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const [pageWidth, setPageWidth] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const updateSize = () => {
|
||||
if (pageContainer.current) {
|
||||
setPageWidth(pageContainer.current.offsetWidth);
|
||||
}
|
||||
};
|
||||
|
||||
// Use setTimeout to wait for the transition to complete
|
||||
const timer = setTimeout(updateSize, 100); // match the transition duration
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [isHeader, pageContainer, containerWH]);
|
||||
//'function `addSignatureBookmark` is used to display the page where the user's signature is located.
|
||||
const addSignatureBookmark = (index) => {
|
||||
const ispageNumber = signPageNumber.includes(index + 1);
|
||||
@@ -49,61 +65,47 @@ function RenderAllPdfPage({
|
||||
);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className=" hidden md:flex flex-row bg-base-100 h-full">
|
||||
<div className="w-[140px]">
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
Pages
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<RSC
|
||||
id="RSC-Example"
|
||||
style={{
|
||||
width: "135px",
|
||||
height: window.innerHeight - 130 + "px"
|
||||
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
Pages
|
||||
</div>
|
||||
<div
|
||||
className={`flex h-[90%] flex-col items-center m-2
|
||||
autoSignScroll hide-scrollbar max-h-[100vh] `}
|
||||
>
|
||||
<Document
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={onDocumentLoad}
|
||||
file={signPdfUrl}
|
||||
>
|
||||
{Array.from(new Array(allPages), (el, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${
|
||||
pageNumber - 1 === index ? "border-[red]" : "border-[#878787]"
|
||||
} border-2 m-[10px] flex justify-center items-center relative`}
|
||||
onClick={() => {
|
||||
setPageNumber(index + 1);
|
||||
if (setSignBtnPosition) {
|
||||
setSignBtnPosition([]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Document
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={onDocumentLoad}
|
||||
file={signPdfUrl}
|
||||
>
|
||||
{Array.from(new Array(allPages), (el, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`${
|
||||
pageNumber - 1 === index
|
||||
? "border-[red]"
|
||||
: "border-[#878787]"
|
||||
} border-2 w-[100px] m-[10px] flex justify-center items-center relative`}
|
||||
onClick={() => {
|
||||
setPageNumber(index + 1);
|
||||
if (setSignBtnPosition) {
|
||||
setSignBtnPosition([]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{signerPos && addSignatureBookmark(index)}
|
||||
{signerPos && addSignatureBookmark(index)}
|
||||
|
||||
<div className="relative z-[1] overflow-hidden">
|
||||
<Page
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={100}
|
||||
height={100}
|
||||
scale={1}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Document>
|
||||
</RSC>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
<div className="relative z-[1] overflow-hidden">
|
||||
<Page
|
||||
key={`page_${index + 1}`}
|
||||
pageNumber={index + 1}
|
||||
width={pageWidth - 60}
|
||||
scale={1}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import RSC from "react-scrollbars-custom";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import {
|
||||
defaultWidthHeight,
|
||||
getContainerScale,
|
||||
handleImageResize,
|
||||
handleSignYourselfImageResize
|
||||
} from "../../constant/Utils";
|
||||
@@ -11,8 +12,6 @@ import Alert from "../../primitives/Alert";
|
||||
|
||||
function RenderPdf({
|
||||
pageNumber,
|
||||
pdfOriginalWidth,
|
||||
pdfNewWidth,
|
||||
drop,
|
||||
signerPos,
|
||||
successEmail,
|
||||
@@ -26,15 +25,13 @@ function RenderPdf({
|
||||
pdfDetails,
|
||||
xyPostion,
|
||||
pdfUrl,
|
||||
numPages,
|
||||
pageDetails,
|
||||
pdfRequest,
|
||||
setCurrentSigner,
|
||||
signerObjectId,
|
||||
signedSigners,
|
||||
setPdfLoadFail,
|
||||
pdfLoad,
|
||||
setPdfLoad,
|
||||
placeholder,
|
||||
pdfLoadFail,
|
||||
setSignerPos,
|
||||
setXyPostion,
|
||||
index,
|
||||
@@ -57,165 +54,94 @@ function RenderPdf({
|
||||
unSignedWidgetId,
|
||||
setIsCheckbox,
|
||||
handleNameModal,
|
||||
handleTextSettingModal,
|
||||
setTempSignerId,
|
||||
uniqueId
|
||||
uniqueId,
|
||||
pdfOriginalWH,
|
||||
scale,
|
||||
setIsSelectId,
|
||||
ispublicTemplate,
|
||||
handleUserDetails
|
||||
}) {
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const newWidth = containerWH.width;
|
||||
const scale = isMobile ? pdfOriginalWidth / newWidth : 1;
|
||||
//check isGuestSigner is present in local if yes than handle login flow header in mobile view
|
||||
const isGuestSigner = localStorage.getItem("isGuestSigner");
|
||||
|
||||
// handle signature block width and height according to screen
|
||||
const posWidth = (pos, signYourself) => {
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
pageNumber,
|
||||
containerWH
|
||||
);
|
||||
const defaultWidth = defaultWidthHeight(pos.type).width;
|
||||
const posWidth = pos.Width ? pos.Width : defaultWidth;
|
||||
let width;
|
||||
if (signYourself) {
|
||||
width = posWidth;
|
||||
return width;
|
||||
return posWidth * scale * containerScale;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
if (pos.isMobile && pos.scale) {
|
||||
if (pos.IsResize) {
|
||||
if (scale > 1) {
|
||||
return posWidth * pos.scale * containerScale * scale;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) / scale;
|
||||
|
||||
return width;
|
||||
return posWidth * containerScale;
|
||||
}
|
||||
} else {
|
||||
width = posWidth;
|
||||
return width;
|
||||
if (scale > 1) {
|
||||
return posWidth * pos.scale * containerScale * scale;
|
||||
} else {
|
||||
return posWidth * pos.scale * containerScale;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
} else {
|
||||
width = (posWidth || defaultWidth) * pos.scale;
|
||||
return width;
|
||||
}
|
||||
} else {
|
||||
width = posWidth ? posWidth : defaultWidth;
|
||||
return width;
|
||||
}
|
||||
return posWidth * scale * containerScale;
|
||||
}
|
||||
}
|
||||
};
|
||||
const posHeight = (pos, signYourself) => {
|
||||
let height;
|
||||
const posHeight = pos.Height;
|
||||
const defaultHeight = defaultWidthHeight(pos.type).height;
|
||||
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
pageNumber,
|
||||
containerWH
|
||||
);
|
||||
const posHeight = pos.Height || defaultWidthHeight(pos.type).height;
|
||||
if (signYourself) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
|
||||
return height;
|
||||
return posHeight * scale * containerScale;
|
||||
} else {
|
||||
if (isMobile) {
|
||||
if (!pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
if (pos.isMobile && pos.scale) {
|
||||
if (pos.IsResize) {
|
||||
if (scale > 1) {
|
||||
return posHeight * pos.scale * containerScale * scale;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) / scale;
|
||||
|
||||
return height;
|
||||
return posHeight * containerScale;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
} else {
|
||||
if (pos.isMobile) {
|
||||
if (pos.IsResize) {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
if (scale > 1) {
|
||||
return posHeight * pos.scale * containerScale * scale;
|
||||
} else {
|
||||
height = (posHeight || defaultHeight) * pos.scale;
|
||||
return height;
|
||||
return posHeight * pos.scale * containerScale;
|
||||
}
|
||||
} else {
|
||||
height = posHeight ? posHeight : defaultHeight;
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const xPos = (pos, signYourself) => {
|
||||
const resizePos = pos.xPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
return posHeight * scale * containerScale;
|
||||
}
|
||||
}
|
||||
};
|
||||
const yPos = (pos, signYourself) => {
|
||||
const resizePos = pos.yPosition;
|
||||
if (signYourself) {
|
||||
return resizePos;
|
||||
} else {
|
||||
//checking both condition mobile and desktop view
|
||||
if (isMobile) {
|
||||
//if pos.isMobile false -- placeholder saved from desktop view then handle position in mobile view divided by scale
|
||||
if (!pos.isMobile) {
|
||||
return resizePos / scale;
|
||||
}
|
||||
//pos.isMobile true -- placeholder save from mobile view(small device) handle position in mobile view(small screen) view divided by scale
|
||||
else {
|
||||
return resizePos * (pos.scale / scale);
|
||||
}
|
||||
} else {
|
||||
//else if pos.isMobile true -- placeholder saved from mobile or tablet view then handle position in desktop view divide by scale
|
||||
|
||||
if (pos.isMobile) {
|
||||
return pos.scale && resizePos * pos.scale;
|
||||
}
|
||||
//else placeholder save from desktop(bigscreen) and show in desktop(bigscreen)
|
||||
else {
|
||||
return resizePos;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//function for render placeholder block over pdf document
|
||||
const checkSignedSignes = (data) => {
|
||||
const checkSignedSigners = (data) => {
|
||||
let checkSign = [];
|
||||
//condition to handle quick send flow and using normal request sign flow
|
||||
checkSign = signedSigners.filter(
|
||||
(sign) => sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
|
||||
);
|
||||
if (data.signerObjId === signerObjectId) {
|
||||
setCurrentSigner(true);
|
||||
}
|
||||
checkSign = signedSigners
|
||||
? signedSigners?.filter(
|
||||
(sign) =>
|
||||
sign?.Id === data?.Id || sign?.objectId === data?.signerObjId
|
||||
)
|
||||
: [];
|
||||
const handleAllUserName = (Id, Role, type) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="text-black text-[8px] font-medium">
|
||||
<div className="text-black text-[8px] font-bold">
|
||||
{
|
||||
pdfDetails[0].Signers?.find(
|
||||
(signer) => signer.objectId === data.signerObjId
|
||||
@@ -231,7 +157,6 @@ function RenderPdf({
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
checkSign.length === 0 &&
|
||||
data.placeHolder.map((placeData, key) => {
|
||||
@@ -258,8 +183,6 @@ function RenderPdf({
|
||||
isShowDropdown={true}
|
||||
isNeedSign={true}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
handleUserName={handleAllUserName}
|
||||
@@ -271,6 +194,13 @@ function RenderPdf({
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
uniqueId={uniqueId}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
ispublicTemplate={ispublicTemplate}
|
||||
handleUserDetails={handleUserDetails}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
@@ -281,6 +211,7 @@ function RenderPdf({
|
||||
})
|
||||
);
|
||||
};
|
||||
//function for render placeholder block over pdf document
|
||||
|
||||
const handleUserName = (Id, Role, type) => {
|
||||
if (Id) {
|
||||
@@ -316,26 +247,25 @@ function RenderPdf({
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
|
||||
{isMobile && scale ? (
|
||||
{isMobile ? (
|
||||
<div
|
||||
data-tut="reactourForth"
|
||||
style={{
|
||||
border: "0.1px solid #ebe8e8",
|
||||
marginTop: isGuestSigner && "30px"
|
||||
}}
|
||||
className={`${
|
||||
isGuestSigner ? "30px" : ""
|
||||
} border-[0.1px] border-[#ebe8e8]`}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
{pdfLoadFail.status &&
|
||||
{containerWH?.width &&
|
||||
pdfOriginalWH.length > 0 &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
{checkSignedSigners(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
@@ -374,8 +304,6 @@ function RenderPdf({
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
@@ -391,6 +319,14 @@ function RenderPdf({
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -422,13 +358,10 @@ function RenderPdf({
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
@@ -441,6 +374,12 @@ function RenderPdf({
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
)
|
||||
);
|
||||
@@ -452,7 +391,9 @@ function RenderPdf({
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Document
|
||||
onLoadError={() => setPdfLoadFail(true)}
|
||||
onLoadError={() => {
|
||||
setPdfLoad(false);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
// ref={pdfRef}'
|
||||
@@ -469,19 +410,16 @@ function RenderPdf({
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
width={containerWH.width}
|
||||
height={containerWH.height}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
width={containerWH.width}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
</Document>
|
||||
</div>
|
||||
</div>
|
||||
@@ -490,29 +428,31 @@ function RenderPdf({
|
||||
style={{
|
||||
position: "relative",
|
||||
boxShadow: "rgba(17, 12, 46, 0.15) 0px 48px 100px 0px",
|
||||
width:
|
||||
pdfOriginalWidth > pdfNewWidth ? pdfNewWidth : pdfOriginalWidth,
|
||||
height: window.innerHeight - 110 + "px"
|
||||
height: window.innerHeight + "px",
|
||||
zIndex: 0
|
||||
}}
|
||||
noScrollY={false}
|
||||
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
|
||||
noScrollX={scale === 1 ? true : false}
|
||||
>
|
||||
<div
|
||||
data-tut="reactourForth"
|
||||
style={{ border: "0.1px solid #ebe8e8", width: pdfOriginalWidth }}
|
||||
style={{
|
||||
width: containerWH?.width && containerWH?.width * scale
|
||||
}}
|
||||
ref={drop}
|
||||
id="container"
|
||||
>
|
||||
{pdfLoadFail.status &&
|
||||
(pdfRequest
|
||||
? signerPos.map((data, key) => {
|
||||
{pdfLoad &&
|
||||
containerWH?.width &&
|
||||
pdfOriginalWH.length > 0 &&
|
||||
(pdfRequest //pdf request sign flow
|
||||
? signerPos?.map((data, key) => {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{checkSignedSignes(data)}
|
||||
{checkSignedSigners(data)}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
: placeholder
|
||||
: placeholder //placeholder and template flow
|
||||
? signerPos.map((data, ind) => {
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
@@ -547,8 +487,6 @@ function RenderPdf({
|
||||
handleLinkUser={handleLinkUser}
|
||||
handleUserName={handleUserName}
|
||||
isSignYourself={false}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
isDragging={isDragging}
|
||||
@@ -564,6 +502,14 @@ function RenderPdf({
|
||||
handleNameModal={handleNameModal}
|
||||
setTempSignerId={setTempSignerId}
|
||||
uniqueId={uniqueId}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
setIsSelectId={setIsSelectId}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
@@ -575,53 +521,55 @@ function RenderPdf({
|
||||
);
|
||||
})
|
||||
: xyPostion.map((data, ind) => {
|
||||
// signyourself flow
|
||||
return (
|
||||
<React.Fragment key={ind}>
|
||||
{data.pageNumber === pageNumber &&
|
||||
data.pos.map((pos) => {
|
||||
return (
|
||||
pos && (
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={(event, dragElement) =>
|
||||
handleStop(event, dragElement, pos.type)
|
||||
}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
containerWH={containerWH}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
xPos={xPos}
|
||||
yPos={yPos}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
<React.Fragment key={pos.key}>
|
||||
<Placeholder
|
||||
pos={pos}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setSignKey={setSignKey}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setIsStamp={setIsStamp}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={(event, dragElement) =>
|
||||
handleStop(event, dragElement, pos.type)
|
||||
}
|
||||
handleSignYourselfImageResize={
|
||||
handleSignYourselfImageResize
|
||||
}
|
||||
index={index}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
setIsSignPad={setIsSignPad}
|
||||
isShowBorder={true}
|
||||
isSignYourself={true}
|
||||
posWidth={posWidth}
|
||||
posHeight={posHeight}
|
||||
pdfDetails={pdfDetails[0]}
|
||||
isDragging={isDragging}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
handleUserName={handleUserName}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setValidateAlert={setValidateAlert}
|
||||
setCurrWidgetsDetails={
|
||||
setCurrWidgetsDetails
|
||||
}
|
||||
handleTextSettingModal={
|
||||
handleTextSettingModal
|
||||
}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pageNumber={pageNumber}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
@@ -631,11 +579,7 @@ function RenderPdf({
|
||||
{/* this component for render pdf document is in middle of the component */}
|
||||
<Document
|
||||
onLoadError={() => {
|
||||
const load = {
|
||||
status: false,
|
||||
type: "failed"
|
||||
};
|
||||
setPdfLoadFail(load);
|
||||
setPdfLoad(false);
|
||||
}}
|
||||
loading={"Loading Document.."}
|
||||
onLoadSuccess={pageDetails}
|
||||
@@ -653,17 +597,18 @@ function RenderPdf({
|
||||
: pdfDetails[0].URL
|
||||
}
|
||||
>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page
|
||||
key={index}
|
||||
pageNumber={pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<Page
|
||||
key={index}
|
||||
width={containerWH.width}
|
||||
scale={scale || 1}
|
||||
className={"-z-[1]"} // when user zoom-in in tablet widgets move backward that's why pass -z-[1]
|
||||
pageNumber={pageNumber}
|
||||
renderAnnotationLayer={false}
|
||||
renderTextLayer={false}
|
||||
onGetAnnotationsError={(error) => {
|
||||
console.log("annotation error", error);
|
||||
}}
|
||||
/>
|
||||
</Document>
|
||||
</div>
|
||||
</RSC>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import SignatureCanvas from "react-signature-canvas";
|
||||
|
||||
function SignPad({
|
||||
isSignPad,
|
||||
isStamp,
|
||||
@@ -61,7 +60,6 @@ function SignPad({
|
||||
} else if (isStamp) {
|
||||
setImage("");
|
||||
}
|
||||
|
||||
setIsSignImg("");
|
||||
} else if (isTab === "uploadImage") {
|
||||
setImage("");
|
||||
@@ -69,11 +67,10 @@ function SignPad({
|
||||
// setIsInitial(false);
|
||||
};
|
||||
//function for set signature url
|
||||
const handleSignatureChange = () => {
|
||||
setSignature(canvasRef.current.toDataURL());
|
||||
setIsSignImg(canvasRef.current.toDataURL());
|
||||
const handleSignatureChange = (data) => {
|
||||
setSignature(data);
|
||||
setIsSignImg(data);
|
||||
};
|
||||
|
||||
//save button component
|
||||
const SaveBtn = () => {
|
||||
return (
|
||||
@@ -101,8 +98,15 @@ function SignPad({
|
||||
} else {
|
||||
if (isTab === "type") {
|
||||
setIsSignImg("");
|
||||
onSaveSign(null, false, textWidth, textHeight);
|
||||
onSaveSign(
|
||||
null,
|
||||
false,
|
||||
!isInitial && textWidth > 150 ? 150 : textWidth,
|
||||
!isInitial && textHeight > 35 ? 35 : textHeight
|
||||
);
|
||||
} else {
|
||||
setIsSignImg("");
|
||||
canvasRef.current.clear();
|
||||
onSaveSign(signatureType);
|
||||
}
|
||||
}
|
||||
@@ -127,6 +131,16 @@ function SignPad({
|
||||
? ""
|
||||
: "pointer-events-none"
|
||||
} op-btn op-btn-primary shadow-lg`}
|
||||
disabled={
|
||||
(isTab === "draw" && isSignImg) ||
|
||||
(isTab === "image" && image) ||
|
||||
(isTab === "mysignature" && isDefaultSign) ||
|
||||
(isTab === "type" && textWidth)
|
||||
? false
|
||||
: image
|
||||
? false
|
||||
: true
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
@@ -139,7 +153,6 @@ function SignPad({
|
||||
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) {
|
||||
@@ -154,8 +167,8 @@ function SignPad({
|
||||
}
|
||||
}
|
||||
|
||||
const trimmedName = currentUserName.trim();
|
||||
const firstCharacter = trimmedName.charAt(0);
|
||||
const trimmedName = currentUserName && currentUserName.trim();
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName = isInitial ? firstCharacter : currentUserName;
|
||||
setSignValue(userName);
|
||||
setFontSelect("Fasthand");
|
||||
@@ -193,7 +206,6 @@ function SignPad({
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
|
||||
//function for convert input text value in image
|
||||
const convertToImg = async (fontStyle, text, color) => {
|
||||
//get text content to convert in image
|
||||
@@ -203,11 +215,11 @@ function SignPad({
|
||||
: fontSelect
|
||||
? fontSelect
|
||||
: "Fasthand";
|
||||
|
||||
const fontSizeValue = "40px";
|
||||
//creating span for getting text content width
|
||||
const span = document.createElement("span");
|
||||
span.textContent = textContent;
|
||||
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
|
||||
span.style.font = `${fontSizeValue} ${fontfamily}`; // here put your text size and font family
|
||||
span.style.color = color ? color : penColor;
|
||||
span.style.display = "hidden";
|
||||
document.body.appendChild(span); // Replace 'container' with the ID of the container element
|
||||
@@ -217,7 +229,8 @@ function SignPad({
|
||||
// Draw the text content on the canvas
|
||||
const ctx = canvasElement.getContext("2d");
|
||||
const pixelRatio = window.devicePixelRatio || 1;
|
||||
const width = span.offsetWidth;
|
||||
const addExtraWidth = isInitial ? 10 : 50;
|
||||
const width = span.offsetWidth + addExtraWidth;
|
||||
const height = span.offsetHeight;
|
||||
setTextWidth(width);
|
||||
setTextHeight(height);
|
||||
@@ -271,7 +284,7 @@ function SignPad({
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa solid fa-pen-nib"
|
||||
className="fa-light fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
@@ -290,8 +303,9 @@ function SignPad({
|
||||
<div className="flex flex-row justify-between mt-[3px]">
|
||||
<div className="flex flex-row justify-between gap-[5px] md:gap-[8px] text-[11px] md:text-base">
|
||||
{isStamp ? (
|
||||
<span className="op-link-primary op-link">
|
||||
{widgetType === "image"
|
||||
<span className="text-base-content font-bold text-lg">
|
||||
{widgetType === "image" ||
|
||||
currWidgetsDetails?.type === "image"
|
||||
? "Upload image"
|
||||
: "Upload stamp image"}
|
||||
</span>
|
||||
@@ -304,6 +318,9 @@ function SignPad({
|
||||
setIsImageSelect(false);
|
||||
setIsTab("draw");
|
||||
setImage();
|
||||
if (isSignImg) {
|
||||
setSignature(isSignImg);
|
||||
}
|
||||
}}
|
||||
className={`${
|
||||
isTab === "draw"
|
||||
@@ -472,8 +489,8 @@ function SignPad({
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<i className="fas fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="uploadImg">Upload</div>
|
||||
<i className="fa-light fa-cloud-upload-alt uploadImgLogo"></i>
|
||||
<div className="text-[10px]">Upload</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
@@ -509,7 +526,7 @@ function SignPad({
|
||||
) : isTab === "type" ? (
|
||||
<div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="signatureText">
|
||||
<span className="mr-[5px] text-[12px]">
|
||||
{isInitial ? "Initials" : "Signature"}:
|
||||
</span>
|
||||
<input
|
||||
@@ -525,7 +542,7 @@ function SignPad({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="fontOptionContainer">
|
||||
<div className="border-[1px] border-[#d6d3d3] mt-[10px] ml-[5px]">
|
||||
{fontOptions.map((font, ind) => {
|
||||
return (
|
||||
<div
|
||||
@@ -577,7 +594,7 @@ function SignPad({
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
handleSignatureChange(canvasRef.current?.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
|
||||
@@ -7,7 +7,7 @@ function Signedby({ pdfDetails }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="hidden md:block w-[180px] h-full bg-base-100">
|
||||
<div className="hidden md:block w-full h-full bg-base-100">
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
Signed By
|
||||
</div>
|
||||
@@ -19,8 +19,12 @@ function Signedby({ pdfDetails }) {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="userName">{pdfDetails.ExtUserPtr.Name}</span>
|
||||
<span className="useEmail">{pdfDetails.ExtUserPtr.Email}</span>
|
||||
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{pdfDetails.ExtUserPtr.Name}
|
||||
</span>
|
||||
<span className="text-[10px] font-medium text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{pdfDetails.ExtUserPtr.Email}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from "react";
|
||||
import { darkenColor, getFirstLetter } from "../../constant/Utils";
|
||||
|
||||
function SignerListComponent(props) {
|
||||
const checkSignerBackColor = (obj) => {
|
||||
if (obj) {
|
||||
let data = "";
|
||||
if (obj?.Id) {
|
||||
data = props.signerPos.filter((data) => data.Id === obj.Id);
|
||||
} else {
|
||||
data = props.signerPos.filter(
|
||||
(data) => data.signerObjId === obj.objectId
|
||||
);
|
||||
}
|
||||
return data && data.length > 0 && data[0].blockColor;
|
||||
}
|
||||
};
|
||||
const checkUserNameColor = (obj) => {
|
||||
const getBackColor = checkSignerBackColor(obj);
|
||||
if (getBackColor) {
|
||||
const color = darkenColor(getBackColor, 0.4);
|
||||
return color;
|
||||
} else {
|
||||
return "#abd1d0";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rounded-xl mx-1 flex flex-row items-center py-[10px] mt-1"
|
||||
style={{ background: checkSignerBackColor(props.obj) }}
|
||||
>
|
||||
<div
|
||||
style={{ background: checkUserNameColor(props.obj) }}
|
||||
className="flex w-[30px] h-[30px] rounded-full justify-center items-center mx-1"
|
||||
>
|
||||
<span className="text-[12px] text-center font-bold text-black uppercase">
|
||||
{getFirstLetter(
|
||||
props.obj?.Name || props.obj?.email || props.obj?.Role
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-[12px] font-bold text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{props.obj?.Name || props?.obj?.Role}
|
||||
</span>
|
||||
<span className="text-[10px] font-medium text-[#424242] w-[100px] whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{props.obj?.Email || props.obj?.email}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SignerListComponent;
|
||||
@@ -6,19 +6,21 @@ function SignerListPlace(props) {
|
||||
return (
|
||||
<div>
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
<span>
|
||||
<span className="relative">
|
||||
{props.title ? props.title : "Recipients"}
|
||||
<span className="text-xs mt-1 ml-0.5">
|
||||
<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 border-[1.5px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
|
||||
<i className="fa-light fa-question rounded-full border-[1px] border-base-content text-[11px] py-[1px] px-[3px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<Tooltip id="my-tooltip" className="z-[100]">
|
||||
<div className="max-w-[450px]">
|
||||
<p className="font-bold">What are template roles?</p>
|
||||
<div className="max-w-[450px] 2xl:max-w-[500px] 2xl:text-[20px] p-[1px]">
|
||||
<p className="font-bold pb-[1px]">
|
||||
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
|
||||
@@ -61,21 +63,21 @@ function SignerListPlace(props) {
|
||||
<div
|
||||
role="button"
|
||||
data-tut="reactourAddbtn"
|
||||
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||
disabled={props?.isMailSend ? true : false}
|
||||
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||
onClick={() => props.handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i> Add role
|
||||
<i className="fa-light fa-plus"></i> Add role
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
role="button"
|
||||
data-tut="reactourAddbtn"
|
||||
data-tut="addRecipient"
|
||||
className="op-btn op-btn-accent op-btn-outline w-full mt-[14px]"
|
||||
disabled={props?.isMailSend ? true : false}
|
||||
onClick={() => props.setIsAddSigner(true)}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i> Add recipients
|
||||
<i className="fa-light fa-plus"></i> Add recipients
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
|
||||
|
||||
function TextFontSetting(props) {
|
||||
return (
|
||||
<ModalUi
|
||||
headerColor={"#dc3545"}
|
||||
isOpen={props.isTextSetting}
|
||||
reduceWidth={"max-w-[350px]"}
|
||||
title={"Text field"}
|
||||
handleClose={() => props.setIsTextSetting(false)}
|
||||
>
|
||||
<div className="h-full p-[20px]">
|
||||
<div className="flex items-center">
|
||||
<span>Font size:</span>
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize ||
|
||||
props.currWidgetsDetails?.options?.fontSize ||
|
||||
"12"
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={size} key={ind}>
|
||||
{size}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="flex flex-row gap-1 items-center ml-4">
|
||||
<span>color: </span>
|
||||
<select
|
||||
value={
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
onChange={(e) => props.setFontColor(e.target.value)}
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
>
|
||||
{fontColorArr.map((color, ind) => {
|
||||
return (
|
||||
<option value={color} key={ind}>
|
||||
{color}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span
|
||||
style={{
|
||||
background:
|
||||
props.fontColor ||
|
||||
props.currWidgetsDetails?.options?.fontColor
|
||||
}}
|
||||
className="w-5 h-[19px] ml-1"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-[1px] bg-[#9f9f9f] w-full mt-[15px] mb-2"></div>
|
||||
<button
|
||||
onClick={() => props.handleSaveFontSize()}
|
||||
type="button"
|
||||
className="op-btn op-btn-primary mt-2"
|
||||
>
|
||||
save
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
);
|
||||
}
|
||||
|
||||
export default TextFontSetting;
|
||||
@@ -37,11 +37,10 @@ function WidgetComponent({
|
||||
sendInOrder,
|
||||
isTemplateFlow,
|
||||
setBlockColor,
|
||||
blockColor,
|
||||
setIsAddSigner
|
||||
setIsAddSigner,
|
||||
uniqueId
|
||||
}) {
|
||||
const [isSignersModal, setIsSignersModal] = useState(false);
|
||||
|
||||
const [, dropdown] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
@@ -187,7 +186,6 @@ function WidgetComponent({
|
||||
const isMobile = window.innerWidth < 767;
|
||||
const scrollContainerRef = useRef(null);
|
||||
const [widget, setWidget] = useState([]);
|
||||
|
||||
const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
@@ -245,88 +243,68 @@ function WidgetComponent({
|
||||
? textWidgetData
|
||||
: widget;
|
||||
|
||||
const handleSelectRecipient = () => {
|
||||
if (
|
||||
signersdata[isSelectListId]?.Email ||
|
||||
signersdata[isSelectListId]?.Role
|
||||
) {
|
||||
const userData =
|
||||
signersdata[isSelectListId]?.Name || signersdata[isSelectListId]?.Role;
|
||||
const name =
|
||||
userData?.length > 20 ? `${userData.slice(0, 20)}...` : userData;
|
||||
return name;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
!isMailSend && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
id="navbar"
|
||||
className="stickyfooter"
|
||||
style={{ width: window.innerWidth + "px" }}
|
||||
>
|
||||
<div className="mx-1">
|
||||
{isSigners && (
|
||||
<div
|
||||
data-tut={dataTut}
|
||||
className="py-[10px] flex justify-center items-center op-card"
|
||||
style={{
|
||||
background: blockColor
|
||||
? blockColor
|
||||
: isSelectListId
|
||||
<div id="navbar" className="fixed z-[99] bottom-0 right-0 w-full">
|
||||
{isSigners && (
|
||||
<div className="w-full mb-[5px] flex justify-center items-center gap-1">
|
||||
<div className="w-full ml-[5px]" onClick={() => handleModal()}>
|
||||
<select
|
||||
data-tut="recipientArea"
|
||||
className="w-full op-select op-select-bordered pointer-events-none"
|
||||
value={handleSelectRecipient()}
|
||||
style={{
|
||||
backgroundColor: isSelectListId
|
||||
? color[isSelectListId % color.length]
|
||||
: color[0]
|
||||
}}
|
||||
onClick={() => handleModal()}
|
||||
>
|
||||
<span className="text-[13px] font-bold flex items-center">
|
||||
{title ? title : "Recipient"}
|
||||
{signersdata[isSelectListId]?.Role && (
|
||||
<div>
|
||||
{signersdata[isSelectListId]?.Name ? (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Name?.length > 12
|
||||
? `${signersdata[isSelectListId].Name.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Name}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
:{" "}
|
||||
{signersdata[isSelectListId]?.Role?.length > 12
|
||||
? `${signersdata[isSelectListId].Role.slice(
|
||||
0,
|
||||
12
|
||||
)}...`
|
||||
: signersdata[isSelectListId]?.Role}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="ml-[6px] text-[16px]">
|
||||
<i className="fa-solid fa-angle-down"></i>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mx-1">
|
||||
{handleAddSigner ? (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="op-btn op-btn-accent w-full my-[2px]"
|
||||
onClick={() => handleAddSigner()}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span>Add role</span>
|
||||
</div>
|
||||
) : (
|
||||
setIsAddSigner && (
|
||||
<div
|
||||
data-tut="reactourAddbtn"
|
||||
className="op-btn op-btn-accent w-full my-[2px]"
|
||||
onClick={() => setIsAddSigner(true)}
|
||||
}}
|
||||
>
|
||||
<i className="fa-solid fa-plus"></i>
|
||||
<span>Add recipients</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
<option value={handleSelectRecipient()}>
|
||||
{handleSelectRecipient()}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="w-[18%]">
|
||||
{handleAddSigner ? (
|
||||
<button
|
||||
data-tut="reactourAddbtn"
|
||||
onClick={() => handleAddSigner()}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
<i className="fa-light fa-plus "></i>
|
||||
</button>
|
||||
) : (
|
||||
setIsAddSigner && (
|
||||
<button
|
||||
data-tut="addRecipient"
|
||||
onClick={() => setIsAddSigner(true)}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
<i className="fa-light fa-plus"></i>
|
||||
</button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
data-tut="addWidgets"
|
||||
ref={scrollContainerRef}
|
||||
className="bg-base-100 border-[2px] border-t-primary"
|
||||
>
|
||||
@@ -349,12 +327,16 @@ function WidgetComponent({
|
||||
data-tut={dataTut}
|
||||
className={`${
|
||||
isMailSend ? "bg-opacity-50 pointer-events-none" : ""
|
||||
} hidden md:block w-[180px] h-full bg-base-100`}
|
||||
} hidden md:block h-full bg-base-100`}
|
||||
>
|
||||
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
|
||||
<span>Fields</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center my-3">
|
||||
|
||||
<div
|
||||
className="p-[15px] flex flex-col pt-4 2xl:m-5"
|
||||
data-tut="addWidgets"
|
||||
>
|
||||
<WidgetList
|
||||
updateWidgets={updateWidgets}
|
||||
handleDivClick={handleDivClick}
|
||||
@@ -388,6 +370,7 @@ function WidgetComponent({
|
||||
sendInOrder={sendInOrder}
|
||||
setSignersData={setSignersData}
|
||||
setBlockColor={setBlockColor}
|
||||
uniqueId={uniqueId}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -4,9 +4,9 @@ import { getWidgetType } from "../../constant/Utils";
|
||||
function WidgetList(props) {
|
||||
return props.updateWidgets.map((item, ind) => {
|
||||
return (
|
||||
<div key={ind} className="mb-[5px]">
|
||||
<div className="2xl:p-3 mb-[5px]" key={ind}>
|
||||
<div
|
||||
className="select-none"
|
||||
className="select-none mx-[2px] md:mx-0 cursor-all-scroll"
|
||||
onClick={() => {
|
||||
props.addPositionOfSignature &&
|
||||
props.addPositionOfSignature("onclick", item);
|
||||
@@ -22,11 +22,10 @@ function WidgetList(props) {
|
||||
}
|
||||
}}
|
||||
onMouseMove={props?.handleDivClick}
|
||||
onMouseDown={() => {
|
||||
props?.handleMouseLeave();
|
||||
}}
|
||||
onMouseDown={() => props?.handleMouseLeave()}
|
||||
onTouchStart={props?.handleDivClick}
|
||||
>
|
||||
{item.ref && getWidgetType(item, props?.marginLeft)}
|
||||
{item.ref && getWidgetType(item)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import ModalUi from "../../primitives/ModalUi";
|
||||
import "../../styles/AddUser.css";
|
||||
import RegexParser from "regex-parser";
|
||||
import { textInputWidget } from "../../constant/Utils";
|
||||
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
|
||||
import { textInputWidget, textWidget } from "../../constant/Utils";
|
||||
import Upgrade from "../../primitives/Upgrade";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
import Tooltip from "../../primitives/Tooltip";
|
||||
import { fontColorArr, fontsizeArr } from "../../constant/Utils";
|
||||
|
||||
const WidgetNameModal = (props) => {
|
||||
const [formdata, setFormdata] = useState({
|
||||
@@ -23,7 +23,7 @@ const WidgetNameModal = (props) => {
|
||||
useEffect(() => {
|
||||
if (props.defaultdata) {
|
||||
setFormdata({
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || "",
|
||||
name: props.defaultdata?.options?.name || "",
|
||||
defaultValue: props.defaultdata?.options?.defaultValue || "",
|
||||
status: props.defaultdata?.options?.status || "required",
|
||||
hint: props.defaultdata?.options?.hint || "",
|
||||
@@ -35,11 +35,11 @@ const WidgetNameModal = (props) => {
|
||||
} else {
|
||||
setFormdata({
|
||||
...formdata,
|
||||
name: props.defaultdata?.options?.name || props?.widgetName || ""
|
||||
name: props.defaultdata?.options?.name || ""
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [props.defaultdata, props.widgetName]);
|
||||
}, [props.defaultdata]);
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
if (props.handleData) {
|
||||
@@ -106,22 +106,10 @@ const WidgetNameModal = (props) => {
|
||||
handleClose={props.handleClose && props.handleClose}
|
||||
title={"Widget info"}
|
||||
>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget) &&
|
||||
!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Field validations are free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className={`${
|
||||
props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget
|
||||
? "pt-0"
|
||||
: ""
|
||||
props.defaultdata?.type === textInputWidget ? "pt-0" : ""
|
||||
} p-[20px] text-base-content`}
|
||||
>
|
||||
<div className="mb-[0.75rem] text-[13px]">
|
||||
@@ -137,67 +125,71 @@ const WidgetNameModal = (props) => {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props.widgetName === textInputWidget) && (
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<>
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex items-center gap-1">
|
||||
<label
|
||||
htmlFor="textvalidate"
|
||||
{isEnableSubscription && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<div className="flex items-center gap-1">
|
||||
<label
|
||||
htmlFor="textvalidate"
|
||||
className={`${
|
||||
!props.isSubscribe && isEnableSubscription
|
||||
? "bg-opacity-50 pointer-events-none"
|
||||
: ""
|
||||
} text-[13px]`}
|
||||
>
|
||||
Validation
|
||||
</label>
|
||||
<Tooltip
|
||||
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
|
||||
/>
|
||||
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
!props.isSubscribe && isEnableSubscription
|
||||
? "bg-opacity-50 pointer-events-none"
|
||||
: ""
|
||||
} text-[13px]`}
|
||||
} flex flex-row gap-[10px] mb-[0.5rem]`}
|
||||
>
|
||||
Validation
|
||||
</label>
|
||||
<Tooltip
|
||||
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
|
||||
/>
|
||||
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</div>
|
||||
<div
|
||||
className={`${
|
||||
!props.isSubscribe && isEnableSubscription
|
||||
? "bg-opacity-50 pointer-events-none"
|
||||
: ""
|
||||
} flex flex-row gap-[10px] mb-[0.5rem]`}
|
||||
>
|
||||
<div className="w-full relative group">
|
||||
<input
|
||||
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
|
||||
name="textvalidate"
|
||||
placeholder="Enter custom regular expression"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
// onBlur={() => handleBlurRegex()}
|
||||
/>
|
||||
<select
|
||||
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
|
||||
name="textvalidate"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onBlur={() => handleBlurRegex()}
|
||||
>
|
||||
<option
|
||||
disabled={formdata?.textvalidate}
|
||||
className="text-[13px]"
|
||||
<div className="w-full relative group">
|
||||
<input
|
||||
className="z-20 relative op-input op-input-bordered rounded-r-none op-input-sm focus:outline-none group-hover:border-base-content w-[87%] md:w-[92%] text-xs"
|
||||
name="textvalidate"
|
||||
placeholder="Enter custom regular expression"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
// onBlur={() => handleBlurRegex()}
|
||||
/>
|
||||
<select
|
||||
className="validationlist op-input op-input-bordered op-input-sm focus:outline-none group-hover:border-base-content w-full text-xs"
|
||||
name="textvalidate"
|
||||
value={formdata.textvalidate}
|
||||
onChange={(e) => handleChange(e)}
|
||||
onBlur={() => handleBlurRegex()}
|
||||
>
|
||||
Select...
|
||||
</option>
|
||||
{inputOpt.map((data, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" key={ind} value={data}>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<option
|
||||
disabled={formdata?.textvalidate}
|
||||
className="text-[13px]"
|
||||
>
|
||||
Select...
|
||||
</option>
|
||||
{inputOpt.map((data, ind) => {
|
||||
return (
|
||||
<option
|
||||
className="text-[13px]"
|
||||
key={ind}
|
||||
value={data}
|
||||
>
|
||||
{data}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)}
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="name" className="text-[13px]">
|
||||
Default value
|
||||
@@ -221,7 +213,7 @@ const WidgetNameModal = (props) => {
|
||||
style={{ fontSize: 12 }}
|
||||
>
|
||||
<i
|
||||
className="fas fa-exclamation-circle text-[15px]"
|
||||
className="fa-light fa-exclamation-circle text-[15px]"
|
||||
style={{ color: "#fab005" }}
|
||||
></i>
|
||||
invalid default value
|
||||
@@ -252,8 +244,7 @@ const WidgetNameModal = (props) => {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{(props.defaultdata?.type === textInputWidget ||
|
||||
props?.widgetName === textInputWidget) && (
|
||||
{props.defaultdata?.type === textInputWidget && (
|
||||
<div className="mb-[0.75rem]">
|
||||
<label htmlFor="hint" className="text-[13px]">
|
||||
Hint
|
||||
@@ -266,6 +257,63 @@ const WidgetNameModal = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{[
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(props.defaultdata?.type) && (
|
||||
<div className="flex items-center mb-[0.75rem]">
|
||||
<span>Font size:</span>
|
||||
<select
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
value={
|
||||
props.fontSize || props.defaultdata?.options?.fontSize || "12"
|
||||
}
|
||||
onChange={(e) => props.setFontSize(e.target.value)}
|
||||
>
|
||||
{fontsizeArr.map((size, ind) => {
|
||||
return (
|
||||
<option className="text-[13px]" value={size} key={ind}>
|
||||
{size}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<div className="flex flex-row gap-1 items-center ml-4">
|
||||
<span>color: </span>
|
||||
<select
|
||||
value={
|
||||
props.fontColor ||
|
||||
props.defaultdata?.options?.fontColor ||
|
||||
"black"
|
||||
}
|
||||
onChange={(e) => props.setFontColor(e.target.value)}
|
||||
className="ml-[7px] op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content text-xs"
|
||||
>
|
||||
{fontColorArr.map((color, ind) => {
|
||||
return (
|
||||
<option value={color} key={ind}>
|
||||
{color}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<span
|
||||
style={{
|
||||
background:
|
||||
props.fontColor ||
|
||||
props.defaultdata?.options?.fontColor ||
|
||||
"black"
|
||||
}}
|
||||
className="w-5 h-[19px] ml-1"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="h-[1px] w-full mb-[16px] bg-[#b7b3b3]"></div>
|
||||
<button type="submit" className="op-btn op-btn-primary">
|
||||
Save
|
||||
|
||||
@@ -27,10 +27,12 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
@@ -118,13 +120,13 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-base font-semibold">Create Folder</h1>
|
||||
<h1 className="text-base font-semibold mt-[0.4rem]">Create Folder</h1>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
Name<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
@@ -135,7 +137,7 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||
<select
|
||||
value={selectedParent}
|
||||
onChange={handleOptions}
|
||||
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
|
||||
className="op-select op-select-bordered op-select-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
>
|
||||
<option>select</option>
|
||||
{folderList.length > 0 &&
|
||||
@@ -150,9 +152,9 @@ const CreateFolder = ({ parentFolderId, onSuccess, folderCls }) => {
|
||||
<button
|
||||
onClick={handleCreateFolder}
|
||||
disabled={isLoader}
|
||||
className="flex items-center rounded p-2 bg-[#32a3ac] text-white mt-3"
|
||||
className="op-btn op-btn-primary op-btn-sm mt-3"
|
||||
>
|
||||
<i className="fa-solid fa-plus mr-1"></i>
|
||||
<i className="fa-light fa-plus"></i>
|
||||
<span>Create</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function DropboxChooser({ children, onSuccess, onCancel }) {
|
||||
return (
|
||||
<div onClick={handleChoose}>
|
||||
{children || (
|
||||
<button className="px-2 py-[2px] text-2xl rounded border-[1px] text-blue-400 border-gray-300 w-full">
|
||||
<button className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[35px] max-w-[45px] flex justify-center items-center">
|
||||
<i className="fa-brands fa-dropbox"></i>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -24,12 +24,14 @@ const FolderModal = (props) => {
|
||||
const FolderQuery = new Parse.Query(props.folderCls);
|
||||
if (folderPtr) {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.descending("Type");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.descending("Type");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
@@ -143,14 +145,14 @@ const FolderModal = (props) => {
|
||||
handleCreate();
|
||||
};
|
||||
return (
|
||||
<div className="text-xs mt-2 ">
|
||||
<div className="text-xs mt-2">
|
||||
<ModalUi
|
||||
title={"Select Folder"}
|
||||
isOpen={props.isOpenModal}
|
||||
handleClose={handleCancel}
|
||||
>
|
||||
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||
<div className="pt-1 text-[#ac4848] text-[14px] font-[500]">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title="OpenSign™ Drive"
|
||||
@@ -171,27 +173,50 @@ const FolderModal = (props) => {
|
||||
{" / "}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<hr />
|
||||
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
||||
</div>
|
||||
<div className="mt-2 mb-3">
|
||||
<div className="max-h-[210px] overflow-auto">
|
||||
{!isAdd &&
|
||||
folderList.length > 0 &&
|
||||
folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.Name}
|
||||
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||
onClick={() => handleSelect(folder)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
{!isAdd && folderList.length > 0
|
||||
? folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.objectId}
|
||||
className={`${
|
||||
folder.Type === "Folder"
|
||||
? "cursor-pointer"
|
||||
: "cursor-default"
|
||||
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
||||
onClick={() =>
|
||||
folder.Type === "Folder" && handleSelect(folder)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{folder.Type === "Folder" ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
)}
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))
|
||||
: !isLoader && (
|
||||
<div className="text-base-content text-center my-2">
|
||||
No data found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isAdd && (
|
||||
<CreateFolder
|
||||
@@ -201,8 +226,8 @@ const FolderModal = (props) => {
|
||||
/>
|
||||
)}
|
||||
{isLoader && (
|
||||
<div className="flex justify-center">
|
||||
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
<div className="flex justify-center my-4">
|
||||
<i className="fa-light fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -210,22 +235,28 @@ const FolderModal = (props) => {
|
||||
<hr />
|
||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||
<div
|
||||
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||
className="op-btn op-btn-seconday op-btn-sm"
|
||||
title="Save Here"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{isAdd ? (
|
||||
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||
<>
|
||||
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
|
||||
<span className="text-xs">Back</span>
|
||||
</>
|
||||
) : (
|
||||
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||
<>
|
||||
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
||||
<span className="">Add folder</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="text-[30px] cursor-pointer"
|
||||
className="op-btn op-btn-primary op-btn-sm"
|
||||
title="Save Here"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<i className="fas fa-save" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
@@ -44,12 +44,14 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
const FolderQuery = new Parse.Query(folderCls);
|
||||
if (folderPtr) {
|
||||
FolderQuery.equalTo("Folder", folderPtr);
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.descending("Type");
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
} else {
|
||||
FolderQuery.doesNotExist("Folder");
|
||||
FolderQuery.equalTo("Type", "Folder");
|
||||
FolderQuery.notEqualTo("IsArchive", true);
|
||||
FolderQuery.descending("Type");
|
||||
FolderQuery.equalTo("CreatedBy", Parse.User.current());
|
||||
}
|
||||
|
||||
const res = await FolderQuery.find();
|
||||
@@ -174,14 +176,16 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
<div className="relative max-w-sm">
|
||||
<div
|
||||
onClick={() => SetIsOpen(true)}
|
||||
className=" cursor-pointer rounded px-[20px] py-[20px] bg-base-100 border-[1px] border-base-200 shadow flex max-w-sm gap-8 items-center"
|
||||
className="cursor-pointer rounded px-[20px] py-[20px] bg-base-100 border-[1px] border-base-200 shadow flex max-w-sm gap-8 items-center"
|
||||
>
|
||||
<div>
|
||||
<i
|
||||
className="far fa-folder-open text-[40px] text-[#33bbff]"
|
||||
style={{ fontSize: "40px" }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[40px] h-[40px] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="font-semibold ">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -190,12 +194,9 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
? selectFolder.Name
|
||||
: "OpenSign™ Drive"}
|
||||
</p>
|
||||
<div
|
||||
className="text-black text-sm"
|
||||
// onClick={() => SetIsOpen(true)}
|
||||
>
|
||||
<div className="text-black text-sm">
|
||||
<i
|
||||
className="fa fa-pencil cursor-pointer"
|
||||
className="fa-light fa-pencil cursor-pointer"
|
||||
title="Select Folder"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
@@ -220,7 +221,7 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
handleClose={handleCancel}
|
||||
>
|
||||
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
|
||||
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
|
||||
<div className="pt-1 text-[#ac4848] text-[14px] font-[500]">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
title="OpenSign™ Drive"
|
||||
@@ -241,27 +242,50 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
{" / "}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<hr />
|
||||
<hr className="bg-[#8a8a8a] mt-[0.750rem]" />
|
||||
</div>
|
||||
<div className="mt-2 mb-3">
|
||||
<div className="mb-2">
|
||||
<div className="max-h-[210px] overflow-auto">
|
||||
{!isAdd &&
|
||||
folderList.length > 0 &&
|
||||
folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.Name}
|
||||
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
|
||||
onClick={() => handleSelect(folder)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<i
|
||||
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
{!isAdd && folderList.length > 0
|
||||
? folderList.map((folder) => (
|
||||
<div
|
||||
key={folder.objectId}
|
||||
className={`${
|
||||
folder.Type === "Folder"
|
||||
? "cursor-pointer"
|
||||
: "cursor-default"
|
||||
} border-b-[1px] border-[#8a8a8a] py-2 mb-0.5"`}
|
||||
onClick={() =>
|
||||
folder.Type === "Folder" && handleSelect(folder)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{folder.Type === "Folder" ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-secondary"
|
||||
>
|
||||
<path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 384 512"
|
||||
className="w-[1.4rem] h-[1.4rem] fill-current op-text-primary"
|
||||
>
|
||||
<path d="M374.629 150.627L233.371 9.373C227.371 3.371 219.23 0 210.746 0H64C28.652 0 0 28.652 0 64V448C0 483.345 28.652 512 64 512H320C355.348 512 384 483.345 384 448V173.254C384 164.767 380.629 156.629 374.629 150.627ZM224 22.629L361.375 160H248C234.781 160 224 149.234 224 136V22.629ZM368 448C368 474.467 346.469 496 320 496H64C37.531 496 16 474.467 16 448V64C16 37.533 37.531 16 64 16H208V136C208 158.062 225.938 176 248 176H368V448ZM96 264C96 268.406 99.594 272 104 272H280C284.406 272 288 268.406 288 264S284.406 256 280 256H104C99.594 256 96 259.594 96 264ZM280 320H104C99.594 320 96 323.594 96 328S99.594 336 104 336H280C284.406 336 288 332.406 288 328S284.406 320 280 320ZM280 384H104C99.594 384 96 387.594 96 392S99.594 400 104 400H280C284.406 400 288 396.406 288 392S284.406 384 280 384Z" />
|
||||
</svg>
|
||||
)}
|
||||
<span className="font-semibold">{folder.Name}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
))
|
||||
: !isLoader && (
|
||||
<div className="text-base-content text-center my-2">
|
||||
No data found
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isAdd && (
|
||||
<CreateFolder
|
||||
@@ -271,8 +295,8 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
/>
|
||||
)}
|
||||
{isLoader && (
|
||||
<div className="flex justify-center">
|
||||
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
<div className="flex justify-center my-4">
|
||||
<i className="fa-light fa-spinner fa-spin-pulse text-[30px]"></i>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -280,22 +304,28 @@ const SelectFolder = ({ required, onSuccess, folderCls, isReset }) => {
|
||||
<hr />
|
||||
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
|
||||
<div
|
||||
className="text-[30px] cursor-pointer text-[#32a3ac]"
|
||||
className="op-btn op-btn-seconday op-btn-sm"
|
||||
title="Save Here"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{isAdd ? (
|
||||
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
|
||||
<>
|
||||
<i className="fa-light fa-arrow-left" aria-hidden="true"></i>
|
||||
<span className="text-xs">Back</span>
|
||||
</>
|
||||
) : (
|
||||
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
|
||||
<>
|
||||
<i className="fa-light fa-square-plus" aria-hidden="true"></i>
|
||||
<span className="">Add folder</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="text-[30px] cursor-pointer"
|
||||
className="op-btn op-btn-primary op-btn-sm"
|
||||
title="Save Here"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<i className="fas fa-save" aria-hidden="true"></i>
|
||||
<i className="fa-light fa-save" aria-hidden="true"></i>Save here
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import Parse from "parse";
|
||||
import "../../../styles/AddUser.css";
|
||||
import AsyncSelect from "react-select/async";
|
||||
|
||||
const SelectSigners = (props) => {
|
||||
|
||||
@@ -167,7 +167,7 @@ const SignersInput = (props) => {
|
||||
}}
|
||||
className="cursor-pointer op-input op-input-bordered focus:outline-none hover:border-base-content max-h-[38px] min-w-[48px] flex justify-center items-center"
|
||||
>
|
||||
<i className="fas fa-plus"></i>
|
||||
<i className="fa-light fa-plus"></i>
|
||||
</div>
|
||||
<AddSignerModal isOpen={modalIsOpen}>
|
||||
<h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]">
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
import Menu from "./Menu";
|
||||
import Submenu from "./SubMenu";
|
||||
import SocialMedia from "./SocialMedia";
|
||||
|
||||
import Parse from "parse";
|
||||
import dp from "../../assets/images/dp.png";
|
||||
import sidebarList from "../../json/menuJson";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { isEnableSubscription } from "../../constant/const";
|
||||
|
||||
const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
const navigate = useNavigate();
|
||||
const [menuList, setmenuList] = useState([]);
|
||||
const [submenuOpen, setSubmenuOpen] = useState(false);
|
||||
let username = localStorage.getItem("username");
|
||||
const username = localStorage.getItem("username");
|
||||
const image = localStorage.getItem("profileImg") || dp;
|
||||
const tenantname = localStorage.getItem("TenantName");
|
||||
const tenantname = localStorage.getItem("Extand_Class")
|
||||
? JSON.parse(localStorage.getItem("Extand_Class"))?.[0]?.Company
|
||||
: "";
|
||||
|
||||
useEffect(() => {
|
||||
if (localStorage.getItem("accesstoken")) {
|
||||
@@ -22,13 +25,79 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
|
||||
const menuItem = async () => {
|
||||
try {
|
||||
var sideMenu = Parse.Object.extend("w_menu");
|
||||
var query = new Parse.Query(sideMenu);
|
||||
query.equalTo("objectId", localStorage.getItem("defaultmenuid"));
|
||||
const results = await query.first();
|
||||
const resultjson = results.toJSON();
|
||||
let result = resultjson;
|
||||
setmenuList(result.menuItems);
|
||||
if (localStorage.getItem("defaultmenuid")) {
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
// console.log("extClass ", extClass);
|
||||
let userRole = "contracts_User";
|
||||
if (extClass && extClass.length > 0) {
|
||||
userRole = extClass[0].UserRole;
|
||||
}
|
||||
if (
|
||||
userRole === "contracts_Admin" ||
|
||||
userRole === "contracts_OrgAdmin"
|
||||
) {
|
||||
const newSidebarList = sidebarList.map((item) => {
|
||||
if (item.title === "Settings") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
if (isEnableSubscription) {
|
||||
newItem.children = [
|
||||
...newItem.children,
|
||||
{
|
||||
icon: "fa-light fa-building-memo",
|
||||
title: "Teams",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "teams"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-users fa-fw",
|
||||
title: "Users",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "users"
|
||||
}
|
||||
];
|
||||
} else {
|
||||
const arr = newItem.children.slice(0, 1);
|
||||
newItem.children = [
|
||||
...arr,
|
||||
{
|
||||
icon: "fa-light fa-users fa-fw",
|
||||
title: "Users",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "users"
|
||||
}
|
||||
];
|
||||
}
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setmenuList(newSidebarList);
|
||||
} else {
|
||||
if (!isEnableSubscription) {
|
||||
const newSidebarList = sidebarList.map((item) => {
|
||||
if (item.title === "Settings") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
const arr = newItem.children.slice(0, 1);
|
||||
newItem.children = arr;
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setmenuList(newSidebarList);
|
||||
} else {
|
||||
setmenuList(sidebarList);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Problem", e);
|
||||
}
|
||||
@@ -42,13 +111,19 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
closeSidebar();
|
||||
setSubmenuOpen({});
|
||||
};
|
||||
const handleProfile = () => {
|
||||
navigate("/profile");
|
||||
};
|
||||
return (
|
||||
<aside
|
||||
className={`absolute md:relative bg-base-100 h-screen overflow-y-auto transition-all z-[100] hide-scrollbar
|
||||
className={`absolute lg:relative bg-base-100 h-screen overflow-y-auto transition-all z-[500] shadow-lg hide-scrollbar
|
||||
${isOpen ? "w-full md:w-[300px]" : "w-0"}`}
|
||||
>
|
||||
<div className="flex px-2 py-3 gap-2 items-center shadow-md">
|
||||
<div className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden">
|
||||
<div
|
||||
onClick={() => handleProfile()}
|
||||
className="w-[75px] h-[75px] rounded-full ring-[2px] ring-offset-2 ring-gray-400 overflow-hidden cursor-pointer"
|
||||
>
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={image}
|
||||
@@ -56,9 +131,15 @@ const Sidebar = ({ isOpen, closeSidebar }) => {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[14px] font-bold text-base-content">{username}</p>
|
||||
<p
|
||||
className={`text-[12px] text-base-content ${
|
||||
onClick={handleProfile}
|
||||
className="text-[14px] font-bold text-base-content cursor-pointer"
|
||||
>
|
||||
{username}
|
||||
</p>
|
||||
<p
|
||||
onClick={handleProfile}
|
||||
className={`cursor-pointer text-[12px] text-base-content ${
|
||||
tenantname ? "mt-2" : ""
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -18,8 +18,8 @@ const Submenu = ({ item, closeSidebar, toggleSubmenu, submenuOpen }) => {
|
||||
<i
|
||||
className={`${
|
||||
submenuOpen[item.title]
|
||||
? "fa-solid fa-angle-down"
|
||||
: "fa-solid fa-angle-right"
|
||||
? "fa-light fa-angle-down"
|
||||
: "fa-light fa-angle-right"
|
||||
}`}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import logo from "../assets/images/logo.png";
|
||||
export const appInfo = {
|
||||
appTitle: "contracts",
|
||||
applogo: logo,
|
||||
appname: "contracts",
|
||||
appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign",
|
||||
baseUrl: process.env.REACT_APP_SERVERURL
|
||||
? process.env.REACT_APP_SERVERURL
|
||||
@@ -18,22 +16,34 @@ export const appInfo = {
|
||||
: "",
|
||||
metaDescription:
|
||||
"The fastest way to sign PDFs & request signatures from others.",
|
||||
objectId: "aIPmIvMzGM",
|
||||
settings: [
|
||||
{
|
||||
role: "contracts_User",
|
||||
menuId: "H9vRfEYKhT",
|
||||
pageType: "dashboard",
|
||||
pageId: "35KBoSgoAK",
|
||||
extended_class: "contracts_Users"
|
||||
},
|
||||
{
|
||||
role: "contracts_Admin",
|
||||
menuId: "VPh91h0ZHk",
|
||||
pageType: "dashboard",
|
||||
pageId: "35KBoSgoAK",
|
||||
extended_class: "contracts_Users"
|
||||
},
|
||||
{
|
||||
role: "contracts_OrgAdmin",
|
||||
menuId: "VPh91h0ZHk",
|
||||
pageType: "dashboard",
|
||||
pageId: "35KBoSgoAK",
|
||||
extended_class: "contracts_Users"
|
||||
},
|
||||
{
|
||||
role: "contracts_Editor",
|
||||
menuId: "H9vRfEYKhT",
|
||||
pageType: "dashboard",
|
||||
pageId: "35KBoSgoAK",
|
||||
extended_class: "contracts_Users"
|
||||
},
|
||||
{
|
||||
role: "contracts_User",
|
||||
menuId: "H9vRfEYKhT",
|
||||
pageType: "dashboard",
|
||||
pageId: "35KBoSgoAK",
|
||||
extended_class: "contracts_Users"
|
||||
}
|
||||
],
|
||||
version: "0.1"
|
||||
]
|
||||
};
|
||||
|
||||
@@ -3,4 +3,11 @@ export const templateCls = "contracts_Template";
|
||||
export const documentCls = "contracts_Document";
|
||||
export const themeColor = "#47a3ad";
|
||||
export const iconColor = "#686968";
|
||||
export const isEnableSubscription = process.env.REACT_APP_ENABLE_SUBSCRIPTION;
|
||||
export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
export const isEnableSubscription =
|
||||
process.env.REACT_APP_ENABLE_SUBSCRIPTION &&
|
||||
process.env.REACT_APP_ENABLE_SUBSCRIPTION?.toLowerCase() === "true"
|
||||
? true
|
||||
: false;
|
||||
export const isStaging =
|
||||
window.location.origin === "https://staging-app.opensignlabs.com";
|
||||
|
||||
@@ -15,3 +15,59 @@ body {
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
/* below css class replace bootstrap background color with daisy */
|
||||
.op-bg-primary {
|
||||
@apply bg-primary;
|
||||
}
|
||||
.op-bg-secondary {
|
||||
@apply bg-secondary;
|
||||
}
|
||||
.op-bg-info {
|
||||
@apply bg-info;
|
||||
}
|
||||
.op-bg-success{
|
||||
@apply bg-success;
|
||||
}
|
||||
.op-bg-warning {
|
||||
@apply bg-warning;
|
||||
}
|
||||
|
||||
/* below css class replace bootstrap text color with daisy */
|
||||
.op-text-primary {
|
||||
@apply text-primary;
|
||||
}
|
||||
.op-text-secondary {
|
||||
@apply text-secondary;
|
||||
}
|
||||
.op-text-info {
|
||||
@apply text-info;
|
||||
}
|
||||
.op-text-success{
|
||||
@apply text-success;
|
||||
}
|
||||
.op-text-warning {
|
||||
@apply text-warning;
|
||||
}
|
||||
.op-border-primary{
|
||||
@apply border-primary
|
||||
}
|
||||
/* CSS for scrollbar customization */
|
||||
* {
|
||||
scrollbar-width: thin; /* For Firefox */
|
||||
scrollbar-color: gray; /* For Firefox */
|
||||
}
|
||||
|
||||
/* For webkit-based browsers like Chrome, Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: gray;
|
||||
border-radius: 10px;
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
export default function reportJson(id) {
|
||||
// console.log("json ", json);
|
||||
const head = ["Sr.No", "Title", "Note", "Folder", "File", "Owner", "Signers"];
|
||||
const declineHead = [
|
||||
"Sr.No",
|
||||
"Title",
|
||||
"Reason",
|
||||
"Folder",
|
||||
"File",
|
||||
"Owner",
|
||||
"Signers"
|
||||
];
|
||||
const contactbook = ["Sr.No", "Title", "Email", "Phone"];
|
||||
const dashboardReportHead = ["Title", "File", "Owner", "Signers"];
|
||||
const templateReport = ["Sr.No", "Title", "File", "Owner", "Signers"];
|
||||
@@ -15,7 +24,7 @@ export default function reportJson(id) {
|
||||
btnId: "1231",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
btnIcon: "fa-light fa-pen",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
@@ -23,7 +32,7 @@ export default function reportJson(id) {
|
||||
btnId: "2142",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -42,8 +51,8 @@ export default function reportJson(id) {
|
||||
btnLabel: "sign",
|
||||
hoverLabel: "Sign",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-signature",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
btnIcon: "fa-light fa-signature",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
}
|
||||
],
|
||||
@@ -60,7 +69,7 @@ export default function reportJson(id) {
|
||||
btnId: "8901",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-share",
|
||||
btnIcon: "fa-light fa-share",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
@@ -68,8 +77,8 @@ export default function reportJson(id) {
|
||||
btnId: "1588",
|
||||
hoverLabel: "View",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
btnIcon: "fa-light fa-eye",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
@@ -77,14 +86,14 @@ export default function reportJson(id) {
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "1630",
|
||||
btnLabel: "Resend",
|
||||
hoverLabel: "Resend",
|
||||
btnIcon: "fa-solid fa-envelope",
|
||||
btnIcon: "fa-light fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "resend"
|
||||
},
|
||||
@@ -92,7 +101,7 @@ export default function reportJson(id) {
|
||||
btnId: "1688",
|
||||
btnLabel: "Revoke",
|
||||
hoverLabel: "Revoke",
|
||||
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||
btnIcon: "fa-light fa-file-circle-xmark",
|
||||
redirectUrl: "",
|
||||
action: "revoke"
|
||||
},
|
||||
@@ -100,7 +109,7 @@ export default function reportJson(id) {
|
||||
btnId: "1488",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -120,7 +129,7 @@ export default function reportJson(id) {
|
||||
btnId: "1378",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa fa-eye",
|
||||
btnIcon: "fa-light fa-eye",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
@@ -128,7 +137,7 @@ export default function reportJson(id) {
|
||||
btnId: "1278",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -140,13 +149,13 @@ export default function reportJson(id) {
|
||||
case "UPr2Fm5WY3":
|
||||
return {
|
||||
reportName: "Declined Documents",
|
||||
heading: head,
|
||||
heading: declineHead,
|
||||
actions: [
|
||||
{
|
||||
btnId: "1458",
|
||||
hoverLabel: "View",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa fa-eye",
|
||||
btnIcon: "fa-light fa-eye",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
@@ -154,7 +163,7 @@ export default function reportJson(id) {
|
||||
btnId: "1358",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -172,7 +181,7 @@ export default function reportJson(id) {
|
||||
btnId: "1898",
|
||||
hoverLabel: "View",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa fa-eye",
|
||||
btnIcon: "fa-light fa-eye",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
@@ -180,7 +189,7 @@ export default function reportJson(id) {
|
||||
btnId: "1998",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -198,7 +207,7 @@ export default function reportJson(id) {
|
||||
btnId: "1999",
|
||||
hoverLabel: "Share",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-share",
|
||||
btnIcon: "fa-light fa-share",
|
||||
redirectUrl: "",
|
||||
action: "share"
|
||||
},
|
||||
@@ -206,8 +215,8 @@ export default function reportJson(id) {
|
||||
btnId: "1999",
|
||||
hoverLabel: "View",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-eye",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
btnIcon: "fa-light fa-eye",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
@@ -215,14 +224,14 @@ export default function reportJson(id) {
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
btnIcon: "fa-light fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "1631",
|
||||
btnLabel: "Resend",
|
||||
hoverLabel: "Resend",
|
||||
btnIcon: "fa-solid fa-envelope",
|
||||
btnIcon: "fa-light fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "resend"
|
||||
},
|
||||
@@ -230,7 +239,7 @@ export default function reportJson(id) {
|
||||
btnId: "6788",
|
||||
btnLabel: "Revoke",
|
||||
hoverLabel: "Revoke",
|
||||
btnIcon: "fa-solid fa-file-circle-xmark",
|
||||
btnIcon: "fa-light fa-file-circle-xmark",
|
||||
redirectUrl: "",
|
||||
action: "revoke"
|
||||
},
|
||||
@@ -238,7 +247,7 @@ export default function reportJson(id) {
|
||||
btnId: "2000",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -257,8 +266,8 @@ export default function reportJson(id) {
|
||||
btnLabel: "sign",
|
||||
hoverLabel: "Sign",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-signature",
|
||||
redirectUrl: "pdfRequestFiles",
|
||||
btnIcon: "fa-light fa-signature",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
}
|
||||
]
|
||||
@@ -273,7 +282,7 @@ export default function reportJson(id) {
|
||||
btnId: "2003",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
btnIcon: "fa-light fa-pen",
|
||||
redirectUrl: "draftDocument",
|
||||
action: "redirect"
|
||||
},
|
||||
@@ -281,7 +290,7 @@ export default function reportJson(id) {
|
||||
btnId: "2004",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa fa-trash",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
@@ -296,8 +305,8 @@ export default function reportJson(id) {
|
||||
{
|
||||
btnId: "2204",
|
||||
hoverLabel: "Delete",
|
||||
btnColor: "op-btn-outline op-btn-error",
|
||||
btnIcon: "fa-solid fa-trash",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
action: "delete"
|
||||
}
|
||||
],
|
||||
@@ -306,68 +315,99 @@ export default function reportJson(id) {
|
||||
"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":
|
||||
case "6TeaPr321t": {
|
||||
const templateAct = [
|
||||
{
|
||||
btnId: "2234",
|
||||
btnLabel: "Use",
|
||||
hoverLabel: "Use",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-light fa-plus",
|
||||
redirectUrl: "placeHolderSign",
|
||||
action: "redirect",
|
||||
selector: "reactourSecond",
|
||||
message:
|
||||
"Click the 'Use' button to create a new document from an existing template. "
|
||||
},
|
||||
{
|
||||
btnId: "1631",
|
||||
btnLabel: "Quick send",
|
||||
hoverLabel: "Quick send",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa-light fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "bulksend",
|
||||
selector: "tourbulksend",
|
||||
message:
|
||||
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
|
||||
},
|
||||
{
|
||||
btnId: "2234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-light 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-light fa-pen",
|
||||
redirectUrl: "template",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1834",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa-light fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const Extand_Class = localStorage.getItem("Extand_Class");
|
||||
const extClass = Extand_Class && JSON.parse(Extand_Class);
|
||||
// console.log("extClass ", extClass);
|
||||
let templateActions = templateAct;
|
||||
if (extClass && extClass.length > 0) {
|
||||
if (extClass?.[0]?.UserRole !== "contracts_User") {
|
||||
templateActions = templateAct.map((item) => {
|
||||
if (item.action === "option") {
|
||||
// Make a shallow copy of the item
|
||||
const newItem = { ...item };
|
||||
newItem.subaction = [
|
||||
{
|
||||
btnId: "1873",
|
||||
btnLabel: "Share with team",
|
||||
hoverLabel: "Share with team",
|
||||
btnIcon: "fa-light fa-share-nodes",
|
||||
redirectUrl: "",
|
||||
action: "sharewith"
|
||||
},
|
||||
...newItem.subaction
|
||||
];
|
||||
|
||||
return newItem;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
reportName: "Templates",
|
||||
heading: templateReport,
|
||||
actions: [
|
||||
{
|
||||
btnId: "2234",
|
||||
btnLabel: "Use",
|
||||
hoverLabel: "Use",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa fa-plus",
|
||||
redirectUrl: "placeHolderSign",
|
||||
action: "redirect",
|
||||
selector: "reactourSecond",
|
||||
message:
|
||||
"Click the 'Use' button to create a new document from an existing template. "
|
||||
},
|
||||
{
|
||||
btnId: "1631",
|
||||
btnLabel: "Quick send",
|
||||
hoverLabel: "Quick send",
|
||||
btnColor: "op-btn-secondary",
|
||||
btnIcon: "fa-solid fa-envelope",
|
||||
redirectUrl: "",
|
||||
action: "bulksend",
|
||||
selector: "tourbulksend",
|
||||
message:
|
||||
"To quickly create and send multiple documents using an existing template, click the 'Quick Send' button."
|
||||
},
|
||||
{
|
||||
btnId: "2234",
|
||||
hoverLabel: "option",
|
||||
btnColor: "",
|
||||
textColor: "black",
|
||||
btnIcon: "fa-solid fa-ellipsis-vertical fa-lg",
|
||||
action: "option",
|
||||
selector: "reactourThird",
|
||||
message:
|
||||
"This menu reveals more options such as Edit & Delete. Use the 'Edit' button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won’t affect existing documents.Use the Delete button you can delete template. ",
|
||||
subaction: [
|
||||
{
|
||||
btnId: "2434",
|
||||
btnLabel: "Edit",
|
||||
hoverLabel: "Edit",
|
||||
btnIcon: "fa-solid fa-pen",
|
||||
redirectUrl: "template",
|
||||
action: "redirect"
|
||||
},
|
||||
{
|
||||
btnId: "1834",
|
||||
btnLabel: "Delete",
|
||||
hoverLabel: "Delete",
|
||||
btnIcon: "fa fa-trash",
|
||||
redirectUrl: "",
|
||||
action: "delete"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
actions: templateActions,
|
||||
helpMsg:
|
||||
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
|
||||
};
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ const dashboardJson = [
|
||||
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||
widget: {
|
||||
type: "Card",
|
||||
icon: "fas fa-signature",
|
||||
bgColor: "bg-[#f0058e]",
|
||||
icon: "fa-light fa-signature",
|
||||
bgColor: "op-bg-primary",
|
||||
label: "Need your Signature",
|
||||
description: null,
|
||||
data: {
|
||||
@@ -29,8 +29,8 @@ const dashboardJson = [
|
||||
colsize: "col-span-12 md:col-span-6 lg:col-span-6",
|
||||
widget: {
|
||||
type: "Card",
|
||||
icon: "fas fa-sign-out-alt",
|
||||
bgColor: "bg-[#2ed8b6]",
|
||||
icon: "fa-light fa-sign-out-alt",
|
||||
bgColor: "op-bg-secondary",
|
||||
label: "Out for signatures",
|
||||
description: null,
|
||||
data: {
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
const sidebarList = [
|
||||
{
|
||||
icon: "fa-light fa-tachometer-alt",
|
||||
title: "Dashboard",
|
||||
target: "",
|
||||
pageType: "dashboard",
|
||||
description: "",
|
||||
objectId: "35KBoSgoAK"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-pen-nib",
|
||||
title: "Sign yourself",
|
||||
target: "_self",
|
||||
pageType: "form",
|
||||
description: "",
|
||||
objectId: "sHAnZphf69"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-paper-plane",
|
||||
title: "Request signatures",
|
||||
target: "_self",
|
||||
pageType: "form",
|
||||
description: "",
|
||||
objectId: "8mZzFxbG1z"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-newspaper",
|
||||
title: "Templates",
|
||||
target: "_self",
|
||||
pageType: null,
|
||||
description: null,
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: "fa-light fa-file-signature",
|
||||
title: "Create template",
|
||||
target: "_self",
|
||||
pageType: "form",
|
||||
description: "",
|
||||
objectId: "template"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-file-contract",
|
||||
title: "Manage templates",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "6TeaPr321t"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-folder",
|
||||
title: "OpenSign™ Drive",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "opensigndrive"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-address-card",
|
||||
title: "Reports",
|
||||
target: "_self",
|
||||
pageType: null,
|
||||
description: "",
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: "fa-light fa-signature",
|
||||
title: "Need your sign",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "4Hhwbp482K"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-tasks",
|
||||
title: "In Progress",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "1MwEuxLEkF"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-check-circle",
|
||||
title: "Completed",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "kQUoW4hUXz"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-edit",
|
||||
title: "Drafts",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "ByHuevtCFY"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-times-circle",
|
||||
title: "Declined",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "UPr2Fm5WY3"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-hourglass-end",
|
||||
title: "Expired",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "zNqBHXHsYH"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-address-book",
|
||||
title: "Contactbook",
|
||||
target: "_self",
|
||||
pageType: "report",
|
||||
description: "",
|
||||
objectId: "5KhaPr482K"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-cog",
|
||||
title: "Settings",
|
||||
target: "_self",
|
||||
pageType: null,
|
||||
description: "",
|
||||
objectId: null,
|
||||
children: [
|
||||
{
|
||||
icon: "fa-light fa-pen-fancy",
|
||||
title: "My Signature",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "managesign"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-key",
|
||||
title: "API Token",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "generatetoken"
|
||||
},
|
||||
{
|
||||
icon: "fa-light fa-globe",
|
||||
title: "Webhook",
|
||||
target: "_self",
|
||||
pageType: "",
|
||||
description: "",
|
||||
objectId: "webhook"
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
export default sidebarList;
|
||||
@@ -1,11 +1,14 @@
|
||||
import { isStaging } from "../constant/const";
|
||||
|
||||
const plans = [
|
||||
{
|
||||
planName: "OPENSIGN™ FREE",
|
||||
img: "free.png",
|
||||
currency: "",
|
||||
monthlyPrice: "Free",
|
||||
yearlyPrice: "Free",
|
||||
subtitle: "Free Unlimited E-signatures, Forever.",
|
||||
btnText: "Subscribe",
|
||||
btn: { text: "Subscribe", color: "op-btn-primary" },
|
||||
url: "",
|
||||
target: "_blank",
|
||||
benefits: [
|
||||
@@ -23,24 +26,44 @@ const plans = [
|
||||
"Decline document support",
|
||||
"Email notifications",
|
||||
"Recipient authentication using OTP",
|
||||
"Bulk send (upto 5 docs)",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"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",
|
||||
"Bulk send (upto 5 docs)",
|
||||
"Public profiles",
|
||||
"And much more"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ PROFESSIONAL",
|
||||
img: "professional.png",
|
||||
currency: "$",
|
||||
monthlyPrice: "29.99",
|
||||
yearlyPrice: "329.99",
|
||||
yearlyPrice: `9.99<sup style="font-size: 17px;">/month</sup>`,
|
||||
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",
|
||||
btn: { text: "Subscribe", color: "op-btn-primary" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328dd84ecda20eeae13878ce581d60240f206/pro-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d1a09dfa8085585cdd4ec4d7f32137f3/professional-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/9627f62a09df1c8ce500f2f4dc8328ddc184411129224f1f29ed84f6cef3b862/pro-yearly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
"Everything in OpenSign™ free",
|
||||
@@ -51,18 +74,74 @@ const plans = [
|
||||
"Zapier integration",
|
||||
"API Access",
|
||||
"100 API signatures included",
|
||||
"DocumentId removal from signed docs",
|
||||
"Custom email templates",
|
||||
"Auto reminders"
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 20 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Everything in OpenSign™ free",
|
||||
"Field validations",
|
||||
"Regular expression validations",
|
||||
"Organize docs in OpenSign™ Drive",
|
||||
"Webhooks",
|
||||
"Zapier integration",
|
||||
"API Access",
|
||||
"240 API signatures included",
|
||||
"Custom email templates",
|
||||
"Auto reminders",
|
||||
"Bulk send (upto 20 docs)",
|
||||
"Premium Public profile usernames",
|
||||
"Embedding (coming soon)"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ TEAMS",
|
||||
img: "teams.png",
|
||||
currency: "$",
|
||||
monthlyPrice: `39.99<sup style="font-size: 17px;">/user</sup>`,
|
||||
yearlyPrice: `19.99<sup style="font-size: 17px;">/user/month</sup>`,
|
||||
subtitle: "Exclusive Access to advanced features.",
|
||||
btn: { text: "Subscribe", color: "op-btn-accent" },
|
||||
url: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901237d0702bfaa959406306635d80f138c/teams-monthly",
|
||||
yearlyUrl: isStaging
|
||||
? "https://billing.zoho.in/subscribe/ed8097273a82b6bf39892c11a3bb3c381eb2705736014cfbdbde1ccf1c7a189d/team-weekly"
|
||||
: "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af9011a864994bbeeec71fcf106188630199d/teams-yearly",
|
||||
target: "_self",
|
||||
benefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"100 API signatures included",
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 50 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
"Mobile app (coming soon)"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"Everything in OpenSign™ professional",
|
||||
"500 API signatures included",
|
||||
"Teams and Organizations",
|
||||
"Share Templates with teams",
|
||||
"Share Templates with individuals",
|
||||
"DocumentId removal from signed docs",
|
||||
"Bulk send (upto 50 docs)",
|
||||
"Request Payments (coming soon)",
|
||||
"Mobile app (coming soon)"
|
||||
]
|
||||
},
|
||||
{
|
||||
planName: "OPENSIGN™ ENTERPRISE",
|
||||
img: "enterprise.png",
|
||||
currency: "",
|
||||
monthlyPrice: "Request Price",
|
||||
yearlyPrice: "Request Price",
|
||||
monthlyPrice: `<p style="font-size: 22px;">Request Price</p>`,
|
||||
yearlyPrice: `<p style="font-size: 22px;">Request Price</p>`,
|
||||
subtitle: "Scalable Features with priority support.",
|
||||
btnText: "Contact us",
|
||||
btn: { text: "Contact us", color: "op-btn-primary" },
|
||||
url: "https://www.opensignlabs.com/contact-us",
|
||||
target: "_blank",
|
||||
benefits: [
|
||||
@@ -72,6 +151,14 @@ const plans = [
|
||||
"Uptime SLA",
|
||||
"SSO support",
|
||||
"Priority support"
|
||||
],
|
||||
yearlyBenefits: [
|
||||
"All features",
|
||||
"Custom domain",
|
||||
"Custom branding",
|
||||
"Uptime SLA",
|
||||
"SSO support",
|
||||
"Priority support"
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import Sidebar from "../components/sidebar/Sidebar";
|
||||
import { useWindowSize } from "../hook/useWindowSize";
|
||||
import Tour from "reactour";
|
||||
import axios from "axios";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Parse from "parse";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { useNavigate, useLocation, Outlet } from "react-router-dom";
|
||||
@@ -13,10 +13,12 @@ import { isEnableSubscription } from "../constant/const";
|
||||
import { useCookies } from "react-cookie";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { showHeader } from "../redux/reducers/showHeader";
|
||||
|
||||
const HomeLayout = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const { width } = useWindowSize();
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const arr = useSelector((state) => state.TourSteps);
|
||||
@@ -38,7 +40,7 @@ const HomeLayout = () => {
|
||||
try {
|
||||
// Use the session token to validate the user
|
||||
const userQuery = new Parse.Query(Parse.User);
|
||||
const user = await userQuery.get(Parse.User.current().id, {
|
||||
const user = await userQuery.get(Parse?.User?.current()?.id, {
|
||||
sessionToken: localStorage.getItem("accesstoken")
|
||||
});
|
||||
if (user) {
|
||||
@@ -65,6 +67,8 @@ const HomeLayout = () => {
|
||||
const indexOfFirstDot = domainName.indexOf(".");
|
||||
// Remove the first dot and get the substring starting from the next character
|
||||
const updateDomain = domainName.substring(indexOfFirstDot); //.opensignlabs.com
|
||||
const serverUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
setCookie("accesstoken", localStorage.getItem("accesstoken"), {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
@@ -73,6 +77,14 @@ const HomeLayout = () => {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
});
|
||||
setCookie("server_url", serverUrl, {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
});
|
||||
setCookie("parse_app_id", parseAppId, {
|
||||
secure: true,
|
||||
domain: updateDomain
|
||||
});
|
||||
};
|
||||
|
||||
async function checkIsSubscribed() {
|
||||
@@ -98,6 +110,7 @@ const HomeLayout = () => {
|
||||
}
|
||||
const showSidebar = () => {
|
||||
setIsOpen((value) => !value);
|
||||
dispatch(showHeader(!isOpen));
|
||||
};
|
||||
useEffect(() => {
|
||||
if (width && width <= 768) {
|
||||
@@ -106,11 +119,7 @@ const HomeLayout = () => {
|
||||
}, [width]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
localStorage.getItem("domain") === "contracts" &&
|
||||
arr &&
|
||||
arr.length > 0
|
||||
) {
|
||||
if (arr && arr.length > 0) {
|
||||
handleDynamicSteps();
|
||||
} else {
|
||||
setIsTour(false);
|
||||
@@ -168,7 +177,6 @@ const HomeLayout = () => {
|
||||
setIsTour(false);
|
||||
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;
|
||||
// console.log("extUserId ", extUserId)
|
||||
@@ -190,7 +198,7 @@ const HomeLayout = () => {
|
||||
|
||||
// console.log("updatedTourStatus ", updatedTourStatus);
|
||||
await axios.put(
|
||||
serverUrl + "classes/" + extUserClass + "/" + extUserId,
|
||||
serverUrl + "classes/contracts_Users/" + extUserId,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
@@ -231,14 +239,14 @@ const HomeLayout = () => {
|
||||
}
|
||||
|
||||
const closeSidebar = () => {
|
||||
if (width <= 768) {
|
||||
if (width <= 1023) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginBtn = () => {
|
||||
try {
|
||||
Parse.User.logOut();
|
||||
Parse?.User?.logOut();
|
||||
} catch (err) {
|
||||
console.log("err ", err);
|
||||
} finally {
|
||||
@@ -248,7 +256,7 @@ const HomeLayout = () => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="sticky top-0 z-[101]">
|
||||
<div className="sticky top-0 z-[501]">
|
||||
{!isLoader && <Header showSidebar={showSidebar} />}
|
||||
</div>
|
||||
{isUserValid ? (
|
||||
|
||||
@@ -0,0 +1,454 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { getAppLogo, openInNewTab } from "../constant/Utils";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { showTenant } from "../redux/reducers/ShowTenant";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Title from "../components/Title";
|
||||
|
||||
const AddAdmin = () => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [name, setName] = useState("");
|
||||
const [phone, setPhone] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [company, setCompany] = useState("");
|
||||
const [jobTitle, setJobTitle] = useState("");
|
||||
const [lengthValid, setLengthValid] = useState(false);
|
||||
const [caseDigitValid, setCaseDigitValid] = useState(false);
|
||||
const [specialCharValid, setSpecialCharValid] = useState(false);
|
||||
const [isAuthorize, setIsAuthorize] = useState(false);
|
||||
const [isSubscribeNews, setIsSubscribeNews] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
const [state, setState] = useState({
|
||||
loading: false,
|
||||
alertType: "success",
|
||||
alertMsg: ""
|
||||
});
|
||||
const togglePasswordVisibility = () => setShowPassword(!showPassword);
|
||||
|
||||
useEffect(() => {
|
||||
checkUserExist();
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
const checkUserExist = async () => {
|
||||
setState((prev) => ({ ...prev, loading: true }));
|
||||
try {
|
||||
const app = await getAppLogo();
|
||||
if (app?.user === "exist") {
|
||||
setErrMsg("Admin already exist.");
|
||||
}
|
||||
} catch (err) {
|
||||
setErrMsg("Something went wrong.");
|
||||
console.log("err in check user exist", err);
|
||||
} finally {
|
||||
setState((prev) => ({ ...prev, loading: false }));
|
||||
}
|
||||
};
|
||||
const clearStorage = async () => {
|
||||
if (Parse.User.current()) {
|
||||
await Parse.User.logOut();
|
||||
}
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const appid = localStorage.getItem("parseAppId");
|
||||
const applogo = localStorage.getItem("appLogo");
|
||||
const defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
const PageLanding = localStorage.getItem("PageLanding");
|
||||
const userSettings = localStorage.getItem("userSettings");
|
||||
|
||||
localStorage.clear();
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("userSettings", userSettings);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (lengthValid && caseDigitValid && specialCharValid) {
|
||||
clearStorage();
|
||||
setState({ loading: true });
|
||||
const userDetails = {
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
company: company,
|
||||
jobTitle: jobTitle
|
||||
};
|
||||
localStorage.setItem("userDetails", JSON.stringify(userDetails));
|
||||
try {
|
||||
event.preventDefault();
|
||||
const user = new Parse.User();
|
||||
user.set("name", name);
|
||||
user.set("email", email);
|
||||
user.set("password", password);
|
||||
user.set("phone", phone);
|
||||
user.set("username", email);
|
||||
const userRes = await user.save();
|
||||
if (userRes) {
|
||||
const params = {
|
||||
userDetails: {
|
||||
jobTitle: jobTitle,
|
||||
company: company,
|
||||
name: name,
|
||||
email: email,
|
||||
phone: phone,
|
||||
role: "contracts_Admin"
|
||||
}
|
||||
};
|
||||
try {
|
||||
const usersignup = await Parse.Cloud.run("addadmin", params);
|
||||
if (usersignup) {
|
||||
if (isSubscribeNews) {
|
||||
subscribeNewsletter();
|
||||
}
|
||||
handleNavigation(userRes.getSessionToken());
|
||||
}
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("err ", error);
|
||||
if (error.code === 202) {
|
||||
const params = { email: email };
|
||||
const res = await Parse.Cloud.run("getUserDetails", params);
|
||||
// console.log("Res ", res);
|
||||
if (res) {
|
||||
alert("User already exists with this username!");
|
||||
setState({ loading: false });
|
||||
} else {
|
||||
// console.log("state.email ", email);
|
||||
try {
|
||||
await Parse.User.requestPasswordReset(email).then(
|
||||
async function (res) {
|
||||
if (res.data === undefined) {
|
||||
alert("Verification mail has been sent to your E-mail!");
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
setState({ loading: false });
|
||||
}
|
||||
} else {
|
||||
alert(error.message);
|
||||
setState({ loading: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleNavigation = async (sessionToken) => {
|
||||
const res = await Parse.User.become(sessionToken);
|
||||
if (res) {
|
||||
const _user = JSON.parse(JSON.stringify(res));
|
||||
// console.log("_user ", _user);
|
||||
localStorage.setItem("accesstoken", sessionToken);
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
localStorage.setItem("profileImg", _user.ProfilePic);
|
||||
} else {
|
||||
localStorage.setItem("profileImg", "");
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
const userSettings = appInfo.settings;
|
||||
const currentUser = Parse.User.current();
|
||||
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (extUser) {
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole && userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const extInfo_stringify = JSON.stringify([extUser]);
|
||||
localStorage.setItem("Extand_Class", extInfo_stringify);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo?.Email);
|
||||
localStorage.setItem("username", extInfo?.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "success",
|
||||
alertMsg: "Registered user successfully."
|
||||
});
|
||||
navigate(`/${menu.pageType}/${menu.pageId}`);
|
||||
} else {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "Role not found."
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "You don't have access, please contact the admin."
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error in fetch extuser", error);
|
||||
const msg = error.message || "Something went wrong.";
|
||||
setState({ loading: false, alertType: "danger", alertMsg: msg });
|
||||
} finally {
|
||||
setTimeout(() => setState({ loading: false, alertMsg: "" }), 2000);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handlePasswordChange = (e) => {
|
||||
const newPassword = e.target.value;
|
||||
setPassword(newPassword);
|
||||
// Check conditions separately
|
||||
setLengthValid(newPassword.length >= 8);
|
||||
setCaseDigitValid(
|
||||
/[a-z]/.test(newPassword) &&
|
||||
/[A-Z]/.test(newPassword) &&
|
||||
/\d/.test(newPassword)
|
||||
);
|
||||
setSpecialCharValid(/[!@#$%^&*()\-_=+{};:,<.>]/.test(newPassword));
|
||||
};
|
||||
const subscribeNewsletter = async () => {
|
||||
try {
|
||||
const params = { name: name, email: email, domain: window.location.host };
|
||||
await Parse.Cloud.run("newsletter", params);
|
||||
// console.log("newsletter ", newsletter);
|
||||
} catch (err) {
|
||||
console.log("err in subscribeNewsletter", err);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="h-screen flex justify-center">
|
||||
<Title title={"Add admin"} />
|
||||
{state.loading ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{errMsg ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-[95%] md:w-[500px]">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50">
|
||||
<h2 className="text-[30px] text-center mt-3 font-medium">
|
||||
Opensign Setup
|
||||
</h2>
|
||||
<NavLink
|
||||
to="https://discord.com/invite/xe9TDuyAyj"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
|
||||
>
|
||||
Join our discord server
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="fa-brands fa-discord ml-1"
|
||||
></i>
|
||||
{/* <span className="fa-sr-only">OpenSign's Discord</span> */}
|
||||
</NavLink>
|
||||
<div className="px-6 py-3 text-xs">
|
||||
<label className="block ">
|
||||
Name <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Email <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value?.toLowerCase())}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Phone <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Company <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={company}
|
||||
onChange={(e) => setCompany(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Job Title{" "}
|
||||
<span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={jobTitle}
|
||||
onChange={(e) => setJobTitle(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Password <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => handlePasswordChange(e)}
|
||||
required
|
||||
/>
|
||||
<span
|
||||
className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer text-base-content`}
|
||||
onClick={togglePasswordVisibility}
|
||||
>
|
||||
{showPassword ? (
|
||||
<i className="fa fa-eye-slash" /> // Close eye icon
|
||||
) : (
|
||||
<i className="fa fa-eye" /> // Open eye icon
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{password.length > 0 && (
|
||||
<div className="mt-1 text-[11px]">
|
||||
<p
|
||||
className={`${
|
||||
lengthValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{lengthValid ? "✓" : "✗"} Password should be 8
|
||||
characters long
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
caseDigitValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{caseDigitValid ? "✓" : "✗"} Password should contain
|
||||
uppercase letter, lowercase letter, digit
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
specialCharValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{specialCharValid ? "✓" : "✗"} Password should contain
|
||||
special character
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-2.5 ml-1 flex flex-row items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-sm"
|
||||
id="termsandcondition"
|
||||
checked={isAuthorize}
|
||||
onChange={(e) => setIsAuthorize(e.target.checked)}
|
||||
required
|
||||
/>
|
||||
<label
|
||||
className="text-xs cursor-pointer ml-1 mb-0"
|
||||
htmlFor="termsandcondition"
|
||||
>
|
||||
I agree to the
|
||||
</label>
|
||||
<span
|
||||
className="underline cursor-pointer ml-1"
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
"https://www.opensignlabs.com/terms-and-conditions"
|
||||
)
|
||||
}
|
||||
>
|
||||
Terms of Service
|
||||
</span>
|
||||
<span>.</span>
|
||||
</div>
|
||||
<div className="mt-2.5 ml-1 flex flex-row items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-sm"
|
||||
id="termsandcondition"
|
||||
checked={isSubscribeNews}
|
||||
onChange={(e) => setIsSubscribeNews(e.target.checked)}
|
||||
/>
|
||||
<label
|
||||
className="text-xs cursor-pointer ml-1 mb-0"
|
||||
htmlFor="termsandcondition"
|
||||
>
|
||||
Subscribe to OpenSign newsletter
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-4 text-center text-xs font-bold mb-3">
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary w-full"
|
||||
disabled={state.loading}
|
||||
>
|
||||
{state.loading ? "Loading..." : "Next"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddAdmin;
|
||||
@@ -21,7 +21,12 @@ function ChangePassword() {
|
||||
user.set("password", newpassword);
|
||||
user
|
||||
.save()
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
let _user = user.toJSON();
|
||||
if (_user) {
|
||||
await Parse.User.become(_user.sessionToken);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
}
|
||||
alert("Password updated successfully.");
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -32,7 +37,6 @@ function ChangePassword() {
|
||||
} else {
|
||||
alert("Your current password is missing or incorrect.");
|
||||
}
|
||||
console.log("Logged in user", user);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert("Your current password is missing or incorrect.");
|
||||
@@ -49,8 +53,8 @@ function ChangePassword() {
|
||||
return <Navigate to="/" />;
|
||||
}
|
||||
return (
|
||||
<div className="w-full bg-base-100 text-base-content shadow rounded p-2">
|
||||
<Title title={"Change Password"} />
|
||||
<div className="w-full bg-base-100 text-base-content shadow rounded-box p-2">
|
||||
<Title title="Change Password" />
|
||||
<div className="text-xl font-bold border-b-[1px] border-gray-300">
|
||||
Change Password
|
||||
</div>
|
||||
@@ -98,7 +102,10 @@ function ChangePassword() {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="op-btn op-btn-primary shadow-md m-2">
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary shadow-md mt-2"
|
||||
>
|
||||
Change Password
|
||||
</button>
|
||||
</form>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import "../styles/loader.css";
|
||||
import GetDashboard from "../components/dashboard/GetDashboard";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import Title from "../components/Title";
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import RenderAllPdfPage from "../components/pdf/RenderAllPdfPage";
|
||||
import "../styles/signature.css";
|
||||
import "../styles/AddUser.css";
|
||||
import Title from "../components/Title";
|
||||
import RenderDebugPdf from "../components/RenderDebugPdf";
|
||||
import { pdfjs } from "react-pdf";
|
||||
@@ -58,7 +56,7 @@ const DebugPdf = () => {
|
||||
const fetchPdfMetadata = async () => {
|
||||
try {
|
||||
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
|
||||
console.log("pdfDataURL ", pdfDataURL);
|
||||
// console.log("pdfDataURL ", pdfDataURL);
|
||||
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
|
||||
const pdfType = await inferPdfType(pdfInfo);
|
||||
setPdfDetails((prevDetails) => ({
|
||||
@@ -76,7 +74,7 @@ const DebugPdf = () => {
|
||||
|
||||
const inferPdfType = async (pdf) => {
|
||||
try {
|
||||
const firstPage = await pdf.getPage(1);
|
||||
const firstPage = await pdf.getPage(2);
|
||||
const scale = 1;
|
||||
const { width, height } = firstPage.getViewport({ scale });
|
||||
setPdfDimension({ width: width, height: height });
|
||||
@@ -217,14 +215,18 @@ const DebugPdf = () => {
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
{copied && (
|
||||
<Alert type="success">
|
||||
<span className="ml-3">Copied</span>
|
||||
</Alert>
|
||||
)}
|
||||
<Title title={"Debug Pdf"} />
|
||||
{width < 800 ? (
|
||||
<HandleError handleError={"Debug PDF only availble for PC"} />
|
||||
) : (
|
||||
<>
|
||||
{!isModal && (
|
||||
<div className="signatureContainer">
|
||||
<div className="flex flex-row justify-between">
|
||||
{/* this component used to render all pdf pages in left side */}
|
||||
<RenderAllPdfPage
|
||||
signPdfUrl={pdf}
|
||||
@@ -253,126 +255,68 @@ const DebugPdf = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ backgroundColor: "white", width: 220 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
<div className="w-[220px] bg-base-100">
|
||||
<div className="text-[18px] font-medium py-[10px] px-[12px] border-b-[1px] border-[gray]">
|
||||
PDF details
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
Name: {pdfDetails?.name}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
Pdf type: {pdfDetails?.pdftype}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
Total Pages: {pdfDetails?.totalPages}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
Current Page: {pdfDetails?.currentPage}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
Base64 : {pdfDetails?.base64.slice(0, 10)}...
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className="op-btn op-btn-outline op-btn-primary op-btn-xs rounded-md w-[25px] h-[25px] text-[12px] m-[2px] "
|
||||
onClick={() => copytoclipboard(pdfDetails?.base64)}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
<i className="fa-light fa-copy"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
<div className="text-[18px] font-medium py-[10px] px-[12px] border-b-[1px] border-[gray]">
|
||||
Last click
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
x co-ordinate: {pdfDetails?.x}
|
||||
</div>
|
||||
<div style={{ fontSize: 14, padding: "5px 12px" }}>
|
||||
<div className="text-[14px] py-[5px] px-[12px]">
|
||||
y co-ordinate: {pdfDetails?.y}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 500,
|
||||
padding: "10px 12px",
|
||||
borderBottom: "1px solid grey",
|
||||
borderTop: "1px solid grey"
|
||||
}}
|
||||
>
|
||||
<div className="text-[18px] font-medium py-[10px] px-[12px] border-y-[1px] border-[gray]">
|
||||
Annotation
|
||||
</div>
|
||||
<ul
|
||||
style={{
|
||||
listStyle: "none",
|
||||
padding: 10,
|
||||
height: 500,
|
||||
overflowY: "auto",
|
||||
scrollbarWidth: 5
|
||||
}}
|
||||
>
|
||||
<ul className=" list-none p-[10px] h-[500px] overflow-y-auto">
|
||||
{annotations.map((coord, index) => (
|
||||
<li key={index}>
|
||||
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
|
||||
<span className="text-[13px] font-medium">{`Box ${
|
||||
index + 1
|
||||
}:`}</span>
|
||||
<code
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: "black",
|
||||
cursor: "pointer"
|
||||
}}
|
||||
>
|
||||
<code className="text-[12px] text-base-content select-none">
|
||||
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
|
||||
</code>
|
||||
<div>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className="op-btn op-btn-outline op-btn-primary op-btn-xs rounded-md w-[23px] h-[20px] text-[12px] m-[2px] "
|
||||
onClick={() =>
|
||||
copytoclipboard(
|
||||
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
<i className="fa-light fa-copy"></i>
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
borderRadius: 4,
|
||||
padding: "3px 5px",
|
||||
border: "1px solid gray",
|
||||
fontSize: 12,
|
||||
margin: 2,
|
||||
cursor: "pointer"
|
||||
}}
|
||||
className="op-btn op-btn-outline op-btn-error op-btn-xs rounded-md w-[23px] h-[20px] text-[12px] m-[2px] "
|
||||
onClick={() => handleDelete(coord.key)}
|
||||
>
|
||||
<i className="fa-solid fa-trash-can"></i>
|
||||
<i className="fa-light fa-trash-can"></i>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
@@ -382,38 +326,16 @@ const DebugPdf = () => {
|
||||
</div>
|
||||
)}
|
||||
<ModalUi title={"Select PDF"} isOpen={isModal}>
|
||||
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
|
||||
<form onSubmit={handleSubmit} className="m-[10px]">
|
||||
<input
|
||||
type="file"
|
||||
onChange={(e) => handleFileChange(e.target.files)}
|
||||
style={{
|
||||
width: "100%",
|
||||
border: "1px solid grey",
|
||||
borderRadius: "4px",
|
||||
padding: "5px",
|
||||
fontSize: 12
|
||||
}}
|
||||
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full h-[40px] text-xs"
|
||||
accept=".pdf"
|
||||
required
|
||||
/>
|
||||
<div
|
||||
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
background: "#32a3ac",
|
||||
borderRadius: "2px",
|
||||
boxShadow:
|
||||
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
|
||||
border: "none",
|
||||
textTransform: "uppercase",
|
||||
fontSize: "13px",
|
||||
fontWeight: "600",
|
||||
padding: "0.375rem 0.75rem",
|
||||
textAlign: "center",
|
||||
color: "#ffffff",
|
||||
outline: "none"
|
||||
}}
|
||||
className="op-btn op-btn-primary w-full mt-3 mb-1"
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
|
||||
@@ -36,9 +36,6 @@ function ForgotPassword() {
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
localStorage.setItem("appLogo", appInfo.applogo);
|
||||
localStorage.setItem("appName", appInfo.appname);
|
||||
// localStorage.setItem("defaultmenuid", props.appInfo.defaultmenuid);
|
||||
// localStorage.setItem("PageLanding", props.appInfo.LandingPageId);
|
||||
localStorage.setItem("userSettings", JSON.stringify(appInfo.settings));
|
||||
if (state.email) {
|
||||
const username = state.email;
|
||||
@@ -64,9 +61,9 @@ function ForgotPassword() {
|
||||
}, []);
|
||||
const saveLogo = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const logo = await getAppLogo();
|
||||
if (logo) {
|
||||
setImage(logo);
|
||||
const app = await getAppLogo();
|
||||
if (app?.logo) {
|
||||
setImage(app?.logo);
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import { isEnableSubscription } from "../constant/const";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
// `Form` render all type of Form on this basis of their provided in path
|
||||
@@ -445,7 +444,7 @@ const Forms = (props) => {
|
||||
setIsSubmit(false);
|
||||
}
|
||||
} else {
|
||||
alert("Please wait until the PDF has been uploading.");
|
||||
alert("Please wait while the document is being uploaded.");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -565,7 +564,7 @@ const Forms = (props) => {
|
||||
setFormData((prev) => ({ ...prev, autoreminder: !formData.autoreminder }));
|
||||
};
|
||||
return (
|
||||
<div className="shadow-md rounded my-2 p-3 bg-base-100 text-base-content md:border-[1px] md:border-gray-600/50">
|
||||
<div className="shadow-md rounded-box my-[2px] p-3 bg-base-100 text-base-content">
|
||||
<Title title={props?.title} />
|
||||
{isAlert && (
|
||||
<Alert type={isErr ? "danger" : "success"}>
|
||||
@@ -644,16 +643,16 @@ const Forms = (props) => {
|
||||
<span className="text-red-500 text-[13px]">*</span>
|
||||
</label>
|
||||
{fileupload.length > 0 ? (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full text-x text-[13px]">
|
||||
<div className="break-all">
|
||||
file selected : {getFileName(fileupload)}
|
||||
<div className="flex gap-1 justify-center items-center">
|
||||
<div className="flex justify-between items-center op-input op-input-bordered op-input-sm w-full h-full text-[13px]">
|
||||
<div className="break-all cursor-default">
|
||||
file selected: {getFileName(fileupload)}
|
||||
</div>
|
||||
<div
|
||||
onClick={() => setFileUpload("")}
|
||||
className="cursor-pointer px-[10px] text-[20px] font-bold text-red-500"
|
||||
>
|
||||
<i className="fa-solid fa-xmark"></i>
|
||||
<i className="fa-light fa-xmark"></i>
|
||||
</div>
|
||||
</div>
|
||||
{process.env.REACT_APP_DROPBOX_API_KEY && (
|
||||
@@ -664,7 +663,7 @@ const Forms = (props) => {
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2 justify-center items-center ">
|
||||
<div className="flex gap-1 justify-center items-center">
|
||||
<input
|
||||
type="file"
|
||||
className="op-file-input op-file-input-bordered op-file-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
@@ -701,15 +700,17 @@ const Forms = (props) => {
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
{props.title === "New Template" && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">Description</label>
|
||||
<input
|
||||
name="Description"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formData.Description}
|
||||
onChange={(e) => handleStrInput(e)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{props.signers && (
|
||||
<SignersInput
|
||||
onChange={handleSigners}
|
||||
@@ -759,16 +760,7 @@ const Forms = (props) => {
|
||||
Send In Order
|
||||
<a data-tooltip-id="sendInOrder-tooltip" className="ml-1">
|
||||
<sup>
|
||||
<i
|
||||
className="fa-solid fa-question rounded-full"
|
||||
style={{
|
||||
borderColor: "#33bbff",
|
||||
color: "#33bbff",
|
||||
fontSize: 13,
|
||||
borderWidth: 1.5,
|
||||
padding: "1.5px 4px"
|
||||
}}
|
||||
></i>
|
||||
<i className="fa-light fa-question rounded-full border-[#33bbff] text-[#33bbff] text-[13px] border-[1px] py-[1.5px] px-[4px]"></i>
|
||||
</sup>
|
||||
</a>
|
||||
<Tooltip id="sendInOrder-tooltip" className="z-50">
|
||||
@@ -837,39 +829,34 @@ const Forms = (props) => {
|
||||
<div className="text-center">No</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs mt-2">
|
||||
{!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Disable Auto reminder is free in beta, this feature will incur a fee later."
|
||||
{isEnableSubscription && (
|
||||
<div className="text-xs mt-2">
|
||||
<span
|
||||
className={
|
||||
isSubscribe
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
Auto reminder{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={formData.autoreminder}
|
||||
onChange={handleAutoReminder}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
>
|
||||
Auto reminder{" "}
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe
|
||||
? "cursor-pointer "
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={formData.autoreminder}
|
||||
onChange={handleAutoReminder}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{formData?.autoreminder === true && (
|
||||
<div className="text-xs mt-2">
|
||||
<label className="block">
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
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 { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed, copytoData, openInNewTab } from "../constant/Utils";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import Loader from "../primitives/Loader";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import Tour from "reactour";
|
||||
const tourSteps = [
|
||||
{
|
||||
selector: '[data-tut="apisubscribe"]',
|
||||
content: "Upgrade now to generate API token"
|
||||
}
|
||||
];
|
||||
|
||||
function GenerateToken() {
|
||||
const navigation = useNavigate();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [apiToken, SetApiToken] = useState("");
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [isGenerate, setIsGenerate] = useState(false);
|
||||
const [isErr, setIsErr] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
useEffect(() => {
|
||||
fetchToken();
|
||||
// eslint-disable-next-line
|
||||
@@ -33,7 +36,6 @@ function GenerateToken() {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
|
||||
const url = parseBaseUrl + "functions/getapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
@@ -42,9 +44,8 @@ function GenerateToken() {
|
||||
};
|
||||
const res = await axios.post(url, {}, { headers: headers });
|
||||
if (res) {
|
||||
SetApiToken(res.data.result.result);
|
||||
SetApiToken(res.data?.result?.result);
|
||||
}
|
||||
|
||||
setIsLoader(false);
|
||||
} catch (err) {
|
||||
SetApiToken();
|
||||
@@ -54,49 +55,43 @@ function GenerateToken() {
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
try {
|
||||
const url = parseBaseUrl + "functions/generateapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
await axios.post(url, {}, { headers: headers }).then((res) => {
|
||||
if (!isSubscribe && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
setIsLoader(true);
|
||||
setIsModal(false);
|
||||
try {
|
||||
const url = parseBaseUrl + "functions/generateapitoken";
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessiontoken: localStorage.getItem("accesstoken")
|
||||
};
|
||||
const res = await axios.post(url, {}, { headers: headers });
|
||||
if (res) {
|
||||
SetApiToken(res.data.result.token);
|
||||
// localStorage.setItem("apiToken", res.data.result.token);
|
||||
setIsGenerate(true);
|
||||
setTimeout(() => {
|
||||
setIsGenerate(false);
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
setIsAlert({ type: "success", msg: "Token generated successfully." });
|
||||
} else {
|
||||
console.error("Error while generating Token");
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
}, 1500);
|
||||
|
||||
console.log("err", error);
|
||||
} catch (error) {
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
console.log("while generating Token", error);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const copytoclipboard = (text) => {
|
||||
copytoData(text);
|
||||
setCopied(true);
|
||||
setIsAlert({ type: "success", msg: "Copied" });
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500); // Reset copied state after 1.5 seconds
|
||||
};
|
||||
const handleModal = () => setIsModal(!isModal);
|
||||
@@ -104,132 +99,118 @@ function GenerateToken() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"API Token"} />
|
||||
{isGenerate && (
|
||||
<Alert type="success">Token generated successfully!</Alert>
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
{copied && <Alert type="success">Copied</Alert>}
|
||||
{isErr && <Alert type="danger">Something went wrong!</Alert>}
|
||||
{isLoader ? (
|
||||
<div className="flex justify-center items-center h-screen">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
|
||||
{!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"
|
||||
}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</h1>
|
||||
<ul
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "w-full flex flex-col p-2 text-sm "
|
||||
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||
}
|
||||
>
|
||||
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
|
||||
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5 ">
|
||||
<span className="">Api Token:</span>{" "}
|
||||
<span id="token" className="md:text-end py-2 md:py-0">
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
{apiToken ? apiToken : "_____"}
|
||||
</span>
|
||||
<button
|
||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
<i className="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex items-center justify-center ">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
<>
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
|
||||
<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"
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary my-2"
|
||||
>
|
||||
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"
|
||||
}
|
||||
isSubscribe={true}
|
||||
/>
|
||||
</h1>
|
||||
<ul className="w-full flex flex-col p-2 text-sm">
|
||||
<li className="flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2">
|
||||
<div className="w-full md:w-[70%] flex-col md:flex-row text-xs md:text-[15px] flex items-center gap-x-5">
|
||||
<span className="ml-1">API Token:</span>{" "}
|
||||
<span
|
||||
id="token"
|
||||
className={`${
|
||||
isSubscribe
|
||||
? ""
|
||||
: "bg-white/20 pointer-events-none select-none"
|
||||
} md:text-end py-2 md:py-0`}
|
||||
>
|
||||
<span
|
||||
className="cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
Upgrade Now
|
||||
{apiToken ? apiToken : "_____"}
|
||||
</span>
|
||||
<button
|
||||
className="op-btn op-btn-accent op-btn-outline op-btn-sm ml-2 cursor-pointer"
|
||||
onClick={() => copytoclipboard(apiToken)}
|
||||
>
|
||||
<i className="fa-light fa-copy"></i>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={"Regenerate Token"}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-base-content">
|
||||
Are you sure you want to regenerate token it will expire old
|
||||
token?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className="op-btn op-btn-primary ml-[2px]"
|
||||
onClick={apiToken ? handleModal : handleSubmit}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
Yes
|
||||
{apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary mt-2 mb-3 px-8"
|
||||
>
|
||||
View Docs
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
<ModalUi
|
||||
isOpen={isModal}
|
||||
title={apiToken ? "Regenerate Token" : "Generate Token"}
|
||||
handleClose={handleModal}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-base-content">
|
||||
Are you sure you want to regenerate token it will expire old
|
||||
token?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4" />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className="op-btn op-btn-primary ml-[2px]"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Tour
|
||||
onRequestClose={() => setIsTour(false)}
|
||||
steps={tourSteps}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ function GuestLogin() {
|
||||
//function generate serverUrl and parseAppId from url and save it in local storage
|
||||
const handleServerUrl = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const applogo = await getAppLogo();
|
||||
if (applogo) {
|
||||
setAppLogo(applogo);
|
||||
const app = await getAppLogo();
|
||||
if (app?.logo) {
|
||||
setAppLogo(app?.logo);
|
||||
} else {
|
||||
setAppLogo(logo);
|
||||
}
|
||||
@@ -42,10 +42,8 @@ function GuestLogin() {
|
||||
localStorage.clear();
|
||||
const parseId = appInfo.appId;
|
||||
const newServer = `${appInfo.baseUrl}/`;
|
||||
const appName = appInfo.appname;
|
||||
localStorage.setItem("baseUrl", newServer);
|
||||
localStorage.setItem("parseAppId", parseId);
|
||||
localStorage.setItem("_appName", appName);
|
||||
//this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data.
|
||||
if (!id) {
|
||||
//`atob` function is used to decode base64
|
||||
@@ -191,7 +189,7 @@ function GuestLogin() {
|
||||
<div className="m-1 md:m-2 p-[30px] text-base-content bg-base-100 op-card shadow-md">
|
||||
<div className="md:w-[250px] md:h-[66px] inline-block overflow-hidden mt-2 mb-11">
|
||||
{appLogo && (
|
||||
<img src={appLogo} className="object-contain h-full" alt="logo" />
|
||||
<img src={appLogo} className="object-contain" alt="logo" />
|
||||
)}
|
||||
</div>
|
||||
{contactId ? (
|
||||
@@ -199,7 +197,7 @@ function GuestLogin() {
|
||||
{!EnterOTP ? (
|
||||
<div className="w-full md:w-[50%] text-base-content">
|
||||
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2">
|
||||
<legend className="text-[12px] text-[#878787] mt-2 mb-1">
|
||||
Verification code is sent to your email
|
||||
</legend>
|
||||
<div className="p-[20px] outline outline-1 outline-slate-300/50 my-2 op-card shadow-md">
|
||||
@@ -207,17 +205,17 @@ function GuestLogin() {
|
||||
type="email"
|
||||
name="mobile"
|
||||
value={email}
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full disabled:text-[#5c5c5c] text-xs"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<div className="mt-3">
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={(e) => handleSendOTPBtn(e)}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? "Loading..." : "Send OTP"}
|
||||
{loading ? "Loading..." : "Get verification code"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -228,7 +226,7 @@ function GuestLogin() {
|
||||
>
|
||||
<h1 className="text-2xl md:text-[30px]">Welcome Back!</h1>
|
||||
<legend className="text-[12px] text-[#878787] mt-2">
|
||||
You will get a OTP via Email
|
||||
You will get a verification code via Email
|
||||
</legend>
|
||||
<div className="p-[20px] pt-[15px] outline outline-1 outline-slate-300/50 op-card my-2 shadow-md">
|
||||
<p className="text-sm">Enter Verification Code</p>
|
||||
@@ -240,7 +238,7 @@ function GuestLogin() {
|
||||
onChange={(e) => setOTP(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<div className="mt-2.5">
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
type="submit"
|
||||
@@ -298,7 +296,7 @@ function GuestLogin() {
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<div className="mt-2.5">
|
||||
<label
|
||||
htmlFor="phone"
|
||||
className="block text-xs text-gray-700 font-semibold"
|
||||
|
||||
@@ -4,12 +4,10 @@ import "../styles/managesign.css";
|
||||
import "../styles/signature.css";
|
||||
import { toDataUrl } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
const ManageSign = () => {
|
||||
const appName = appInfo.appname;
|
||||
const [penColor, setPenColor] = useState("blue");
|
||||
const [initialPen, setInitialPen] = useState("blue");
|
||||
const [image, setImage] = useState();
|
||||
@@ -39,7 +37,7 @@ const ManageSign = () => {
|
||||
objectId: User.id
|
||||
};
|
||||
try {
|
||||
const signCls = `${appName}_Signature`;
|
||||
const signCls = "contracts_Signature";
|
||||
const signQuery = new Parse.Query(signCls);
|
||||
signQuery.equalTo("UserId", userId);
|
||||
const signRes = await signQuery.first();
|
||||
@@ -174,7 +172,7 @@ const ManageSign = () => {
|
||||
};
|
||||
|
||||
const saveEntry = async (obj) => {
|
||||
const signCls = `${appName}_Signature`;
|
||||
const signCls = "contracts_Signature";
|
||||
const User = Parse.User.current().id;
|
||||
const userId = { __type: "Pointer", className: "_User", objectId: User };
|
||||
if (id) {
|
||||
@@ -228,12 +226,7 @@ const ManageSign = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleSignatureBtn = () => {
|
||||
if (imageRef.current) {
|
||||
imageRef.current.value = "";
|
||||
}
|
||||
setImage("");
|
||||
};
|
||||
|
||||
const handleUploadBtn = () => {
|
||||
imageRef.current.click();
|
||||
};
|
||||
@@ -242,166 +235,130 @@ const ManageSign = () => {
|
||||
setIsValue(true);
|
||||
};
|
||||
return (
|
||||
<div className="relative h-full bg-base-100 text-base-content flex shadow rounded overflow-auto">
|
||||
<div className="relative h-full bg-base-100 text-base-content flex shadow-md rounded-box overflow-auto">
|
||||
{isLoader && (
|
||||
<div className="absolute bg-black bg-opacity-30 z-50 w-full h-full flex justify-center items-center">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isAlert?.message && <Alert type={isAlert.type}>{isAlert.message}</Alert>}
|
||||
<div
|
||||
className="mainDiv"
|
||||
style={{
|
||||
width: "100%",
|
||||
paddingRight: "10px"
|
||||
}}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="font-[700] text-[15px] pb-[8px]">My Signature</div>
|
||||
<div className="signBlock mt-4">
|
||||
<div>
|
||||
<div className="relative w-full">
|
||||
<div className="ml-[5px] my-[20px] md:m-[20px]">
|
||||
<div className="text-[20px] font-semibold m-[10px] md:m-0 mb-2">
|
||||
My Signature
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row gap-0 md:gap-[12px]">
|
||||
<div className="relative">
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
Signature
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
<div className="relative">
|
||||
<div className="flex flex-row justify-between w-1/2 pl-[10px]">
|
||||
<div className="flex flex-row justify-around items-center gap-[10px] mb-[10px]">
|
||||
<>
|
||||
<span
|
||||
onClick={() => handleSignatureBtn()}
|
||||
className="signature"
|
||||
>
|
||||
Signature
|
||||
</span>
|
||||
</>
|
||||
|
||||
<span
|
||||
onClick={() => handleUploadBtn()}
|
||||
className="imgupload"
|
||||
>
|
||||
Upload Image
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
onChange={onImageChange}
|
||||
className="filetype"
|
||||
accept="image/*"
|
||||
ref={imageRef}
|
||||
hidden
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<div>
|
||||
{image ? (
|
||||
<div className="signatureCanvas relative border-[2px] border-[#888]">
|
||||
<img
|
||||
alt="preview image"
|
||||
src={image}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
width: "456px",
|
||||
height: "180px",
|
||||
className: "signatureCanvas"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
<div>
|
||||
{image ? (
|
||||
<div className="signatureCanvas relative border-[2px] border-[#888] rounded-box overflow-hidden">
|
||||
<img
|
||||
alt="signature"
|
||||
src={image}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
</div>
|
||||
) : (
|
||||
<SignatureCanvas
|
||||
ref={canvasRef}
|
||||
penColor={penColor}
|
||||
canvasProps={{
|
||||
width: "456px",
|
||||
height: "180px",
|
||||
className: "signatureCanvas rounded-box"
|
||||
}}
|
||||
className="penContainerDefault"
|
||||
>
|
||||
<div>
|
||||
{!image && (
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "row" }}
|
||||
>
|
||||
{allColor.map((data, key) => {
|
||||
return (
|
||||
<i
|
||||
style={{
|
||||
margin: "5px",
|
||||
color: data,
|
||||
borderBottom:
|
||||
key === 0 && penColor === "blue"
|
||||
? "2px solid blue"
|
||||
: key === 1 && penColor === "red"
|
||||
? "2px solid red"
|
||||
: key === 2 && penColor === "black"
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
onClick={() => {
|
||||
if (key === 0) {
|
||||
setPenColor("blue");
|
||||
} else if (key === 1) {
|
||||
setPenColor("red");
|
||||
} else if (key === 2) {
|
||||
setPenColor("black");
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa solid fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
type="button"
|
||||
className="clearbtn cursor-pointer"
|
||||
onClick={() => handleClear()}
|
||||
>
|
||||
Clear
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
handleSignatureChange(canvasRef.current.toDataURL())
|
||||
}
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
<div className="penContainerDefault flex flex-row justify-between">
|
||||
<div>
|
||||
{!image && (
|
||||
<div className="flex flex-row">
|
||||
{allColor.map((data, key) => {
|
||||
return (
|
||||
<i
|
||||
style={{
|
||||
margin: "5px",
|
||||
color: data,
|
||||
borderBottom:
|
||||
key === 0 && penColor === "blue"
|
||||
? "2px solid blue"
|
||||
: key === 1 && penColor === "red"
|
||||
? "2px solid red"
|
||||
: key === 2 && penColor === "black"
|
||||
? "2px solid black"
|
||||
: "2px solid white"
|
||||
}}
|
||||
onClick={() => {
|
||||
if (key === 0) {
|
||||
setPenColor("blue");
|
||||
} else if (key === 1) {
|
||||
setPenColor("red");
|
||||
} else if (key === 2) {
|
||||
setPenColor("black");
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa-light fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 text-sm md:text-base mr-1">
|
||||
<div
|
||||
type="button"
|
||||
className="op-link"
|
||||
onClick={() => handleUploadBtn()}
|
||||
>
|
||||
Upload image
|
||||
</div>
|
||||
<div
|
||||
type="button"
|
||||
className="op-link"
|
||||
onClick={() => handleClear()}
|
||||
>
|
||||
Clear
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{warning && (
|
||||
<div
|
||||
className="warning signWarning"
|
||||
style={{ fontSize: 12 }}
|
||||
>
|
||||
<i
|
||||
className="fas fa-exclamation-circle"
|
||||
style={{ color: "#fab005", fontSize: 15 }}
|
||||
></i>{" "}
|
||||
Please upload signature/Image
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{warning && (
|
||||
<span className="customwarning signWarning text-[12px] w-[220px] md:w-[300px]">
|
||||
<i className="fa-light fa-exclamation-circle text-[#fab005] text-[15px] mr-[4px]"></i>
|
||||
Please upload signature/Image
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ position: "relative" }}>
|
||||
<div style={{ margin: "6px 5px 18px" }}>
|
||||
<span className="signature">Initials</span>
|
||||
</div>
|
||||
<div className="relative">
|
||||
<span className="font-medium select-none flex mb-[10px] pl-[10px]">
|
||||
Initials
|
||||
</span>
|
||||
<div>
|
||||
{isInitials ? (
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
border: "2px solid #888"
|
||||
}}
|
||||
className="intialSignature"
|
||||
>
|
||||
<div className="intialSignature relative border-[1px] border-[#888] rounded-box overflow-hidden">
|
||||
<img
|
||||
alt="preview image"
|
||||
alt="inititals"
|
||||
src={Initials}
|
||||
style={{
|
||||
width: "100%",
|
||||
@@ -415,7 +372,7 @@ const ManageSign = () => {
|
||||
ref={initailsRef}
|
||||
penColor={initialPen}
|
||||
canvasProps={{
|
||||
className: "intialSignature"
|
||||
className: "intialSignature rounded-box"
|
||||
}}
|
||||
backgroundColor="rgb(255, 255, 255)"
|
||||
onEnd={() =>
|
||||
@@ -424,17 +381,10 @@ const ManageSign = () => {
|
||||
dotSize={1}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between"
|
||||
}}
|
||||
className="penContainerInitial"
|
||||
>
|
||||
<div className="flex flex-row justify-between w-[183px]">
|
||||
<div>
|
||||
{!isInitials && (
|
||||
<div style={{ display: "flex", flexDirection: "row" }}>
|
||||
<div className="flex flex-row">
|
||||
{allColor.map((data, key) => {
|
||||
return (
|
||||
<i
|
||||
@@ -460,7 +410,7 @@ const ManageSign = () => {
|
||||
}
|
||||
}}
|
||||
key={key}
|
||||
className="fa solid fa-pen-nib"
|
||||
className="fa-light fa-pen-nib"
|
||||
width={20}
|
||||
height={20}
|
||||
></i>
|
||||
@@ -472,7 +422,7 @@ const ManageSign = () => {
|
||||
<div>
|
||||
<div
|
||||
type="button"
|
||||
className="clearbtn cursor-pointer"
|
||||
className="op-link text-sm md:text-base mr-1"
|
||||
onClick={() => handleClearInitials()}
|
||||
>
|
||||
Clear
|
||||
@@ -482,7 +432,7 @@ const ManageSign = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ paddingTop: 10 }}>
|
||||
<div className="pt-3 ml-2 md:ml-0">
|
||||
<button
|
||||
className="op-btn op-btn-primary"
|
||||
onClick={(e) => handleSubmit(e)}
|
||||
|
||||
@@ -272,7 +272,7 @@ function Opensigndrive() {
|
||||
setIsFolderLoader(true);
|
||||
const getParentObjId = folderName[folderName.length - 1];
|
||||
const parentId = getParentObjId && getParentObjId.objectId;
|
||||
const foldercls = `${localStorage.getItem("_appName")}_Document`;
|
||||
const foldercls = "contracts_Document";
|
||||
const folderPtr = {
|
||||
__type: "Pointer",
|
||||
className: foldercls,
|
||||
@@ -465,7 +465,6 @@ function Opensigndrive() {
|
||||
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 = [];
|
||||
@@ -483,7 +482,7 @@ function Opensigndrive() {
|
||||
updatedTourStatus = [{ driveTour: true }];
|
||||
}
|
||||
await axios.put(
|
||||
serverUrl + "classes/" + extUserClass + "/" + extUserId,
|
||||
serverUrl + "classes/contracts_Users/" + extUserId,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
@@ -522,7 +521,7 @@ function Opensigndrive() {
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="bg-base-100 text-base-content rounded-xl w-full">
|
||||
<div className="bg-base-100 text-base-content rounded-box w-full shadow-md">
|
||||
<Title title={"OpenSign™ Drive"} drive={true} />
|
||||
<ModalUi
|
||||
isOpen={isAlert.isShow}
|
||||
@@ -598,11 +597,13 @@ function Opensigndrive() {
|
||||
{isLoading.isLoad ? (
|
||||
<div className="flex flex-col justify-center items-center h-[100vh] w-full">
|
||||
<Loader />
|
||||
<span className="text-[13px] text-[gray]">{isLoading.message}</span>
|
||||
<span className="text-[13px] text-base-content">
|
||||
{isLoading.message}
|
||||
</span>
|
||||
</div>
|
||||
) : handleError ? (
|
||||
<div className="flex justify-center items-center h-[100vh] w-full">
|
||||
<span className="text-[20px] text-[gray]">{handleError}</span>
|
||||
<span className="text-[20px] text-base-content">{handleError}</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
@@ -625,7 +626,7 @@ function Opensigndrive() {
|
||||
>
|
||||
{handleFolderTab(folderName)}
|
||||
</div>
|
||||
<div className="dropMenuBD">
|
||||
<div className="flex flex-row items-center">
|
||||
<div
|
||||
id="folder-menu"
|
||||
className={
|
||||
@@ -635,7 +636,7 @@ function Opensigndrive() {
|
||||
>
|
||||
<div className="sort" data-tut="reactourSecond">
|
||||
<i
|
||||
className="fa fa-plus-square"
|
||||
className="fa-light fa-plus-square"
|
||||
aria-hidden="true"
|
||||
style={{ fontSize: "25px", color: `${iconColor}` }}
|
||||
></i>
|
||||
@@ -647,24 +648,27 @@ function Opensigndrive() {
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
className="dropdown-item text-[10px] md:text-[13px]"
|
||||
onClick={() => setIsFolder(true)}
|
||||
>
|
||||
<i className="fa fa-plus mr-[5px]" aria-hidden="true"></i>
|
||||
<i
|
||||
className="fa-light fa-plus mr-[5px]"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
Create folder
|
||||
</span>
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
className="dropdown-item text-[10px] md:text-[13px]"
|
||||
onClick={() => navigate("/form/sHAnZphf69")}
|
||||
>
|
||||
<i className="fas fa-pen-nib mr-[5px"></i>
|
||||
<i className="fa-light fa-pen-nib mr-[5px]"></i>
|
||||
Sign Yourself
|
||||
</span>
|
||||
<span
|
||||
className="dropdown-item itemColor"
|
||||
className="dropdown-item text-[10px] md:text-[13px]"
|
||||
onClick={() => navigate("/form/8mZzFxbG1z")}
|
||||
>
|
||||
<i className="fa fa-file-signature mr-[5px"></i>
|
||||
<i className="fa-light fa-file-signature mr-[5px]"></i>
|
||||
Request Signatures
|
||||
</span>
|
||||
</div>
|
||||
@@ -681,7 +685,7 @@ function Opensigndrive() {
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i
|
||||
className="fa fa-sort-amount-asc mr-[5px] text-[14px]"
|
||||
className="fa-light fa-sort-amount-asc mr-[5px] text-[14px]"
|
||||
aria-hidden="true"
|
||||
style={{ color: `${iconColor}` }}
|
||||
></i>
|
||||
@@ -704,18 +708,18 @@ function Opensigndrive() {
|
||||
setSelectedSort(value);
|
||||
sortingData(value, null, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
className="dropdown-item text-[10px] md:text-[13px]"
|
||||
style={{
|
||||
paddingLeft: selectedSort !== value && "33px"
|
||||
paddingLeft: selectedSort !== value && "31px"
|
||||
}}
|
||||
>
|
||||
{selectedSort === value && (
|
||||
<i
|
||||
className="fa fa-check mr-[5px"
|
||||
className="fa-light fa-check"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
)}
|
||||
{value}
|
||||
<span className="ml-[5px]">{value}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -729,18 +733,18 @@ function Opensigndrive() {
|
||||
setSortingOrder(order);
|
||||
sortingData(null, order, pdfData);
|
||||
}}
|
||||
className="dropdown-item itemColor"
|
||||
className="dropdown-item text-[10px] md:text-[13px]"
|
||||
style={{
|
||||
paddingLeft: sortingOrder !== order && "33px"
|
||||
paddingLeft: sortingOrder !== order && "31px"
|
||||
}}
|
||||
>
|
||||
{sortingOrder === order && (
|
||||
<i
|
||||
className="fa fa-check mr-[5px"
|
||||
className="fa-light fa-check"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
)}
|
||||
{order}
|
||||
<span className="ml-[5px]">{order}</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -752,7 +756,7 @@ function Opensigndrive() {
|
||||
<div className="sort" onClick={() => setIsList(!isList)}>
|
||||
<i
|
||||
onClick={() => setIsList(!isList)}
|
||||
className="fa fa-th-large"
|
||||
className="fa-light fa-th-large"
|
||||
style={{ fontSize: "24px", color: `${iconColor}` }}
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
@@ -764,7 +768,7 @@ function Opensigndrive() {
|
||||
onClick={() => setIsList(!isList)}
|
||||
>
|
||||
<i
|
||||
className="fa fa-list"
|
||||
className="fa-light fa-list"
|
||||
aria-hidden="true"
|
||||
style={{ fontSize: "23px", color: `${iconColor}` }}
|
||||
></i>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Title from "../components/Title";
|
||||
|
||||
const PageNotFound = ({ prefix }) => {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded">
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
<Title title={"Page Not Found"} />
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import "../styles/pgsignup.css";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import { useDispatch } from "react-redux";
|
||||
@@ -71,6 +70,19 @@ const PgSignUp = () => {
|
||||
});
|
||||
if (res) {
|
||||
localStorage.removeItem("userDetails");
|
||||
try {
|
||||
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||
email: userDetails.email
|
||||
});
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const _currentRole = userRole;
|
||||
const _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const extInfo_stringify = JSON.stringify([extUser]);
|
||||
localStorage.setItem("Extand_Class", extInfo_stringify);
|
||||
} catch (err) {
|
||||
console.log("Err in fetching extuser", err);
|
||||
}
|
||||
navigate(
|
||||
"/" + userSettings[0].pageType + "/" + userSettings[0].pageId
|
||||
);
|
||||
@@ -108,8 +120,6 @@ const PgSignUp = () => {
|
||||
}
|
||||
};
|
||||
const saveUser = async (obj) => {
|
||||
// const domain = localStorage.getItem("domain");
|
||||
// console.log("domain", domain)
|
||||
try {
|
||||
const zohoRes = await axios.post(
|
||||
parseBaseUrl + "functions/zohodetails",
|
||||
@@ -210,8 +220,6 @@ const PgSignUp = () => {
|
||||
if (res.data) {
|
||||
let _user = res.data;
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", _user.email);
|
||||
localStorage.setItem("username", _user.name);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
@@ -221,283 +229,151 @@ const PgSignUp = () => {
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
let userRoles = [];
|
||||
if (appInfo.settings) {
|
||||
let userSettings = appInfo.settings;
|
||||
|
||||
//Get Current user roles
|
||||
let url = `${baseUrl}functions/UserGroups`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: _user.sessionToken
|
||||
};
|
||||
|
||||
let body = {
|
||||
appname: localStorage.getItem("_appName")
|
||||
};
|
||||
await axios
|
||||
.post(url, JSON.stringify(body), { headers: headers })
|
||||
.then((roles) => {
|
||||
if (roles) {
|
||||
userRoles = roles.data.result;
|
||||
let _currentRole = "";
|
||||
if (userRoles.length > 1) {
|
||||
if (
|
||||
userRoles[0] ===
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
_currentRole = userRoles[1];
|
||||
} else {
|
||||
_currentRole = userRoles[0];
|
||||
}
|
||||
} else {
|
||||
_currentRole = userRoles[0];
|
||||
}
|
||||
if (
|
||||
_currentRole !==
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
userSettings.forEach(async (element) => {
|
||||
if (element.role === _currentRole) {
|
||||
let _role = _currentRole.replace(
|
||||
`${localStorage.getItem("_appName")}_`,
|
||||
""
|
||||
);
|
||||
localStorage.setItem("_user_role", _role);
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem(
|
||||
"extended_class",
|
||||
element.extended_class
|
||||
);
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
}).then(
|
||||
(result) => {
|
||||
const results = [result];
|
||||
let tenentInfo = [];
|
||||
if (results) {
|
||||
let extendedInfo_stringify =
|
||||
JSON.stringify(results);
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
extendedInfo_stringify
|
||||
);
|
||||
let extendedInfo = JSON.parse(
|
||||
extendedInfo_stringify
|
||||
);
|
||||
if (extendedInfo.length > 1) {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
tenentInfo[0].tenentName || ""
|
||||
);
|
||||
}
|
||||
|
||||
localStorage.setItem("showpopup", true);
|
||||
localStorage.setItem(
|
||||
"PageLanding",
|
||||
element.pageId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"pageType",
|
||||
element.pageType
|
||||
);
|
||||
setIsLoader(false);
|
||||
navigate("/", { replace: true });
|
||||
} else {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
tenentInfo[0].tenentName || ""
|
||||
);
|
||||
}
|
||||
localStorage.setItem(
|
||||
"PageLanding",
|
||||
element.pageId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"pageType",
|
||||
element.pageType
|
||||
);
|
||||
setIsLoader(false);
|
||||
alert("Registered user successfully");
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${element.pageType}/${element.pageId}`;
|
||||
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
} else {
|
||||
alert("Registered user successfully");
|
||||
localStorage.setItem("PageLanding", element.pageId);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
setIsLoader(false);
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${element.pageType}/${element.pageId}`;
|
||||
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
setIsLoader(false);
|
||||
console.error("Error while fetching Follow", error);
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
const userSettings = appInfo.settings;
|
||||
const currentUser = Parse.User.current();
|
||||
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
});
|
||||
if (extUser) {
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole && userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const extInfo_stringify = JSON.stringify([extUser]);
|
||||
localStorage.setItem("Extand_Class", extInfo_stringify);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo?.Email);
|
||||
localStorage.setItem("username", extInfo?.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
setIsLoader(false);
|
||||
});
|
||||
alert("Registered user successfully");
|
||||
const redirectUrl =
|
||||
location?.state?.from || `/${menu.pageType}/${menu.pageId}`;
|
||||
// Redirect to the appropriate URL after successful login
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
} else {
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
setIsLoader(false);
|
||||
console.log(error);
|
||||
console.log("Err in fetch extuser", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="w-full h-screen">
|
||||
<Title title={"Pgsignup page"} />
|
||||
{isLoader ? (
|
||||
<div className="flex justify-center items-center h-[100vh]">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<form id="signup" className="pgsignup-content" onSubmit={handleSubmit}>
|
||||
<div className="pgsignup-container">
|
||||
<h1 className="text-2xl md:text-3xl font-bold">Choose Password</h1>
|
||||
<hr className="hrt" />
|
||||
<label htmlFor="password">
|
||||
<b className="text-[13px]">Password</b>
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter Password"
|
||||
name="password"
|
||||
className="inputt"
|
||||
value={formData.password}
|
||||
onChange={handlePassowordChange}
|
||||
required
|
||||
/>
|
||||
|
||||
<label htmlFor="confirmPassword">
|
||||
<b className="text-[13px]">Confirm Password</b>
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Confirm Password"
|
||||
name="confirmPassword"
|
||||
className="confirmInputt"
|
||||
style={{
|
||||
border: validationMessage
|
||||
? validationMessage === "Passwords match!"
|
||||
? "2px solid green"
|
||||
: "2px solid red"
|
||||
: "2px solid transparent",
|
||||
outline: "none"
|
||||
}}
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleConFirmPassowordChange}
|
||||
required
|
||||
/>
|
||||
<div
|
||||
className="icon"
|
||||
style={{
|
||||
margin: validationMessage ? "0 2px 0 2px" : "0 2px 22px 2px",
|
||||
color:
|
||||
validationMessage === "Passwords match!" ? "green" : "red",
|
||||
fontSize: 11
|
||||
}}
|
||||
>
|
||||
{validationMessage}
|
||||
</div>
|
||||
{formData.password.length > 0 && (
|
||||
<div className="mt-1 text-[11px]">
|
||||
<p
|
||||
className={`${
|
||||
lengthValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{lengthValid ? "✓" : "✗"} Password should be 8 characters long
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
caseDigitValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{caseDigitValid ? "✓" : "✗"} Password should contain uppercase
|
||||
letter, lowercase letter, digit
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
specialCharValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{specialCharValid ? "✓" : "✗"} Password should contain special
|
||||
character
|
||||
</p>
|
||||
<div className="py-10 lg:p-16 flex justify-center">
|
||||
<form
|
||||
id="signup"
|
||||
className="w-[90%] md:w-[55%] bg-base-100 text-base-content op-card shadow-md"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<div className="p-[16px]">
|
||||
<h1 className="text-2xl md:text-3xl font-bold">
|
||||
Choose Password
|
||||
</h1>
|
||||
<hr className="border-[1px] border-[#cecece] mb-[5px]" />
|
||||
<label htmlFor="password">
|
||||
<b className="text-[13px]">Password</b>
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter Password"
|
||||
name="password"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formData.password}
|
||||
onChange={handlePassowordChange}
|
||||
required
|
||||
/>
|
||||
<label htmlFor="confirmPassword">
|
||||
<b className="text-[13px]">Confirm Password</b>
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Confirm Password"
|
||||
name="confirmPassword"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
style={{
|
||||
border: validationMessage
|
||||
? validationMessage === "Passwords match!"
|
||||
? "1px solid green"
|
||||
: "1px solid red"
|
||||
: ""
|
||||
}}
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleConFirmPassowordChange}
|
||||
required
|
||||
/>
|
||||
<div
|
||||
className={`${
|
||||
validationMessage === "Passwords match!"
|
||||
? "text-[green]"
|
||||
: "text-[red]"
|
||||
} ml-[13px] text-[11px]`}
|
||||
>
|
||||
{validationMessage}
|
||||
</div>
|
||||
)}
|
||||
<div className="clearfix">
|
||||
<button type="submit" className="signupbtn">
|
||||
{formData.password.length > 0 && (
|
||||
<div className="mt-1 text-[11px]">
|
||||
<p
|
||||
className={`${
|
||||
lengthValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{lengthValid ? "✓" : "✗"} Password should be 8 characters
|
||||
long
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
caseDigitValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{caseDigitValid ? "✓" : "✗"} Password should contain
|
||||
uppercase letter, lowercase letter, digit
|
||||
</p>
|
||||
<p
|
||||
className={`${
|
||||
specialCharValid ? "text-green-600" : "text-red-600"
|
||||
}`}
|
||||
>
|
||||
{specialCharValid ? "✓" : "✗"} Password should contain
|
||||
special character
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary w-full mt-[8px]"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import checkmark from "../assets/images/checkmark.png";
|
||||
import plansArr from "../json/plansArr";
|
||||
import Title from "../components/Title";
|
||||
@@ -16,7 +16,7 @@ const listItemStyle = {
|
||||
|
||||
const PlanSubscriptions = () => {
|
||||
const navigate = useNavigate();
|
||||
const [yearlyVisible, setYearlyVisible] = useState(false);
|
||||
const [yearlyVisible, setYearlyVisible] = useState(true);
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const extUser =
|
||||
localStorage.getItem("Extand_Class") &&
|
||||
@@ -29,10 +29,15 @@ const PlanSubscriptions = () => {
|
||||
};
|
||||
const userDetails = JSON.parse(localStorage.getItem("userDetails")) || user;
|
||||
// console.log("userDetails ", userDetails);
|
||||
const name =
|
||||
userDetails && userDetails.name
|
||||
? "first_name=" + encodeURIComponent(userDetails.name)
|
||||
: "";
|
||||
const fullname =
|
||||
userDetails && userDetails.name ? userDetails.name.split(" ") : "";
|
||||
const firstname = fullname?.[0]
|
||||
? "first_name=" + encodeURIComponent(fullname?.[0])
|
||||
: "";
|
||||
const lastname = fullname?.[1]
|
||||
? "&last_name=" + encodeURIComponent(fullname?.[1])
|
||||
: "";
|
||||
const name = firstname ? firstname + lastname : "";
|
||||
const email =
|
||||
userDetails && userDetails.email
|
||||
? "&email=" + encodeURIComponent(userDetails.email)
|
||||
@@ -53,33 +58,37 @@ const PlanSubscriptions = () => {
|
||||
company +
|
||||
phone;
|
||||
useEffect(() => {
|
||||
// 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);
|
||||
const handleFreePlan = async (item) => {
|
||||
if (item.url) {
|
||||
const url = yearlyVisible ? item.yearlyUrl + details : item.url + details;
|
||||
if (user) {
|
||||
localStorage.setItem("userDetails", JSON.stringify(user));
|
||||
}
|
||||
openInNewTab(url, item?.target);
|
||||
} else {
|
||||
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!");
|
||||
}
|
||||
} catch (err) {
|
||||
setIsLoader(false);
|
||||
console.log("err in free subscribe", err.message);
|
||||
alert("Somenthing went wrong, please try again later!");
|
||||
}
|
||||
};
|
||||
return (
|
||||
@@ -109,7 +118,7 @@ const PlanSubscriptions = () => {
|
||||
role="tab"
|
||||
className={`${yearlyVisible ? "op-tab-active" : ""} op-tab`}
|
||||
>
|
||||
Yearly (10% off)
|
||||
Yearly (upto 66% off)
|
||||
</a>
|
||||
</div>
|
||||
<ul className="op-card flex flex-col md:flex-row h-full bg-base-100 justify-center shadow-lg">
|
||||
@@ -122,19 +131,43 @@ const PlanSubscriptions = () => {
|
||||
<h3 className="text-[#002862] uppercase">
|
||||
{item.planName}
|
||||
</h3>
|
||||
<div className="w-[150px] h-[150px]">
|
||||
<div className="w-[120px] h-[120px] overflow-hidden">
|
||||
<img
|
||||
className="icon-basic mx-auto"
|
||||
src="https://js.zohostatic.com/books/zfwidgets/assets/images/plan.png"
|
||||
alt="freeimg"
|
||||
className="w-full h-full object-contain"
|
||||
src={require(`../assets/images/${item?.img}`)}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-3xl">
|
||||
{item.currency && <small>{item.currency}</small>}
|
||||
{yearlyVisible
|
||||
? item?.yearlyPrice
|
||||
: item.monthlyPrice}
|
||||
{yearlyVisible ? (
|
||||
<>
|
||||
{item?.yearlyPrice.includes("<") ? (
|
||||
<div
|
||||
className="inline-block"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item?.yearlyPrice
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span>{item?.yearlyPrice}</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{item?.monthlyPrice.includes("<") ? (
|
||||
<div
|
||||
className="inline-block"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: item?.monthlyPrice
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<span>{item?.monthlyPrice}</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
<p className="font-semibold pt-2 text-sm">
|
||||
{yearlyVisible ? "Billed Yearly" : "Billed Monthly"}
|
||||
@@ -160,36 +193,29 @@ const PlanSubscriptions = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{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>
|
||||
)}
|
||||
<button
|
||||
className={`${
|
||||
item?.btn?.color ? item?.btn?.color : "op-btn-primary"
|
||||
} w-full text-white py-2 op-btn uppercase hover:text-white cursor-pointer`}
|
||||
onClick={() => handleFreePlan(item)}
|
||||
>
|
||||
{item?.btn?.text}
|
||||
</button>
|
||||
</div>
|
||||
<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">
|
||||
<span className="relative">{subitem}</span>
|
||||
</li>
|
||||
))}
|
||||
{!yearlyVisible &&
|
||||
item.benefits.map((subitem, index) => (
|
||||
<li style={listItemStyle} key={index} className="m-1">
|
||||
<span className="relative">{subitem}</span>
|
||||
</li>
|
||||
))}
|
||||
{yearlyVisible &&
|
||||
item?.yearlyBenefits?.map((subitem, index) => (
|
||||
<li style={listItemStyle} key={index} className="m-1">
|
||||
<span className="relative">{subitem}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -22,6 +22,7 @@ const Report = () => {
|
||||
const [form, setForm] = useState("");
|
||||
const [tourData, setTourData] = useState([]);
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [isPublic, setIsPublic] = useState({});
|
||||
const abortController = new AbortController();
|
||||
const docPerPage = 10;
|
||||
|
||||
@@ -156,6 +157,13 @@ const Report = () => {
|
||||
? [...prevRecord, ...res.data.result]
|
||||
: res.data.result
|
||||
);
|
||||
const listData = res.data.result;
|
||||
setIsPublic(
|
||||
listData.reduce((acc, item) => {
|
||||
acc[item.objectId] = item?.IsPublic || false;
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
}
|
||||
setIsLoader(false);
|
||||
@@ -193,6 +201,8 @@ const Report = () => {
|
||||
report_help={reporthelp}
|
||||
tourData={tourData}
|
||||
isDontShow={isDontShow}
|
||||
setIsPublic={setIsPublic}
|
||||
isPublic={isPublic}
|
||||
/>
|
||||
) : (
|
||||
<PageNotFound prefix={"Report"} />
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import axios from "axios";
|
||||
import { appInfo } from "../constant/appinfo";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { fetchSubscription } from "../constant/Utils";
|
||||
@@ -136,8 +135,6 @@ const SSOVerify = () => {
|
||||
};
|
||||
// `thirdpartyLoginfn` is used to save necessary parameters locally for the logged-in user
|
||||
const thirdpartyLoginfn = async (sessionToken) => {
|
||||
const baseUrl = localStorage.getItem("baseUrl");
|
||||
const parseAppId = localStorage.getItem("parseAppId");
|
||||
try {
|
||||
const validUser = await Parse.User.become(sessionToken);
|
||||
if (validUser) {
|
||||
@@ -151,76 +148,44 @@ const SSOVerify = () => {
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
let userRoles = [];
|
||||
const userSettings = appInfo.settings;
|
||||
//Get Current user roles
|
||||
const url = `${baseUrl}functions/UserGroups`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: _user.sessionToken
|
||||
};
|
||||
const body = { appname: appInfo.appname };
|
||||
const UserGroupsRes = await axios.post(url, JSON.stringify(body), {
|
||||
headers: headers
|
||||
});
|
||||
userRoles = (UserGroupsRes.data && UserGroupsRes.data.result) || [];
|
||||
if (userRoles) {
|
||||
let _currentRole = "";
|
||||
const excludeRoles = ["contracts_Guest", `contracts_appeditor`];
|
||||
const filteredRole = userRoles.filter(
|
||||
(x) => !excludeRoles.includes(x)
|
||||
);
|
||||
if (filteredRole?.length > 1) {
|
||||
console.log("user has two roles");
|
||||
// _currentRole = filteredRole.filter((x) => x === "contracts_User");
|
||||
} else {
|
||||
_currentRole = filteredRole?.[0] || "";
|
||||
}
|
||||
if (_currentRole) {
|
||||
const roleSetting = userSettings?.find(
|
||||
(setting) => setting.role === _currentRole
|
||||
);
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${roleSetting.pageType}/${roleSetting.pageId}`;
|
||||
const _role = _currentRole.replace(`${appInfo.appname}_`, "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem(
|
||||
"extended_class",
|
||||
roleSetting.extended_class
|
||||
);
|
||||
try {
|
||||
const extUser = await Parse.Cloud.run("getUserDetails", {
|
||||
email: _user.email
|
||||
});
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", _extUser.Email);
|
||||
localStorage.setItem("username", _extUser.Name);
|
||||
localStorage.setItem("scriptId", true);
|
||||
let tenentInfo = [];
|
||||
const results = [extUser];
|
||||
if (results) {
|
||||
let extendedInfo_stringify = JSON.stringify(results);
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
extendedInfo_stringify
|
||||
);
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
})
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const redirectUrl =
|
||||
location?.state?.from ||
|
||||
`/${menu.pageType}/${menu.pageId}`;
|
||||
const _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const extUser_stringify = JSON.stringify([extUser]);
|
||||
localStorage.setItem("Extand_Class", extUser_stringify);
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", _extUser.Email);
|
||||
localStorage.setItem("username", _extUser.Name);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_extUser.TenantId) {
|
||||
const obj = {
|
||||
tenentId: _extUser.TenantId.objectId,
|
||||
tenentName: _extUser.TenantId.TenantName || ""
|
||||
const tenant = {
|
||||
Id: _extUser?.TenantId?.objectId || "",
|
||||
Name: _extUser?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", obj.tenentId);
|
||||
tenentInfo.push(obj);
|
||||
dispatch(showTenant(obj.tenentName || ""));
|
||||
localStorage.setItem("TenantName", obj.tenentName || "");
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
localStorage.setItem("PageLanding", roleSetting.pageId);
|
||||
localStorage.setItem("defaultmenuid", roleSetting.menuId);
|
||||
localStorage.setItem("pageType", roleSetting.pageType);
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
if (isEnableSubscription) {
|
||||
const res = await fetchSubscription();
|
||||
const plan = res.plan;
|
||||
@@ -232,35 +197,32 @@ const SSOVerify = () => {
|
||||
localStorage.removeItem("userDetails");
|
||||
navigate(redirectUrl);
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(`/subscription`, {
|
||||
replace: true
|
||||
});
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(redirectUrl);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert(
|
||||
"Error: You don't have access, please contact the admin."
|
||||
);
|
||||
setMessage(
|
||||
"Error: You don't have access, please contact the admin."
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
alert("user not exist.");
|
||||
setMessage("Error: User not exist.");
|
||||
console.log("err in get extUser", err);
|
||||
}
|
||||
} else {
|
||||
alert("Role does not exists.");
|
||||
setMessage("Error: Role does not exists.");
|
||||
}
|
||||
} else {
|
||||
alert("Role does not exists.");
|
||||
setMessage("Error: Role does not exists.");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Err in fetch extuser", error);
|
||||
alert("user not exist.");
|
||||
setMessage("Error: User not exist.");
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("err in usergroups", err);
|
||||
console.log("err in getuserdetails", err);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -44,14 +44,8 @@ const Signup = () => {
|
||||
let baseUrl = localStorage.getItem("baseUrl");
|
||||
let appid = localStorage.getItem("parseAppId");
|
||||
let applogo = localStorage.getItem("appLogo");
|
||||
let domain = localStorage.getItem("domain");
|
||||
let appversion = localStorage.getItem("appVersion");
|
||||
let appTitle = localStorage.getItem("appTitle");
|
||||
let defaultmenuid = localStorage.getItem("defaultmenuid");
|
||||
let PageLanding = localStorage.getItem("PageLanding");
|
||||
let _appName = localStorage.getItem("_appName");
|
||||
let _app_objectId = localStorage.getItem("_app_objectId");
|
||||
let appName = localStorage.getItem("appName");
|
||||
let userSettings = localStorage.getItem("userSettings");
|
||||
|
||||
localStorage.clear();
|
||||
@@ -59,14 +53,8 @@ const Signup = () => {
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
localStorage.setItem("appLogo", applogo);
|
||||
localStorage.setItem("domain", domain);
|
||||
localStorage.setItem("appversion", appversion);
|
||||
localStorage.setItem("appTitle", appTitle);
|
||||
localStorage.setItem("defaultmenuid", defaultmenuid);
|
||||
localStorage.setItem("PageLanding", PageLanding);
|
||||
localStorage.setItem("_appName", _appName);
|
||||
localStorage.setItem("_app_objectId", _app_objectId);
|
||||
localStorage.setItem("appName", appName);
|
||||
localStorage.setItem("userSettings", userSettings);
|
||||
localStorage.setItem("baseUrl", baseUrl);
|
||||
localStorage.setItem("parseAppId", appid);
|
||||
@@ -194,8 +182,6 @@ const Signup = () => {
|
||||
if (res.data) {
|
||||
let _user = res.data;
|
||||
localStorage.setItem("UserInformation", JSON.stringify(_user));
|
||||
localStorage.setItem("userEmail", _user.email);
|
||||
localStorage.setItem("username", _user.name);
|
||||
localStorage.setItem("accesstoken", _user.sessionToken);
|
||||
localStorage.setItem("scriptId", true);
|
||||
if (_user.ProfilePic) {
|
||||
@@ -205,252 +191,84 @@ const Signup = () => {
|
||||
}
|
||||
// Check extended class user role and tenentId
|
||||
try {
|
||||
let userRoles = [];
|
||||
if (appInfo.settings) {
|
||||
let userSettings = appInfo.settings;
|
||||
|
||||
//Get Current user roles
|
||||
let url = `${baseUrl}functions/UserGroups`;
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-Parse-Application-Id": parseAppId,
|
||||
sessionToken: _user.sessionToken
|
||||
};
|
||||
|
||||
let body = {
|
||||
appname: localStorage.getItem("_appName")
|
||||
};
|
||||
await axios
|
||||
.post(url, JSON.stringify(body), { headers: headers })
|
||||
.then((roles) => {
|
||||
if (roles) {
|
||||
userRoles = roles.data.result;
|
||||
let _currentRole = "";
|
||||
if (userRoles.length > 1) {
|
||||
if (
|
||||
userRoles[0] ===
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
_currentRole = userRoles[1];
|
||||
} else {
|
||||
_currentRole = userRoles[0];
|
||||
const userSettings = appInfo.settings;
|
||||
const currentUser = Parse.User.current();
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: currentUser.get("email")
|
||||
})
|
||||
.then(async (extUser) => {
|
||||
if (extUser) {
|
||||
const IsDisabled = extUser?.get("IsDisabled") || false;
|
||||
if (!IsDisabled) {
|
||||
const userRole = extUser?.get("UserRole");
|
||||
const menu =
|
||||
userRole &&
|
||||
userSettings.find((menu) => menu.role === userRole);
|
||||
if (menu) {
|
||||
const _currentRole = userRole;
|
||||
const _role = _currentRole.replace("contracts_", "");
|
||||
localStorage.setItem("_user_role", _role);
|
||||
const extInfo_stringify = JSON.stringify([extUser]);
|
||||
localStorage.setItem("Extand_Class", extInfo_stringify);
|
||||
const extInfo = JSON.parse(JSON.stringify(extUser));
|
||||
localStorage.setItem("userEmail", extInfo?.Email);
|
||||
localStorage.setItem("username", extInfo?.Name);
|
||||
if (extInfo?.TenantId) {
|
||||
const tenant = {
|
||||
Id: extInfo?.TenantId?.objectId || "",
|
||||
Name: extInfo?.TenantId?.TenantName || ""
|
||||
};
|
||||
localStorage.setItem("TenantId", tenant?.Id);
|
||||
dispatch(showTenant(tenant?.Name));
|
||||
localStorage.setItem("TenantName", tenant?.Name);
|
||||
}
|
||||
} else {
|
||||
_currentRole = userRoles[0];
|
||||
}
|
||||
if (
|
||||
_currentRole !==
|
||||
`${localStorage.getItem("_appName")}_appeditor`
|
||||
) {
|
||||
userSettings.forEach(async (element) => {
|
||||
if (element.role === _currentRole) {
|
||||
let _role = _currentRole.replace(
|
||||
`${localStorage.getItem("_appName")}_`,
|
||||
""
|
||||
);
|
||||
localStorage.setItem("_user_role", _role);
|
||||
// Get TenentID from Extendend Class
|
||||
localStorage.setItem(
|
||||
"extended_class",
|
||||
element.extended_class
|
||||
);
|
||||
await Parse.Cloud.run("getUserDetails", {
|
||||
email: email
|
||||
}).then(
|
||||
(result) => {
|
||||
let tenentInfo = [];
|
||||
const results = [result];
|
||||
if (results) {
|
||||
let extendedInfo_stringify =
|
||||
JSON.stringify(results);
|
||||
localStorage.setItem(
|
||||
"Extand_Class",
|
||||
extendedInfo_stringify
|
||||
);
|
||||
let extendedInfo = JSON.parse(
|
||||
extendedInfo_stringify
|
||||
);
|
||||
if (extendedInfo.length > 1) {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
tenentInfo[0].tenentName || ""
|
||||
);
|
||||
}
|
||||
|
||||
localStorage.setItem("showpopup", true);
|
||||
localStorage.setItem(
|
||||
"IncludedApps",
|
||||
JSON.stringify(tenentInfo)
|
||||
);
|
||||
|
||||
localStorage.setItem(
|
||||
"PageLanding",
|
||||
element.pageId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"pageType",
|
||||
element.pageType
|
||||
);
|
||||
setState({ loading: false });
|
||||
navigate("/");
|
||||
} else {
|
||||
extendedInfo.forEach((x) => {
|
||||
if (x.TenantId) {
|
||||
let obj = {
|
||||
tenentId: x.TenantId.objectId,
|
||||
tenentName: x.TenantId.TenantName || ""
|
||||
};
|
||||
localStorage.setItem(
|
||||
"TenantId",
|
||||
x.TenantId.objectId
|
||||
);
|
||||
tenentInfo.push(obj);
|
||||
}
|
||||
});
|
||||
if (tenentInfo.length) {
|
||||
dispatch(
|
||||
showTenant(tenentInfo[0].tenentName || "")
|
||||
);
|
||||
localStorage.setItem(
|
||||
"TenantName",
|
||||
tenentInfo[0].tenentName || ""
|
||||
);
|
||||
}
|
||||
localStorage.setItem(
|
||||
"PageLanding",
|
||||
element.pageId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem(
|
||||
"pageType",
|
||||
element.pageType
|
||||
);
|
||||
setState({ loading: false });
|
||||
if (isEnableSubscription) {
|
||||
if (isFreeplan) {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
alert("Registered user successfully");
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("Registered user successfully");
|
||||
localStorage.setItem("PageLanding", element.pageId);
|
||||
localStorage.setItem(
|
||||
"defaultmenuid",
|
||||
element.menuId
|
||||
);
|
||||
localStorage.setItem("pageType", element.pageType);
|
||||
setState({ loading: false });
|
||||
if (isEnableSubscription) {
|
||||
if (isFreeplan) {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
} else {
|
||||
navigate(
|
||||
`/${element.pageType}/${element.pageId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"You dont have access to this application!"
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
localStorage.setItem("accesstoken", null);
|
||||
console.error("Error while fetching Follow", error);
|
||||
}
|
||||
);
|
||||
localStorage.setItem("PageLanding", menu.pageId);
|
||||
localStorage.setItem("defaultmenuid", menu.menuId);
|
||||
localStorage.setItem("pageType", menu.pageType);
|
||||
setState({ loading: false });
|
||||
if (isEnableSubscription) {
|
||||
if (isFreeplan) {
|
||||
navigate(`/${menu.pageType}/${menu.pageId}`);
|
||||
} else {
|
||||
navigate(`/subscription`, { replace: true });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
alert("Registered user successfully");
|
||||
navigate(`/${menu.pageType}/${menu.pageId}`);
|
||||
}
|
||||
} else {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User Role Not Found!"
|
||||
alertMsg: "Role not found."
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
setTimeout(() => {
|
||||
setState({ loading: false, alertMsg: "" });
|
||||
}, 2000);
|
||||
}
|
||||
} else {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "User Role Not Found!"
|
||||
alertMsg: "You don't have access, please contact the admin."
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
setTimeout(() => {
|
||||
setState({ loading: false, alertMsg: "" });
|
||||
}, 2000);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("err", err);
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg:
|
||||
"Does not have permissions to access this application!"
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log("error in fetch extuser", error);
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: "You don't have access, please contact the admin."
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
setState({ loading: false, alertMsg: "" });
|
||||
}, 2000);
|
||||
});
|
||||
} catch (error) {
|
||||
// alert(`${error.message}`);
|
||||
setState({
|
||||
@@ -458,12 +276,8 @@ const Signup = () => {
|
||||
alertType: "danger",
|
||||
alertMsg: `${error.message}`
|
||||
});
|
||||
setTimeout(function () {
|
||||
setState({
|
||||
loading: false,
|
||||
alertType: "danger",
|
||||
alertMsg: ""
|
||||
});
|
||||
setTimeout(() => {
|
||||
setState({ loading: false, alertMsg: "" });
|
||||
}, 2000);
|
||||
console.log(error);
|
||||
}
|
||||
@@ -477,9 +291,9 @@ const Signup = () => {
|
||||
|
||||
const saveLogo = async () => {
|
||||
if (isEnableSubscription) {
|
||||
const logo = await getAppLogo();
|
||||
if (logo) {
|
||||
setImage(logo);
|
||||
const app = await getAppLogo();
|
||||
if (app?.logo) {
|
||||
setImage(app?.logo);
|
||||
} else {
|
||||
setImage(appInfo?.applogo || undefined);
|
||||
}
|
||||
@@ -601,9 +415,9 @@ const Signup = () => {
|
||||
onClick={togglePasswordVisibility}
|
||||
>
|
||||
{showPassword ? (
|
||||
<i className="fa fa-eye-slash" /> // Close eye icon
|
||||
<i className="fa-light fa-eye-slash" /> // Close eye icon
|
||||
) : (
|
||||
<i className="fa fa-eye" /> // Open eye icon
|
||||
<i className="fa-light fa-eye" /> // Open eye icon
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, useRef, useEffect } from "react";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import "../styles/signature.css";
|
||||
import Parse from "parse";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import Confetti from "react-confetti";
|
||||
import axios from "axios";
|
||||
import LoaderWithMsg from "../primitives/LoaderWithMsg";
|
||||
@@ -17,7 +16,6 @@ import {
|
||||
contractDocument,
|
||||
embedDocId,
|
||||
multiSignEmbed,
|
||||
pdfNewWidthFun,
|
||||
onImageSelect,
|
||||
calculateInitialWidthHeight,
|
||||
defaultWidthHeight,
|
||||
@@ -31,9 +29,11 @@ import {
|
||||
getTenantDetails,
|
||||
checkIsSubscribed,
|
||||
convertPdfArrayBuffer,
|
||||
textInputWidget,
|
||||
fetchImageBase64,
|
||||
changeImageWH,
|
||||
handleSendOTP
|
||||
handleSendOTP,
|
||||
getContainerScale
|
||||
} from "../constant/Utils";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Tour from "reactour";
|
||||
@@ -46,7 +46,10 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn";
|
||||
import Title from "../components/Title";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption";
|
||||
import { useSelector } from "react-redux";
|
||||
import TextFontSetting from "../components/pdf/TextFontSetting";
|
||||
import VerifyEmail from "../components/pdf/VerifyEmail";
|
||||
import PdfZoom from "../components/pdf/PdfZoom";
|
||||
import Loader from "../primitives/Loader";
|
||||
//For signYourself inProgress section signer can add sign and complete doc sign.
|
||||
function SignYourSelf() {
|
||||
@@ -68,10 +71,12 @@ function SignYourSelf() {
|
||||
const signRef = useRef(null);
|
||||
const dragRef = useRef(null);
|
||||
const [dragKey, setDragKey] = useState();
|
||||
const [fontSize, setFontSize] = useState();
|
||||
const [fontColor, setFontColor] = useState();
|
||||
const [signKey, setSignKey] = useState();
|
||||
const [imgWH, setImgWH] = useState({});
|
||||
const [pdfNewWidth, setPdfNewWidth] = useState();
|
||||
const [pdfOriginalWidth, setPdfOriginalWidth] = useState();
|
||||
const [pdfOriginalWH, setPdfOriginalWH] = useState([]);
|
||||
const [successEmail, setSuccessEmail] = useState(false);
|
||||
const imageRef = useRef(null);
|
||||
const [myInitial, setMyInitial] = useState("");
|
||||
@@ -97,13 +102,11 @@ function SignYourSelf() {
|
||||
const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({
|
||||
status: false
|
||||
});
|
||||
const [isTextSetting, setIsTextSetting] = useState(false);
|
||||
const [currWidgetsDetails, setCurrWidgetsDetails] = useState({});
|
||||
const [isCheckbox, setIsCheckbox] = useState(false);
|
||||
const [widgetType, setWidgetType] = useState("");
|
||||
const [pdfLoadFail, setPdfLoadFail] = useState({
|
||||
status: false,
|
||||
type: "load"
|
||||
});
|
||||
const [pdfLoad, setPdfLoad] = useState(false);
|
||||
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
|
||||
const [isDontShow, setIsDontShow] = useState(false);
|
||||
const [extUserId, setExtUserId] = useState("");
|
||||
@@ -114,6 +117,9 @@ function SignYourSelf() {
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(true);
|
||||
const [isVerifyModal, setIsVerifyModal] = useState(false);
|
||||
const [otp, setOtp] = useState("");
|
||||
const [zoomPercent, setZoomPercent] = useState(0);
|
||||
const isHeader = useSelector((state) => state.showHeader);
|
||||
const [scale, setScale] = useState(1);
|
||||
const divRef = useRef(null);
|
||||
const nodeRef = useRef(null);
|
||||
const [, drop] = useDrop({
|
||||
@@ -123,10 +129,7 @@ function SignYourSelf() {
|
||||
isOver: !!monitor.isOver()
|
||||
})
|
||||
});
|
||||
const isMobile = window.innerWidth < 767;
|
||||
|
||||
const pdfRef = useRef();
|
||||
|
||||
const [{ isDragSign }, dragSignature] = useDrag({
|
||||
type: "BOX",
|
||||
item: {
|
||||
@@ -154,7 +157,6 @@ function SignYourSelf() {
|
||||
return object.pageNumber === pageNumber;
|
||||
});
|
||||
// rowlevel={JSON.parse(localStorage.getItem("rowlevel"))}
|
||||
|
||||
const rowLevel =
|
||||
localStorage.getItem("rowlevel") &&
|
||||
JSON.parse(localStorage.getItem("rowlevel"));
|
||||
@@ -182,17 +184,25 @@ function SignYourSelf() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (divRef.current) {
|
||||
const pdfWidth = pdfNewWidthFun(divRef);
|
||||
setPdfNewWidth(pdfWidth);
|
||||
setContainerWH({
|
||||
width: divRef.current.offsetWidth,
|
||||
height: divRef.current.offsetHeight
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current]);
|
||||
const updateSize = () => {
|
||||
if (divRef.current) {
|
||||
const pdfWidth = divRef.current.offsetWidth;
|
||||
setPdfNewWidth(pdfWidth);
|
||||
setContainerWH({
|
||||
width: divRef.current.offsetWidth,
|
||||
height: divRef.current.offsetHeight
|
||||
});
|
||||
setScale(1);
|
||||
setZoomPercent(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Use setTimeout to wait for the transition to complete
|
||||
const timer = setTimeout(updateSize, 100); // match the transition duration
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [divRef.current, isHeader]);
|
||||
//function for get document details for perticular signer with signer'object id
|
||||
const getDocumentDetails = async (showComplete) => {
|
||||
let isCompleted;
|
||||
@@ -214,14 +224,17 @@ function SignYourSelf() {
|
||||
} else {
|
||||
setHandleError("Error: Something went wrong!");
|
||||
}
|
||||
|
||||
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
|
||||
if (isCompleted) {
|
||||
setIsCelebration(true);
|
||||
setTimeout(() => {
|
||||
setIsCelebration(false);
|
||||
}, 5000);
|
||||
setIsCompleted(true);
|
||||
setPdfUrl(documentData[0].SignedUrl);
|
||||
const alreadySign = {
|
||||
status: true,
|
||||
mssg: "You have successfully signed the document!"
|
||||
mssg: "Congratulations! 🎉 This document has been successfully signed by you!"
|
||||
};
|
||||
if (showComplete) {
|
||||
setShowAlreadySignDoc(alreadySign);
|
||||
@@ -229,12 +242,8 @@ function SignYourSelf() {
|
||||
setIsUiLoading(false);
|
||||
setIsSignPad(false);
|
||||
setIsEmail(true);
|
||||
setIsCelebration(true);
|
||||
setXyPostion([]);
|
||||
setSignBtnPosition([]);
|
||||
setTimeout(() => {
|
||||
setIsCelebration(false);
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
@@ -255,9 +264,9 @@ function SignYourSelf() {
|
||||
}
|
||||
await axios
|
||||
.get(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts_Signature?where={"UserId": {"__type": "Pointer","className": "_User", "objectId":"${
|
||||
jsonSender.objectId
|
||||
}"}}`,
|
||||
{
|
||||
@@ -434,6 +443,7 @@ function SignYourSelf() {
|
||||
};
|
||||
//function for setting position after drop signature button over pdf
|
||||
const addPositionOfSignature = (item, monitor) => {
|
||||
setCurrWidgetsDetails({});
|
||||
const key = randomId();
|
||||
let dropData = [];
|
||||
let dropObj = {};
|
||||
@@ -442,61 +452,70 @@ function SignYourSelf() {
|
||||
);
|
||||
const dragTypeValue = item?.text ? item.text : monitor.type;
|
||||
const widgetValue = getWidgetValue(dragTypeValue);
|
||||
const widgetWidth = defaultWidthHeight(dragTypeValue).width;
|
||||
const widgetHeight = defaultWidthHeight(dragTypeValue).height;
|
||||
const widgetTypeExist = ["name", "company", "job title", "email"].includes(
|
||||
dragTypeValue
|
||||
);
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
pageNumber,
|
||||
containerWH
|
||||
);
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
if (item === "onclick") {
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).width
|
||||
: "";
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).height
|
||||
: "";
|
||||
dropObj = {
|
||||
xPosition: containerWH.width / 2 - widgetWidth / 2,
|
||||
yPosition: containerWH.height / 2 - widgetHeight / 2,
|
||||
isDrag: false,
|
||||
xPosition: containerWH.width / 2,
|
||||
yPosition: containerWH.height / 2,
|
||||
isStamp:
|
||||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||||
key: key,
|
||||
type: dragTypeValue,
|
||||
yBottom: window.innerHeight / 2 - 60,
|
||||
|
||||
Width: widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).width
|
||||
: "",
|
||||
Height: widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||
: dragTypeValue === "initials"
|
||||
? defaultWidthHeight(dragTypeValue).height
|
||||
: "",
|
||||
scale: containerScale,
|
||||
Width: getWidth / (containerScale * scale),
|
||||
Height: getHeight / (containerScale * scale),
|
||||
options: addWidgetOptions(dragTypeValue)
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
} else {
|
||||
//This method returns the offset of the current pointer (mouse) position relative to the client viewport.
|
||||
const offset = monitor.getClientOffset();
|
||||
//adding and updating drop position in array when user drop signature button in div
|
||||
const containerRect = document
|
||||
.getElementById("container")
|
||||
.getBoundingClientRect();
|
||||
//`containerRect.left`, The distance from the left of the viewport to the left side of the element.
|
||||
//`containerRect.top` The distance from the top of the viewport to the top of the element.
|
||||
|
||||
const x = offset.x - containerRect.left;
|
||||
const y = offset.y - containerRect.top;
|
||||
|
||||
const getXPosition = signBtnPosition[0] ? x - signBtnPosition[0].xPos : x;
|
||||
const getYPosition = signBtnPosition[0] ? y - signBtnPosition[0].yPos : y;
|
||||
const getWidth = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getWidth
|
||||
: defaultWidthHeight(dragTypeValue).width;
|
||||
const getHeight = widgetTypeExist
|
||||
? calculateInitialWidthHeight(widgetValue).getHeight
|
||||
: defaultWidthHeight(dragTypeValue).height;
|
||||
dropObj = {
|
||||
xPosition: signBtnPosition[0] ? x - signBtnPosition[0].xPos : x,
|
||||
yPosition: signBtnPosition[0] ? y - signBtnPosition[0].yPos : y,
|
||||
// isDrag: false,
|
||||
xPosition: getXPosition / (containerScale * scale),
|
||||
yPosition: getYPosition / (containerScale * scale),
|
||||
isStamp:
|
||||
(dragTypeValue === "stamp" || dragTypeValue === "image") && true,
|
||||
key: key,
|
||||
type: dragTypeValue,
|
||||
Width: widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
|
||||
: defaultWidthHeight(dragTypeValue).width,
|
||||
Height: widgetTypeExist
|
||||
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
|
||||
: defaultWidthHeight(dragTypeValue).height,
|
||||
options: addWidgetOptions(dragTypeValue)
|
||||
Width: getWidth / (containerScale * scale),
|
||||
Height: getHeight / (containerScale * scale),
|
||||
options: addWidgetOptions(dragTypeValue),
|
||||
scale: containerScale
|
||||
};
|
||||
|
||||
dropData.push(dropObj);
|
||||
@@ -506,16 +525,12 @@ function SignYourSelf() {
|
||||
return object.pageNumber === pageNumber;
|
||||
});
|
||||
const updateData = filterDropPos[0].pos;
|
||||
|
||||
// if (updateData.length > dropData.length) {
|
||||
const newSignPos = updateData.concat(dropData);
|
||||
let xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
pos: newSignPos
|
||||
};
|
||||
xyPostion.splice(index, 1, xyPos);
|
||||
|
||||
// }
|
||||
} else {
|
||||
const xyPos = {
|
||||
pageNumber: pageNumber,
|
||||
@@ -539,6 +554,18 @@ function SignYourSelf() {
|
||||
setIsInitial(true);
|
||||
} else if (dragTypeValue === "checkbox") {
|
||||
setIsCheckbox(true);
|
||||
} else if (
|
||||
[
|
||||
textInputWidget,
|
||||
textWidget,
|
||||
"name",
|
||||
"company",
|
||||
"job title",
|
||||
"email"
|
||||
].includes(dragTypeValue)
|
||||
) {
|
||||
setFontSize(12);
|
||||
setFontColor("black");
|
||||
}
|
||||
setWidgetType(dragTypeValue);
|
||||
setSelectWidgetId(key);
|
||||
@@ -669,13 +696,16 @@ function SignYourSelf() {
|
||||
const pdfBytes = await multiSignEmbed(
|
||||
xyPostion,
|
||||
pdfDoc,
|
||||
pdfOriginalWidth,
|
||||
isSignYourSelfFlow,
|
||||
scale,
|
||||
pdfOriginalWH,
|
||||
containerWH
|
||||
);
|
||||
// console.log("pdf", pdfBytes);
|
||||
//function for call to embed signature in pdf and get digital signature pdf
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
if (pdfBytes) {
|
||||
await signPdfFun(pdfBytes, documentId);
|
||||
}
|
||||
} catch (err) {
|
||||
setIsUiLoading(false);
|
||||
if (err && err.message.includes("is encrypted.")) {
|
||||
@@ -713,7 +743,7 @@ function SignYourSelf() {
|
||||
if (
|
||||
tenantDetails?.CompletionBody &&
|
||||
tenantDetails?.CompletionSubject &&
|
||||
(!isEnableSubscription || getIsSubscribe)
|
||||
getIsSubscribe
|
||||
) {
|
||||
isCustomCompletionMail = true;
|
||||
}
|
||||
@@ -741,7 +771,7 @@ function SignYourSelf() {
|
||||
console.log("error", e);
|
||||
}
|
||||
}
|
||||
//change image width and height to 104/44 in png base64
|
||||
//change image width and height to 100/40 in png base64
|
||||
const getNewse64 = await changeImageWH(base64Sign);
|
||||
//remove suffiix of base64
|
||||
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
|
||||
@@ -781,22 +811,21 @@ function SignYourSelf() {
|
||||
setDragKey(key);
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
//function for set and update x and y postion after drag and drop signature tab
|
||||
const handleStop = (event, dragElement) => {
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
if (isDragging && dragElement) {
|
||||
event.preventDefault();
|
||||
const containerRect = document
|
||||
.getElementById("container")
|
||||
.getBoundingClientRect();
|
||||
|
||||
const ybottom = containerRect.height - dragElement.y;
|
||||
|
||||
const containerScale = getContainerScale(
|
||||
pdfOriginalWH,
|
||||
pageNumber,
|
||||
containerWH
|
||||
);
|
||||
if (dragKey >= 0) {
|
||||
const filterDropPos = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (filterDropPos.length > 0) {
|
||||
const getXYdata = xyPostion[index].pos;
|
||||
const getPosData = getXYdata;
|
||||
@@ -804,10 +833,8 @@ function SignYourSelf() {
|
||||
if (url.key === dragKey) {
|
||||
return {
|
||||
...url,
|
||||
xPosition: dragElement.x,
|
||||
yPosition: dragElement.y,
|
||||
isDrag: true,
|
||||
yBottom: ybottom
|
||||
xPosition: dragElement.x / (containerScale * scale),
|
||||
yPosition: dragElement.y / (containerScale * scale)
|
||||
};
|
||||
}
|
||||
return url;
|
||||
@@ -827,26 +854,24 @@ function SignYourSelf() {
|
||||
setIsDragging(false);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
//function for get pdf page details
|
||||
const pageDetails = async (pdf) => {
|
||||
const load = {
|
||||
status: true
|
||||
};
|
||||
setPdfLoadFail(load);
|
||||
pdf.getPage(1).then((pdfPage) => {
|
||||
const pageWidth = pdfPage.view[2];
|
||||
|
||||
setPdfOriginalWidth(pageWidth);
|
||||
});
|
||||
let pdfWHObj = [];
|
||||
const totalPages = pdf?.numPages;
|
||||
for (let index = 0; index < totalPages; index++) {
|
||||
const getPage = await pdf.getPage(index + 1);
|
||||
const scale = 1;
|
||||
const { width, height } = getPage.getViewport({ scale });
|
||||
pdfWHObj.push({ pageNumber: index + 1, width, height });
|
||||
}
|
||||
setPdfOriginalWH(pdfWHObj);
|
||||
setPdfLoad(true);
|
||||
};
|
||||
|
||||
//function for change page numver of pdf
|
||||
function changePage(offset) {
|
||||
setSignBtnPosition([]);
|
||||
setPageNumber((prevPageNumber) => prevPageNumber + offset);
|
||||
}
|
||||
|
||||
//function for image upload or update
|
||||
const onImageChange = (event) => {
|
||||
if (event.target.files && event.target.files[0]) {
|
||||
@@ -896,18 +921,30 @@ function SignYourSelf() {
|
||||
|
||||
setXyPostion(getUpdatePosition);
|
||||
};
|
||||
//function for capture position on hover signature button
|
||||
//function for capture position on hover or touch widgets button
|
||||
const handleDivClick = (e) => {
|
||||
const isTouchEvent = e.type.startsWith("touch");
|
||||
const divRect = e.currentTarget.getBoundingClientRect();
|
||||
const mouseX = e.clientX - divRect.left;
|
||||
const mouseY = e.clientY - divRect.top;
|
||||
|
||||
const xyPosition = {
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
};
|
||||
|
||||
setXYSignature(xyPosition);
|
||||
let mouseX, mouseY;
|
||||
if (isTouchEvent) {
|
||||
const touch = e.touches[0]; // Get the first touch point
|
||||
mouseX = touch.clientX - divRect.left;
|
||||
mouseY = touch.clientY - divRect.top;
|
||||
setSignBtnPosition([
|
||||
{
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
mouseX = e.clientX - divRect.left;
|
||||
mouseY = e.clientY - divRect.top;
|
||||
const xyPosition = {
|
||||
xPos: mouseX,
|
||||
yPos: mouseY
|
||||
};
|
||||
setXYSignature(xyPosition);
|
||||
}
|
||||
};
|
||||
|
||||
//function for capture position of x and y on hover signature button last position
|
||||
@@ -955,7 +992,7 @@ function SignYourSelf() {
|
||||
};
|
||||
const tourConfig = [
|
||||
{
|
||||
selector: '[data-tut="reactourFirst"]',
|
||||
selector: '[data-tut="addWidgets"]',
|
||||
content: () => (
|
||||
<TourContentWithBtn
|
||||
message={`Select and drag your preferred widgets onto the PDF to customize your document before signing. Choose the perfect spots for each modification to tailor the document to your needs.`}
|
||||
@@ -998,9 +1035,9 @@ function SignYourSelf() {
|
||||
}
|
||||
await axios
|
||||
.put(
|
||||
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
|
||||
"_appName"
|
||||
)}${contractName}/${signerUserId}`,
|
||||
`${localStorage.getItem(
|
||||
"baseUrl"
|
||||
)}classes/contracts${contractName}/${signerUserId}`,
|
||||
{
|
||||
TourStatus: updatedTourStatus
|
||||
},
|
||||
@@ -1083,11 +1120,48 @@ function SignYourSelf() {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setCurrWidgetsDetails({});
|
||||
setIsCheckbox(false);
|
||||
};
|
||||
const handleTextSettingModal = (value) => {
|
||||
setIsTextSetting(value);
|
||||
};
|
||||
const handleSaveFontSize = () => {
|
||||
const getPageNumer = xyPostion.filter(
|
||||
(data) => data.pageNumber === pageNumber
|
||||
);
|
||||
|
||||
if (getPageNumer.length > 0) {
|
||||
const getXYdata = getPageNumer[0].pos;
|
||||
const getPosData = getXYdata;
|
||||
const addSignPos = getPosData.map((position) => {
|
||||
if (position.key === signKey) {
|
||||
return {
|
||||
...position,
|
||||
options: {
|
||||
...position.options,
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || "12",
|
||||
fontColor:
|
||||
fontColor || currWidgetsDetails?.options?.fontColor || "black"
|
||||
}
|
||||
};
|
||||
}
|
||||
return position;
|
||||
});
|
||||
const updateXYposition = xyPostion.map((obj, ind) => {
|
||||
if (ind === index) {
|
||||
return { ...obj, pos: addSignPos };
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
setXyPostion(updateXYposition);
|
||||
setFontSize();
|
||||
setFontColor();
|
||||
handleTextSettingModal(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Title title={"Self Sign"} />
|
||||
@@ -1098,23 +1172,20 @@ function SignYourSelf() {
|
||||
) : (
|
||||
<div>
|
||||
{isUiLoading && (
|
||||
<div className="absolute h-[100vh] w-full z-[999] flex flex-col justify-center items-start bg-[#e6f2f2] bg-opacity-80">
|
||||
<div className="absolute h-[100vh] w-full z-[999] flex flex-col justify-center items-center bg-[#e6f2f2] bg-opacity-80">
|
||||
<Loader />
|
||||
<span className="font-bold text-[13px]">
|
||||
<span style={{ fontSize: "13px", fontWeight: "bold" }}>
|
||||
This might take some time
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isCelebration && (
|
||||
<div className="relative z-[999]">
|
||||
<div className="relative z-[1000]">
|
||||
<Confetti width={window.innerWidth} height={window.innerHeight} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300"
|
||||
ref={divRef}
|
||||
>
|
||||
<div className="relative op-card overflow-hidden flex flex-col md:flex-row justify-between bg-base-300">
|
||||
{!isEmailVerified && (
|
||||
<VerifyEmail
|
||||
isVerifyModal={isVerifyModal}
|
||||
@@ -1128,7 +1199,7 @@ function SignYourSelf() {
|
||||
/>
|
||||
)}
|
||||
{/* this component used for UI interaction and show their functionality */}
|
||||
{pdfLoadFail && !checkTourStatus && (
|
||||
{pdfLoad && !checkTourStatus && (
|
||||
<Tour
|
||||
onRequestClose={closeTour}
|
||||
steps={tourConfig}
|
||||
@@ -1149,195 +1220,197 @@ function SignYourSelf() {
|
||||
setPageNumber={setPageNumber}
|
||||
setSignBtnPosition={setSignBtnPosition}
|
||||
pageNumber={pageNumber}
|
||||
containerWH={containerWH}
|
||||
/>
|
||||
<div className=" w-full md:w-[57%] flex mr-4">
|
||||
<PdfZoom
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
containerWH={containerWH}
|
||||
setZoomPercent={setZoomPercent}
|
||||
zoomPercent={zoomPercent}
|
||||
/>
|
||||
<div className=" w-full md:w-[95%] ">
|
||||
<ModalUi
|
||||
isOpen={isAlert.isShow}
|
||||
title={isAlert?.header || "Alert"}
|
||||
handleClose={() => {
|
||||
setIsAlert({
|
||||
isShow: false,
|
||||
alertMessage: ""
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{isAlert.alertMessage}</p>
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
{/* pdf render view */}
|
||||
<div
|
||||
style={{
|
||||
marginLeft: !isMobile && pdfOriginalWidth > 500 && "20px",
|
||||
marginRight: !isMobile && pdfOriginalWidth > 500 && "20px"
|
||||
}}
|
||||
>
|
||||
<ModalUi
|
||||
isOpen={isAlert.isShow}
|
||||
title={isAlert?.header || "Alert"}
|
||||
handleClose={() => {
|
||||
setIsAlert({
|
||||
isShow: false,
|
||||
alertMessage: ""
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{isAlert.alertMessage}</p>
|
||||
{/* this modal is used show this document is already sign */}
|
||||
<ModalUi
|
||||
isOpen={showAlreadySignDoc.status}
|
||||
title={"Document signed"}
|
||||
handleClose={() => {
|
||||
setShowAlreadySignDoc({ status: false });
|
||||
}}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{showAlreadySignDoc.mssg}</p>
|
||||
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
className="op-btn op-btn-ghost shadow-md"
|
||||
onClick={() => setShowAlreadySignDoc({ status: false })}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<DropdownWidgetOption
|
||||
type="checkbox"
|
||||
title="Checkbox"
|
||||
showDropdown={isCheckbox}
|
||||
setShowDropdown={setIsCheckbox}
|
||||
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
isSignYourself={true}
|
||||
handleClose={handleCloseModal}
|
||||
/>
|
||||
<PlaceholderCopy
|
||||
isPageCopy={isPageCopy}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
allPages={allPages}
|
||||
pageNumber={pageNumber}
|
||||
signKey={signKey}
|
||||
/>
|
||||
{/* this is modal of signature pad */}
|
||||
<SignPad
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setImage={setImage}
|
||||
isImageSelect={isImageSelect}
|
||||
imageRef={imageRef}
|
||||
onImageChange={onImageChange}
|
||||
setSignature={setSignature}
|
||||
image={image}
|
||||
onSaveImage={saveImage}
|
||||
onSaveSign={saveSign}
|
||||
defaultSign={defaultSignImg}
|
||||
myInitial={myInitial}
|
||||
isInitial={isInitial}
|
||||
setIsInitial={setIsInitial}
|
||||
setIsStamp={setIsStamp}
|
||||
widgetType={widgetType}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
{/*render email component to send email after finish signature on document */}
|
||||
<EmailComponent
|
||||
isEmail={isEmail}
|
||||
pdfUrl={pdfUrl}
|
||||
setIsEmail={setIsEmail}
|
||||
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
||||
setSuccessEmail={setSuccessEmail}
|
||||
sender={jsonSender}
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
activeMailAdapter={activeMailAdapter}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
pageNumber={pageNumber}
|
||||
allPages={allPages}
|
||||
changePage={changePage}
|
||||
pdfUrl={pdfUrl}
|
||||
embedWidgetsData={embedWidgetsData}
|
||||
pdfDetails={pdfDetails}
|
||||
isShowHeader={true}
|
||||
currentSigner={true}
|
||||
alreadySign={pdfUrl ? true : false}
|
||||
isSignYourself={true}
|
||||
setIsEmail={setIsEmail}
|
||||
isCompleted={isCompleted}
|
||||
/>
|
||||
<div
|
||||
ref={divRef}
|
||||
data-tut="reactourSecond"
|
||||
className="h-full md:h-[95%]"
|
||||
>
|
||||
{containerWH?.width && containerWH?.height && (
|
||||
<RenderPdf
|
||||
pageNumber={pageNumber}
|
||||
pdfOriginalWH={pdfOriginalWH}
|
||||
pdfNewWidth={pdfNewWidth}
|
||||
drop={drop}
|
||||
successEmail={successEmail}
|
||||
nodeRef={nodeRef}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
isDragging={isDragging}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setIsStamp={setIsStamp}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setSignKey={setSignKey}
|
||||
pdfDetails={pdfDetails}
|
||||
setIsDragging={setIsDragging}
|
||||
xyPostion={xyPostion}
|
||||
pdfRef={pdfRef}
|
||||
pdfUrl={pdfUrl}
|
||||
numPages={numPages}
|
||||
pageDetails={pageDetails}
|
||||
pdfLoad={pdfLoad}
|
||||
setPdfLoad={setPdfLoad}
|
||||
setXyPostion={setXyPostion}
|
||||
index={index}
|
||||
containerWH={containerWH}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
setValidateAlert={setValidateAlert}
|
||||
handleTextSettingModal={handleTextSettingModal}
|
||||
setScale={setScale}
|
||||
scale={scale}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ModalUi>
|
||||
|
||||
{/* this modal is used show this document is already sign */}
|
||||
<ModalUi
|
||||
isOpen={showAlreadySignDoc.status}
|
||||
title={"Document signed"}
|
||||
handleClose={() => {
|
||||
setShowAlreadySignDoc({ status: false });
|
||||
}}
|
||||
>
|
||||
<div className="p-[20px] h-full">
|
||||
<p>{showAlreadySignDoc.mssg}</p>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
className="op-btn op-btn-ghost shadow-md"
|
||||
onClick={() => setShowAlreadySignDoc({ status: false })}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<DropdownWidgetOption
|
||||
type="checkbox"
|
||||
title="Checkbox"
|
||||
showDropdown={isCheckbox}
|
||||
setShowDropdown={setIsCheckbox}
|
||||
handleSaveWidgetsOptions={handleSaveWidgetsOptions}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
isSignYourself={true}
|
||||
handleClose={handleCloseModal}
|
||||
/>
|
||||
<PlaceholderCopy
|
||||
isPageCopy={isPageCopy}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
xyPostion={xyPostion}
|
||||
setXyPostion={setXyPostion}
|
||||
allPages={allPages}
|
||||
pageNumber={pageNumber}
|
||||
signKey={signKey}
|
||||
/>
|
||||
{/* this is modal of signature pad */}
|
||||
<SignPad
|
||||
isSignPad={isSignPad}
|
||||
isStamp={isStamp}
|
||||
setIsImageSelect={setIsImageSelect}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setImage={setImage}
|
||||
isImageSelect={isImageSelect}
|
||||
imageRef={imageRef}
|
||||
onImageChange={onImageChange}
|
||||
setSignature={setSignature}
|
||||
image={image}
|
||||
onSaveImage={saveImage}
|
||||
onSaveSign={saveSign}
|
||||
defaultSign={defaultSignImg}
|
||||
myInitial={myInitial}
|
||||
isInitial={isInitial}
|
||||
setIsInitial={setIsInitial}
|
||||
setIsStamp={setIsStamp}
|
||||
widgetType={widgetType}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
/>
|
||||
{/*render email component to send email after finish signature on document */}
|
||||
<EmailComponent
|
||||
isEmail={isEmail}
|
||||
pdfUrl={pdfUrl}
|
||||
setIsEmail={setIsEmail}
|
||||
pdfName={pdfDetails[0] && pdfDetails[0].Name}
|
||||
setSuccessEmail={setSuccessEmail}
|
||||
sender={jsonSender}
|
||||
setIsAlert={setIsAlert}
|
||||
extUserId={extUserId}
|
||||
activeMailAdapter={activeMailAdapter}
|
||||
/>
|
||||
{/* pdf header which contain funish back button */}
|
||||
<Header
|
||||
pageNumber={pageNumber}
|
||||
allPages={allPages}
|
||||
changePage={changePage}
|
||||
pdfUrl={pdfUrl}
|
||||
embedWidgetsData={embedWidgetsData}
|
||||
pdfDetails={pdfDetails}
|
||||
isShowHeader={true}
|
||||
currentSigner={true}
|
||||
alreadySign={pdfUrl ? true : false}
|
||||
isSignYourself={true}
|
||||
setIsEmail={setIsEmail}
|
||||
isCompleted={isCompleted}
|
||||
/>
|
||||
|
||||
<div data-tut="reactourSecond">
|
||||
{containerWH && (
|
||||
<RenderPdf
|
||||
pageNumber={pageNumber}
|
||||
pdfOriginalWidth={pdfOriginalWidth}
|
||||
pdfNewWidth={pdfNewWidth}
|
||||
drop={drop}
|
||||
successEmail={successEmail}
|
||||
nodeRef={nodeRef}
|
||||
handleTabDrag={handleTabDrag}
|
||||
handleStop={handleStop}
|
||||
isDragging={isDragging}
|
||||
setIsSignPad={setIsSignPad}
|
||||
setIsStamp={setIsStamp}
|
||||
handleDeleteSign={handleDeleteSign}
|
||||
setSignKey={setSignKey}
|
||||
pdfDetails={pdfDetails}
|
||||
setIsDragging={setIsDragging}
|
||||
xyPostion={xyPostion}
|
||||
pdfRef={pdfRef}
|
||||
pdfUrl={pdfUrl}
|
||||
numPages={numPages}
|
||||
pageDetails={pageDetails}
|
||||
setPdfLoadFail={setPdfLoadFail}
|
||||
pdfLoadFail={pdfLoadFail}
|
||||
setXyPostion={setXyPostion}
|
||||
index={index}
|
||||
containerWH={containerWH}
|
||||
setIsPageCopy={setIsPageCopy}
|
||||
setIsInitial={setIsInitial}
|
||||
setWidgetType={setWidgetType}
|
||||
setSelectWidgetId={setSelectWidgetId}
|
||||
selectWidgetId={selectWidgetId}
|
||||
setIsCheckbox={setIsCheckbox}
|
||||
setCurrWidgetsDetails={setCurrWidgetsDetails}
|
||||
setValidateAlert={setValidateAlert}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/*if document is not completed then render signature and stamp button in the right side */}
|
||||
{/*else document is completed then render signed by signer name in the right side */}
|
||||
<div
|
||||
style={{
|
||||
maxHeight: window.innerHeight - 70 + "px",
|
||||
backgroundColor: "white"
|
||||
}}
|
||||
className="overflow-y-auto hide-scrollbar"
|
||||
>
|
||||
{!isCompleted ? (
|
||||
<div>
|
||||
<WidgetComponent
|
||||
dataTut="reactourFirst"
|
||||
pdfUrl={pdfUrl}
|
||||
dragSignature={dragSignature}
|
||||
signRef={signRef}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
isDragSign={isDragSign}
|
||||
dragStamp={dragStamp}
|
||||
dragRef={dragRef}
|
||||
isDragStamp={isDragStamp}
|
||||
handleAllDelete={handleAllDelete}
|
||||
xyPostion={xyPostion}
|
||||
isSignYourself={true}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
isMailSend={false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Signedby pdfDetails={pdfDetails[0]} />
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full md:w-[23%] bg-base-100 overflow-y-auto hide-scrollbar">
|
||||
<div className={`max-h-screen`}>
|
||||
{!isCompleted ? (
|
||||
<div>
|
||||
<WidgetComponent
|
||||
pdfUrl={pdfUrl}
|
||||
dragSignature={dragSignature}
|
||||
signRef={signRef}
|
||||
handleDivClick={handleDivClick}
|
||||
handleMouseLeave={handleMouseLeave}
|
||||
isDragSign={isDragSign}
|
||||
dragStamp={dragStamp}
|
||||
dragRef={dragRef}
|
||||
isDragStamp={isDragStamp}
|
||||
handleAllDelete={handleAllDelete}
|
||||
xyPostion={xyPostion}
|
||||
isSignYourself={true}
|
||||
addPositionOfSignature={addPositionOfSignature}
|
||||
isMailSend={false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<Signedby pdfDetails={pdfDetails[0]} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1364,6 +1437,16 @@ function SignYourSelf() {
|
||||
</button>
|
||||
</div>
|
||||
</ModalUi>
|
||||
<TextFontSetting
|
||||
isTextSetting={isTextSetting}
|
||||
setIsTextSetting={setIsTextSetting}
|
||||
fontSize={fontSize}
|
||||
setFontSize={setFontSize}
|
||||
fontColor={fontColor}
|
||||
setFontColor={setFontColor}
|
||||
handleSaveFontSize={handleSaveFontSize}
|
||||
currWidgetsDetails={currWidgetsDetails}
|
||||
/>
|
||||
</DndProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import pad from "../assets/images/pad.svg";
|
||||
import AddTeam from "../components/AddTeam";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribedTeam } from "../constant/Utils";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import Title from "../components/Title";
|
||||
|
||||
const heading = ["Sr.No", "Name", "Parent Team", "Active"];
|
||||
const actions = [
|
||||
{
|
||||
btnId: "1231",
|
||||
hoverLabel: "Edit",
|
||||
btnColor: "op-btn-primary",
|
||||
btnIcon: "fa-light fa-pen",
|
||||
redirectUrl: "",
|
||||
action: "edit"
|
||||
}
|
||||
];
|
||||
|
||||
const TeamList = () => {
|
||||
const recordperPage = 10;
|
||||
const [teamList, setTeamList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const location = useLocation();
|
||||
const isDashboard =
|
||||
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isEditModal, setIsEditModal] = useState({});
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
|
||||
const getPaginationRange = () => {
|
||||
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
|
||||
const pages = [];
|
||||
const totalPages = Math.ceil(teamList.length / recordperPage);
|
||||
if (totalPages <= totalPageNumbers) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
const leftSiblingIndex = Math.max(currentPage - 1, 1);
|
||||
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
|
||||
|
||||
const showLeftDots = leftSiblingIndex > 2;
|
||||
const showRightDots = rightSiblingIndex < totalPages - 2;
|
||||
|
||||
const firstPageIndex = 1;
|
||||
const lastPageIndex = totalPages;
|
||||
|
||||
if (!showLeftDots && showRightDots) {
|
||||
let leftItemCount = 3;
|
||||
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
|
||||
|
||||
pages.push(...leftRange);
|
||||
pages.push("...");
|
||||
pages.push(totalPages);
|
||||
} else if (showLeftDots && !showRightDots) {
|
||||
let rightItemCount = 3;
|
||||
let rightRange = Array.from(
|
||||
{ length: rightItemCount },
|
||||
(_, i) => totalPages - rightItemCount + i + 1
|
||||
);
|
||||
|
||||
pages.push(firstPageIndex);
|
||||
pages.push("...");
|
||||
pages.push(...rightRange);
|
||||
} else if (showLeftDots && showRightDots) {
|
||||
let middleRange = Array.from(
|
||||
{ length: 3 },
|
||||
(_, i) => leftSiblingIndex + i
|
||||
);
|
||||
|
||||
pages.push(firstPageIndex);
|
||||
pages.push("...");
|
||||
pages.push(...middleRange);
|
||||
pages.push("...");
|
||||
pages.push(lastPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
const pageNumbers = getPaginationRange();
|
||||
useEffect(() => {
|
||||
fetchTeamList();
|
||||
}, []);
|
||||
async function fetchTeamList() {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser) {
|
||||
const admin =
|
||||
extUser?.UserRole &&
|
||||
(extUser?.UserRole === "contracts_Admin" ||
|
||||
extUser?.UserRole === "contracts_OrgAdmin")
|
||||
? true
|
||||
: false;
|
||||
setIsAdmin(admin);
|
||||
}
|
||||
const teams = await Parse.Cloud.run("getteams");
|
||||
if (teams.length > 0) {
|
||||
const _teamRes = JSON.parse(JSON.stringify(teams));
|
||||
setTeamList(_teamRes);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in fetch teamlist", err);
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
// Get current list
|
||||
const indexOfLastDoc = currentPage * recordperPage;
|
||||
const indexOfFirstDoc = indexOfLastDoc - recordperPage;
|
||||
const currentList = teamList?.slice(indexOfFirstDoc, indexOfLastDoc);
|
||||
|
||||
const handleClose = () => {
|
||||
setIsActiveModal({});
|
||||
setIsEditModal({});
|
||||
};
|
||||
|
||||
// Change page
|
||||
const paginateFront = () => setCurrentPage(currentPage + 1);
|
||||
const paginateBack = () => setCurrentPage(currentPage - 1);
|
||||
const handleActionBtn = (act, item) => {
|
||||
if (act.action === "edit") {
|
||||
setIsEditModal({ [item.objectId]: true });
|
||||
}
|
||||
};
|
||||
const handleToggleBtn = (team) => {
|
||||
setIsActiveModal({ [team.objectId]: true });
|
||||
};
|
||||
const handleToggleSubmit = async (team) => {
|
||||
const index = teamList.findIndex((obj) => obj.objectId === team.objectId);
|
||||
if (index !== -1) {
|
||||
setIsActiveModal({});
|
||||
setIsActLoader({ [team.objectId]: true });
|
||||
const newArray = [...teamList];
|
||||
const IsActive = newArray[index].IsActive;
|
||||
newArray[index] = { ...newArray[index], IsActive: !IsActive };
|
||||
setTeamList(newArray);
|
||||
try {
|
||||
await Parse.Cloud.run("updateteam", {
|
||||
IsActive: !IsActive === false ? "false" : "true",
|
||||
TeamId: team.objectId
|
||||
});
|
||||
// console.log("teamRes ", teamRes);
|
||||
setIsAlert({
|
||||
type: !IsActive === false ? "danger" : "success",
|
||||
msg: !IsActive === false ? "Team disabled." : "Team enabled."
|
||||
});
|
||||
} catch (err) {
|
||||
setIsAlert({ type: "danger", msg: "something went wrong." });
|
||||
console.log("err in disable team", err);
|
||||
} finally {
|
||||
setIsActLoader({});
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleTeamInfo = (team) => {
|
||||
setTeamList((prev) => [team, ...prev]);
|
||||
};
|
||||
const handleEditChange = (e, team) => {
|
||||
const index = teamList.findIndex((obj) => obj.objectId === team.objectId);
|
||||
if (index !== -1) {
|
||||
const newArray = [...teamList];
|
||||
newArray[index] = { ...newArray[index], Name: e.target.value };
|
||||
setTeamList(newArray);
|
||||
}
|
||||
};
|
||||
const updateTeamName = async (e, team) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsActLoader({ [team.objectId]: true });
|
||||
setIsEditModal({});
|
||||
try {
|
||||
const data = { Name: team.Name, TeamId: team.objectId };
|
||||
await Parse.Cloud.run("updateteam", data);
|
||||
// console.log("updateTeamRes ", updateTeamRes);
|
||||
setIsAlert({ type: "success", msg: "Team Update successfully." });
|
||||
} catch (Err) {
|
||||
console.log("Err in update team name"), Err;
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
} finally {
|
||||
setIsActLoader({});
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={"Teams"} />
|
||||
{isLoader && (
|
||||
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{Object.keys(isActLoader)?.length > 0 && (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isSubscribe && isEnableSubscription && !isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
Teams{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={"Teams"} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleFormModal()}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div className={` overflow-x-auto w-full`}>
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
{heading?.map((item, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<th className="px-4 py-2">{item}</th>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{teamList?.length > 0 && (
|
||||
<>
|
||||
{currentList.map((item, index) => (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
{heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
{startIndex + index + 1}
|
||||
</th>
|
||||
)}
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.Name}
|
||||
</td>
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.ParentId?.Name || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.Name !== "All Users" && (
|
||||
<>
|
||||
<label className="cursor-pointer relative block items-center mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all op-toggle-secondary"
|
||||
checked={item?.IsActive}
|
||||
onChange={() => handleToggleBtn(item)}
|
||||
/>
|
||||
</label>
|
||||
{isActiveModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Team status"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
Are you sure you want to{" "}
|
||||
{item?.IsActive
|
||||
? "disable"
|
||||
: "enable"}{" "}
|
||||
this team?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() =>
|
||||
handleToggleSubmit(item)
|
||||
}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-white flex flex-wrap gap-1">
|
||||
{item?.Name !== "All Users" && (
|
||||
<>
|
||||
{actions?.length > 0 &&
|
||||
actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() =>
|
||||
handleActionBtn(act, item)
|
||||
}
|
||||
title={act.hoverLabel}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm w-[50px]`}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isEditModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Edit Team"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<form
|
||||
className="m-[20px]"
|
||||
onSubmit={(e) =>
|
||||
updateTeamName(e, item)
|
||||
}
|
||||
>
|
||||
<label className="text-xs font-semibold text-base-content ml-1">
|
||||
Name of Team{" "}
|
||||
<span className="text-[red] text-[13px]">
|
||||
*
|
||||
</span>
|
||||
</label>
|
||||
<input
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-base-content w-full text-xs"
|
||||
value={item.Name}
|
||||
onChange={(e) =>
|
||||
handleEditChange(e, item)
|
||||
}
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary mt-3"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</ModalUi>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{teamList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{teamList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{teamList?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">No Data Available</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
title={"Add Team"}
|
||||
isOpen={isModal}
|
||||
handleClose={handleFormModal}
|
||||
>
|
||||
<AddTeam
|
||||
setIsAlert={setIsAlert}
|
||||
handleTeamInfo={handleTeamInfo}
|
||||
closePopup={handleFormModal}
|
||||
/>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold">
|
||||
404
|
||||
</h1>
|
||||
<p className="text-[30px] lg:text-[50px]">Page Not Found</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isSubscribe && isEnableSubscription && !isLoader && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard plan={"TEAMS"} price={"20"} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TeamList;
|
||||
@@ -0,0 +1,179 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Loader from "../primitives/Loader";
|
||||
import Parse from "parse";
|
||||
import { NavLink, useNavigate } from "react-router-dom";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Title from "../components/Title";
|
||||
|
||||
const UpdateExistUserAdmin = () => {
|
||||
const navigate = useNavigate();
|
||||
const [formdata, setFormdata] = useState({ email: "", masterkey: "" });
|
||||
const [loader, setLoader] = useState(true);
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
const [isSubmitLoading, setIsSubmitLoading] = useState(false);
|
||||
const [isAlert, setIsAlert] = useState({ type: "danger", msg: "" });
|
||||
useEffect(() => {
|
||||
checkIsAdminExist();
|
||||
}, []);
|
||||
|
||||
const checkIsAdminExist = async () => {
|
||||
try {
|
||||
const isAdminExist = await Parse.Cloud.run("checkadminexist");
|
||||
if (isAdminExist !== "not_exist") {
|
||||
console.log("isAdminExist ", isAdminExist);
|
||||
setErrMsg("Admin already exists.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("Err in checkadminexist", err);
|
||||
setErrMsg("Something went wrong.");
|
||||
} finally {
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsSubmitLoading(true);
|
||||
try {
|
||||
const updateUserAsAdmin = await Parse.Cloud.run(
|
||||
"updateuserasadmin",
|
||||
formdata
|
||||
);
|
||||
// console.log("updateUserAsAdmin ", updateUserAsAdmin);
|
||||
if (updateUserAsAdmin === "admin_created") {
|
||||
setIsAlert({ type: "success", msg: "Admin created" });
|
||||
navigate("/");
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("err in updateuserasadmin", err.code);
|
||||
if (err.code === 404) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: "Invalid masterkey" }));
|
||||
} else if (err.code === 101) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: "User not found" }));
|
||||
} else if (err.code === 137) {
|
||||
setIsAlert((prev) => ({ ...prev, msg: "Admin already exist" }));
|
||||
} else {
|
||||
setErrMsg("Something went wrong.");
|
||||
}
|
||||
} finally {
|
||||
setIsSubmitLoading(false);
|
||||
setTimeout(() => {
|
||||
setIsAlert(() => ({ type: "danger", msg: "" }));
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="h-screen flex justify-center">
|
||||
<Title title={"Add Admin"} />
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
{loader ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{errMsg ? (
|
||||
<div className="text-[grey] flex justify-center items-center text-lg md:text-2xl">
|
||||
{errMsg}
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-[95%] md:w-[500px]">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="w-full my-4 op-card bg-base-100 shadow-md outline outline-1 outline-slate-300/50 overflow-hidden">
|
||||
{isSubmitLoading && (
|
||||
<div className="absolute z-40 w-full h-full flex justify-center bg-black/30">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
<h2 className="text-[30px] text-center mt-3 font-medium">
|
||||
Opensign Setup
|
||||
</h2>
|
||||
<NavLink
|
||||
to="https://discord.com/invite/xe9TDuyAyj"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-center text-sm mt-1 text-[blue] cursor-pointer"
|
||||
>
|
||||
Join our discord server
|
||||
<i
|
||||
aria-hidden="true"
|
||||
className="fa-brands fa-discord ml-1"
|
||||
></i>
|
||||
{/* <span className="fa-sr-only">OpenSign's Discord</span> */}
|
||||
</NavLink>
|
||||
<div className="px-6 py-3 text-xs">
|
||||
<label>
|
||||
Email <span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formdata.email}
|
||||
onChange={(e) =>
|
||||
setFormdata((prev) => ({
|
||||
...prev,
|
||||
email: e.target.value?.toLowerCase()
|
||||
}))
|
||||
}
|
||||
required
|
||||
/>
|
||||
<hr className="my-2 border-none" />
|
||||
<label>
|
||||
Master key{" "}
|
||||
<span className="text-[red] text-[13px]">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
value={formdata.masterkey}
|
||||
onChange={(e) =>
|
||||
setFormdata((prev) => ({
|
||||
...prev,
|
||||
masterkey: e.target.value
|
||||
}))
|
||||
}
|
||||
required
|
||||
/>
|
||||
|
||||
{/*
|
||||
<div className="mt-2.5 ml-1 flex flex-row items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-checkbox op-checkbox-sm"
|
||||
id="termsandcondition"
|
||||
checked={isSubscribeNews}
|
||||
onChange={(e) => setIsSubscribeNews(e.target.checked)}
|
||||
/>
|
||||
<label
|
||||
className="text-xs cursor-pointer ml-1 mb-0"
|
||||
htmlFor="termsandcondition"
|
||||
>
|
||||
Subscribe to OpenSign newsletter
|
||||
</label>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="mx-4 text-center text-xs font-bold mb-3">
|
||||
<button
|
||||
type="submit"
|
||||
className="op-btn op-btn-primary w-full"
|
||||
disabled={loader}
|
||||
>
|
||||
{loader ? "Loading..." : "Next"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateExistUserAdmin;
|
||||
@@ -0,0 +1,426 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Parse from "parse";
|
||||
import Alert from "../primitives/Alert";
|
||||
import Loader from "../primitives/Loader";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import pad from "../assets/images/pad.svg";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import AddUser from "../components/AddUser";
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribedTeam } from "../constant/Utils";
|
||||
import Title from "../components/Title";
|
||||
const heading = ["Sr.No", "Name", "Email", "Phone", "Role", "Team", "Active"];
|
||||
// const actions = [];
|
||||
const UserList = () => {
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [isLoader, setIsLoader] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const location = useLocation();
|
||||
const isDashboard =
|
||||
location?.pathname === "/dashboard/35KBoSgoAK" ? true : false;
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isActiveModal, setIsActiveModal] = useState({});
|
||||
const [isActLoader, setIsActLoader] = useState({});
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const recordperPage = 10;
|
||||
const startIndex = (currentPage - 1) * recordperPage; // user per page
|
||||
|
||||
const getPaginationRange = () => {
|
||||
const totalPageNumbers = 7; // Adjust this value to show more/less page numbers
|
||||
const pages = [];
|
||||
const totalPages = Math.ceil(userList.length / recordperPage);
|
||||
if (totalPages <= totalPageNumbers) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
const leftSiblingIndex = Math.max(currentPage - 1, 1);
|
||||
const rightSiblingIndex = Math.min(currentPage + 1, totalPages);
|
||||
|
||||
const showLeftDots = leftSiblingIndex > 2;
|
||||
const showRightDots = rightSiblingIndex < totalPages - 2;
|
||||
|
||||
const firstPageIndex = 1;
|
||||
const lastPageIndex = totalPages;
|
||||
|
||||
if (!showLeftDots && showRightDots) {
|
||||
let leftItemCount = 3;
|
||||
let leftRange = Array.from({ length: leftItemCount }, (_, i) => i + 1);
|
||||
|
||||
pages.push(...leftRange);
|
||||
pages.push("...");
|
||||
pages.push(totalPages);
|
||||
} else if (showLeftDots && !showRightDots) {
|
||||
let rightItemCount = 3;
|
||||
let rightRange = Array.from(
|
||||
{ length: rightItemCount },
|
||||
(_, i) => totalPages - rightItemCount + i + 1
|
||||
);
|
||||
|
||||
pages.push(firstPageIndex);
|
||||
pages.push("...");
|
||||
pages.push(...rightRange);
|
||||
} else if (showLeftDots && showRightDots) {
|
||||
let middleRange = Array.from(
|
||||
{ length: 3 },
|
||||
(_, i) => leftSiblingIndex + i
|
||||
);
|
||||
|
||||
pages.push(firstPageIndex);
|
||||
pages.push("...");
|
||||
pages.push(...middleRange);
|
||||
pages.push("...");
|
||||
pages.push(lastPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
const pageNumbers = getPaginationRange();
|
||||
useEffect(() => {
|
||||
fetchUserList();
|
||||
}, []);
|
||||
async function fetchUserList() {
|
||||
try {
|
||||
setIsLoader(true);
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
} else {
|
||||
setIsSubscribe(true);
|
||||
}
|
||||
const extUser = JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
|
||||
if (extUser) {
|
||||
const admin =
|
||||
extUser?.UserRole &&
|
||||
(extUser?.UserRole === "contracts_Admin" ||
|
||||
extUser?.UserRole === "contracts_OrgAdmin")
|
||||
? true
|
||||
: false;
|
||||
setIsAdmin(admin);
|
||||
}
|
||||
const res = await Parse.Cloud.run("getuserlistbyorg", {
|
||||
organizationId: extUser.OrganizationId.objectId
|
||||
});
|
||||
const _userRes = JSON.parse(JSON.stringify(res));
|
||||
setUserList(_userRes);
|
||||
} catch (err) {
|
||||
console.log("Err in fetch userlist", err);
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
setIsLoader(false);
|
||||
}
|
||||
}
|
||||
const handleFormModal = () => {
|
||||
setIsModal(!isModal);
|
||||
};
|
||||
|
||||
// Change page
|
||||
const paginateFront = () => setCurrentPage(currentPage + 1);
|
||||
const paginateBack = () => setCurrentPage(currentPage - 1);
|
||||
// const handleActionBtn = (act, item) => {
|
||||
// if (act.action === "delete") {
|
||||
// setIsDeleteModal({ [item.objectId]: true });
|
||||
// }
|
||||
// };
|
||||
const handleUserData = (userData) => {
|
||||
setUserList((prev) => [userData, ...prev]);
|
||||
};
|
||||
// `formatRow` is used to show data in poper manner like
|
||||
// if data is of array type then it will join array items with ","
|
||||
// if data is of object type then it Name values will be show in row
|
||||
// if no data available it will show hyphen "-"
|
||||
const formatRow = (row) => {
|
||||
if (Array.isArray(row)) {
|
||||
let updateArr = row.map((x) => x.Name);
|
||||
return updateArr.join(", ");
|
||||
} else if (typeof row === "object" && row !== null) {
|
||||
return row?.Name || "-";
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
};
|
||||
const handleClose = () => {
|
||||
setIsActiveModal({});
|
||||
};
|
||||
const handleToggleSubmit = async (user) => {
|
||||
const index = userList.findIndex((obj) => obj.objectId === user.objectId);
|
||||
if (index !== -1) {
|
||||
setIsActiveModal({});
|
||||
setIsActLoader({ [user.objectId]: true });
|
||||
const newArray = [...userList];
|
||||
const IsDisabled = newArray[index]?.IsDisabled;
|
||||
newArray[index] = { ...newArray[index], IsDisabled: !IsDisabled };
|
||||
setUserList(newArray);
|
||||
try {
|
||||
const extUser = new Parse.Object("contracts_Users");
|
||||
extUser.id = user.objectId;
|
||||
extUser.set("IsDisabled", !IsDisabled);
|
||||
await extUser.save();
|
||||
setIsAlert({
|
||||
type: !IsDisabled === true ? "danger" : "success",
|
||||
msg: !IsDisabled === true ? "User deactivated." : "User activated."
|
||||
});
|
||||
} catch (err) {
|
||||
setIsAlert({ type: "danger", msg: "something went wrong." });
|
||||
console.log("err in disable team", err);
|
||||
} finally {
|
||||
setIsActLoader({});
|
||||
setTimeout(() => setIsAlert({ type: "success", msg: "" }), 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
const handleToggleBtn = (user) => {
|
||||
setIsActiveModal({ [user.objectId]: true });
|
||||
};
|
||||
return (
|
||||
<div className="relative">
|
||||
<Title title={isAdmin ? "Users" : "Page not found"} />
|
||||
{isLoader && (
|
||||
<div className="absolute w-full h-[300px] md:h-[400px] flex justify-center items-center z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{Object.keys(isActLoader)?.length > 0 && (
|
||||
<div className="absolute w-full h-full flex justify-center items-center bg-black bg-opacity-30 z-30 rounded-box">
|
||||
<Loader />
|
||||
</div>
|
||||
)}
|
||||
{isSubscribe && !isLoader && (
|
||||
<>
|
||||
{isAdmin ? (
|
||||
<div className="p-2 w-full bg-base-100 text-base-content op-card shadow-lg">
|
||||
{isAlert.msg && (
|
||||
<Alert type={isAlert.type}>
|
||||
<div className="ml-3">{isAlert.msg}</div>
|
||||
</Alert>
|
||||
)}
|
||||
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
|
||||
<div className="font-light">
|
||||
Users{" "}
|
||||
<span className="text-xs md:text-[13px] font-normal">
|
||||
<Tooltip message={"users from Teams"} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleFormModal()}
|
||||
>
|
||||
<i className="fa-light fa-square-plus text-accent text-[40px]"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div className={` overflow-x-auto w-full`}>
|
||||
<table className="op-table border-collapse w-full">
|
||||
<thead className="text-[14px]">
|
||||
<tr className="border-y-[1px]">
|
||||
{heading?.map((item, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<th className="px-4 py-2">{item}</th>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-[12px]">
|
||||
{userList?.length > 0 && (
|
||||
<>
|
||||
{userList.map((item, index) => (
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
{heading.includes("Sr.No") && (
|
||||
<th className="px-4 py-2">
|
||||
{startIndex + index + 1}
|
||||
</th>
|
||||
)}
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
{item?.Name}{" "}
|
||||
</td>
|
||||
<td className="px-4 py-2 ">{item?.Email || "-"}</td>
|
||||
<td className="px-4 py-2">{item?.Phone || "-"}</td>
|
||||
<td className="px-4 py-2">
|
||||
{item?.UserRole?.split("_").pop() || "-"}
|
||||
</td>
|
||||
<td className="px-4 py-2">
|
||||
{formatRow(item.TeamIds)}
|
||||
</td>
|
||||
{item.UserRole !== "contracts_Admin" && (
|
||||
<td className="px-4 py-2 font-semibold">
|
||||
<label className="cursor-pointer relative block items-center mb-0">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all op-toggle-secondary"
|
||||
checked={item?.IsDisabled !== true}
|
||||
onChange={() => handleToggleBtn(item)}
|
||||
/>
|
||||
</label>
|
||||
{isActiveModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"User status"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
Are you sure you want to{" "}
|
||||
{item?.IsDisabled
|
||||
? "activate"
|
||||
: "deactivate"}{" "}
|
||||
this user?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() =>
|
||||
handleToggleSubmit(item)
|
||||
}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</td>
|
||||
)}
|
||||
{/* <td className="px-3 py-2 text-white grid grid-cols-2">
|
||||
{actions?.length > 0 &&
|
||||
actions.map((act, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => handleActionBtn(act, item)}
|
||||
title={act.hoverLabel}
|
||||
className={`${
|
||||
act?.btnColor ? act.btnColor : ""
|
||||
} op-btn op-btn-sm`}
|
||||
>
|
||||
<i className={act.btnIcon}></i>
|
||||
</button>
|
||||
))}
|
||||
{isDeleteModal[item.objectId] && (
|
||||
<ModalUi
|
||||
isOpen
|
||||
title={"Delete User"}
|
||||
handleClose={handleClose}
|
||||
>
|
||||
<div className="m-[20px]">
|
||||
<div className="text-lg font-normal text-black">
|
||||
Are you sure you want to delete this user?
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-4 " />
|
||||
<div className="flex items-center mt-3 gap-2 text-white">
|
||||
<button
|
||||
onClick={() => handleDelete(item)}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
)}
|
||||
</td> */}
|
||||
</tr>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="op-join flex flex-wrap items-center p-2">
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateBack()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
Prev
|
||||
</button>
|
||||
)}
|
||||
{pageNumbers.map((x, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setCurrentPage(x)}
|
||||
disabled={x === "..."}
|
||||
className={`${
|
||||
x === currentPage ? "op-btn-active" : ""
|
||||
} op-join-item op-btn op-btn-sm`}
|
||||
>
|
||||
{x}
|
||||
</button>
|
||||
))}
|
||||
{userList.length > recordperPage && (
|
||||
<button
|
||||
onClick={() => paginateFront()}
|
||||
className="op-join-item op-btn op-btn-sm"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{userList?.length <= 0 && (
|
||||
<div
|
||||
className={`${
|
||||
isDashboard ? "h-[317px]" : ""
|
||||
} flex flex-col items-center justify-center w-ful bg-base-100 text-base-content rounded-xl py-4`}
|
||||
>
|
||||
<div className="w-[60px] h-[60px] overflow-hidden">
|
||||
<img
|
||||
className="w-full h-full object-contain"
|
||||
src={pad}
|
||||
alt="img"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm font-semibold">No Data Available</div>
|
||||
</div>
|
||||
)}
|
||||
<ModalUi
|
||||
title={"Add User"}
|
||||
isOpen={isModal}
|
||||
handleClose={handleFormModal}
|
||||
>
|
||||
<AddUser
|
||||
setIsAlert={setIsAlert}
|
||||
handleUserData={handleUserData}
|
||||
closePopup={handleFormModal}
|
||||
/>
|
||||
</ModalUi>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box">
|
||||
<div className="text-center">
|
||||
<h1 className="text-[60px] lg:text-[120px] font-semibold">
|
||||
404
|
||||
</h1>
|
||||
<p className="text-[30px] lg:text-[50px]">Page Not Found</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!isSubscribe && isEnableSubscription && !isLoader && (
|
||||
<div data-tut="apisubscribe">
|
||||
<SubscribeCard plan={"TEAMS"} price={"20"} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserList;
|
||||
@@ -1,8 +1,4 @@
|
||||
import React, {
|
||||
useState,
|
||||
useEffect
|
||||
// useRef
|
||||
} from "react";
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import { Navigate, useNavigate } from "react-router-dom";
|
||||
import Parse from "parse";
|
||||
import { SaveFileSize } from "../constant/saveFileSize";
|
||||
@@ -10,10 +6,13 @@ import dp from "../assets/images/dp.png";
|
||||
import Title from "../components/Title";
|
||||
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, handleSendOTP } from "../constant/Utils";
|
||||
import {
|
||||
checkIsSubscribed,
|
||||
checkIsSubscribedTeam,
|
||||
handleSendOTP
|
||||
} from "../constant/Utils";
|
||||
import Upgrade from "../primitives/Upgrade";
|
||||
import ModalUi from "../primitives/ModalUi";
|
||||
import Loader from "../primitives/Loader";
|
||||
@@ -32,10 +31,11 @@ function UserProfile() {
|
||||
const [percentage, setpercentage] = useState(0);
|
||||
const [isDisableDocId, setIsDisableDocId] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
// const [publicUserName, setPublicUserName] = useState(
|
||||
// extendUser && extendUser?.[0]?.UserName
|
||||
// );
|
||||
// const previousPublicUserName = useRef(publicUserName);
|
||||
const [isUpgrade, setIsUpgrade] = useState(false);
|
||||
const [publicUserName, setPublicUserName] = useState(
|
||||
extendUser && extendUser?.[0]?.UserName
|
||||
);
|
||||
const previousPublicUserName = useRef(publicUserName);
|
||||
const [company, setCompany] = useState(
|
||||
extendUser && extendUser?.[0]?.Company
|
||||
);
|
||||
@@ -46,7 +46,11 @@ function UserProfile() {
|
||||
const [otp, setOtp] = useState("");
|
||||
const [otpLoader, setOtpLoader] = useState(false);
|
||||
const [isEmailVerified, setIsEmailVerified] = useState(false);
|
||||
// const [userNameError, setUserNameError] = useState("");
|
||||
const [userNameError, setUserNameError] = useState("");
|
||||
const [tagLine, setTagLine] = useState(
|
||||
extendUser && extendUser?.[0]?.Tagline
|
||||
);
|
||||
const [isTeam, setIsTeam] = useState(false);
|
||||
useEffect(() => {
|
||||
getUserDetail();
|
||||
}, []);
|
||||
@@ -58,7 +62,9 @@ function UserProfile() {
|
||||
const HeaderDocId = jsonSender[0]?.HeaderDocId;
|
||||
if (isEnableSubscription) {
|
||||
const getIsSubscribe = await checkIsSubscribed();
|
||||
const getIsTeam = await checkIsSubscribedTeam();
|
||||
setIsSubscribe(getIsSubscribe);
|
||||
setIsTeam(getIsTeam);
|
||||
}
|
||||
if (HeaderDocId) {
|
||||
setIsDisableDocId(HeaderDocId);
|
||||
@@ -85,31 +91,31 @@ function UserProfile() {
|
||||
}
|
||||
};
|
||||
//function to check public username already exist
|
||||
// const handleCheckPublicUserName = async () => {
|
||||
// try {
|
||||
// const res = await Parse.Cloud.run("getpublicusername", {
|
||||
// username: publicUserName
|
||||
// });
|
||||
// if (res) {
|
||||
// setIsLoader(false);
|
||||
// setUserNameError("user name already exist");
|
||||
// setTimeout(() => {
|
||||
// setUserNameError("");
|
||||
// }, 3000);
|
||||
// return res;
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.log("error in getpublicusername cloud function");
|
||||
// }
|
||||
// };
|
||||
const handleCheckPublicUserName = async () => {
|
||||
try {
|
||||
const res = await Parse.Cloud.run("getpublicusername", {
|
||||
username: publicUserName
|
||||
});
|
||||
if (res) {
|
||||
setIsLoader(false);
|
||||
setUserNameError("user name already exist");
|
||||
setTimeout(() => {
|
||||
setUserNameError("");
|
||||
}, 3000);
|
||||
return res;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("error in getpublicusername cloud function");
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
let phn = Phone,
|
||||
res = "";
|
||||
//condition to call cloud function when user change publicUserName
|
||||
// if (previousPublicUserName.current !== publicUserName) {
|
||||
// res = await handleCheckPublicUserName();
|
||||
// }
|
||||
if (publicUserName && previousPublicUserName.current !== publicUserName) {
|
||||
res = await handleCheckPublicUserName();
|
||||
}
|
||||
if (!res) {
|
||||
setIsLoader(true);
|
||||
try {
|
||||
@@ -155,7 +161,6 @@ function UserProfile() {
|
||||
|
||||
// `updateExtUser` is used to update user details in extended class
|
||||
const updateExtUser = async (obj) => {
|
||||
const extClass = localStorage.getItem("extended_class");
|
||||
const extData = JSON.parse(localStorage.getItem("Extand_Class"));
|
||||
const ExtUserId = extData[0].objectId;
|
||||
const body = {
|
||||
@@ -163,12 +168,13 @@ function UserProfile() {
|
||||
Name: obj.Name,
|
||||
HeaderDocId: isDisableDocId,
|
||||
JobTitle: jobTitle,
|
||||
Company: company
|
||||
// UserName: publicUserName || ""
|
||||
Company: company,
|
||||
UserName: publicUserName || "",
|
||||
Tagline: tagLine || ""
|
||||
};
|
||||
|
||||
await axios.put(
|
||||
parseBaseUrl + "classes/" + extClass + "/" + ExtUserId,
|
||||
parseBaseUrl + "classes/contracts_Users/" + ExtUserId,
|
||||
body,
|
||||
{
|
||||
headers: {
|
||||
@@ -185,6 +191,7 @@ function UserProfile() {
|
||||
const json = JSON.parse(JSON.stringify([res]));
|
||||
const extRes = JSON.stringify(json);
|
||||
localStorage.setItem("Extand_Class", extRes);
|
||||
previousPublicUserName.current = publicUserName;
|
||||
// console.log("updateRes ", updateRes);
|
||||
};
|
||||
// file upload function
|
||||
@@ -278,23 +285,20 @@ function UserProfile() {
|
||||
alert("OTP sent on you email");
|
||||
};
|
||||
//function to handle onchange username and restrict 6-characters username for free users
|
||||
// const handleOnchangeUserName = (e) => {
|
||||
// const value = e.target.value;
|
||||
// if (value.length > 6 && !isSubscribe) {
|
||||
// setUserNameError("Please upgrade to allow more than 6 characters.");
|
||||
// setTimeout(() => {
|
||||
// setUserNameError("");
|
||||
// }, 2000);
|
||||
// } else {
|
||||
// setPublicUserName(e.target.value);
|
||||
// }
|
||||
// };
|
||||
const handleOnchangeUserName = (e) => {
|
||||
const newValue = e.target.value.replace(/\s+/g, "");
|
||||
setPublicUserName(newValue);
|
||||
};
|
||||
|
||||
const handleOnchangeTagLine = (e) => {
|
||||
setTagLine(e.target.value);
|
||||
};
|
||||
const handleCancel = () => {
|
||||
setEditMode(false);
|
||||
SetName(localStorage.getItem("username"));
|
||||
SetPhone(UserProfile && UserProfile.phone);
|
||||
setImage(localStorage.getItem("profileImg"));
|
||||
// setPublicUserName(extendUser && extendUser?.[0]?.UserName);
|
||||
setPublicUserName(extendUser && extendUser?.[0]?.UserName);
|
||||
setCompany(extendUser && extendUser?.[0]?.Company);
|
||||
setJobTitle(extendUser?.[0]?.JobTitle);
|
||||
setIsDisableDocId(extendUser?.[0]?.HeaderDocId);
|
||||
@@ -308,14 +312,14 @@ function UserProfile() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-center items-center w-full relative">
|
||||
{/* {userNameError && (
|
||||
{userNameError && (
|
||||
<div
|
||||
className={`z-[1000] fixed top-[50%] transform border-[1px] text-sm border-[#f0a8a8] bg-[#f4bebe] text-[#c42121] rounded py-[.75rem] px-[1.25rem]`}
|
||||
>
|
||||
{userNameError}
|
||||
</div>
|
||||
)} */}
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded-xl w-[450px]">
|
||||
)}
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box w-[450px]">
|
||||
<div className="flex flex-col justify-center items-center my-4">
|
||||
<div className="w-[200px] h-[200px] overflow-hidden rounded-full">
|
||||
<img
|
||||
@@ -442,85 +446,126 @@ function UserProfile() {
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
{/* {isEnableSubscription && (
|
||||
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold">
|
||||
Public profile :{" "}
|
||||
<Tooltip
|
||||
message={`this is your public URL. Copy or share it
|
||||
with the signer, and you will be able to see
|
||||
all your publicly set templates.`}
|
||||
/>
|
||||
</span>
|
||||
<div className="flex text-xs items-center">
|
||||
<span>opensign-me.vercel.app/</span>
|
||||
{editmode ? (
|
||||
<input
|
||||
onChange={handleOnchangeUserName}
|
||||
value={publicUserName}
|
||||
disabled={!editmode}
|
||||
placeholder="enter user name"
|
||||
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
|
||||
{isEnableSubscription && (
|
||||
<>
|
||||
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold flex gap-1">
|
||||
Public profile :{" "}
|
||||
<Tooltip
|
||||
maxWidth="max-w-[250px]"
|
||||
message={`This is your public URL. Copy or share it with the signer, and you will be able to see all your publicly set templates.`}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
value={extendUser?.[0]?.UserName}
|
||||
disabled
|
||||
placeholder="enter user name"
|
||||
className="op-input op-input-bordered op-input-xs"
|
||||
</span>
|
||||
<div className="flex md:flex-row flex-col md:items-center">
|
||||
<span className="mb-1 md:mb-1">opensign.me/</span>
|
||||
{editmode ? (
|
||||
<input
|
||||
maxLength={40}
|
||||
style={{
|
||||
border:
|
||||
!isSubscribe &&
|
||||
publicUserName?.length > 0 &&
|
||||
publicUserName?.length < 9 &&
|
||||
"solid red"
|
||||
}}
|
||||
onChange={handleOnchangeUserName}
|
||||
value={publicUserName}
|
||||
disabled={!editmode}
|
||||
placeholder="enter user name"
|
||||
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
value={extendUser?.[0]?.UserName}
|
||||
disabled
|
||||
placeholder="enter user name"
|
||||
className="op-input op-input-bordered op-input-xs"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex md:flex-row flex-col md:justify-between md:items-center border-t-[1px] border-gray-300 py-2 break-all">
|
||||
<span className="font-semibold flex gap-1">
|
||||
Tagline :{" "}
|
||||
<Tooltip
|
||||
maxWidth="max-w-[250px]"
|
||||
message={`This Tagline will appear on your OpenSign public profile for ex. https://opensign.me/alex`}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex md:flex-row flex-col md:items-center">
|
||||
{editmode ? (
|
||||
<input
|
||||
onChange={handleOnchangeTagLine}
|
||||
value={tagLine}
|
||||
disabled={!editmode}
|
||||
placeholder="enter tagline"
|
||||
className="op-input op-input-bordered focus:outline-none hover:border-base-content op-input-xs"
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
value={extendUser?.[0]?.Tagline}
|
||||
disabled
|
||||
placeholder="enter tagline"
|
||||
className="op-input op-input-bordered op-input-xs"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
{isEnableSubscription && (
|
||||
<li className="border-y-[1px] border-gray-300 break-all">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span
|
||||
className={
|
||||
isTeam ? "font-semibold" : "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
Disable DocumentId :{" "}
|
||||
<Tooltip
|
||||
url={
|
||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||
}
|
||||
/>
|
||||
{!isTeam && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isTeam
|
||||
? `${editmode ? "cursor-pointer" : ""}`
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
disabled={isTeam ? false : true}
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={isDisableDocId}
|
||||
onChange={handleDisableDocId}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</li>
|
||||
)} */}
|
||||
<li className="border-y-[1px] border-gray-300 break-all">
|
||||
<div className="flex justify-between items-center py-2">
|
||||
<span
|
||||
className={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "font-semibold"
|
||||
: "font-semibold text-gray-300"
|
||||
}
|
||||
>
|
||||
Disable DocumentId :{" "}
|
||||
<Tooltip
|
||||
url={
|
||||
"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"
|
||||
}
|
||||
/>
|
||||
{!isSubscribe && isEnableSubscription && <Upgrade />}
|
||||
</span>
|
||||
<label
|
||||
className={`${
|
||||
isSubscribe || !isEnableSubscription
|
||||
? `${editmode ? "cursor-pointer" : ""}`
|
||||
: "pointer-events-none opacity-50"
|
||||
} relative block items-center mb-0`}
|
||||
>
|
||||
<input
|
||||
disabled={editmode ? false : true}
|
||||
type="checkbox"
|
||||
className="op-toggle transition-all checked:[--tglbg:#3368ff] checked:bg-white"
|
||||
checked={isDisableDocId}
|
||||
onChange={handleDisableDocId}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{!isEnableSubscription && (
|
||||
<PremiumAlertHeader
|
||||
message={
|
||||
"Disable documentId is free in beta, this feature will incur a fee later."
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<div className="flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4">
|
||||
<div
|
||||
className={`${
|
||||
!isEnableSubscription ? "border-t-[1px] border-gray-300" : ""
|
||||
} flex justify-center gap-2 pt-2 pb-3 md:pt-3 md:pb-4`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) =>
|
||||
editmode ? handleSubmit(e) : setEditMode(true)
|
||||
}
|
||||
onClick={(e) => {
|
||||
if (
|
||||
!isSubscribe &&
|
||||
publicUserName?.length > 0 &&
|
||||
publicUserName?.length < 9
|
||||
) {
|
||||
setIsUpgrade(true);
|
||||
} else {
|
||||
editmode ? handleSubmit(e) : setEditMode(true);
|
||||
}
|
||||
}}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{editmode ? "Save" : "Edit"}
|
||||
@@ -577,6 +622,38 @@ function UserProfile() {
|
||||
)}
|
||||
</ModalUi>
|
||||
)}
|
||||
{isUpgrade && (
|
||||
<div className="op-modal op-modal-open">
|
||||
<div className="max-h-90 bg-base-100 w-[95%] md:max-w-[500px] rounded-box relative">
|
||||
<>
|
||||
<div
|
||||
className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-primary-content absolute right-2 top-2 z-40"
|
||||
onClick={() => setIsUpgrade(false)}
|
||||
>
|
||||
✕
|
||||
</div>
|
||||
|
||||
<div className="op-card op-bg-primary text-primary-content w-full shadow-lg">
|
||||
<div className="op-card-body">
|
||||
<h2 className="op-card-title">Upgrade to Plan</h2>
|
||||
<p>
|
||||
To have a username less than 8 character please
|
||||
subscribe
|
||||
</p>
|
||||
<div className="op-card-actions justify-end">
|
||||
<button
|
||||
onClick={() => navigate("/subscription")}
|
||||
className="op-btn op-btn-accent"
|
||||
>
|
||||
Upgrade Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</React.Fragment>
|
||||
|
||||
@@ -1,28 +1,31 @@
|
||||
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 { isEnableSubscription } from "../constant/const";
|
||||
import { checkIsSubscribed, openInNewTab } from "../constant/Utils";
|
||||
import Parse from "parse";
|
||||
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
|
||||
import Tooltip from "../primitives/Tooltip";
|
||||
import Loader from "../primitives/Loader";
|
||||
|
||||
import SubscribeCard from "../primitives/SubscribeCard";
|
||||
import Tour from "reactour";
|
||||
const tourSteps = [
|
||||
{
|
||||
selector: '[data-tut="webhooksubscribe"]',
|
||||
content: "Upgrade now to set webhook"
|
||||
}
|
||||
];
|
||||
function Webhook() {
|
||||
const navigation = useNavigate();
|
||||
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
|
||||
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
|
||||
const [webhook, setWebhook] = useState();
|
||||
const [isLoader, setIsLoader] = useState(true);
|
||||
const [isGenerate, setIsGenerate] = useState(false);
|
||||
const [isErr, setIsErr] = useState(false);
|
||||
const [isModal, setIsModal] = useState(false);
|
||||
const [isSubscribe, setIsSubscribe] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const [isAlert, setIsAlert] = useState({ type: "success", msg: "" });
|
||||
const [isTour, setIsTour] = useState(false);
|
||||
useEffect(() => {
|
||||
fetchWebhook();
|
||||
// eslint-disable-next-line
|
||||
@@ -60,31 +63,22 @@ function Webhook() {
|
||||
"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);
|
||||
}
|
||||
});
|
||||
const res = await axios.post(url, params, { headers: headers });
|
||||
if (res.data && res.data.result && res.data.result.Webhook) {
|
||||
setWebhook(res.data.result.Webhook);
|
||||
setIsAlert({ type: "success", msg: "Webhook added successfully." });
|
||||
} else {
|
||||
console.error("Error while generating webhook");
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
}
|
||||
} catch (error) {
|
||||
setIsAlert({ type: "danger", msg: "Something went wrong." });
|
||||
console.log("err while generating webhook", error);
|
||||
} finally {
|
||||
setIsLoader(false);
|
||||
setIsErr(true);
|
||||
setTimeout(() => {
|
||||
setIsErr(false);
|
||||
setIsAlert({ type: "success", msg: "" });
|
||||
}, 1500);
|
||||
|
||||
console.log("err", error);
|
||||
}
|
||||
} else {
|
||||
setError("The webhook url should always start with https://");
|
||||
@@ -94,131 +88,111 @@ function Webhook() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleModal = () => setIsModal(!isModal);
|
||||
const handleModal = () => {
|
||||
if (!isSubscribe && isEnableSubscription) {
|
||||
setIsTour(true);
|
||||
} else {
|
||||
setIsModal(!isModal);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Title title={"Webhook"} />
|
||||
{isGenerate && <Alert type="success">Webhook added successfully.</Alert>}
|
||||
{isErr && <Alert type="danger">Something went wrong.</Alert>}
|
||||
|
||||
{isAlert.msg && <Alert type={isAlert.type}>{isAlert.msg}</Alert>}
|
||||
{isLoader ? (
|
||||
<div className="h-[100vh] flex justify-center items-center">
|
||||
<Loader />
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow rounded">
|
||||
{!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={
|
||||
isSubscribe || !isEnableSubscription
|
||||
? "w-full flex flex-col p-2 text-sm "
|
||||
: "w-full flex flex-col p-2 text-sm opacity-20 pointer-events-none select-none"
|
||||
}
|
||||
>
|
||||
<li
|
||||
className={`flex flex-col md:flex-row justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
|
||||
>
|
||||
<div className="w-[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>
|
||||
<>
|
||||
<div className="bg-base-100 text-base-content flex flex-col justify-center shadow-md rounded-box mb-3">
|
||||
<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"}>
|
||||
<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-5">
|
||||
<span className="">Webhook:</span>{" "}
|
||||
<span id="token" className=" md:text-end cursor-pointer">
|
||||
{webhook ? webhook : "_____"}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-primary"
|
||||
>
|
||||
{webhook ? "Update Webhook" : "Add Webhook"}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-primary "
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary mt-2 mb-3 px-7"
|
||||
>
|
||||
{webhook ? "Update Webhook" : "Add Webhook"}
|
||||
View Docs
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="flex items-center justify-center ">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
openInNewTab(
|
||||
"https://docs.opensignlabs.com/docs/API-docs/save-update-webhook"
|
||||
)
|
||||
}
|
||||
className="op-btn op-btn-secondary my-2"
|
||||
>
|
||||
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-base-content">
|
||||
<label className="text-sm ml-2">Webhook</label>
|
||||
<input
|
||||
value={webhook}
|
||||
onChange={(e) => setWebhook(e.target.value)}
|
||||
placeholder="Enter webhook url"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-3" />
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className="op-btn op-btn-primary ml-[2px]"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
<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-base-content">
|
||||
<label className="text-sm ml-2">Webhook</label>
|
||||
<input
|
||||
value={webhook}
|
||||
onChange={(e) => setWebhook(e.target.value)}
|
||||
placeholder="Enter webhook url"
|
||||
className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs"
|
||||
/>
|
||||
</div>
|
||||
<hr className="bg-[#ccc] mt-3" />
|
||||
<div className="flex items-center mt-3 gap-2">
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
className="op-btn op-btn-primary ml-[2px]"
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
<button
|
||||
onClick={handleModal}
|
||||
className="op-btn op-btn-secondary"
|
||||
>
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalUi>
|
||||
</div>
|
||||
{!isSubscribe && isEnableSubscription && (
|
||||
<div data-tut="webhooksubscribe">
|
||||
<SubscribeCard />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Tour
|
||||
onRequestClose={() => setIsTour(false)}
|
||||
steps={tourSteps}
|
||||
isOpen={isTour}
|
||||
closeWithMask={false}
|
||||
disableKeyboardNavigation={["esc"]}
|
||||
scrollOffset={-100}
|
||||
rounded={5}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||